Projects STRLCPY jadx Commits a2ac7f2c
🤬
  • feat(script): add code area popup menu action

  • Loading...
  • Skylot committed 1 year ago
    a2ac7f2c
    1 parent ca98f2f3
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/api/plugins/gui/JadxGuiContext.java
    1 1  package jadx.api.plugins.gui;
    2 2   
     3 +import java.util.function.Consumer;
     4 +import java.util.function.Function;
     5 + 
     6 +import javax.swing.KeyStroke;
     7 + 
     8 +import org.jetbrains.annotations.Nullable;
     9 + 
     10 +import jadx.api.metadata.ICodeNodeRef;
     11 + 
    3 12  public interface JadxGuiContext {
    4 13   
    5 14   /**
    skipped 1 lines
    7 16   */
    8 17   void uiRun(Runnable runnable);
    9 18   
     19 + /**
     20 + * Add global menu entry ('Plugins' section)
     21 + */
    10 22   void addMenuAction(String name, Runnable action);
     23 + 
     24 + /**
     25 + * Add code viewer popup menu entry
     26 + *
     27 + * @param name entry title
     28 + * @param enabled check if entry should be enabled, called on popup creation
     29 + * @param keyBinding optional assigned keybinding {@link KeyStroke#getKeyStroke(String)}
     30 + */
     31 + void addPopupMenuAction(String name,
     32 + @Nullable Function<ICodeNodeRef, Boolean> enabled,
     33 + @Nullable String keyBinding,
     34 + Consumer<ICodeNodeRef> action);
     35 + 
     36 + /**
     37 + * Attach new key binding to main window
     38 + *
     39 + * @param id unique ID string
     40 + * @param keyBinding keybinding string {@link KeyStroke#getKeyStroke(String)}
     41 + * @param action runnable action
     42 + * @return false if already registered
     43 + */
     44 + boolean registerGlobalKeyBinding(String id, String keyBinding, Runnable action);
     45 + 
     46 + void copyToClipboard(String str);
    11 47  }
    12 48   
  • ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/JadxWrapper.java
    skipped 92 lines
    93 93   decompiler = null;
    94 94   }
    95 95   if (guiPluginsContext != null) {
    96  - guiPluginsContext.reset();
     96 + resetGuiPluginsContext();
    97 97   guiPluginsContext = null;
    98 98   }
    99 99   }
    skipped 40 lines
    140 140   private void initGuiPluginsContext() {
    141 141   guiPluginsContext = new GuiPluginsContext(mainWindow);
    142 142   decompiler.getPluginsContext().setGuiContext(guiPluginsContext);
     143 + }
     144 + 
     145 + public GuiPluginsContext getGuiPluginsContext() {
     146 + return guiPluginsContext;
     147 + }
     148 + 
     149 + public void resetGuiPluginsContext() {
     150 + guiPluginsContext.reset();
    143 151   }
    144 152   
    145 153   /**
    skipped 171 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/plugins/context/CodePopupAction.java
     1 +package jadx.gui.plugins.context;
     2 + 
     3 +import java.util.function.Consumer;
     4 +import java.util.function.Function;
     5 + 
     6 +import javax.swing.KeyStroke;
     7 + 
     8 +import org.jetbrains.annotations.Nullable;
     9 + 
     10 +import jadx.api.metadata.ICodeNodeRef;
     11 +import jadx.gui.treemodel.JNode;
     12 +import jadx.gui.ui.codearea.CodeArea;
     13 +import jadx.gui.ui.codearea.JNodeAction;
     14 + 
     15 +public class CodePopupAction {
     16 + private final String name;
     17 + private final Function<ICodeNodeRef, Boolean> enabledCheck;
     18 + private final String keyBinding;
     19 + private final Consumer<ICodeNodeRef> action;
     20 + 
     21 + public CodePopupAction(String name, Function<ICodeNodeRef, Boolean> enabled, String keyBinding, Consumer<ICodeNodeRef> action) {
     22 + this.name = name;
     23 + this.enabledCheck = enabled;
     24 + this.keyBinding = keyBinding;
     25 + this.action = action;
     26 + }
     27 + 
     28 + public JNodeAction buildAction(CodeArea codeArea) {
     29 + return new NodeAction(this, codeArea);
     30 + }
     31 + 
     32 + private static class NodeAction extends JNodeAction {
     33 + private final CodePopupAction data;
     34 + 
     35 + public NodeAction(CodePopupAction data, CodeArea codeArea) {
     36 + super(data.name, codeArea);
     37 + if (data.keyBinding != null) {
     38 + KeyStroke key = KeyStroke.getKeyStroke(data.keyBinding);
     39 + if (key == null) {
     40 + throw new IllegalArgumentException("Failed to parse key stroke: " + data.keyBinding);
     41 + }
     42 + addKeyBinding(key, data.name);
     43 + }
     44 + this.data = data;
     45 + }
     46 + 
     47 + @Override
     48 + public boolean isActionEnabled(@Nullable JNode node) {
     49 + if (node == null) {
     50 + return false;
     51 + }
     52 + ICodeNodeRef codeNode = node.getCodeNodeRef();
     53 + if (codeNode == null) {
     54 + return false;
     55 + }
     56 + return data.enabledCheck.apply(codeNode);
     57 + }
     58 + 
     59 + @Override
     60 + public void runAction(JNode node) {
     61 + Runnable r = () -> data.action.accept(node.getCodeNodeRef());
     62 + getCodeArea().getMainWindow().getBackgroundExecutor().execute(data.name, r);
     63 + }
     64 + }
     65 +}
     66 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/plugins/context/GuiPluginsContext.java
    1 1  package jadx.gui.plugins.context;
    2 2   
     3 +import java.util.ArrayList;
     4 +import java.util.List;
     5 +import java.util.function.Consumer;
     6 +import java.util.function.Function;
     7 + 
    3 8  import javax.swing.JMenu;
     9 +import javax.swing.JPanel;
     10 +import javax.swing.KeyStroke;
    4 11   
     12 +import org.jetbrains.annotations.Nullable;
    5 13  import org.slf4j.Logger;
    6 14  import org.slf4j.LoggerFactory;
    7 15   
     16 +import jadx.api.metadata.ICodeNodeRef;
    8 17  import jadx.api.plugins.gui.JadxGuiContext;
    9 18  import jadx.gui.ui.MainWindow;
     19 +import jadx.gui.ui.codearea.CodeArea;
     20 +import jadx.gui.ui.codearea.JNodePopupBuilder;
    10 21  import jadx.gui.utils.UiUtils;
    11 22  import jadx.gui.utils.ui.ActionHandler;
    12 23   
    skipped 1 lines
    14 25   private static final Logger LOG = LoggerFactory.getLogger(GuiPluginsContext.class);
    15 26   
    16 27   private final MainWindow mainWindow;
     28 + 
     29 + private final List<CodePopupAction> codePopupActionList = new ArrayList<>();
    17 30   
    18 31   public GuiPluginsContext(MainWindow mainWindow) {
    19 32   this.mainWindow = mainWindow;
    20 33   }
    21 34   
    22 35   public void reset() {
     36 + codePopupActionList.clear();
    23 37   JMenu pluginsMenu = mainWindow.getPluginsMenu();
    24 38   pluginsMenu.removeAll();
    25 39   pluginsMenu.setVisible(false);
    skipped 17 lines
    43 57   JMenu pluginsMenu = mainWindow.getPluginsMenu();
    44 58   pluginsMenu.add(item);
    45 59   pluginsMenu.setVisible(true);
     60 + }
     61 + 
     62 + @Override
     63 + public void addPopupMenuAction(String name, @Nullable Function<ICodeNodeRef, Boolean> enabled,
     64 + @Nullable String keyBinding, Consumer<ICodeNodeRef> action) {
     65 + codePopupActionList.add(new CodePopupAction(name, enabled, keyBinding, action));
     66 + }
     67 + 
     68 + public void appendPopupMenus(CodeArea codeArea, JNodePopupBuilder popup) {
     69 + if (codePopupActionList.isEmpty()) {
     70 + return;
     71 + }
     72 + popup.addSeparator();
     73 + for (CodePopupAction codePopupAction : codePopupActionList) {
     74 + popup.add(codePopupAction.buildAction(codeArea));
     75 + }
     76 + }
     77 + 
     78 + @Override
     79 + public boolean registerGlobalKeyBinding(String id, String keyBinding, Runnable action) {
     80 + KeyStroke keyStroke = KeyStroke.getKeyStroke(keyBinding);
     81 + if (keyStroke == null) {
     82 + throw new IllegalArgumentException("Failed to parse key binding: " + keyBinding);
     83 + }
     84 + JPanel mainPanel = (JPanel) mainWindow.getContentPane();
     85 + Object prevBinding = mainPanel.getInputMap().get(keyStroke);
     86 + if (prevBinding != null) {
     87 + return false;
     88 + }
     89 + UiUtils.addKeyBinding(mainPanel, keyStroke, id, action);
     90 + return true;
     91 + }
     92 + 
     93 + @Override
     94 + public void copyToClipboard(String str) {
     95 + UiUtils.copyToClipboard(str);
    46 96   }
    47 97  }
    48 98   
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/plugins/script/KtLintUtils.kt
    skipped 1 lines
    2 2   
    3 3  import com.pinterest.ktlint.core.KtLint
    4 4  import com.pinterest.ktlint.core.LintError
     5 +import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties.indentStyleProperty
     6 +import com.pinterest.ktlint.core.api.EditorConfigOverride
    5 7  import com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider
     8 +import org.ec4j.core.model.PropertyType
    6 9  import org.slf4j.Logger
    7 10  import org.slf4j.LoggerFactory
    8 11   
    skipped 3 lines
    12 15   
    13 16   val rules = lazy { StandardRuleSetProvider().getRuleProviders() }
    14 17   
     18 + val configOverride = lazy {
     19 + EditorConfigOverride.from(
     20 + indentStyleProperty to PropertyType.IndentStyleValue.tab
     21 + )
     22 + }
     23 + 
    15 24   fun format(code: String, fileName: String): String {
    16 25   val params = KtLint.ExperimentalParams(
    17 26   text = code,
    18 27   fileName = fileName,
    19 28   ruleProviders = rules.value,
     29 + editorConfigOverride = configOverride.value,
    20 30   script = true,
    21 31   cb = { e: LintError, corrected ->
    22 32   if (!corrected) {
    skipped 10 lines
    33 43   text = code,
    34 44   fileName = fileName,
    35 45   ruleProviders = rules.value,
     46 + editorConfigOverride = configOverride.value,
    36 47   script = true,
    37 48   cb = { e: LintError, corrected ->
    38 49   if (!corrected) {
    skipped 9 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/plugins/script/ScriptCodeArea.java
    skipped 60 lines
    61 61   }
    62 62   
    63 63   public void updateCode(String newCode) {
     64 + int caretPos = getCaretPosition();
    64 65   setText(newCode);
     66 + setCaretPosition(caretPos);
    65 67   scriptNode.setChanged(true);
    66 68   }
    67 69   
    skipped 10 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/plugins/script/ScriptContentPanel.java
    skipped 22 lines
    23 23   
    24 24  import kotlin.script.experimental.api.ScriptDiagnostic;
    25 25   
     26 +import jadx.gui.JadxWrapper;
    26 27  import jadx.gui.settings.JadxSettings;
    27 28  import jadx.gui.settings.LineNumbersMode;
    28 29  import jadx.gui.treemodel.JInputScript;
    skipped 102 lines
    131 132   MainWindow mainWindow = tabbedPane.getMainWindow();
    132 133   mainWindow.getBackgroundExecutor().execute(NLS.str("script.run"), () -> {
    133 134   try {
    134  - mainWindow.getWrapper().getDecompiler().reloadPasses();
     135 + JadxWrapper wrapper = mainWindow.getWrapper();
     136 + wrapper.resetGuiPluginsContext();
     137 + wrapper.getDecompiler().reloadPasses();
    135 138   } catch (Exception e) {
    136 139   LOG.error("Passes reload failed", e);
    137 140   }
    skipped 11 lines
    149 152   
    150 153   ScriptCompiler scriptCompiler = new ScriptCompiler(fileName);
    151 154   ScriptAnalyzeResult result = scriptCompiler.analyze(code, scriptArea.getCaretPosition());
    152  - List<ScriptDiagnostic> errors = result.getErrors();
    153  - for (ScriptDiagnostic error : errors) {
    154  - LOG.warn("Parse error: {}", error);
     155 + List<ScriptDiagnostic> issues = result.getIssues();
     156 + for (ScriptDiagnostic issue : issues) {
     157 + LOG.warn("Compiler issue: {}", issue);
    155 158   }
     159 + boolean success = issues.stream().map(ScriptDiagnostic::getSeverity)
     160 + .noneMatch(s -> s == ScriptDiagnostic.Severity.ERROR || s == ScriptDiagnostic.Severity.FATAL);
    156 161   
    157 162   List<LintError> lintErrs = Collections.emptyList();
    158  - if (errors.isEmpty()) {
     163 + if (success) {
    159 164   lintErrs = getLintIssues(code, fileName);
    160 165   }
    161 166   
    162 167   errorService.clearErrors();
    163  - errorService.addErrors(errors);
     168 + errorService.addCompilerIssues(issues);
    164 169   errorService.addLintErrors(lintErrs);
    165 170   errorService.apply();
    166  - boolean success = errors.isEmpty();
    167 171   if (!success) {
    168  - resultLabel.setText("Parsing errors: " + errors.size());
     172 + resultLabel.setText("Compiler issues: " + issues.size());
    169 173   } else if (!lintErrs.isEmpty()) {
    170 174   resultLabel.setText("Lint issues: " + lintErrs.size());
    171 175   }
    skipped 63 lines
  • ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/plugins/script/ScriptErrorService.java
    skipped 61 lines
    62 62   }
    63 63   }
    64 64   
    65  - public void addErrors(List<ScriptDiagnostic> errors) {
    66  - for (ScriptDiagnostic error : errors) {
     65 + public void addCompilerIssues(List<ScriptDiagnostic> issues) {
     66 + for (ScriptDiagnostic issue : issues) {
    67 67   DefaultParserNotice notice;
    68  - SourceCode.Location loc = error.getLocation();
     68 + SourceCode.Location loc = issue.getLocation();
    69 69   if (loc == null) {
    70  - notice = new DefaultParserNotice(this, error.getMessage(), 0);
     70 + notice = new DefaultParserNotice(this, issue.getMessage(), 0);
    71 71   } else {
    72 72   try {
    73 73   int line = loc.getStart().getLine();
    74 74   int offset = scriptArea.getLineStartOffset(line - 1) + loc.getStart().getCol();
    75 75   int len = loc.getEnd() == null ? -1 : loc.getEnd().getCol() - loc.getStart().getCol();
    76  - notice = new DefaultParserNotice(this, error.getMessage(), line, offset - 1, len);
     76 + notice = new DefaultParserNotice(this, issue.getMessage(), line, offset - 1, len);
     77 + notice.setLevel(convertLevel(issue.getSeverity()));
    77 78   } catch (Exception e) {
    78  - LOG.error("Failed to convert script error", e);
     79 + LOG.error("Failed to convert script issue", e);
    79 80   continue;
    80 81   }
    81 82   }
    82 83   addNotice(notice);
    83 84   }
     85 + }
     86 + 
     87 + private static ParserNotice.Level convertLevel(ScriptDiagnostic.Severity severity) {
     88 + switch (severity) {
     89 + case FATAL:
     90 + case ERROR:
     91 + return ParserNotice.Level.ERROR;
     92 + case WARNING:
     93 + return ParserNotice.Level.WARNING;
     94 + case INFO:
     95 + case DEBUG:
     96 + return ParserNotice.Level.INFO;
     97 + }
     98 + return ParserNotice.Level.ERROR;
    84 99   }
    85 100   
    86 101   public void addLintErrors(List<LintError> errors) {
    skipped 22 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java
    skipped 20 lines
    21 21  import jadx.core.deobf.NameMapper;
    22 22  import jadx.core.dex.attributes.AFlag;
    23 23  import jadx.core.dex.info.AccessInfo;
     24 +import jadx.core.dex.nodes.ICodeNode;
    24 25  import jadx.gui.ui.MainWindow;
    25 26  import jadx.gui.ui.TabbedPane;
    26 27  import jadx.gui.ui.codearea.ClassCodeContentPanel;
    skipped 135 lines
    162 163   @Override
    163 164   public JavaNode getJavaNode() {
    164 165   return cls;
     166 + }
     167 + 
     168 + @Override
     169 + public ICodeNode getCodeNodeRef() {
     170 + return cls.getClassNode();
    165 171   }
    166 172   
    167 173   @Override
    skipped 92 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JField.java
    skipped 15 lines
    16 16  import jadx.api.data.ICodeRename;
    17 17  import jadx.api.data.impl.JadxCodeRename;
    18 18  import jadx.api.data.impl.JadxNodeRef;
     19 +import jadx.api.metadata.ICodeNodeRef;
    19 20  import jadx.core.deobf.NameMapper;
    20 21  import jadx.core.dex.attributes.AFlag;
    21 22  import jadx.core.dex.info.AccessInfo;
    skipped 23 lines
    45 46   @Override
    46 47   public JavaNode getJavaNode() {
    47 48   return field;
     49 + }
     50 + 
     51 + @Override
     52 + public ICodeNodeRef getCodeNodeRef() {
     53 + return field.getFieldNode();
    48 54   }
    49 55   
    50 56   @Override
    skipped 130 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JMethod.java
    skipped 16 lines
    17 17  import jadx.api.data.ICodeRename;
    18 18  import jadx.api.data.impl.JadxCodeRename;
    19 19  import jadx.api.data.impl.JadxNodeRef;
     20 +import jadx.api.metadata.ICodeNodeRef;
    20 21  import jadx.core.deobf.NameMapper;
    21 22  import jadx.core.dex.attributes.AFlag;
    22 23  import jadx.core.dex.info.AccessInfo;
    skipped 28 lines
    51 52   
    52 53   public JavaMethod getJavaMethod() {
    53 54   return mth;
     55 + }
     56 + 
     57 + @Override
     58 + public ICodeNodeRef getCodeNodeRef() {
     59 + return mth.getMethodNode();
    54 60   }
    55 61   
    56 62   @Override
    skipped 220 lines
  • ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java
    skipped 13 lines
    14 14   
    15 15  import jadx.api.ICodeInfo;
    16 16  import jadx.api.JavaNode;
     17 +import jadx.api.metadata.ICodeNodeRef;
    17 18  import jadx.gui.ui.MainWindow;
    18 19  import jadx.gui.ui.TabbedPane;
    19 20  import jadx.gui.ui.panel.ContentPanel;
    skipped 12 lines
    32 33   }
    33 34   
    34 35   public JavaNode getJavaNode() {
     36 + return null;
     37 + }
     38 + 
     39 + public ICodeNodeRef getCodeNodeRef() {
    35 40   return null;
    36 41   }
    37 42   
    skipped 96 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JVariable.java
    skipped 10 lines
    11 11  import jadx.api.data.impl.JadxCodeRef;
    12 12  import jadx.api.data.impl.JadxCodeRename;
    13 13  import jadx.api.data.impl.JadxNodeRef;
     14 +import jadx.api.metadata.ICodeNodeRef;
    14 15  import jadx.core.deobf.NameMapper;
    15 16  import jadx.gui.ui.MainWindow;
    16 17  import jadx.gui.utils.UiUtils;
    skipped 21 lines
    38 39   @Override
    39 40   public JClass getRootClass() {
    40 41   return jMth.getRootClass();
     42 + }
     43 + 
     44 + @Override
     45 + public ICodeNodeRef getCodeNodeRef() {
     46 + return var.getVarNode();
    41 47   }
    42 48   
    43 49   @Override
    skipped 71 lines
  • ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java
    skipped 119 lines
    120 120   popup.addSeparator();
    121 121   popup.add(new FridaAction(this));
    122 122   popup.add(new XposedAction(this));
     123 + getMainWindow().getWrapper().getGuiPluginsContext().appendPopupMenus(this, popup);
    123 124   
    124 125   // move caret on mouse right button click
    125 126   popup.getMenu().addPopupMenuListener(new DefaultPopupMenuListener() {
    skipped 176 lines
  • ■ ■ ■ ■ ■
    jadx-plugins/jadx-script/examples/build.gradle.kts
    skipped 10 lines
    11 11   
    12 12   // manual imports (IDE can't import dependencies by scripts annotations)
    13 13   implementation("com.github.javafaker:javafaker:1.0.2")
     14 + implementation("org.apache.commons:commons-text:1.10.0")
    14 15  }
    15 16   
    16 17  sourceSets {
    skipped 8 lines
  • ■ ■ ■ ■ ■ ■
    jadx-plugins/jadx-script/examples/scripts/deobf_by_code.jadx.kts
    skipped 4 lines
    5 5  import jadx.api.plugins.input.insns.Opcode
    6 6  import jadx.core.dex.nodes.MethodNode
    7 7   
    8  -val renamesMap = mapOf(
     8 +val renamesMap = mapOf(
    9 9   "specificString" to "newMethodName",
    10 10   "AA6" to "aa6Method"
    11 11  )
    skipped 2 lines
    14 14   
    15 15  var n = 0
    16 16  jadx.rename.all { _, node ->
    17  - var newName : String? = null
     17 + var newName: String? = null
    18 18   if (node is MethodNode) {
    19 19   // use quick instructions scanner
    20 20   node.codeReader?.visitInstructions { insn ->
    skipped 19 lines
  • ■ ■ ■ ■ ■ ■
    jadx-plugins/jadx-script/examples/scripts/gui_custom_frida.jadx.kts
     1 +@file:DependsOn("org.apache.commons:commons-text:1.10.0")
     2 + 
     3 +import jadx.api.metadata.ICodeNodeRef
     4 +import jadx.core.codegen.TypeGen
     5 +import jadx.core.dex.instructions.args.ArgType
     6 +import jadx.core.dex.nodes.ClassNode
     7 +import jadx.core.dex.nodes.FieldNode
     8 +import jadx.core.dex.nodes.MethodNode
     9 +import jadx.core.utils.exceptions.JadxRuntimeException
     10 +import org.apache.commons.text.StringEscapeUtils
     11 + 
     12 +val jadx = getJadxInstance()
     13 + 
     14 +jadx.gui.ifAvailable {
     15 + addPopupMenuAction(
     16 + "Custom Frida snippet (g)",
     17 + enabled = ::isActionEnabled,
     18 + keyBinding = "G",
     19 + action = ::runAction
     20 + )
     21 +}
     22 + 
     23 +fun isActionEnabled(node: ICodeNodeRef): Boolean {
     24 + return node is MethodNode || node is ClassNode || node is FieldNode
     25 +}
     26 + 
     27 +fun runAction(node: ICodeNodeRef) {
     28 + try {
     29 + val fridaSnippet = generateFridaSnippet(node)
     30 + log.info { "Custom frida snippet:\n$fridaSnippet" }
     31 + jadx.gui.copyToClipboard(fridaSnippet)
     32 + } catch (e: Exception) {
     33 + log.error(e) { "Failed to generate Frida code snippet" }
     34 + }
     35 +}
     36 + 
     37 +fun generateFridaSnippet(node: ICodeNodeRef): String {
     38 + return when (node) {
     39 + is MethodNode -> generateMethodSnippet(node)
     40 + is ClassNode -> generateClassSnippet(node)
     41 + is FieldNode -> generateFieldSnippet(node)
     42 + else -> throw JadxRuntimeException("Unsupported node type: " + node.javaClass)
     43 + }
     44 +}
     45 + 
     46 +fun generateClassSnippet(cls: ClassNode): String {
     47 + return """let ${cls.name} = Java.use("${StringEscapeUtils.escapeEcmaScript(cls.rawName)}");"""
     48 +}
     49 + 
     50 +fun generateMethodSnippet(mthNode: MethodNode): String {
     51 + val methodInfo = mthNode.methodInfo
     52 + val methodName = if (methodInfo.isConstructor) {
     53 + "\$init"
     54 + } else {
     55 + StringEscapeUtils.escapeEcmaScript(methodInfo.name)
     56 + }
     57 + val overload = if (isOverloaded(mthNode)) {
     58 + ".overload(${methodInfo.argumentsTypes.joinToString(transform = this::parseArgType)})"
     59 + } else {
     60 + ""
     61 + }
     62 + val shortClassName = mthNode.parentClass.name
     63 + val argNames = mthNode.collectArgsWithoutLoading().map { a -> a.name }
     64 + val args = argNames.joinToString(separator = ", ")
     65 + val logArgs = if (argNames.isNotEmpty()) {
     66 + argNames.joinToString(separator = " + ', ' + ", prefix = " + ', ' + ") { p -> "'$p: ' + $p" }
     67 + } else {
     68 + ""
     69 + }
     70 + val clsSnippet = generateClassSnippet(mthNode.parentClass)
     71 + return if (methodInfo.isConstructor || methodInfo.returnType == ArgType.VOID) {
     72 + // no return value
     73 + """
     74 + $clsSnippet
     75 + $shortClassName["$methodName"]$overload.implementation = function ($args) {
     76 + console.log('$shortClassName.$methodName is called'$logArgs);
     77 + this["$methodName"]($args);
     78 + };
     79 + """.trimIndent()
     80 + } else {
     81 + """
     82 + $clsSnippet
     83 + $shortClassName["$methodName"]$overload.implementation = function ($args) {
     84 + console.log('$shortClassName.$methodName is called'$logArgs);
     85 + let ret = this["$methodName"]($args);
     86 + console.log('$shortClassName.$methodName return: ' + ret);
     87 + return ret;
     88 + };
     89 + """.trimIndent()
     90 + }
     91 +}
     92 + 
     93 +fun generateFieldSnippet(fld: FieldNode): String {
     94 + var rawFieldName = StringEscapeUtils.escapeEcmaScript(fld.name)
     95 + for (methodNode in fld.parentClass.methods) {
     96 + if (methodNode.name == rawFieldName) {
     97 + rawFieldName = "_$rawFieldName"
     98 + break
     99 + }
     100 + }
     101 + return """
     102 + ${generateClassSnippet(fld.parentClass)}
     103 + ${fld.name} = ${fld.parentClass.name}.$rawFieldName.value;
     104 + """.trimIndent()
     105 +}
     106 + 
     107 +fun isOverloaded(methodNode: MethodNode): Boolean {
     108 + return methodNode.parentClass.methods.stream().anyMatch { m: MethodNode ->
     109 + m.name == methodNode.name && methodNode.methodInfo.shortId != m.methodInfo.shortId
     110 + }
     111 +}
     112 + 
     113 +fun parseArgType(x: ArgType): String {
     114 + val typeStr = if (x.isArray) {
     115 + TypeGen.signature(x).replace("/", ".")
     116 + } else {
     117 + x.toString()
     118 + }
     119 + return "'$typeStr'"
     120 +}
     121 + 
  • ■ ■ ■ ■ ■ ■
    jadx-plugins/jadx-script/jadx-script-ide/src/main/kotlin/ScriptCompiler.kt
    skipped 22 lines
    23 23  )
    24 24   
    25 25  data class ScriptAnalyzeResult(
    26  - val errors: List<ScriptDiagnostic>,
     26 + val issues: List<ScriptDiagnostic>,
    27 27   val renderType: String?,
    28 28   val reports: List<ScriptDiagnostic>
    29 29  )
    skipped 14 lines
    44 44   val result = analyze(code.toScriptSource(scriptName), cursor)
    45 45   val analyzerResult = result.valueOrNull()
    46 46   return ScriptAnalyzeResult(
    47  - errors = analyzerResult?.get(ReplAnalyzerResult.analysisDiagnostics)?.toList() ?: emptyList(),
     47 + issues = analyzerResult?.get(ReplAnalyzerResult.analysisDiagnostics)?.toList() ?: emptyList(),
    48 48   renderType = analyzerResult?.get(ReplAnalyzerResult.renderedResultType),
    49 49   reports = result.reports
    50 50   )
    skipped 15 lines
  • ■ ■ ■ ■ ■ ■
    jadx-plugins/jadx-script/jadx-script-runtime/src/main/kotlin/jadx/plugins/script/runtime/data/Gui.kt
    1 1  package jadx.plugins.script.runtime.data
    2 2   
     3 +import jadx.api.metadata.ICodeNodeRef
    3 4  import jadx.api.plugins.gui.JadxGuiContext
    4 5  import jadx.plugins.script.runtime.JadxScriptInstance
    5 6   
    skipped 9 lines
    15 16   }
    16 17   
    17 18   fun ui(block: () -> Unit) {
    18  - guiContext?.uiRun(block)
     19 + context().uiRun(block)
    19 20   }
    20 21   
    21 22   fun addMenuAction(name: String, action: () -> Unit) {
    22  - guiContext?.addMenuAction(name, action)
     23 + context().addMenuAction(name, action)
     24 + }
     25 + 
     26 + fun addPopupMenuAction(
     27 + name: String,
     28 + enabled: (ICodeNodeRef) -> Boolean = { _ -> true },
     29 + keyBinding: String? = null,
     30 + action: (ICodeNodeRef) -> Unit
     31 + ) {
     32 + context().addPopupMenuAction(name, enabled, keyBinding, action)
    23 33   }
     34 + 
     35 + fun registerGlobalKeyBinding(id: String, keyBinding: String, action: () -> Unit): Boolean {
     36 + return context().registerGlobalKeyBinding(id, keyBinding, action)
     37 + }
     38 + 
     39 + fun copyToClipboard(str: String) {
     40 + context().copyToClipboard(str)
     41 + }
     42 + 
     43 + private fun context(): JadxGuiContext =
     44 + guiContext ?: throw IllegalStateException("GUI plugins context not available!")
    24 45  }
    25 46   
Please wait...
Page is in error, reload to recover