From 7853e5b14ce68139b8c0943216e38a9a15d47782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Fri, 30 May 2025 19:44:56 +0200 Subject: [PATCH] AS3 highlighter - namespaces go to definition --- .../abc/avm2/model/FullMultinameAVM2Item.java | 15 +++- .../flash/abc/avm2/model/GetLexAVM2Item.java | 19 ++++- .../decompiler/flash/abc/types/Multiname.java | 47 +++++++++- .../flash/abc/types/traits/Trait.java | 4 +- .../flash/gui/abc/DecompiledEditorPane.java | 8 ++ .../decompiler/flash/gui/abc/UsageFrame.java | 3 +- .../flash/gui/editor/VariableMarker.java | 85 ++++++++++++++----- 7 files changed, 153 insertions(+), 28 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/FullMultinameAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/FullMultinameAVM2Item.java index 1eef15b25..8facc70a9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/FullMultinameAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/FullMultinameAVM2Item.java @@ -16,17 +16,20 @@ */ package com.jpexs.decompiler.flash.abc.avm2.model; +import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.GraphTargetVisitorInterface; import com.jpexs.decompiler.graph.TypeItem; import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.Reference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -207,7 +210,17 @@ public class FullMultinameAVM2Item extends AVM2Item { if ("*".equals(simpleName)) { writer.append("*"); } else { - writer.append(constants.getMultiname(multinameIndex).getNameWithCustomNamespace(localData.abc, fullyQualifiedNames, false, true)); + Reference customNsRef = new Reference<>(null); + String localName = constants.getMultiname(multinameIndex).getNameAndCustomNamespace(localData.abc, localData.fullyQualifiedNames, false, true, customNsRef); + DottedChain customNs = customNsRef.getVal(); + if (customNs != null) { + String nsname = customNs.getLast(); + String identifier = IdentifiersDeobfuscation.printIdentifier(true, nsname); + writer.hilightSpecial(identifier, HighlightSpecialType.TYPE_NAME, customNs.toRawString()); + writer.appendNoHilight("::"); + } + + writer.append(localName); } } else { writer.append("§§multiname(").append(multinameIndex).append(")"); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/GetLexAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/GetLexAVM2Item.java index e9aaa81fa..4e9f0cfd5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/GetLexAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/GetLexAVM2Item.java @@ -16,13 +16,16 @@ */ package com.jpexs.decompiler.flash.abc.avm2.model; +import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.Reference; import java.util.Objects; /** @@ -86,9 +89,21 @@ public class GetLexAVM2Item extends AVM2Item { @Override public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) { - String localName = propertyName.getNameWithCustomNamespace(localData.abc, localData.fullyQualifiedNames, false, true); + Reference customNsRef = new Reference<>(null); + String localName = propertyName.getNameAndCustomNamespace(localData.abc, localData.fullyQualifiedNames, false, true, customNsRef); + DottedChain customNs = customNsRef.getVal(); + if (customNs != null) { + String nsname = customNs.getLast(); + String identifier = IdentifiersDeobfuscation.printIdentifier(true, nsname); + writer.hilightSpecial(identifier, HighlightSpecialType.TYPE_NAME, customNs.toRawString()); + writer.appendNoHilight("::"); + getSrcData().localName = nsname + "::" + localName; + return writer.append(localName); + } + + getSrcData().localName = localName; - return writer.append(propertyName.getNameWithCustomNamespace(localData.abc, localData.fullyQualifiedNames, false, true)); + return writer.append(localName); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java index 032892b2c..c553be4d5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java @@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing; import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.Helper; +import com.jpexs.helpers.Reference; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -682,7 +683,51 @@ public class Multiname { if (nsname != null && !"AS3".equals(nsname)) { String identifier = dontDeobfuscate ? nsname : IdentifiersDeobfuscation.printIdentifier(true, nsname); if (identifier != null && !identifier.isEmpty()) { - return nsname + "::" + name; + return identifier + "::" + name; + } + } + } + + if (nskind == Namespace.KIND_PACKAGE && fullyQualifiedNames != null && !fullyQualifiedNames.isEmpty() && fullyQualifiedNames.contains(DottedChain.parseWithSuffix(name))) { + DottedChain dc = getNameWithNamespace(abc.constants, withSuffix); + return dontDeobfuscate ? dc.toRawString() : dc.toPrintableString(true); + } + return (isAttribute() ? "@" : "") + (dontDeobfuscate ? name : IdentifiersDeobfuscation.printIdentifier(true, name)) + (withSuffix ? getNamespaceSuffix() : ""); + } + } + + /** + * Gets the name with custom namespace. + * + * @param abc ABC + * @param fullyQualifiedNames Fully qualified names + * @param dontDeobfuscate Don't deobfuscate flag + * @param withSuffix With suffix flag + * @param customNamespaceRef Custom namespace + * @return Name with custom namespace + */ + public String getNameAndCustomNamespace(ABC abc, List fullyQualifiedNames, boolean dontDeobfuscate, boolean withSuffix, Reference customNamespaceRef) { + if (kind == TYPENAME) { + return typeNameToStr(abc.constants, fullyQualifiedNames, dontDeobfuscate, withSuffix); + } + if (name_index == -1) { + return ""; + } + if (name_index == 0) { + return isAttribute() ? "@*" : "*"; + } else { + String name = abc.constants.getString(name_index); + + int nskind = getSimpleNamespaceKind(abc.constants); + if (nskind == Namespace.KIND_NAMESPACE || nskind == Namespace.KIND_PACKAGE_INTERNAL) { + DottedChain dc = abc.findCustomNsOfMultiname(this); + String nsname = dc != null ? dc.getLast() : null; + + if (nsname != null && !"AS3".equals(nsname)) { + String identifier = dontDeobfuscate ? nsname : IdentifiersDeobfuscation.printIdentifier(true, nsname); + if (identifier != null && !identifier.isEmpty()) { + customNamespaceRef.setVal(dc); + return name; } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java index 4269eb820..f5ea545fa 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java @@ -628,10 +628,10 @@ public abstract class Trait implements Cloneable, Serializable { writer.append("§§namespace(\""); writer.append(Helper.escapeActionScriptString(m.getSimpleNamespaceName(abc.constants).toRawString())); writer.append("\") "); - } else if (nsname != null) { + } else if (dc != null && nsname != null) { String identifier = IdentifiersDeobfuscation.printIdentifier(true, nsname); if (identifier != null && !identifier.isEmpty()) { - writer.appendNoHilight(identifier).appendNoHilight(" "); + writer.hilightSpecial(identifier, HighlightSpecialType.TYPE_NAME, dc.toRawString()).appendNoHilight(" "); } } else if (nskind != 0) { diff --git a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java index 52f505ae4..e61e02d24 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java @@ -1034,6 +1034,14 @@ public class DecompiledEditorPane extends DebuggableEditorPane implements CaretL public int getClassIndex() { return classIndex; } + + public int getScriptIndex() { + ScriptPack pack = getScriptLeaf(); + if (pack == null) { + return -1; + } + return pack.scriptIndex; + } private ABC getABC() { return script == null ? null : script.abc; diff --git a/src/com/jpexs/decompiler/flash/gui/abc/UsageFrame.java b/src/com/jpexs/decompiler/flash/gui/abc/UsageFrame.java index ec520cea0..7c749f8ac 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/UsageFrame.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/UsageFrame.java @@ -156,7 +156,8 @@ public class UsageFrame extends AppDialog implements MouseListener { }; Main.getMainFrame().getPanel().setLoadingScrollPosEnabled(false); - if (decompiledTextArea.getClassIndex() == icu.getClassIndex() && abcPanel.abc == newAbc) { + if (decompiledTextArea.getScriptIndex() == icu.getScriptIndex() && + (decompiledTextArea.getClassIndex() == icu.getClassIndex() || icu.getClassIndex() == -1) && abcPanel.abc == newAbc) { setTrait.run(); } else { decompiledTextArea.addScriptListener(setTrait); diff --git a/src/com/jpexs/decompiler/flash/gui/editor/VariableMarker.java b/src/com/jpexs/decompiler/flash/gui/editor/VariableMarker.java index 2cd4bb0cb..18f112455 100644 --- a/src/com/jpexs/decompiler/flash/gui/editor/VariableMarker.java +++ b/src/com/jpexs/decompiler/flash/gui/editor/VariableMarker.java @@ -68,7 +68,7 @@ import org.pushingpixels.substance.internal.ui.SubstanceScrollBarUI; /** * This class highlights Variable and error tokens. */ -public class VariableMarker implements SyntaxComponent, CaretListener, PropertyChangeListener, DocumentListener, LinkHandler { +public class VariableMarker implements SyntaxComponent, CaretListener, PropertyChangeListener, DocumentListener { public static final String DEFAULT_TOKENTYPES = "IDENTIFIER, KEYWORD, REGEX"; public static final String PROPERTY_COLOR = "ActionVariableMarker.Color"; @@ -107,8 +107,18 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC private UnderlinePainter underLinePainter = new UnderlinePainter(new Color(0, 0, 255), null); private UnderlinePainter underLineMarkOccurencesPainter = new UnderlinePainter(new Color(0, 0, 255), DEFAULT_COLOR); + private UnderlinePainter underLineExternalPainter = new UnderlinePainter(new Color(0, 255, 0), null); + private UnderlinePainter underLineExternalMarkOccurencesPainter = new UnderlinePainter(new Color(0, 255, 0), DEFAULT_COLOR); private Token lastUnderlined; + private LinkType lastUnderlinedLinkType = LinkType.NO_LINK; + + + public static enum LinkType { + NO_LINK, + LINK_THIS_SCRIPT, + LINK_OTHER_SCRIPT; + } /** * Constructs a new Token highlighter @@ -146,6 +156,8 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC Markers.removeMarkers(pane, marker); Markers.removeMarkers(pane, underLinePainter); Markers.removeMarkers(pane, underLineMarkOccurencesPainter); + Markers.removeMarkers(pane, underLineExternalPainter); + Markers.removeMarkers(pane, underLineExternalMarkOccurencesPainter); occurencesPositions.clear(); } @@ -236,12 +248,29 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC Token definitionToken = getIdentifierTokenAt(sDoc, definitionPos < 0 ? -(definitionPos + 1) : definitionPos); if (definitionToken != null) { if (definitionPosToReferences.containsKey(definitionPos)) { - Markers.markToken(pane, definitionToken, lastUnderlined == definitionToken ? underLineMarkOccurencesPainter : marker); + + Markers.SimpleMarker markerKind = marker; + if (lastUnderlined == definitionToken) { + if (lastUnderlinedLinkType == LinkType.LINK_OTHER_SCRIPT) { + markerKind = underLineExternalMarkOccurencesPainter; + } else { + markerKind = underLineMarkOccurencesPainter; + } + } + Markers.markToken(pane, definitionToken, markerKind); occurencesPositions.add(definitionToken.start); for (int i : definitionPosToReferences.get(definitionPos)) { Token referenceToken = getIdentifierTokenAt(sDoc, i); if (referenceToken != null) { - Markers.markToken(pane, referenceToken, lastUnderlined == referenceToken ? underLineMarkOccurencesPainter : marker); + markerKind = marker; + if (lastUnderlined == referenceToken) { + if (lastUnderlinedLinkType == LinkType.LINK_OTHER_SCRIPT) { + markerKind = underLineExternalMarkOccurencesPainter; + } else { + markerKind = underLineMarkOccurencesPainter; + } + } + Markers.markToken(pane, referenceToken, markerKind); occurencesPositions.add(referenceToken.start); } } @@ -367,30 +396,40 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC if (t == null || lastUnderlined == null || !t.equals(lastUnderlined)) { MyMarkers.removeMarkers(pane, underLinePainter); MyMarkers.removeMarkers(pane, underLineMarkOccurencesPainter); - - if (t != null && isLink(t)) { + MyMarkers.removeMarkers(pane, underLineExternalPainter); + MyMarkers.removeMarkers(pane, underLineExternalMarkOccurencesPainter); + + lastUnderlinedLinkType = t == null ? LinkType.NO_LINK : getLinkType(t); + if (t != null && lastUnderlinedLinkType != LinkType.NO_LINK) { lastUnderlined = t; pane.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { lastUnderlined = null; - removeMarkers(); markTokenAt(pane.getCaretPosition()); } } else { lastUnderlined = null; + lastUnderlinedLinkType = LinkType.NO_LINK; } } if (lastUnderlined != null) { Highlighter.HighlightPainter painter = underLinePainter; if (occurencesPositions.contains(lastUnderlined.start)) { - painter = underLineMarkOccurencesPainter; + if (lastUnderlinedLinkType == LinkType.LINK_OTHER_SCRIPT) { + painter = underLineExternalMarkOccurencesPainter; + } else { + painter = underLineMarkOccurencesPainter; + } removeMarkers(); markTokenAt(pane.getCaretPosition()); } else { - MyMarkers.markToken(pane, lastUnderlined, painter); + if (lastUnderlinedLinkType == LinkType.LINK_OTHER_SCRIPT) { + painter = underLineExternalPainter; + } + MyMarkers.markToken(pane, lastUnderlined, painter); } } else { pane.setCursor(Cursor.getDefaultCursor()); @@ -400,6 +439,10 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC lastUnderlined = null; MyMarkers.removeMarkers(pane, underLinePainter); MyMarkers.removeMarkers(pane, underLineMarkOccurencesPainter); + MyMarkers.removeMarkers(pane, underLineExternalPainter); + MyMarkers.removeMarkers(pane, underLineExternalMarkOccurencesPainter); + + removeMarkers(); markTokenAt(pane.getCaretPosition()); } @@ -411,7 +454,7 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC public void mouseClicked(MouseEvent e) { if (ctrlDown) { Token t = ((LineMarkedEditorPane) pane).tokenAtPos(lastCursorPos); - if (t != null && isLink(t)) { + if (t != null && getLinkType(t) != LinkType.NO_LINK) { e.consume(); handleLink(t); } @@ -554,19 +597,23 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC documentUpdated(); } - @Override - public boolean isLink(Token token) { - boolean linkBasic = referenceToDefinition.containsKey(token.start) && referenceToDefinition.get(token.start) >= 0; - if (linkBasic) { - return true; + private LinkType getLinkType(Token token) { + if (definitionPosToReferences.containsKey(token.start)) { + return LinkType.NO_LINK; + } + boolean linkThisScript = referenceToDefinition.containsKey(token.start) && referenceToDefinition.get(token.start) >= 0; + if (linkThisScript) { + return LinkType.LINK_THIS_SCRIPT; } if (pane.isEditable()) { - return false; + return LinkType.NO_LINK; } - return ((LineMarkedEditorPane) pane).getLinkHandler().isLink(token); + if (((LineMarkedEditorPane) pane).getLinkHandler().isLink(token)) { + return LinkType.LINK_OTHER_SCRIPT; + } + return LinkType.NO_LINK; } - @Override public void handleLink(Token token) { Integer definition = referenceToDefinition.get(token.start); if (definition != null && definition >= 0) { @@ -580,8 +627,4 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC } } - @Override - public Highlighter.HighlightPainter linkPainter() { - return ((LineMarkedEditorPane) pane).linkPainter(); - } }