scriptDirs,
- ResourceFile scriptFile, HelpLocation help) {
+ ResourceFile scriptFile, GhidraScriptProvider scriptProvider, HelpLocation help) {
super(title, true, true, true, false);
this.componentProvider = componentProvider;
- this.provider = GhidraScriptUtil.getProvider(scriptFile);
+ this.provider = scriptProvider;
this.scriptFile = scriptFile;
this.paths = new ArrayList<>(scriptDirs);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/SaveNewScriptDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/SaveNewScriptDialog.java
index 3551c56f40..0a38d46f82 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/SaveNewScriptDialog.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/SaveNewScriptDialog.java
@@ -19,14 +19,15 @@ import java.awt.Component;
import java.io.File;
import generic.jar.ResourceFile;
+import ghidra.app.script.GhidraScriptProvider;
import ghidra.util.HelpLocation;
class SaveNewScriptDialog extends SaveDialog {
SaveNewScriptDialog(Component parent, String title,
GhidraScriptComponentProvider componentProvider, ResourceFile scriptFile,
- HelpLocation help) {
- super(parent, title, componentProvider, scriptFile, help);
+ GhidraScriptProvider scriptProvider, HelpLocation help) {
+ super(parent, title, componentProvider, scriptFile, scriptProvider, help);
}
/**
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/AbstractPythonScriptProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/AbstractPythonScriptProvider.java
new file mode 100644
index 0000000000..4db3bc398d
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/AbstractPythonScriptProvider.java
@@ -0,0 +1,99 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.script;
+
+import java.io.*;
+import java.util.regex.Pattern;
+
+import generic.jar.ResourceFile;
+
+/**
+ * An abstract {@link GhidraScriptProvider} used to provide common functionality to different
+ * types of Python script implementations
+ */
+public abstract class AbstractPythonScriptProvider extends GhidraScriptProvider {
+
+ private static final Pattern BLOCK_COMMENT = Pattern.compile("'''");
+
+ @Override
+ public abstract String getDescription();
+
+ @Override
+ public abstract String getRuntimeEnvironmentName();
+
+ @Override
+ public abstract GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer)
+ throws GhidraScriptLoadException;
+
+ @Override
+ public void createNewScript(ResourceFile newScript, String category) throws IOException {
+ try (PrintWriter writer = new PrintWriter(new FileWriter(newScript.getFile(false)))) {
+ writeHeader(writer, category);
+ writer.println("");
+ writeBody(writer);
+ writer.println("");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * In Python this is a triple single quote sequence, "'''".
+ *
+ * @return the Pattern for Python block comment openings
+ */
+ @Override
+ public Pattern getBlockCommentStart() {
+ return BLOCK_COMMENT;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * In Python this is a triple single quote sequence, "'''".
+ *
+ * @return the Pattern for Python block comment openings
+ */
+ @Override
+ public Pattern getBlockCommentEnd() {
+ return BLOCK_COMMENT;
+ }
+
+ @Override
+ public String getCommentCharacter() {
+ return "#";
+ }
+
+ @Override
+ protected String getCertifyHeaderStart() {
+ return "## ###";
+ }
+
+ @Override
+ protected String getCertificationBodyPrefix() {
+ return "#";
+ }
+
+ @Override
+ protected String getCertifyHeaderEnd() {
+ return "##";
+ }
+
+ @Override
+ public String getExtension() {
+ return ".py";
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java
index a68f214945..8d0a5cb1c9 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java
@@ -105,6 +105,19 @@ public abstract class GhidraScriptProvider
public abstract void createNewScript(ResourceFile newScript, String category)
throws IOException;
+ /**
+ * Returns an optional runtime environment name of a {@link GhidraScriptProvider} that scripts
+ * can specify they require to run under. Useful for when more than one
+ * {@link GhidraScriptProvider} uses the same file extension.
+ *
+ * @return an optional runtime environment name of a {@link GhidraScriptProvider} that scripts
+ * can specify they require to run under (could be null if there is no requirement)
+ * @see ScriptInfo#AT_RUNTIME
+ */
+ public String getRuntimeEnvironmentName() {
+ return null;
+ }
+
/**
* Returns a Pattern that matches block comment openings.
*
@@ -161,6 +174,9 @@ public abstract class GhidraScriptProvider
if (metadataItem.equals(ScriptInfo.AT_CATEGORY)) {
writer.print(category);
}
+ else if (metadataItem.equals(ScriptInfo.AT_RUNTIME)) {
+ writer.print(getRuntimeEnvironmentName());
+ }
writer.println("");
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java
index 542097febf..180b024e29 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java
@@ -276,29 +276,31 @@ public class GhidraScriptUtil {
}
/**
- * Returns a list of all Ghidra script providers
+ * Returns a list of all supported Ghidra script providers
*
- * @return a list of all Ghidra script providers
+ * @return a list of all supported Ghidra script providers
*/
// Note: this method is synchronized so that two threads do not try to create the list when null
public static synchronized List getProviders() {
if (providers == null) {
- List newProviders =
- new ArrayList<>(ClassSearcher.getInstances(GhidraScriptProvider.class));
- Collections.sort(newProviders);
- providers = newProviders;
+ providers = ClassSearcher.getInstances(GhidraScriptProvider.class)
+ .stream()
+ .filter(p -> !(p instanceof UnsupportedScriptProvider))
+ .sorted()
+ .toList();
}
return providers;
}
/**
- * Returns the corresponding Ghidra script providers
- * for the specified script file.
+ * Returns the corresponding Ghidra script provider for the specified script file.
+ *
* @param scriptFile the script file
- * @return the Ghidra script provider
+ * @return the Ghidra script provider or {@link UnsupportedScriptProvider} if the script file
+ * does not exist or no provider matches
*/
public static GhidraScriptProvider getProvider(ResourceFile scriptFile) {
- return findProvider(scriptFile.getName());
+ return findProvider(scriptFile);
}
/**
@@ -308,11 +310,46 @@ public class GhidraScriptUtil {
* @return true if a provider exists that can process the specified file
*/
public static boolean hasScriptProvider(ResourceFile scriptFile) {
- return findProvider(scriptFile.getName()) != null;
+ return findProvider(scriptFile) != null;
}
/**
- * Find the provider whose extension matches the given filename extension.
+ * Find the first provider whose extension matches the given file's extension and whose
+ * {@link ScriptInfo#AT_RUNTIME} matches
+ *
+ * @param scriptFile the script file (not guaranteed to exist if this method is called because
+ * the script manager is creating a new script and all it has to go off of initially is
+ * the desired file extension...in this case there will not be a @runtime tag yet)
+ * @return the matching provider or null if no provider matches
+ */
+ private static GhidraScriptProvider findProvider(ResourceFile scriptFile) {
+ GhidraScriptProvider baseProvider = null;
+ String fileName = scriptFile.getName().toLowerCase();
+ for (GhidraScriptProvider provider : getProviders()) {
+ String extension = provider.getExtension().toLowerCase();
+ if (fileName.endsWith(extension)) {
+ baseProvider = provider;
+ if (!scriptFile.exists()) {
+ // Use UnsupportedScriptProvider. The provider will be updated later when
+ // the file actually exists and we can properly look for an @runtime tag
+ // (or confirm that one is not defined)
+ break;
+ }
+ String runtime = new ScriptInfo(provider, scriptFile).getRuntimeEnvironmentName();
+ if (runtime == null ||
+ runtime.equalsIgnoreCase(provider.getRuntimeEnvironmentName())) {
+ return provider;
+ }
+ }
+ }
+ if (baseProvider != null) {
+ return new UnsupportedScriptProvider(baseProvider);
+ }
+ return null;
+ }
+
+ /**
+ * Find the first provider whose extension matches the given filename extension.
*
* @param fileName name of script file
* @return the first matching provider or null if no provider matches
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java
index e66ee56237..809180208f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java
@@ -69,6 +69,11 @@ public class JavaScriptProvider extends GhidraScriptProvider {
return ".java";
}
+ @Override
+ public String getRuntimeEnvironmentName() {
+ return "Java";
+ }
+
@Override
public boolean deleteScript(ResourceFile sourceFile) {
try {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java
index 324a51bfb0..786666dd45 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java
@@ -46,12 +46,13 @@ public class ScriptInfo {
static final String AT_KEYBINDING = "@keybinding";
static final String AT_MENUPATH = "@menupath";
static final String AT_TOOLBAR = "@toolbar";
+ static final String AT_RUNTIME = "@runtime";
// omit from METADATA to avoid pre-populating in new scripts
private static final String AT_IMPORTPACKAGE = "@importpackage";
public static final String[] METADATA =
- { AT_AUTHOR, AT_CATEGORY, AT_KEYBINDING, AT_MENUPATH, AT_TOOLBAR, };
+ { AT_AUTHOR, AT_CATEGORY, AT_KEYBINDING, AT_MENUPATH, AT_TOOLBAR, AT_RUNTIME };
private GhidraScriptProvider provider;
private ResourceFile sourceFile;
@@ -68,6 +69,7 @@ public class ScriptInfo {
private String toolbar;
private ImageIcon toolbarImage;
private String importpackage;
+ private String runtime;
/**
* Constructs a new script.
@@ -94,6 +96,7 @@ public class ScriptInfo {
toolbarImage = null;
importpackage = null;
keybindingErrorMessage = null;
+ runtime = null;
}
/**
@@ -129,6 +132,26 @@ public class ScriptInfo {
return author;
}
+ /**
+ * Returns the name of the required runtime environment
+ * @return the name of the required runtime environment
+ * @see GhidraScriptProvider#getRuntimeEnvironmentName()
+ */
+ public String getRuntimeEnvironmentName() {
+ parseHeader();
+ return runtime;
+ }
+
+ /**
+ * Returns the {@link GhidraScriptProvider} currently associated with the script
+ * @return The {@link GhidraScriptProvider} currently associated with the script
+ */
+ public GhidraScriptProvider getProvider() {
+ parseHeader();
+ provider = GhidraScriptUtil.getProvider(sourceFile);
+ return provider;
+ }
+
/**
* Returns true if the script has compile errors.
* @return true if the script has compile errors
@@ -163,6 +186,16 @@ public class ScriptInfo {
this.isDuplicate = isDuplicate;
}
+ /**
+ * Returns true if this script has an {@link UnsupportedScriptProvider}. This will typically
+ * happen when a script defines a wrong {@link ScriptInfo#AT_RUNTIME} tag.
+ *
+ * @return True if this script has an {@link UnsupportedScriptProvider}; otherwise, false
+ */
+ public boolean hasUnsupportedProvider() {
+ return provider instanceof UnsupportedScriptProvider;
+ }
+
/**
* Returns the script description.
* @return the script description
@@ -329,6 +362,9 @@ public class ScriptInfo {
else if (line.startsWith(AT_IMPORTPACKAGE)) {
importpackage = getTagValue(AT_IMPORTPACKAGE, line);
}
+ else if (line.startsWith(AT_RUNTIME)) {
+ runtime = getTagValue(AT_RUNTIME, line);
+ }
}
catch (Exception e) {
Msg.debug(this, "Unexpected exception reading script metadata " + "line: " + line, e);
@@ -514,6 +550,7 @@ public class ScriptInfo {
String htmlCategory = bold("Category:") + space + escapeHTML(toString(category));
String htmlKeyBinding = bold("Key Binding:") + space + getKeybindingToolTip();
String htmlMenuPath = bold("Menu Path:") + space + escapeHTML(toString(menupath));
+ String htmlRuntime = bold("Runtime Environment:") + space + escapeHTML(toString(runtime));
StringBuilder buffer = new StringBuilder();
buffer.append("").append(space).append(escapeHTML(getName())).append("
");
@@ -529,6 +566,8 @@ public class ScriptInfo {
buffer.append(HTML_NEW_LINE);
buffer.append(space).append(htmlMenuPath);
buffer.append(HTML_NEW_LINE);
+ buffer.append(space).append(htmlRuntime);
+ buffer.append(HTML_NEW_LINE);
buffer.append(HTML_NEW_LINE);
return wrapAsHTML(buffer.toString());
}
@@ -560,7 +599,7 @@ public class ScriptInfo {
* @return true if the script either has compiler errors, or is a duplicate
*/
public boolean hasErrors() {
- return isCompileErrors() || isDuplicate();
+ return isCompileErrors() || isDuplicate() || hasUnsupportedProvider();
}
/**
@@ -575,6 +614,10 @@ public class ScriptInfo {
return "Script is a duplicate of another script";
}
+ if (hasUnsupportedProvider()) {
+ return "Script's @runtime tag specifies an unsupported runtime environment";
+ }
+
return null;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/UnsupportedScriptProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/UnsupportedScriptProvider.java
new file mode 100644
index 0000000000..bf9720c20b
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/UnsupportedScriptProvider.java
@@ -0,0 +1,97 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.script;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.regex.Pattern;
+
+import generic.jar.ResourceFile;
+
+/**
+ * A stub provider for unsupported scripts. These will typically be scripts with supported
+ * extensions but unsupported {@link ScriptInfo#AT_RUNTIME} tags.
+ */
+public class UnsupportedScriptProvider extends GhidraScriptProvider {
+
+ private GhidraScriptProvider baseProvider;
+
+ public UnsupportedScriptProvider() {
+ // Necessary for instantiation from the ClassSearcher
+ }
+
+ /**
+ * Creates a new {@link UnsupportedScriptProvider} that is derived from the given base provider.
+ * The base provider is any provider with a compatible extension, but without the required
+ * {@link ScriptInfo#AT_RUNTIME} tag.
+ *
+ * @param baseProvider The base {@link GhidraScriptProvider}
+ */
+ public UnsupportedScriptProvider(GhidraScriptProvider baseProvider) {
+ this.baseProvider = baseProvider;
+ }
+
+ @Override
+ public String getDescription() {
+ return "";
+ }
+
+ @Override
+ public String getExtension() {
+ return baseProvider.getExtension();
+ }
+
+ @Override
+ public GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer)
+ throws GhidraScriptLoadException {
+ throw new GhidraScriptLoadException("Script is not supported.");
+ }
+
+ @Override
+ public void createNewScript(ResourceFile newScript, String category) throws IOException {
+ // Do nothing
+ }
+
+ @Override
+ public String getCommentCharacter() {
+ return baseProvider.getCommentCharacter();
+ }
+
+ @Override
+ public Pattern getBlockCommentStart() {
+ return baseProvider.getBlockCommentStart();
+ }
+
+ @Override
+ public Pattern getBlockCommentEnd() {
+ return baseProvider.getBlockCommentEnd();
+ }
+
+ @Override
+ protected String getCertifyHeaderStart() {
+ return baseProvider.getCertifyHeaderStart();
+ }
+
+ @Override
+ protected String getCertificationBodyPrefix() {
+ return baseProvider.getCertificationBodyPrefix();
+ }
+
+ @Override
+ protected String getCertifyHeaderEnd() {
+ return baseProvider.getCertifyHeaderEnd();
+ }
+}
diff --git a/Ghidra/Features/Jython/ghidra_scripts/AddCommentToProgramScriptPy.py b/Ghidra/Features/Jython/ghidra_scripts/AddCommentToProgramScriptPy.py
index d85657ca4f..0d9f112a53 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/AddCommentToProgramScriptPy.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/AddCommentToProgramScriptPy.py
@@ -19,6 +19,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
+#@runtime Jython
from ghidra.program.model.address.Address import *
diff --git a/Ghidra/Features/Jython/ghidra_scripts/AskScriptPy.py b/Ghidra/Features/Jython/ghidra_scripts/AskScriptPy.py
index 61f79eb33f..9edac8366b 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/AskScriptPy.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/AskScriptPy.py
@@ -22,6 +22,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
+#@runtime Jython
from ghidra.framework.model import DomainFile
from ghidra.framework.model import DomainFolder
diff --git a/Ghidra/Features/Jython/ghidra_scripts/CallAnotherScriptForAllProgramsPy.py b/Ghidra/Features/Jython/ghidra_scripts/CallAnotherScriptForAllProgramsPy.py
index 82c32790d2..e0225381f8 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/CallAnotherScriptForAllProgramsPy.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/CallAnotherScriptForAllProgramsPy.py
@@ -21,6 +21,7 @@
# NOTE: Script will only process unversioned and checked-out files.
#@category Examples.Python
+#@runtime Jython
from ghidra.app.script import GhidraState
from ghidra.framework.model import *
diff --git a/Ghidra/Features/Jython/ghidra_scripts/CallAnotherScriptPy.py b/Ghidra/Features/Jython/ghidra_scripts/CallAnotherScriptPy.py
index c0d423af61..088323c84b 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/CallAnotherScriptPy.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/CallAnotherScriptPy.py
@@ -19,6 +19,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
+#@runtime Jython
runScript("HelloWorldScript.java")
runScript("HelloWorldPopupScript.java")
diff --git a/Ghidra/Features/Jython/ghidra_scripts/ChooseDataTypeScriptPy.py b/Ghidra/Features/Jython/ghidra_scripts/ChooseDataTypeScriptPy.py
index be36f8fdf9..9153659da4 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/ChooseDataTypeScriptPy.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/ChooseDataTypeScriptPy.py
@@ -19,6 +19,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
+#@runtime Jython
from ghidra.app.util.datatype import DataTypeSelectionDialog
from ghidra.framework.plugintool import PluginTool
diff --git a/Ghidra/Features/Jython/ghidra_scripts/ExampleColorScriptPy.py b/Ghidra/Features/Jython/ghidra_scripts/ExampleColorScriptPy.py
index 39da09d7ce..41728659d7 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/ExampleColorScriptPy.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/ExampleColorScriptPy.py
@@ -19,6 +19,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
+#@runtime Jython
from ghidra.app.plugin.core.colorizer import ColorizingService
from ghidra.app.script import GhidraScript
diff --git a/Ghidra/Features/Jython/ghidra_scripts/FormatExampleScriptPy.py b/Ghidra/Features/Jython/ghidra_scripts/FormatExampleScriptPy.py
index 01192093a3..6a38017e21 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/FormatExampleScriptPy.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/FormatExampleScriptPy.py
@@ -20,6 +20,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
+#@runtime Jython
from time import *
import java.util.Calendar
diff --git a/Ghidra/Features/Jython/ghidra_scripts/ImportSymbolsScript.py b/Ghidra/Features/Jython/ghidra_scripts/ImportSymbolsScript.py
index 20802dcbeb..ae4a502483 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/ImportSymbolsScript.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/ImportSymbolsScript.py
@@ -24,6 +24,7 @@
# Omitting the address space or memory region specifier from the address will result in the function or label being created in the default address space.
# @author unkown; edited by matedealer
# @category Data
+# @runtime Jython
#
from ghidra.program.model.symbol.SourceType import *
diff --git a/Ghidra/Features/Jython/ghidra_scripts/PrintNonZeroPurge.py b/Ghidra/Features/Jython/ghidra_scripts/PrintNonZeroPurge.py
index a92323676a..4b6d53d2d2 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/PrintNonZeroPurge.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/PrintNonZeroPurge.py
@@ -14,6 +14,7 @@
# limitations under the License.
##
# Prints out all the functions in the program that have a non-zero stack purge size
+# @runtime Jython
for func in currentProgram.getFunctionManager().getFunctions(currentProgram.evaluateAddress("0"), 1):
if func.getStackPurgeSize() != 0:
diff --git a/Ghidra/Features/Jython/ghidra_scripts/ToolPropertiesExampleScriptPy.py b/Ghidra/Features/Jython/ghidra_scripts/ToolPropertiesExampleScriptPy.py
index ff082b2ea6..6216ab354e 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/ToolPropertiesExampleScriptPy.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/ToolPropertiesExampleScriptPy.py
@@ -18,7 +18,8 @@
# DISCLAIMER: This is a recreation of a Java Ghidra script for example
# use only. Please run the Java version in a production environment.
-#@category Examples.Python
+#@category Examples.Python
+#@runtime Jython
from ghidra.framework.options import Options
from ghidra.framework.plugintool import PluginTool
diff --git a/Ghidra/Features/Jython/ghidra_scripts/external_module_callee.py b/Ghidra/Features/Jython/ghidra_scripts/external_module_callee.py
index 0444128956..89ad8757d5 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/external_module_callee.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/external_module_callee.py
@@ -15,6 +15,7 @@
##
# Example of being imported by a Ghidra Python script/module
# @category: Examples.Python
+# @runtime Jython
# The following line will fail if this module is imported from external_module_caller.py,
# because only the script that gets directly launched by Ghidra inherits fields and methods
diff --git a/Ghidra/Features/Jython/ghidra_scripts/external_module_caller.py b/Ghidra/Features/Jython/ghidra_scripts/external_module_caller.py
index cbe32a5b86..40b08e1681 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/external_module_caller.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/external_module_caller.py
@@ -15,6 +15,7 @@
##
# Example of importing an external Ghidra Python module
# @category: Examples.Python
+# @runtime Jython
# Import the external module that wants to access the Ghidra scripting API.
# NOTE: see external_module_callee.py for additional tips.
diff --git a/Ghidra/Features/Jython/ghidra_scripts/ghidra_basics.py b/Ghidra/Features/Jython/ghidra_scripts/ghidra_basics.py
index 8828e19c10..c120e8a7a7 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/ghidra_basics.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/ghidra_basics.py
@@ -15,6 +15,7 @@
##
# Examples of basic Ghidra scripting in Python
# @category: Examples.Python
+# @runtime Jython
# Get info about the current program
print
diff --git a/Ghidra/Features/Jython/ghidra_scripts/jython_basics.py b/Ghidra/Features/Jython/ghidra_scripts/jython_basics.py
index 3dc3cd8347..503fd2adad 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/jython_basics.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/jython_basics.py
@@ -15,6 +15,7 @@
##
# Examples of Jython-specific functionality
# @category: Examples.Python
+# @runtime Jython
# Using Java data structures from Jython
python_list = [1, 2, 3]
diff --git a/Ghidra/Features/Jython/ghidra_scripts/python_basics.py b/Ghidra/Features/Jython/ghidra_scripts/python_basics.py
index dff8d2f2b5..71ee658cd1 100644
--- a/Ghidra/Features/Jython/ghidra_scripts/python_basics.py
+++ b/Ghidra/Features/Jython/ghidra_scripts/python_basics.py
@@ -15,6 +15,7 @@
##
# Examples of basic Python
# @category: Examples.Python
+# @runtime Jython
# Python data types
my_int = 32
diff --git a/Ghidra/Features/Jython/src/main/java/ghidra/jython/JythonScriptProvider.java b/Ghidra/Features/Jython/src/main/java/ghidra/jython/JythonScriptProvider.java
index f1b0cf43ae..c91041bd4b 100644
--- a/Ghidra/Features/Jython/src/main/java/ghidra/jython/JythonScriptProvider.java
+++ b/Ghidra/Features/Jython/src/main/java/ghidra/jython/JythonScriptProvider.java
@@ -15,71 +15,17 @@
*/
package ghidra.jython;
-import java.io.*;
-import java.util.regex.Pattern;
+import java.io.PrintWriter;
import generic.jar.ResourceFile;
import ghidra.app.script.*;
+import ghidra.util.classfinder.ExtensionPointProperties;
-public class JythonScriptProvider extends GhidraScriptProvider {
-
- private static final Pattern BLOCK_COMMENT = Pattern.compile("'''");
-
- @Override
- public void createNewScript(ResourceFile newScript, String category) throws IOException {
- PrintWriter writer = new PrintWriter(new FileWriter(newScript.getFile(false)));
- writeHeader(writer, category);
- writer.println("");
- writeBody(writer);
- writer.println("");
- writer.close();
- }
-
- /**
- * {@inheritDoc}
- *
- *
- * In Jython this is a triple single quote sequence, "'''".
- *
- * @return the Pattern for Jython block comment openings
- */
- @Override
- public Pattern getBlockCommentStart() {
- return BLOCK_COMMENT;
- }
-
- /**
- * {@inheritDoc}
- *
- *
- * In Jython this is a triple single quote sequence, "'''".
- *
- * @return the Pattern for Jython block comment openings
- */
- @Override
- public Pattern getBlockCommentEnd() {
- return BLOCK_COMMENT;
- }
-
- @Override
- public String getCommentCharacter() {
- return "#";
- }
-
- @Override
- protected String getCertifyHeaderStart() {
- return "## ###";
- }
-
- @Override
- protected String getCertificationBodyPrefix() {
- return "#";
- }
-
- @Override
- protected String getCertifyHeaderEnd() {
- return "##";
- }
+/**
+ * A {@link GhidraScriptProvider} used to run Jython scripts
+ */
+@ExtensionPointProperties(priority = 1000) // Enforce high priority so Jython is the default Python provider
+public class JythonScriptProvider extends AbstractPythonScriptProvider {
@Override
public String getDescription() {
@@ -87,8 +33,8 @@ public class JythonScriptProvider extends GhidraScriptProvider {
}
@Override
- public String getExtension() {
- return ".py";
+ public String getRuntimeEnvironmentName() {
+ return "Jython";
}
@Override
@@ -105,4 +51,5 @@ public class JythonScriptProvider extends GhidraScriptProvider {
throw new GhidraScriptLoadException(e);
}
}
+
}
diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java
index 0101856efb..f7ccf0cc71 100644
--- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java
+++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java
@@ -82,7 +82,7 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator
scriptDirs.add(new ResourceFile("/User/home/ghidra_scripts"));
SaveDialog dialog = new SaveDialog(tool.getToolFrame(), "Save Script", provider,
- scriptDirs, scriptFile, helpLocation);
+ scriptDirs, scriptFile, new JavaScriptProvider(), helpLocation);
tool.showDialog(dialog);
}, false);
@@ -208,7 +208,7 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator
scriptDirs.add(new ResourceFile("/User/home/ghidra_scripts"));
SaveDialog dialog = new SaveDialog(tool.getToolFrame(), "Rename Script", provider,
- scriptDirs, scriptFile, helpLocation);
+ scriptDirs, scriptFile, new JavaScriptProvider(), helpLocation);
tool.showDialog(dialog);
}, false);