mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
introduce GhidraBundle, cleanup packages, rename path->status
This commit is contained in:
parent
7873bb32fa
commit
431d7ac752
22 changed files with 466 additions and 441 deletions
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.script.osgi;
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
|
@ -38,33 +38,33 @@ import generic.jar.ResourceFile;
|
|||
import ghidra.app.script.*;
|
||||
|
||||
public class BundleCompiler {
|
||||
|
||||
public static final String GENERATED_ACTIVATOR_CLASSNAME = "GeneratedActivator";
|
||||
private BundleHost bh;
|
||||
|
||||
public BundleCompiler(BundleHost bh) {
|
||||
static final String GENERATED_ACTIVATOR_CLASSNAME = "GeneratedActivator";
|
||||
|
||||
BundleCompiler(BundleHost bh) {
|
||||
this.bh = bh;
|
||||
}
|
||||
|
||||
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
|
||||
/**
|
||||
* compile a source directory to an exploded bundle
|
||||
*
|
||||
* @param sbi the bundle info to build
|
||||
* @param sb the bundle info to build
|
||||
* @param writer for updating the user during compilation
|
||||
* @throws IOException for source/manifest file reading/generation and binary deletion/creation
|
||||
* @throws OSGiException if generation of bundle metadata fails
|
||||
*/
|
||||
public void compileToExplodedBundle(SourceBundleInfo sbi, PrintWriter writer)
|
||||
void compileToExplodedBundle(GhidraSourceBundle sb, PrintWriter writer)
|
||||
throws IOException, OSGiException {
|
||||
|
||||
sbi.compileAttempted();
|
||||
sbi.setSummary(String.format("build %d files, skipping %d%s", sbi.getNewSourcesCount(),
|
||||
sbi.getFailingSourcesCount(), sbi.newManifestFile() ? ", new manifest" : ""));
|
||||
sb.compileAttempted();
|
||||
sb.setSummary(String.format("build %d files, skipping %d%s", sb.getNewSourcesCount(),
|
||||
sb.getFailingSourcesCount(), sb.newManifestFile() ? ", new manifest" : ""));
|
||||
|
||||
ResourceFile srcdir = sbi.getSourceDir();
|
||||
Path bindir = sbi.getBinDir();
|
||||
ResourceFile srcdir = sb.getSourceDir();
|
||||
Path bindir = sb.getBinDir();
|
||||
Files.createDirectories(bindir);
|
||||
|
||||
List<String> options = new ArrayList<>();
|
||||
|
@ -79,7 +79,7 @@ public class BundleCompiler {
|
|||
|
||||
// final JavaFileManager rfm = new ResourceFileJavaFileManager(Collections.singletonList(bi.getSourceDir()));
|
||||
final JavaFileManager rfm =
|
||||
new ResourceFileJavaFileManager(Collections.singletonList(sbi.getSourceDir()));
|
||||
new ResourceFileJavaFileManager(Collections.singletonList(sb.getSourceDir()));
|
||||
|
||||
BundleJavaManager bjm = new BundleJavaManager(bh.getHostFramework(), rfm, options);
|
||||
// The phidias BundleJavaManager is for compiling from within a bundle -- it makes the
|
||||
|
@ -88,7 +88,7 @@ public class BundleCompiler {
|
|||
|
||||
// XXX skip this if there's a source manifest, emit warnings about @imports
|
||||
// get wires for currently active bundles to satisfy all requirements
|
||||
List<BundleRequirement> reqs = sbi.getAllReqs();
|
||||
List<BundleRequirement> reqs = sb.getAllReqs();
|
||||
List<BundleWiring> bundleWirings = bh.resolve(reqs);
|
||||
|
||||
if (!reqs.isEmpty()) {
|
||||
|
@ -98,19 +98,19 @@ public class BundleCompiler {
|
|||
writer.printf(" %s\n", req.toString());
|
||||
}
|
||||
|
||||
sbi.setSummary(
|
||||
sb.setSummary(
|
||||
String.format("%d missing @import%s:", reqs.size(), reqs.size() > 1 ? "s" : "",
|
||||
reqs.stream().flatMap(
|
||||
r -> OSGiUtils.extractPackages(r.toString()).stream()).distinct().collect(
|
||||
Collectors.joining(","))));
|
||||
}
|
||||
else {
|
||||
sbi.setSummary("");
|
||||
sb.setSummary("");
|
||||
}
|
||||
// XXX add sources that will fail to call attention
|
||||
List<ResourceFile> newSource = sbi.getNewSources();
|
||||
List<ResourceFile> newSource = sb.getNewSources();
|
||||
for (BundleRequirement req : reqs) {
|
||||
newSource.addAll(sbi.req2file.get(req.toString()));
|
||||
newSource.addAll(sb.req2file.get(req.toString()));
|
||||
}
|
||||
|
||||
// send the capabilities to phidias
|
||||
|
@ -142,7 +142,7 @@ public class BundleCompiler {
|
|||
writer.write(err);
|
||||
ResourceFileJavaFileObject sf = (ResourceFileJavaFileObject) d.getSource();
|
||||
ResourceFile rf = sf.getFile();
|
||||
sbi.buildError(rf, err); // remember all errors for this file
|
||||
sb.buildError(rf, err); // remember all errors for this file
|
||||
if (sourceFiles.remove(sf)) {
|
||||
writer.printf("skipping %s\n", sf.toString());
|
||||
// if it's a script, mark it for having compile errors
|
||||
|
@ -162,9 +162,8 @@ public class BundleCompiler {
|
|||
}
|
||||
}
|
||||
// buildErrors is now up to date, set status
|
||||
if (sbi.getFailingSourcesCount() > 0) {
|
||||
sbi.appendSummary(
|
||||
String.format("%d failing source files", sbi.getFailingSourcesCount()));
|
||||
if (sb.getFailingSourcesCount() > 0) {
|
||||
sb.appendSummary(String.format("%d failing source files", sb.getFailingSourcesCount()));
|
||||
}
|
||||
|
||||
ResourceFile smf = new ResourceFile(srcdir, "META-INF" + File.separator + "MANIFEST.MF");
|
||||
|
@ -203,7 +202,7 @@ public class BundleCompiler {
|
|||
manifest = analyzer.calcManifest();
|
||||
}
|
||||
catch (Exception e) {
|
||||
sbi.appendSummary("bad manifest");
|
||||
sb.appendSummary("bad manifest");
|
||||
throw new OSGiException("failed to calculate manifest by analyzing code", e);
|
||||
}
|
||||
Attributes ma = manifest.getMainAttributes();
|
||||
|
@ -219,22 +218,24 @@ public class BundleCompiler {
|
|||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
sbi.appendSummary("failed bnd analysis");
|
||||
sb.appendSummary("failed bnd analysis");
|
||||
throw new OSGiException("failed to query classes while searching for activator", e);
|
||||
}
|
||||
if (activator_classname == null) {
|
||||
activator_classname = GENERATED_ACTIVATOR_CLASSNAME;
|
||||
if (!buildDefaultActivator(bindir, activator_classname, writer)) {
|
||||
sbi.appendSummary("failed to build generated activator");
|
||||
sb.appendSummary("failed to build generated activator");
|
||||
return;
|
||||
}
|
||||
// since we add the activator after bndtools built the imports, we should add its imports too
|
||||
String imps = ma.getValue(Constants.IMPORT_PACKAGE);
|
||||
if (imps == null) {
|
||||
ma.putValue(Constants.IMPORT_PACKAGE, "ghidra.app.script.osgi");
|
||||
ma.putValue(Constants.IMPORT_PACKAGE,
|
||||
GhidraBundleActivator.class.getPackageName());
|
||||
}
|
||||
else {
|
||||
ma.putValue(Constants.IMPORT_PACKAGE, imps + ",ghidra.app.script.osgi");
|
||||
ma.putValue(Constants.IMPORT_PACKAGE,
|
||||
imps + "," + GhidraBundleActivator.class.getPackageName());
|
||||
}
|
||||
}
|
||||
ma.putValue(Constants.BUNDLE_ACTIVATOR, activator_classname);
|
||||
|
@ -265,7 +266,7 @@ public class BundleCompiler {
|
|||
|
||||
try (PrintWriter out =
|
||||
new PrintWriter(Files.newBufferedWriter(activator_dest, Charset.forName("UTF-8")))) {
|
||||
out.println("import ghidra.app.script.osgi.GhidraBundleActivator;");
|
||||
out.println("import " + GhidraBundleActivator.class.getName() + ";");
|
||||
out.println("import org.osgi.framework.BundleActivator;");
|
||||
out.println("import org.osgi.framework.BundleContext;");
|
||||
out.println("public class " + GENERATED_ACTIVATOR_CLASSNAME +
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.script.osgi;
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
||||
|
@ -35,15 +35,13 @@ import org.osgi.framework.launch.Framework;
|
|||
import org.osgi.framework.wiring.*;
|
||||
import org.osgi.service.log.*;
|
||||
|
||||
import generic.io.NullPrintWriter;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.*;
|
||||
|
||||
// XXX this class should be part of a service/plugin
|
||||
public class BundleHost {
|
||||
// XXX embedded OSGi should be a service, but ScriptProviders don't have any way to access services
|
||||
// XXX ScriptProviders don't have any way to access services or other way to access system wide resources
|
||||
static private BundleHost _instance;
|
||||
|
||||
static public BundleHost getInstance() {
|
||||
|
@ -69,16 +67,28 @@ public class BundleHost {
|
|||
}
|
||||
}
|
||||
|
||||
// XXX this should be remembered in bundlehosts's savestate
|
||||
HashMap<ResourceFile, SourceBundleInfo> file2sbi = new HashMap<>();
|
||||
// XXX this should be remembered by bundlehosts's savestate
|
||||
HashMap<ResourceFile, GhidraBundle> file2sbi = new HashMap<>();
|
||||
|
||||
public SourceBundleInfo getSourceBundleInfo(ResourceFile sourceDir) {
|
||||
return file2sbi.computeIfAbsent(sourceDir, sd -> new SourceBundleInfo(this, sd));
|
||||
public GhidraBundle getGhidraBundle(ResourceFile path) {
|
||||
return file2sbi.computeIfAbsent(path, this::createGhidraBundle);
|
||||
}
|
||||
|
||||
public GhidraBundle createGhidraBundle(ResourceFile path) {
|
||||
switch (GhidraBundle.getType(path)) {
|
||||
case SourceDir:
|
||||
return new GhidraSourceBundle(this, path);
|
||||
case BndScript:
|
||||
case Jar:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// XXX consumers must clean up after themselves
|
||||
public void removeSourceBundleInfo(ResourceFile sourceDir) {
|
||||
file2sbi.remove(sourceDir);
|
||||
public void removeSourceBundleInfo(ResourceFile path) {
|
||||
file2sbi.remove(path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,22 +96,17 @@ public class BundleHost {
|
|||
*
|
||||
* @param imports Import-Package value
|
||||
* @return deduced requirements or null if there was an error
|
||||
* @throws OSGiException on parse failure
|
||||
* @throws BundleException on parse failure
|
||||
*/
|
||||
static List<BundleRequirement> parseImports(String imports) throws OSGiException {
|
||||
static List<BundleRequirement> parseImports(String imports) throws BundleException {
|
||||
|
||||
// parse it with Felix's ManifestParser to a list of BundleRequirement objects
|
||||
Map<String, Object> headerMap = new HashMap<>();
|
||||
headerMap.put(Constants.IMPORT_PACKAGE, imports);
|
||||
ManifestParser mp;
|
||||
try {
|
||||
mp = new ManifestParser(null, null, null, headerMap);
|
||||
return mp.getRequirements();
|
||||
}
|
||||
catch (org.osgi.framework.BundleException e) {
|
||||
throw new OSGiException("parsing Import-Package: " + imports, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cache of data corresponding to a source directory that is bound to be an exploded bundle
|
||||
|
@ -415,6 +420,14 @@ public class BundleHost {
|
|||
}
|
||||
}
|
||||
|
||||
public void deactivateSynchronously(String bundleLoc)
|
||||
throws GhidraBundleException, InterruptedException {
|
||||
Bundle b = getBundle(bundleLoc);
|
||||
if (b != null) {
|
||||
deactivateSynchronously(b);
|
||||
}
|
||||
}
|
||||
|
||||
void forceStopFelix() {
|
||||
Task t = new Task("killing felix", false, false, true, true) {
|
||||
@Override
|
||||
|
@ -568,76 +581,9 @@ public class BundleHost {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* compile a source bundle if it's out of sync
|
||||
*
|
||||
* @param sbi the bundle info
|
||||
* @param writer where to write issues
|
||||
* @throws OSGiException if bundle operations fail
|
||||
* @throws IOException if there are issues with the contents of the bundle
|
||||
* @return the activated bundle
|
||||
* @throws InterruptedException if interrupted while waiting for bundle state change
|
||||
*/
|
||||
public boolean compileSourceBundle(SourceBundleInfo sbi, PrintWriter writer)
|
||||
throws OSGiException, IOException, InterruptedException {
|
||||
if (writer == null) {
|
||||
writer = new NullPrintWriter();
|
||||
}
|
||||
|
||||
boolean needsCompile = false;
|
||||
|
||||
sbi.updateFromFilesystem(writer);
|
||||
|
||||
int failing = sbi.getFailingSourcesCount();
|
||||
int newSourcecount = sbi.getNewSourcesCount();
|
||||
|
||||
long lastBundleActivation = 0; // XXX record last bundle activation in bundlestatusmodel
|
||||
if (failing > 0 && (lastBundleActivation > sbi.getLastCompileAttempt())) {
|
||||
needsCompile = true;
|
||||
}
|
||||
|
||||
if (newSourcecount == 0) {
|
||||
if (failing > 0) {
|
||||
writer.printf("%s hasn't changed, with %d file%s failing in previous build(s):\n",
|
||||
sbi.getSourceDir().toString(), failing, failing > 1 ? "s" : "");
|
||||
writer.printf("%s\n", sbi.getPreviousBuildErrors());
|
||||
}
|
||||
if (sbi.newManifestFile()) {
|
||||
needsCompile = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
needsCompile = true;
|
||||
}
|
||||
|
||||
if (needsCompile) {
|
||||
writer.printf("%d new files, %d skipped, %s\n", newSourcecount, failing,
|
||||
sbi.newManifestFile() ? ", new manifest" : "");
|
||||
|
||||
// if there a bundle is currently active, uninstall it
|
||||
Bundle b = sbi.getBundle();
|
||||
if (b != null) {
|
||||
deactivateSynchronously(b);
|
||||
}
|
||||
|
||||
// once we've committed to recompile and regenerate generated classes, delete the old stuff
|
||||
sbi.deleteOldBinaries();
|
||||
|
||||
BundleCompiler bundleCompiler = new BundleCompiler(this);
|
||||
|
||||
long startTime = System.nanoTime();
|
||||
bundleCompiler.compileToExplodedBundle(sbi, writer);
|
||||
long endTime = System.nanoTime();
|
||||
writer.printf("%3.2f seconds compile time.\n", (endTime - startTime) / 1e9);
|
||||
fireSourceBundleCompiled(sbi);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
List<OSGiListener> osgiListeners = new ArrayList<>();
|
||||
|
||||
void fireSourceBundleCompiled(SourceBundleInfo sbi) {
|
||||
void fireSourceBundleCompiled(GhidraSourceBundle sbi) {
|
||||
synchronized (osgiListeners) {
|
||||
for (OSGiListener l : osgiListeners) {
|
||||
l.sourceBundleCompiled(sbi);
|
|
@ -13,67 +13,33 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.script.osgi;
|
||||
|
||||
import java.io.File;
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import generic.util.Path;
|
||||
|
||||
/**
|
||||
* The BundlePath class represents the runtime state and user preferences for OSGi bundles in Ghidra.
|
||||
* The BundleStatus class represents the runtime state and user preferences for OSGi bundles in Ghidra.
|
||||
*/
|
||||
public class BundlePath extends Path {
|
||||
final Type type;
|
||||
public class BundleStatus extends Path {
|
||||
final GhidraBundle.Type type;
|
||||
|
||||
boolean active = false;
|
||||
boolean busy = false;
|
||||
String summary;
|
||||
|
||||
public static enum Type {
|
||||
BndScript, Jar, SourceDir, INVALID
|
||||
}
|
||||
|
||||
static public Type getType(File f) {
|
||||
if (f.isDirectory()) {
|
||||
return Type.SourceDir;
|
||||
}
|
||||
String n = f.getName().toLowerCase();
|
||||
if (n.endsWith(".bnd")) {
|
||||
return Type.BndScript;
|
||||
}
|
||||
if (n.endsWith(".jar")) {
|
||||
return Type.Jar;
|
||||
}
|
||||
return Type.INVALID;
|
||||
}
|
||||
|
||||
static public Type getType(ResourceFile rf) {
|
||||
if (rf.isDirectory()) {
|
||||
return Type.SourceDir;
|
||||
}
|
||||
String n = rf.getName().toLowerCase();
|
||||
if (n.endsWith(".bnd")) {
|
||||
return Type.BndScript;
|
||||
}
|
||||
if (n.endsWith(".jar")) {
|
||||
return Type.Jar;
|
||||
}
|
||||
return Type.INVALID;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
public GhidraBundle.Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
BundlePath(String path, boolean enabled, boolean readonly) {
|
||||
BundleStatus(String path, boolean enabled, boolean readonly) {
|
||||
super(path, enabled, false /*editable */, readonly);
|
||||
type = getType(getPath());
|
||||
type = GhidraBundle.getType(getPath());
|
||||
}
|
||||
|
||||
BundlePath(ResourceFile path, boolean enabled, boolean readonly) {
|
||||
BundleStatus(ResourceFile path, boolean enabled, boolean readonly) {
|
||||
super(path, enabled, false /* editable */, readonly);
|
||||
type = getType(getPath());
|
||||
type = GhidraBundle.getType(getPath());
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
|
@ -109,4 +75,8 @@ public class BundlePath extends Path {
|
|||
return summary;
|
||||
}
|
||||
|
||||
public boolean pathExists() {
|
||||
return exists();
|
||||
}
|
||||
|
||||
}
|
|
@ -14,16 +14,23 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.script.osgi;
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
public interface BundleStatusListener {
|
||||
|
||||
/**
|
||||
* Called when the list of bundle paths changes
|
||||
*/
|
||||
public void bundlesChanged();
|
||||
default public void bundlesChanged() {
|
||||
//
|
||||
}
|
||||
|
||||
public void bundleEnablementChanged(BundlePath path, boolean newValue);
|
||||
public void bundleActivationChanged(BundlePath path, boolean newValue);
|
||||
default public void bundleEnablementChanged(BundleStatus status, boolean newValue) {
|
||||
//
|
||||
}
|
||||
|
||||
default public void bundleActivationChanged(BundleStatus status, boolean newValue) {
|
||||
//
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.script.osgi;
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
@ -25,12 +25,11 @@ import org.osgi.framework.Bundle;
|
|||
import docking.widgets.table.AbstractSortedTableModel;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.osgi.*;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
||||
public class BundleStatusModel extends AbstractSortedTableModel<BundleStatus> {
|
||||
List<Column> columns = new ArrayList<>();
|
||||
|
||||
class Column {
|
||||
|
@ -45,15 +44,15 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
boolean editable(BundlePath path) {
|
||||
boolean editable(BundleStatus status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object getValue(BundlePath path) {
|
||||
Object getValue(BundleStatus status) {
|
||||
return null;
|
||||
}
|
||||
|
||||
void setValue(BundlePath path, Object aValue) {
|
||||
void setValue(BundleStatus status, Object aValue) {
|
||||
throw new RuntimeException(name + " is not editable!");
|
||||
}
|
||||
|
||||
|
@ -61,55 +60,55 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
|
||||
Column enabledColumn = new Column("Enabled", Boolean.class) {
|
||||
@Override
|
||||
boolean editable(BundlePath path) {
|
||||
return path.exists();
|
||||
boolean editable(BundleStatus status) {
|
||||
return status.pathExists();
|
||||
}
|
||||
|
||||
@Override
|
||||
Object getValue(BundlePath path) {
|
||||
return path.isEnabled();
|
||||
Object getValue(BundleStatus status) {
|
||||
return status.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
void setValue(BundlePath path, Object newValue) {
|
||||
path.setEnabled((Boolean) newValue);
|
||||
fireBundleEnablementChanged(path, (Boolean) newValue);
|
||||
void setValue(BundleStatus status, Object newValue) {
|
||||
status.setEnabled((Boolean) newValue);
|
||||
fireBundleEnablementChanged(status, (Boolean) newValue);
|
||||
}
|
||||
};
|
||||
Column activeColumn = new Column("Active", Boolean.class) {
|
||||
@Override
|
||||
boolean editable(BundlePath path) {
|
||||
return path.exists(); // XXX maybe only if it's already enabled
|
||||
boolean editable(BundleStatus status) {
|
||||
return status.pathExists(); // XXX maybe only if it's already enabled
|
||||
}
|
||||
|
||||
@Override
|
||||
Object getValue(BundlePath path) {
|
||||
return path.isActive();
|
||||
Object getValue(BundleStatus status) {
|
||||
return status.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
void setValue(BundlePath path, Object newValue) {
|
||||
path.setActive((Boolean) newValue);
|
||||
fireBundleActivationChanged(path, (Boolean) newValue);
|
||||
void setValue(BundleStatus status, Object newValue) {
|
||||
status.setActive((Boolean) newValue);
|
||||
fireBundleActivationChanged(status, (Boolean) newValue);
|
||||
}
|
||||
};
|
||||
Column typeColumn = new Column("Type", String.class) {
|
||||
@Override
|
||||
Object getValue(BundlePath path) {
|
||||
return path.getType().toString();
|
||||
Object getValue(BundleStatus status) {
|
||||
return status.getType().toString();
|
||||
}
|
||||
};
|
||||
|
||||
Column pathColumn = new Column("Path", BundlePath.class) {
|
||||
Column pathColumn = new Column("Path", String.class) {
|
||||
@Override
|
||||
Object getValue(BundlePath path) {
|
||||
return path;
|
||||
Object getValue(BundleStatus status) {
|
||||
return status.getPath().toString();
|
||||
}
|
||||
};
|
||||
Column summaryColumn = new Column("Summary", BundlePath.class) {
|
||||
Column summaryColumn = new Column("Summary", String.class) {
|
||||
@Override
|
||||
Object getValue(BundlePath path) {
|
||||
return path.getSummary();
|
||||
Object getValue(BundleStatus status) {
|
||||
return status.getSummary();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -127,41 +126,29 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
}
|
||||
|
||||
private BundleStatusProvider provider;
|
||||
private List<BundlePath> paths;
|
||||
private List<BundleStatus> statuses;
|
||||
private BundleHost bundleHost;
|
||||
OSGiListener bundleListener;
|
||||
|
||||
private Map<String, BundlePath> loc2bp = new HashMap<>();
|
||||
private Map<String, BundleStatus> loc2status = new HashMap<>();
|
||||
|
||||
BundlePath getPath(String bundleLocation) {
|
||||
return loc2bp.get(bundleLocation);
|
||||
BundleStatus getStatus(String bundleLocation) {
|
||||
return loc2status.get(bundleLocation);
|
||||
}
|
||||
|
||||
public String getBundleLoc(BundlePath bp) {
|
||||
switch (bp.getType()) {
|
||||
case Jar:
|
||||
return bp.getPath().getAbsolutePath();
|
||||
case SourceDir:
|
||||
SourceBundleInfo bi = bundleHost.getSourceBundleInfo(bp.getPath());
|
||||
return bi.getBundleLoc();
|
||||
case BndScript:
|
||||
// XXX
|
||||
case INVALID:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
public String getBundleLoc(BundleStatus status) {
|
||||
return bundleHost.getGhidraBundle(status.getPath()).getBundleLoc();
|
||||
}
|
||||
|
||||
/**
|
||||
* (re)compute cached mapping from bundleloc to bundlepath
|
||||
*/
|
||||
private void computeCache() {
|
||||
loc2bp.clear();
|
||||
for (BundlePath bp : paths) {
|
||||
String loc = getBundleLoc(bp);
|
||||
loc2status.clear();
|
||||
for (BundleStatus status : statuses) {
|
||||
String loc = getBundleLoc(status);
|
||||
if (loc != null) {
|
||||
loc2bp.put(loc, bp);
|
||||
loc2status.put(loc, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,18 +159,20 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
this.bundleHost = bundleHost;
|
||||
|
||||
// add unmodifiable paths
|
||||
this.paths = GhidraScriptUtil.getSystemScriptPaths().stream().distinct().map(
|
||||
f -> new BundlePath(f, true, true)).collect(Collectors.toList());
|
||||
this.statuses = GhidraScriptUtil.getSystemScriptPaths().stream().distinct().map(
|
||||
f -> new BundleStatus(f, true, true)).collect(Collectors.toList());
|
||||
// add user path
|
||||
this.paths.add(0, new BundlePath(GhidraScriptUtil.getUserScriptDirectory(), true, false));
|
||||
this.statuses.add(0,
|
||||
new BundleStatus(GhidraScriptUtil.getUserScriptDirectory(), true, false));
|
||||
|
||||
computeCache();
|
||||
|
||||
bundleHost.addListener(bundleListener = new OSGiListener() {
|
||||
@Override
|
||||
public void sourceBundleCompiled(SourceBundleInfo sbi) {
|
||||
BundlePath bp = getPath(sbi.getBundleLoc());
|
||||
public void sourceBundleCompiled(GhidraSourceBundle sb) {
|
||||
BundleStatus bp = getStatus(sb.getBundleLoc());
|
||||
if (bp != null) {
|
||||
bp.setSummary(sbi.getSummary());
|
||||
bp.setSummary(sb.getSummary());
|
||||
int row = getRowIndex(bp);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
|
@ -191,7 +180,7 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
|
||||
@Override
|
||||
public void bundleActivationChange(Bundle b, boolean newActivation) {
|
||||
BundlePath bp = getPath(b.getLocation());
|
||||
BundleStatus bp = getStatus(b.getLocation());
|
||||
if (newActivation) {
|
||||
if (bp != null) {
|
||||
bp.setActive(true);
|
||||
|
@ -219,71 +208,70 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
bundleHost.removeListener(bundleListener);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
paths.clear();
|
||||
}
|
||||
|
||||
List<BundlePath> getAllPaths() {
|
||||
return new ArrayList<BundlePath>(paths);
|
||||
}
|
||||
|
||||
public List<ResourceFile> getPaths() {
|
||||
public List<ResourceFile> getEnabledPaths() {
|
||||
List<ResourceFile> list = new ArrayList<>();
|
||||
for (BundlePath path : paths) {
|
||||
if (path.isEnabled()) {
|
||||
list.add(path.getPath());
|
||||
for (BundleStatus status : statuses) {
|
||||
if (status.isEnabled()) {
|
||||
list.add(status.getPath());
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void addPath(BundlePath path) {
|
||||
if (paths.contains(path)) {
|
||||
private void addStatus(BundleStatus path) {
|
||||
if (statuses.contains(path)) {
|
||||
return;
|
||||
}
|
||||
String loc = getBundleLoc(path);
|
||||
if (loc != null) {
|
||||
loc2bp.put(loc, path);
|
||||
loc2status.put(loc, path);
|
||||
}
|
||||
|
||||
int index = paths.size();
|
||||
paths.add(path);
|
||||
int index = statuses.size();
|
||||
statuses.add(path);
|
||||
fireTableRowsInserted(index, index);
|
||||
}
|
||||
|
||||
private BundlePath addNewPath(ResourceFile path, boolean enabled, boolean readonly) {
|
||||
BundlePath p = new BundlePath(path, enabled, readonly);
|
||||
addPath(p);
|
||||
private BundleStatus addNewStatus(ResourceFile path, boolean enabled, boolean readonly) {
|
||||
BundleStatus p = new BundleStatus(path, enabled, readonly);
|
||||
addStatus(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
private BundlePath addNewPath(String path, boolean enabled, boolean readonly) {
|
||||
BundlePath p = new BundlePath(path, enabled, readonly);
|
||||
addPath(p);
|
||||
private BundleStatus addNewStatus(String path, boolean enabled, boolean readonly) {
|
||||
BundleStatus p = new BundleStatus(path, enabled, readonly);
|
||||
addStatus(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* create new BundleStatus objects for each of the given files
|
||||
*
|
||||
* @param files the files.. given...
|
||||
* @param enabled mark them all as enabled
|
||||
* @param readonly mark them all as readonly
|
||||
*/
|
||||
void addNewPaths(List<File> files, boolean enabled, boolean readonly) {
|
||||
for (File f : files) {
|
||||
BundlePath p = new BundlePath(new ResourceFile(f), enabled, readonly);
|
||||
addPath(p);
|
||||
BundleStatus status = new BundleStatus(new ResourceFile(f), enabled, readonly);
|
||||
addStatus(status);
|
||||
}
|
||||
fireBundlesChanged();
|
||||
}
|
||||
|
||||
void remove(int[] selectedRows) {
|
||||
List<BundlePath> list = new ArrayList<>();
|
||||
List<BundleStatus> toRemove = new ArrayList<>();
|
||||
for (int selectedRow : selectedRows) {
|
||||
list.add(paths.get(selectedRow));
|
||||
toRemove.add(statuses.get(selectedRow));
|
||||
}
|
||||
for (BundlePath path : list) {
|
||||
if (!path.isReadOnly()) {
|
||||
paths.remove(path);
|
||||
loc2bp.remove(getBundleLoc(path));
|
||||
for (BundleStatus status : toRemove) {
|
||||
if (!status.isReadOnly()) {
|
||||
statuses.remove(status);
|
||||
loc2status.remove(getBundleLoc(status));
|
||||
}
|
||||
else {
|
||||
Msg.showInfo(this, this.provider.getComponent(), "Unabled to remove path",
|
||||
"System path cannot be removed: " + path.toString());
|
||||
"System path cannot be removed: " + status.toString());
|
||||
}
|
||||
}
|
||||
fireTableDataChanged();
|
||||
|
@ -299,7 +287,7 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return paths.size();
|
||||
return statuses.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -309,8 +297,8 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
BundlePath path = paths.get(rowIndex);
|
||||
return getColumn(columnIndex).editable(path);
|
||||
BundleStatus status = statuses.get(rowIndex);
|
||||
return getColumn(columnIndex).editable(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -320,15 +308,15 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
|
||||
@Override
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||
BundlePath path = paths.get(rowIndex);
|
||||
getColumn(columnIndex).setValue(path, aValue);
|
||||
// XXX: preclude RowObjectSelectionManager.repairSelection
|
||||
BundleStatus status = statuses.get(rowIndex);
|
||||
getColumn(columnIndex).setValue(status, aValue);
|
||||
// XXX: avoid RowObjectSelectionManager.repairSelection by selecting the row we're editing
|
||||
provider.selectRow(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getColumnValueForRow(BundlePath path, int columnIndex) {
|
||||
return getColumn(columnIndex).getValue(path);
|
||||
public Object getColumnValueForRow(BundleStatus status, int columnIndex) {
|
||||
return getColumn(columnIndex).getValue(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -338,12 +326,12 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "BundlePathManagerModel";
|
||||
return BundleStatusModel.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BundlePath> getModelData() {
|
||||
return paths;
|
||||
public List<BundleStatus> getModelData() {
|
||||
return statuses;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -353,7 +341,7 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
*/
|
||||
public boolean enablePath(ResourceFile file) {
|
||||
ResourceFile dir = file.isDirectory() ? file : file.getParentFile();
|
||||
for (BundlePath path : getAllPaths()) {
|
||||
for (BundleStatus path : statuses) {
|
||||
if (path.getPath().equals(dir)) {
|
||||
if (!path.isEnabled()) {
|
||||
path.setEnabled(true);
|
||||
|
@ -364,7 +352,7 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
addNewPath(dir, true, false);
|
||||
addNewStatus(dir, true, false);
|
||||
Preferences.setProperty(BundleStatusProvider.preferenceForLastSelectedBundle,
|
||||
dir.getAbsolutePath());
|
||||
fireBundlesChanged();
|
||||
|
@ -377,8 +365,8 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
* @return true if the bundle is managed and not marked readonly
|
||||
*/
|
||||
public boolean isWriteable(ResourceFile bundle) {
|
||||
Optional<BundlePath> o = paths.stream().filter(
|
||||
bp -> bp.isDirectory() && bp.getPath().equals(bundle)).findFirst();
|
||||
Optional<BundleStatus> o = statuses.stream().filter(
|
||||
status -> status.isDirectory() && status.getPath().equals(bundle)).findFirst();
|
||||
return o.isPresent() && !o.get().isReadOnly();
|
||||
}
|
||||
|
||||
|
@ -387,10 +375,10 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
*
|
||||
* each path is marked editable and non-readonly
|
||||
*
|
||||
* @param testingPaths the paths to use
|
||||
* @param testingPaths the statuses to use
|
||||
*/
|
||||
public void setPathsForTesting(List<String> testingPaths) {
|
||||
this.paths = testingPaths.stream().map(f -> new BundlePath(f, true, false)).collect(
|
||||
this.statuses = testingPaths.stream().map(f -> new BundleStatus(f, true, false)).collect(
|
||||
Collectors.toList());
|
||||
computeCache();
|
||||
fireTableDataChanged();
|
||||
|
@ -403,7 +391,7 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
* @param path the path to insert
|
||||
*/
|
||||
public void insertPathForTesting(String path) {
|
||||
addNewPath(path, true, false);
|
||||
addNewStatus(path, true, false);
|
||||
}
|
||||
|
||||
private ArrayList<BundleStatusListener> listeners = new ArrayList<>();
|
||||
|
@ -430,7 +418,7 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
}
|
||||
}
|
||||
|
||||
void fireBundleEnablementChanged(BundlePath path, boolean newValue) {
|
||||
void fireBundleEnablementChanged(BundleStatus path, boolean newValue) {
|
||||
synchronized (listeners) {
|
||||
for (BundleStatusListener listener : listeners) {
|
||||
listener.bundleEnablementChanged(path, newValue);
|
||||
|
@ -438,7 +426,7 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
}
|
||||
}
|
||||
|
||||
void fireBundleActivationChanged(BundlePath path, boolean newValue) {
|
||||
void fireBundleActivationChanged(BundleStatus path, boolean newValue) {
|
||||
synchronized (listeners) {
|
||||
for (BundleStatusListener listener : listeners) {
|
||||
listener.bundleActivationChanged(path, newValue);
|
||||
|
@ -447,7 +435,7 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Restores the paths from the specified SaveState object.
|
||||
* Restores the statuses from the specified SaveState object.
|
||||
* @param ss the SaveState object
|
||||
*/
|
||||
public void restoreState(SaveState ss) {
|
||||
|
@ -460,49 +448,47 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
|
|||
boolean[] enableArr = ss.getBooleans("BundleStatus_ENABLE", new boolean[pathArr.length]);
|
||||
boolean[] readonlyArr = ss.getBooleans("BundleStatus_READ", new boolean[pathArr.length]);
|
||||
|
||||
List<BundlePath> currentPaths = getAllPaths();
|
||||
clear();
|
||||
List<BundleStatus> currentPaths = new ArrayList<>(statuses);
|
||||
statuses.clear();
|
||||
|
||||
for (int i = 0; i < pathArr.length; i++) {
|
||||
BundlePath currentPath = getPath(pathArr[i], currentPaths);
|
||||
if (currentPath != null) {
|
||||
currentPaths.remove(currentPath);
|
||||
addNewPath(pathArr[i], enableArr[i], readonlyArr[i]);
|
||||
BundleStatus currentStatus = getStatus(pathArr[i], currentPaths);
|
||||
if (currentStatus != null) {
|
||||
currentPaths.remove(currentStatus);
|
||||
addNewStatus(pathArr[i], enableArr[i], readonlyArr[i]);
|
||||
}
|
||||
else if (!readonlyArr[i]) {
|
||||
// skip read-only paths which are not present in the current config
|
||||
// skip read-only statuses which are not present in the current config
|
||||
// This is needed to thin-out old default entries
|
||||
addNewPath(pathArr[i], enableArr[i], readonlyArr[i]);
|
||||
addNewStatus(pathArr[i], enableArr[i], readonlyArr[i]);
|
||||
}
|
||||
}
|
||||
fireBundlesChanged();
|
||||
}
|
||||
|
||||
private static BundlePath getPath(String filepath, List<BundlePath> paths) {
|
||||
for (BundlePath path : paths) {
|
||||
if (filepath.equals(path.getPathAsString())) {
|
||||
return path;
|
||||
private static BundleStatus getStatus(String filepath, List<BundleStatus> statuses) {
|
||||
for (BundleStatus status : statuses) {
|
||||
if (filepath.equals(status.getPathAsString())) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the paths to the specified SaveState object.
|
||||
* Saves the statuses to the specified SaveState object.
|
||||
* @param ss the SaveState object
|
||||
*/
|
||||
public void saveState(SaveState ss) {
|
||||
List<BundlePath> currentPaths = getAllPaths();
|
||||
|
||||
String[] pathArr = new String[currentPaths.size()];
|
||||
boolean[] enableArr = new boolean[currentPaths.size()];
|
||||
boolean[] readonlyArr = new boolean[currentPaths.size()];
|
||||
String[] pathArr = new String[statuses.size()];
|
||||
boolean[] enableArr = new boolean[statuses.size()];
|
||||
boolean[] readonlyArr = new boolean[statuses.size()];
|
||||
|
||||
int index = 0;
|
||||
for (BundlePath path : currentPaths) {
|
||||
pathArr[index] = path.getPathAsString();
|
||||
enableArr[index] = path.isEnabled();
|
||||
readonlyArr[index] = path.isReadOnly();
|
||||
for (BundleStatus status : statuses) {
|
||||
pathArr[index] = status.getPathAsString();
|
||||
enableArr[index] = status.isEnabled();
|
||||
readonlyArr[index] = status.isReadOnly();
|
||||
++index;
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.script.osgi;
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
|
@ -27,12 +27,15 @@ import javax.swing.table.TableColumn;
|
|||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||
import docking.widgets.table.*;
|
||||
import ghidra.app.script.osgi.BundleHost;
|
||||
import ghidra.app.services.ConsoleService;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.filechooser.GhidraFileChooserModel;
|
||||
import ghidra.util.filechooser.GhidraFileFilter;
|
||||
import ghidra.util.task.*;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
|
@ -49,14 +52,29 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
|
|||
private Color selectionColor;
|
||||
private GhidraFileChooser fileChooser;
|
||||
private GhidraFileFilter filter;
|
||||
private final BundleHost bundleHost;
|
||||
|
||||
public void notifyTableChanged() {
|
||||
bundleStatusTable.notifyTableChanged(new TableModelEvent(bundleStatusModel));
|
||||
}
|
||||
|
||||
public BundleStatusProvider(PluginTool tool, String owner, BundleHost bundleHost) {
|
||||
public BundleStatusProvider(PluginTool tool, String owner) {
|
||||
super(tool, "Bundle Status Manager", owner);
|
||||
this.bundleHost = BundleHost.getInstance();
|
||||
this.bundleStatusModel = new BundleStatusModel(this, bundleHost);
|
||||
bundleStatusModel.addListener(new BundleStatusListener() {
|
||||
@Override
|
||||
public void bundleEnablementChanged(BundleStatus status, boolean enabled) {
|
||||
if (!enabled && status.isActive()) {
|
||||
startActivateDeactiveTask(status, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleActivationChanged(BundleStatus status, boolean newValue) {
|
||||
startActivateDeactiveTask(status, newValue);
|
||||
}
|
||||
});
|
||||
|
||||
this.filter = new GhidraFileFilter() {
|
||||
@Override
|
||||
|
@ -66,7 +84,7 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
|
|||
|
||||
@Override
|
||||
public boolean accept(File path, GhidraFileChooserModel model) {
|
||||
return BundlePath.getType(path) != BundlePath.Type.INVALID;
|
||||
return GhidraBundle.getType(path) != GhidraBundle.Type.INVALID;
|
||||
}
|
||||
};
|
||||
this.fileChooser = null;
|
||||
|
@ -108,7 +126,7 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
|
|||
buttonPanel.add(removeButton, gbc);
|
||||
|
||||
bundleStatusTable = new GTable(bundleStatusModel);
|
||||
bundleStatusTable.setName("BUNDLEPATH_TABLE");
|
||||
bundleStatusTable.setName("BUNDLESTATUS_TABLE");
|
||||
bundleStatusTable.setSelectionBackground(selectionColor);
|
||||
bundleStatusTable.setSelectionForeground(Color.BLACK);
|
||||
bundleStatusTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
|
@ -133,9 +151,9 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
|
|||
column.setCellRenderer(new GBooleanCellRenderer() {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
BundlePath path = (BundlePath) data.getRowObject();
|
||||
BundleStatus status = (BundleStatus) data.getRowObject();
|
||||
Component x = super.getTableCellRendererComponent(data);
|
||||
if (path.getBusy()) {
|
||||
if (status.getBusy()) {
|
||||
cb.setVisible(false);
|
||||
cb.setEnabled(false);
|
||||
setHorizontalAlignment(SwingConstants.CENTER);
|
||||
|
@ -156,7 +174,7 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
|
|||
|
||||
FontMetrics fontmetrics = panel.getFontMetrics(panel.getFont());
|
||||
column.setMaxWidth(10 +
|
||||
SwingUtilities.computeStringWidth(fontmetrics, BundlePath.Type.SourceDir.toString()));
|
||||
SwingUtilities.computeStringWidth(fontmetrics, GhidraBundle.Type.SourceDir.toString()));
|
||||
|
||||
column = bundleStatusTable.getColumnModel().getColumn(bundleStatusModel.pathColumn.index);
|
||||
column.setCellRenderer(new GTableCellRenderer() {
|
||||
|
@ -164,15 +182,15 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
|
|||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
JLabel c = (JLabel) super.getTableCellRendererComponent(data);
|
||||
|
||||
BundlePath path = (BundlePath) data.getValue();
|
||||
if (!path.exists()) {
|
||||
BundleStatus status = (BundleStatus) data.getValue();
|
||||
if (!status.pathExists()) {
|
||||
c.setForeground(Color.RED);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
});
|
||||
|
||||
GTableFilterPanel<BundlePath> filterPanel =
|
||||
GTableFilterPanel<BundleStatus> filterPanel =
|
||||
new GTableFilterPanel<>(bundleStatusTable, bundleStatusModel);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(bundleStatusTable);
|
||||
|
@ -272,6 +290,39 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
|
|||
bundleStatusTable.dispose();
|
||||
}
|
||||
|
||||
private void startActivateDeactiveTask(BundleStatus status, boolean activate) {
|
||||
status.setBusy(true);
|
||||
notifyTableChanged();
|
||||
ConsoleService console = getTool().getService(ConsoleService.class);
|
||||
|
||||
new TaskLauncher(new Task((activate ? "Activating" : "Deactivating ") + " bundle...") {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
try {
|
||||
GhidraBundle sb = bundleHost.getGhidraBundle(status.getPath());
|
||||
if (activate) {
|
||||
sb.build(console.getStdErr());
|
||||
bundleHost.activateSynchronously(sb.getBundleLoc());
|
||||
}
|
||||
else { // deactivate
|
||||
bundleHost.deactivateSynchronously(sb.getBundleLoc());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace(console.getStdErr());
|
||||
status.setActive(!activate);
|
||||
|
||||
Msg.showError(this, getComponent(), "bundle activation failed", e.getMessage());
|
||||
}
|
||||
finally {
|
||||
status.setBusy(false);
|
||||
notifyTableChanged();
|
||||
}
|
||||
}
|
||||
}, null, 1000);
|
||||
}
|
||||
|
||||
// XXX workaround for RowObjectSelection.. repair
|
||||
void selectRow(int rowIndex) {
|
||||
bundleStatusTable.selectRow(rowIndex);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import org.osgi.framework.Bundle;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
|
||||
public interface GhidraBundle {
|
||||
|
||||
enum Type {
|
||||
BndScript, Jar, SourceDir, INVALID
|
||||
}
|
||||
|
||||
/**
|
||||
* attempt to build loadable bundle, if possible
|
||||
*
|
||||
* @param writer console for user messages
|
||||
* @return true if build was successful
|
||||
* @throws Exception XXX
|
||||
*/
|
||||
boolean build(PrintWriter writer) throws Exception;
|
||||
|
||||
String getBundleLoc();
|
||||
|
||||
Bundle getBundle() throws GhidraBundleException;
|
||||
|
||||
Bundle install() throws GhidraBundleException;
|
||||
|
||||
String getSummary();
|
||||
|
||||
static GhidraBundle.Type getType(ResourceFile rf) {
|
||||
if (rf.isDirectory()) {
|
||||
return GhidraBundle.Type.SourceDir;
|
||||
}
|
||||
String n = rf.getName().toLowerCase();
|
||||
if (n.endsWith(".bnd")) {
|
||||
return GhidraBundle.Type.BndScript;
|
||||
}
|
||||
if (n.endsWith(".jar")) {
|
||||
return GhidraBundle.Type.Jar;
|
||||
}
|
||||
return GhidraBundle.Type.INVALID;
|
||||
}
|
||||
|
||||
static public GhidraBundle.Type getType(File f) {
|
||||
if (f.isDirectory()) {
|
||||
return GhidraBundle.Type.SourceDir;
|
||||
}
|
||||
String n = f.getName().toLowerCase();
|
||||
if (n.endsWith(".bnd")) {
|
||||
return GhidraBundle.Type.BndScript;
|
||||
}
|
||||
if (n.endsWith(".jar")) {
|
||||
return GhidraBundle.Type.Jar;
|
||||
}
|
||||
return GhidraBundle.Type.INVALID;
|
||||
}
|
||||
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.script.osgi;
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ghidra.app.script.osgi;
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -51,9 +51,9 @@ public class GhidraBundleException extends OSGiException {
|
|||
BundleException be = (BundleException) e;
|
||||
switch (be.getType()) {
|
||||
default:
|
||||
case BundleException.UNSPECIFIED:
|
||||
return "No exception type";
|
||||
|
||||
case BundleException.UNSPECIFIED:
|
||||
return "UNSPECIFIED";
|
||||
/**
|
||||
* The operation was unsupported. This type can be used anywhere a
|
||||
* BundleException can be thrown.
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.script.osgi;
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
|
@ -22,20 +22,23 @@ import java.util.*;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleException;
|
||||
import org.osgi.framework.wiring.BundleRequirement;
|
||||
|
||||
import generic.io.NullPrintWriter;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.osgi.BundleHost.BuildFailure;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.ScriptInfo;
|
||||
import ghidra.app.script.osgi.BundleHost.BuildFailure;
|
||||
|
||||
/**
|
||||
* The SourceBundleInfo class is a cache of information for bundles built from source directories.
|
||||
*/
|
||||
public class SourceBundleInfo {
|
||||
private final BundleHost bundleHost;
|
||||
public class GhidraSourceBundle implements GhidraBundle {
|
||||
final private BundleHost bundleHost;
|
||||
final private ResourceFile sourceDir;
|
||||
final String symbolicName;
|
||||
|
||||
final private String symbolicName;
|
||||
final private Path binDir;
|
||||
final private String bundleLoc;
|
||||
boolean foundNewManifest;
|
||||
|
@ -51,9 +54,9 @@ public class SourceBundleInfo {
|
|||
|
||||
// cached values parsed form @imports tags on default-package source files
|
||||
|
||||
public SourceBundleInfo(BundleHost bundleHost, ResourceFile sourceDir) {
|
||||
public GhidraSourceBundle(BundleHost bundleHost, ResourceFile sourceDirectory) {
|
||||
this.bundleHost = bundleHost;
|
||||
this.sourceDir = sourceDir;
|
||||
this.sourceDir = sourceDirectory;
|
||||
this.symbolicName = BundleHost.getSymbolicNameFromSourceDir(sourceDir);
|
||||
this.binDir = GhidraScriptUtil.getCompiledBundlesDir().resolve(symbolicName);
|
||||
|
||||
|
@ -61,6 +64,12 @@ public class SourceBundleInfo {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* for testing only!!!
|
||||
*
|
||||
* @param sourceFile a ghidra script file
|
||||
* @return the directory its class is compiled to
|
||||
*/
|
||||
static public Path getBindirFromScriptFile(ResourceFile sourceFile) {
|
||||
ResourceFile tmpSourceDir = sourceFile.getParentFile();
|
||||
String tmpSymbolicName = BundleHost.getSymbolicNameFromSourceDir(tmpSourceDir);
|
||||
|
@ -88,6 +97,7 @@ public class SourceBundleInfo {
|
|||
return binDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle install() throws GhidraBundleException {
|
||||
return bundleHost.installFromLoc(getBundleLoc());
|
||||
}
|
||||
|
@ -105,10 +115,9 @@ public class SourceBundleInfo {
|
|||
|
||||
/**
|
||||
* update buildReqs based on \@imports tag in java files from the default package
|
||||
*
|
||||
* @throws OSGiException on failure to parse the \@imports tag
|
||||
* @throws GhidraBundleException on failure to parse the \@imports tag
|
||||
*/
|
||||
private void computeRequirements() throws OSGiException {
|
||||
private void computeRequirements() throws GhidraBundleException {
|
||||
// parse metadata from all Java source in sourceDir
|
||||
buildReqs.clear();
|
||||
req2file.clear();
|
||||
|
@ -119,7 +128,13 @@ public class SourceBundleInfo {
|
|||
ScriptInfo si = GhidraScriptUtil.getScriptInfo(rf);
|
||||
String imps = si.getImports();
|
||||
if (imps != null && !imps.isEmpty()) {
|
||||
List<BundleRequirement> reqs = BundleHost.parseImports(imps);
|
||||
List<BundleRequirement> reqs;
|
||||
try {
|
||||
reqs = BundleHost.parseImports(imps);
|
||||
}
|
||||
catch (BundleException e) {
|
||||
throw new GhidraBundleException(getBundleLoc(), "parsing manifest", e);
|
||||
}
|
||||
buildReqs.put(rf, reqs);
|
||||
for (BundleRequirement req : reqs) {
|
||||
req2file.computeIfAbsent(req.toString(), x -> new ArrayList<>()).add(rf);
|
||||
|
@ -235,16 +250,75 @@ public class SourceBundleInfo {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
public Bundle getBundle() {
|
||||
@Override
|
||||
public Bundle getBundle() throws GhidraBundleException {
|
||||
return bundleHost.getBundle(getBundleLoc());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBundleLoc() {
|
||||
return bundleLoc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean build(PrintWriter writer) throws Exception {
|
||||
if (writer == null) {
|
||||
writer = new NullPrintWriter();
|
||||
}
|
||||
|
||||
boolean needsCompile = false;
|
||||
|
||||
updateFromFilesystem(writer);
|
||||
|
||||
int failing = getFailingSourcesCount();
|
||||
int newSourcecount = getNewSourcesCount();
|
||||
|
||||
long lastBundleActivation = 0; // XXX record last bundle activation in bundlestatusmodel
|
||||
if (failing > 0 && (lastBundleActivation > getLastCompileAttempt())) {
|
||||
needsCompile = true;
|
||||
}
|
||||
|
||||
if (newSourcecount == 0) {
|
||||
if (failing > 0) {
|
||||
writer.printf("%s hasn't changed, with %d file%s failing in previous build(s):\n",
|
||||
getSourceDir().toString(), failing, failing > 1 ? "s" : "");
|
||||
writer.printf("%s\n", getPreviousBuildErrors());
|
||||
}
|
||||
if (newManifestFile()) {
|
||||
needsCompile = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
needsCompile = true;
|
||||
}
|
||||
|
||||
if (needsCompile) {
|
||||
writer.printf("%d new files, %d skipped, %s\n", newSourcecount, failing,
|
||||
newManifestFile() ? ", new manifest" : "");
|
||||
|
||||
// if there a bundle is currently active, uninstall it
|
||||
Bundle b = getBundle();
|
||||
if (b != null) {
|
||||
bundleHost.deactivateSynchronously(b);
|
||||
}
|
||||
|
||||
// once we've committed to recompile and regenerate generated classes, delete the old stuff
|
||||
deleteOldBinaries();
|
||||
|
||||
BundleCompiler bundleCompiler = new BundleCompiler(bundleHost);
|
||||
|
||||
long startTime = System.nanoTime();
|
||||
bundleCompiler.compileToExplodedBundle(this, writer);
|
||||
long endTime = System.nanoTime();
|
||||
writer.printf("%3.2f seconds compile time.\n", (endTime - startTime) / 1e9);
|
||||
bundleHost.fireSourceBundleCompiled(this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -13,10 +13,10 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.script.osgi;
|
||||
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
|
||||
public class OSGiException extends UsrException {
|
||||
public OSGiException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
|
@ -1,10 +1,10 @@
|
|||
package ghidra.app.script.osgi;
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import org.osgi.framework.Bundle;
|
||||
|
||||
public interface OSGiListener {
|
||||
|
||||
void sourceBundleCompiled(SourceBundleInfo sbi);
|
||||
void sourceBundleCompiled(GhidraSourceBundle sb);
|
||||
|
||||
void bundleActivationChange(Bundle b, boolean newActivation);
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ghidra.app.script.osgi;
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
|
@ -29,7 +29,6 @@ import javax.swing.tree.TreePath;
|
|||
import javax.swing.tree.TreeSelectionModel;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.osgi.framework.Bundle;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.KeyBindingData;
|
||||
|
@ -40,10 +39,8 @@ import docking.widgets.tree.GTree;
|
|||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.support.BreadthFirstIterator;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.script.osgi.*;
|
||||
import ghidra.app.plugin.core.osgi.*;
|
||||
import ghidra.app.script.*;
|
||||
import ghidra.app.script.osgi.BundleHost;
|
||||
import ghidra.app.script.osgi.SourceBundleInfo;
|
||||
import ghidra.app.services.ConsoleService;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
|
@ -99,10 +96,9 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
};
|
||||
|
||||
GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin, BundleHost bundleHost) {
|
||||
GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin) {
|
||||
super(plugin.getTool(), "Script Manager", plugin.getName());
|
||||
this.plugin = plugin;
|
||||
this.bundleHost = bundleHost;
|
||||
|
||||
setHelpLocation(new HelpLocation(plugin.getName(), plugin.getName()));
|
||||
setIcon(ResourceManager.loadImage("images/play.png"));
|
||||
|
@ -116,7 +112,6 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
void dispose() {
|
||||
|
||||
editorMap.clear();
|
||||
scriptCategoryTree.dispose();
|
||||
scriptTable.dispose();
|
||||
|
@ -138,7 +133,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
private void performRefresh() {
|
||||
GhidraScriptUtil.setScriptBundlePaths(bundleStatusProvider.getModel().getPaths());
|
||||
GhidraScriptUtil.setScriptBundlePaths(bundleStatusProvider.getModel().getEnabledPaths());
|
||||
GhidraScriptUtil.clearMetadata();
|
||||
refresh();
|
||||
}
|
||||
|
@ -341,7 +336,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
public List<ResourceFile> getScriptDirectories() {
|
||||
return bundleStatusProvider.getModel().getPaths().stream().filter(
|
||||
return bundleStatusProvider.getModel().getEnabledPaths().stream().filter(
|
||||
ResourceFile::isDirectory).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
@ -397,7 +392,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
void runScript(String scriptName, TaskListener listener) {
|
||||
List<ResourceFile> dirPaths = bundleStatusProvider.getModel().getPaths();
|
||||
List<ResourceFile> dirPaths = bundleStatusProvider.getModel().getEnabledPaths();
|
||||
for (ResourceFile dir : dirPaths) {
|
||||
ResourceFile scriptSource = new ResourceFile(dir, scriptName);
|
||||
if (scriptSource.exists()) {
|
||||
|
@ -457,7 +452,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
console.addErrorMessage("", "Unable to instantiate script: " + scriptName);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
console.addErrorMessage("", "Unable to locate script class: " + e.getMessage());
|
||||
console.addErrorMessage("", "Unable to locate script class: " + scriptName);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// show the error icon
|
||||
|
@ -537,7 +533,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
private void updateAvailableScriptFilesForAllPaths() {
|
||||
List<ResourceFile> scriptsToRemove = tableModel.getScripts();
|
||||
List<ResourceFile> scriptAccumulator = new ArrayList<>();
|
||||
List<ResourceFile> bundlePaths = bundleStatusProvider.getModel().getPaths();
|
||||
List<ResourceFile> bundlePaths = bundleStatusProvider.getModel().getEnabledPaths();
|
||||
for (ResourceFile bundlePath : bundlePaths) {
|
||||
if (bundlePath.isDirectory()) {
|
||||
updateAvailableScriptFilesForDirectory(scriptsToRemove, scriptAccumulator,
|
||||
|
@ -732,56 +728,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
return true;
|
||||
}
|
||||
|
||||
final private BundleHost bundleHost;
|
||||
|
||||
void startActivateDeactiveTask(BundlePath path, boolean activate) {
|
||||
path.setBusy(true);
|
||||
bundleStatusProvider.notifyTableChanged();
|
||||
ConsoleService console = plugin.getTool().getService(ConsoleService.class);
|
||||
|
||||
new TaskLauncher(new Task((activate ? "Activating" : "Deactivating ") + " bundle...") {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
try {
|
||||
if (activate) {
|
||||
if (path.getType() == BundlePath.Type.SourceDir) {
|
||||
SourceBundleInfo sbi = bundleHost.getSourceBundleInfo(path.getPath());
|
||||
bundleHost.compileSourceBundle(sbi, console.getStdErr());
|
||||
}
|
||||
String loc = bundleStatusProvider.getModel().getBundleLoc(path);
|
||||
if (loc != null) {
|
||||
bundleHost.activateSynchronously(loc);
|
||||
}
|
||||
}
|
||||
else { // deactivate
|
||||
String loc = bundleStatusProvider.getModel().getBundleLoc(path);
|
||||
if (loc != null) {
|
||||
Bundle bundle = bundleHost.getBundle(loc);
|
||||
if (bundle != null) {
|
||||
bundleHost.deactivateSynchronously(bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace(console.getStdErr());
|
||||
path.setActive(!activate);
|
||||
|
||||
Msg.showError(this, GhidraScriptComponentProvider.this.getComponent(),
|
||||
"bundle activation failed", e.getMessage());
|
||||
}
|
||||
finally {
|
||||
path.setBusy(false);
|
||||
bundleStatusProvider.notifyTableChanged();
|
||||
}
|
||||
}
|
||||
}, null, 1000);
|
||||
|
||||
}
|
||||
|
||||
private void build() {
|
||||
bundleStatusProvider =
|
||||
new BundleStatusProvider(plugin.getTool(), plugin.getName(), bundleHost);
|
||||
bundleStatusProvider = new BundleStatusProvider(plugin.getTool(), plugin.getName());
|
||||
|
||||
bundleStatusProvider.getModel().addListener(new BundleStatusListener() {
|
||||
@Override
|
||||
|
@ -791,20 +739,11 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void bundleEnablementChanged(BundlePath path, boolean enabled) {
|
||||
if (!enabled && path.isActive()) {
|
||||
startActivateDeactiveTask(path, false);
|
||||
}
|
||||
|
||||
if (path.isDirectory()) {
|
||||
public void bundleEnablementChanged(BundleStatus status, boolean enabled) {
|
||||
if (status.isDirectory()) {
|
||||
performRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleActivationChanged(BundlePath path, boolean newValue) {
|
||||
startActivateDeactiveTask(path, newValue);
|
||||
}
|
||||
});
|
||||
|
||||
scriptRoot = new RootNode();
|
||||
|
@ -1075,7 +1014,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
bundleStatusProvider.getModel().restoreState(saveState);
|
||||
|
||||
// pull in the just-loaded paths
|
||||
List<ResourceFile> paths = bundleStatusProvider.getModel().getPaths();
|
||||
List<ResourceFile> paths = bundleStatusProvider.getModel().getEnabledPaths();
|
||||
GhidraScriptUtil.setScriptBundlePaths(paths);
|
||||
actionManager.restoreUserDefinedKeybindings(saveState);
|
||||
actionManager.restoreScriptsThatAreInTool(saveState);
|
||||
|
@ -1213,7 +1152,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
|
||||
public List<ResourceFile> getWritableScriptDirectories() {
|
||||
BundleStatusModel m = bundleStatusProvider.getModel();
|
||||
return m.getPaths().stream().filter(ResourceFile::isDirectory).filter(
|
||||
return m.getEnabledPaths().stream().filter(ResourceFile::isDirectory).filter(
|
||||
m::isWriteable).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import ghidra.app.plugin.ProgramPlugin;
|
|||
import ghidra.app.plugin.core.eclipse.EclipseConnection;
|
||||
import ghidra.app.plugin.core.eclipse.EclipseIntegrationOptionsPlugin;
|
||||
import ghidra.app.script.GhidraState;
|
||||
import ghidra.app.script.osgi.BundleHost;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
|
@ -51,13 +50,10 @@ public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScript
|
|||
|
||||
final private GhidraScriptComponentProvider provider;
|
||||
|
||||
final private BundleHost bundleHost;
|
||||
|
||||
public GhidraScriptMgrPlugin(PluginTool tool) {
|
||||
super(tool, true, true, true);
|
||||
|
||||
bundleHost = BundleHost.getInstance();
|
||||
provider = new GhidraScriptComponentProvider(this, bundleHost);
|
||||
provider = new GhidraScriptComponentProvider(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -169,7 +169,7 @@ public class GhidraScriptUtil {
|
|||
* @deprecated accessing class file directly precludes OSGi wiring according to requirements and capabilities
|
||||
*/
|
||||
@Deprecated
|
||||
public static List<ResourceFile> getExplodedBundlePaths() {
|
||||
public static List<ResourceFile> getExplodedCompiledSourceBundlePaths() {
|
||||
try {
|
||||
return Files.list(getOsgiDir()).filter(Files::isDirectory).map(
|
||||
x -> new ResourceFile(x.toFile())).collect(Collectors.toList());
|
||||
|
|
|
@ -16,21 +16,20 @@
|
|||
package ghidra.app.script;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import org.osgi.framework.Bundle;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.osgi.*;
|
||||
import ghidra.app.plugin.core.osgi.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class JavaScriptProvider extends GhidraScriptProvider {
|
||||
static public SourceBundleInfo getBundleInfoForSource(ResourceFile sourceFile) {
|
||||
static public GhidraSourceBundle getBundleForSource(ResourceFile sourceFile) {
|
||||
ResourceFile sourceDir = getSourceDirectoryContaining(sourceFile);
|
||||
if (sourceDir == null) {
|
||||
return null;
|
||||
}
|
||||
return BundleHost.getInstance().getSourceBundleInfo(sourceDir);
|
||||
return (GhidraSourceBundle) BundleHost.getInstance().getGhidraBundle(sourceDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,24 +44,23 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||
|
||||
@Override
|
||||
public boolean deleteScript(ResourceFile sourceFile) {
|
||||
Bundle b = getBundleInfoForSource(sourceFile).getBundle();
|
||||
if (b != null) {
|
||||
try {
|
||||
Bundle b = getBundleForSource(sourceFile).getBundle();
|
||||
if (b != null) {
|
||||
BundleHost.getInstance().deactivateSynchronously(b);
|
||||
}
|
||||
}
|
||||
catch (GhidraBundleException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
Msg.error(this, "while stopping script's bundle to delete it", e);
|
||||
Msg.error(this, "while deactivating bundle for delete", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return super.deleteScript(sourceFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer)
|
||||
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
||||
|
||||
try {
|
||||
Class<?> clazz = loadClass(sourceFile, writer);
|
||||
Object object;
|
||||
|
@ -80,25 +78,21 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||
return null; // class is not GhidraScript
|
||||
|
||||
}
|
||||
catch (OSGiException | IOException | InterruptedException | IllegalArgumentException
|
||||
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ClassNotFoundException("", e);
|
||||
}
|
||||
}
|
||||
|
||||
static public Class<?> loadClass(ResourceFile sourceFile, PrintWriter writer)
|
||||
throws IOException, OSGiException, ClassNotFoundException, InterruptedException {
|
||||
static public Class<?> loadClass(ResourceFile sourceFile, PrintWriter writer) throws Exception {
|
||||
|
||||
BundleHost bundleHost = BundleHost.getInstance();
|
||||
|
||||
SourceBundleInfo bi = getBundleInfoForSource(sourceFile);
|
||||
bundleHost.compileSourceBundle(bi, writer);
|
||||
|
||||
// as much source as possible built, install bundle and start it if necessary
|
||||
Bundle b = bi.getBundle();
|
||||
if (b == null) {
|
||||
b = bi.install();
|
||||
}
|
||||
GhidraSourceBundle bi = getBundleForSource(sourceFile);
|
||||
bi.build(writer);
|
||||
Bundle b = bi.install();
|
||||
bundleHost.activateSynchronously(b);
|
||||
|
||||
String classname = bi.classNameForScript(sourceFile);
|
||||
|
|
|
@ -48,7 +48,7 @@ import generic.jar.ResourceFile;
|
|||
import generic.test.TestUtils;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.console.ConsoleComponentProvider;
|
||||
import ghidra.app.plugin.core.script.osgi.BundleStatusProvider;
|
||||
import ghidra.app.plugin.core.osgi.BundleStatusProvider;
|
||||
import ghidra.app.script.*;
|
||||
import ghidra.app.services.ConsoleService;
|
||||
import ghidra.framework.Application;
|
||||
|
@ -979,7 +979,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
// destroy any NewScriptxxx files...and Temp ones too
|
||||
BundleStatusProvider bundleStatusProvider =
|
||||
(BundleStatusProvider) TestUtils.getInstanceField("bundleStatusProvider", provider);
|
||||
List<ResourceFile> paths = bundleStatusProvider.getModel().getPaths();
|
||||
List<ResourceFile> paths = bundleStatusProvider.getModel().getEnabledPaths();
|
||||
for (ResourceFile path : paths) {
|
||||
File file = path.getFile(false);
|
||||
File[] listFiles = file.listFiles();
|
||||
|
|
|
@ -26,9 +26,9 @@ import org.junit.Test;
|
|||
import docking.widgets.table.SelectionManager;
|
||||
import generic.jar.ResourceFile;
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.app.plugin.core.osgi.GhidraSourceBundle;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.JavaScriptProvider;
|
||||
import ghidra.app.script.osgi.SourceBundleInfo;
|
||||
import ghidra.test.ScriptTaskListener;
|
||||
import ghidra.util.TaskUtilities;
|
||||
import utilities.util.FileUtilities;
|
||||
|
@ -149,7 +149,7 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
|
|||
|
||||
// remove all class files from the user script bin dir
|
||||
File userScriptsBinDir =
|
||||
SourceBundleInfo.getBindirFromScriptFile(new ResourceFile(newScriptFile)).toFile();
|
||||
GhidraSourceBundle.getBindirFromScriptFile(new ResourceFile(newScriptFile)).toFile();
|
||||
File[] userScriptBinDirFiles;
|
||||
if (userScriptsBinDir.exists()) {
|
||||
userScriptBinDirFiles = userScriptsBinDir.listFiles(classFileFilter);
|
||||
|
@ -195,7 +195,7 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
|
|||
|
||||
// verify that the generated class file is placed in the default scripting home/bin
|
||||
File userScriptsBinDir =
|
||||
SourceBundleInfo.getBindirFromScriptFile(systemScriptFile).toFile();
|
||||
GhidraSourceBundle.getBindirFromScriptFile(systemScriptFile).toFile();
|
||||
String className = scriptName.replace(".java", ".class");
|
||||
File expectedClassFile = new File(userScriptsBinDir, className);
|
||||
|
||||
|
@ -232,7 +232,7 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
|
|||
|
||||
// verify a bin dir was created and that the class file is in it
|
||||
File binDir =
|
||||
SourceBundleInfo.getBindirFromScriptFile(new ResourceFile(newScriptFile)).toFile();
|
||||
GhidraSourceBundle.getBindirFromScriptFile(new ResourceFile(newScriptFile)).toFile();
|
||||
assertTrue("bin output dir not created", binDir.exists());
|
||||
|
||||
File scriptClassFile = new File(binDir, rawScriptName + ".class");
|
||||
|
|
|
@ -30,7 +30,7 @@ import docking.action.DockingActionIf;
|
|||
import docking.widgets.filter.FilterTextField;
|
||||
import docking.widgets.list.ListPanel;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.script.osgi.BundleStatusProvider;
|
||||
import ghidra.app.plugin.core.osgi.BundleStatusProvider;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.JavaScriptProvider;
|
||||
import ghidra.util.StringUtilities;
|
||||
|
|
|
@ -138,7 +138,7 @@ public class GhidraPythonInterpreter extends InteractiveInterpreter {
|
|||
systemState.path.append(Py.newString(resourceFile.getFile(false).getAbsolutePath()));
|
||||
}
|
||||
|
||||
for (ResourceFile resourceFile : GhidraScriptUtil.getExplodedBundlePaths()) {
|
||||
for (ResourceFile resourceFile : GhidraScriptUtil.getExplodedCompiledSourceBundlePaths()) {
|
||||
systemState.path.append(Py.newString(resourceFile.getFile(false).getAbsolutePath()));
|
||||
}
|
||||
|
||||
|
|
|
@ -27,8 +27,8 @@ import docking.widgets.tree.GTree;
|
|||
import docking.widgets.tree.GTreeNode;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.console.ConsoleComponentProvider;
|
||||
import ghidra.app.plugin.core.osgi.BundleStatusProvider;
|
||||
import ghidra.app.plugin.core.script.*;
|
||||
import ghidra.app.plugin.core.script.osgi.BundleStatusProvider;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.services.ConsoleService;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue