/* * Copyright (C) 2010-2014 JPEXS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.jpexs.decompiler.flash.gui.abc; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructSuperIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallSuperIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallSuperVoidIns; import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; import com.jpexs.decompiler.flash.abc.types.ClassInfo; import com.jpexs.decompiler.flash.abc.types.InstanceInfo; import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.abc.types.ScriptInfo; import com.jpexs.decompiler.flash.abc.types.traits.Trait; import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.gui.AppStrings; import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.helpers.HilightedText; import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.helpers.Cache; import java.awt.Point; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import javax.swing.SwingUtilities; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import jsyntaxpane.SyntaxDocument; import jsyntaxpane.Token; import jsyntaxpane.TokenType; public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretListener { private List highlights = new ArrayList<>(); private List specialHighlights = new ArrayList<>(); private List traitHighlights = new ArrayList<>(); private List methodHighlights = new ArrayList<>(); private List classHighlights = new ArrayList<>(); private Highlighting currentMethodHighlight; private Highlighting currentTraitHighlight; private ABC abc; private ScriptPack script; public int lastTraitIndex = 0; public boolean ignoreCarret = false; private boolean reset = false; private final ABCPanel abcPanel; private int classIndex = -1; private boolean isStatic = false; private final Cache cache = Cache.getInstance(true); private final List scriptListeners = new ArrayList<>(); public void addScriptListener(Runnable l) { scriptListeners.add(l); } public ABCPanel getAbcPanel() { return abcPanel; } public void removeScriptListener(Runnable l) { scriptListeners.remove(l); } public void fireScript() { for (int i = 0; i < scriptListeners.size(); i++) { scriptListeners.get(i).run(); } } public Trait getCurrentTrait() { return abc.findTraitByTraitId(classIndex, lastTraitIndex); } public ScriptPack getScriptLeaf() { return script; } public boolean getIsStatic() { return isStatic; } public void setNoTrait() { abcPanel.detailPanel.showCard(DetailPanel.UNSUPPORTED_TRAIT_CARD, null); } public void hilightSpecial(String type, int index) { int startPos; int endPos; if (currentMethodHighlight == null) { if (currentTraitHighlight == null) { return; } startPos = currentTraitHighlight.startPos; endPos = currentTraitHighlight.startPos + currentTraitHighlight.len; } else { startPos = currentMethodHighlight.startPos; endPos = currentMethodHighlight.startPos + currentMethodHighlight.len; } List allh = new ArrayList<>(); for (Highlighting h : traitHighlights) { if (h.getPropertyString("index").equals("" + lastTraitIndex)) { for (Highlighting sh : specialHighlights) { if (sh.startPos >= h.startPos && (sh.startPos + sh.len < h.startPos + h.len)) { allh.add(sh); } } } } if (currentMethodHighlight != null) { for (Highlighting h : specialHighlights) { if (h.startPos >= startPos && (h.startPos + h.len < endPos)) { allh.add(h); } } } for (Highlighting h : allh) { if (h.getPropertyString("subtype").equals(type) && ((long) h.getPropertyLong("index") == index)) { ignoreCarret = true; if (h.startPos <= getDocument().getLength()) { setCaretPosition(h.startPos); } getCaret().setVisible(true); ignoreCarret = false; break; } } } public void hilightOffset(long offset) { if (currentMethodHighlight == null) { return; } List allh = new ArrayList<>(); for (Highlighting h : traitHighlights) { if (h.getPropertyString("index").equals("" + lastTraitIndex)) { Highlighting h2 = Highlighting.search(highlights, "offset", "" + offset, h.startPos, h.startPos + h.len); if (h2 != null) { ignoreCarret = true; if (h2.startPos <= getDocument().getLength()) { setCaretPosition(h2.startPos); } getCaret().setVisible(true); ignoreCarret = false; } } } } public synchronized void setClassIndex(int classIndex) { this.classIndex = classIndex; } private boolean displayMethod(int pos, int methodIndex, String name, Trait trait, boolean isStatic) { if (abc == null) { return false; } int bi = abc.findBodyIndex(methodIndex); if (bi == -1) { return false; } //fix for inner functions: if (trait instanceof TraitMethodGetterSetter) { TraitMethodGetterSetter tm = (TraitMethodGetterSetter) trait; if (tm.method_info != methodIndex) { trait = null; } } if (trait instanceof TraitFunction) { TraitFunction tf = (TraitFunction) trait; if (tf.method_info != methodIndex) { trait = null; } } abcPanel.detailPanel.showCard(DetailPanel.METHOD_TRAIT_CARD, trait); MethodCodePanel methodCodePanel = abcPanel.detailPanel.methodTraitPanel.methodCodePanel; if (reset || (methodCodePanel.getBodyIndex() != bi)) { methodCodePanel.setBodyIndex(bi, abc, name, trait); abcPanel.detailPanel.setEditMode(false); this.isStatic = isStatic; } boolean success = false; Highlighting h = Highlighting.search(highlights, pos); if (h != null) { methodCodePanel.hilighOffset(h.getPropertyLong("offset")); success = true; } Highlighting sh = Highlighting.search(specialHighlights, pos); if (sh != null) { methodCodePanel.hilighSpecial(sh.getPropertyString("subtype"), sh.getPropertyString("index")); success = true; } return success; } public void displayClass(int classIndex, int scriptIndex) { if (abcPanel.navigator.getClassIndex() != classIndex) { abcPanel.navigator.setClassIndex(classIndex, scriptIndex); } } public void resetEditing() { reset = true; caretUpdate(null); reset = false; } public int getMultinameUnderMouseCursor(Point pt) { return getMultinameAtPos(viewToModel(pt)); } public int getMultinameUnderCaret() { return getMultinameAtPos(getCaretPosition()); } public int getLocalDeclarationOfPos(int pos,Reference type) { Highlighting sh = Highlighting.search(specialHighlights, pos); Highlighting h = Highlighting.search(highlights, pos); if (h == null) { return -1; } List tms = Highlighting.searchAll(methodHighlights, pos, null, null, -1, -1); if (tms.isEmpty()) { return -1; } for (Highlighting tm : tms) { List tm_tms = Highlighting.searchAll(methodHighlights, -1, "index", tm.getPropertyString("index"), -1, -1); //is it already declaration? if ("true".equals(h.getPropertyString("declaration")) || (sh != null && "true".equals(sh.getPropertyString("declaration")))) { return -1; //no jump } String lname=h.getPropertyString("localName"); if("this".equals(lname)){ Highlighting ch= Highlighting.search(classHighlights, pos); int cindex=(int)(long)ch.getPropertyLong("index"); type.setVal(abc.instance_info.get(cindex).getName(abc.constants).getNameWithNamespace(abc.constants, true)); return ch.startPos; } Map search = h.getProperties(); search.remove("index"); search.remove("subtype"); search.remove("offset"); if (search.isEmpty()) { return -1; } search.put("declaration", "true"); for (Highlighting tm1 : tm_tms) { Highlighting rh = Highlighting.search(highlights, search, tm1.startPos, tm1.startPos + tm1.len); if (rh == null) { rh = Highlighting.search(specialHighlights, search, tm1.startPos, tm1.startPos + tm1.len); } if (rh != null) { type.setVal(rh.getPropertyString("declaredType")); return rh.startPos; } } } return -1; } public boolean getPropertyTypeAtPos(int pos, Reference abcIndex,Reference classIndex, Reference traitIndex, Reference classTrait, Reference multinameIndex){ int m = getMultinameAtPos(pos,true); if (m <= 0) { return false; } SyntaxDocument sd=(SyntaxDocument)getDocument(); Token t = sd.getTokenAt(pos+1); Token lastToken = t; Token prev = null; while(t.type == TokenType.IDENTIFIER || t.type == TokenType.KEYWORD || t.type == TokenType.REGEX){ prev = sd.getPrevToken(t); if(prev!=null){ if(!".".equals(prev.getString(sd))){ break; } t = sd.getPrevToken(prev); }else{ break; } } if(t.type != TokenType.IDENTIFIER && t.type != TokenType.KEYWORD || t.type == TokenType.REGEX){ return false; } Reference locTypeRef = new Reference<>(""); getLocalDeclarationOfPos(t.start,locTypeRef); String currentType=locTypeRef.getVal(); if(currentType.equals("*")){ return false; } boolean found=false; t = sd.getNextToken(t); while(t!=lastToken && !currentType.equals("*")){ t = sd.getNextToken(t); String ident=t.getString(sd); found = false; loopi:for(int i=0;i-1){ InstanceInfo ii = a.instance_info.get(cindex); for(int j=0;j(), false /*NOT RAW!*/))){ classIndex.setVal(cindex); abcIndex.setVal(i); traitIndex.setVal(j); classTrait.setVal(false); multinameIndex.setVal(tr.name_index); currentType = ii.getName(a.constants).getNameWithNamespace(a.constants, true); found = true; break loopi; } } ClassInfo ci = a.class_info.get(cindex); for(int j=0;j(), false /*NOT RAW!*/))){ classIndex.setVal(cindex); abcIndex.setVal(i); traitIndex.setVal(j); classTrait.setVal(true); multinameIndex.setVal(tr.name_index); currentType = ii.getName(a.constants).getNameWithNamespace(a.constants, true); found = true; break loopi; } } } } if(!found){ return false; } t = sd.getNextToken(t); if(!".".equals(t.getString(sd))){ break; } } return true; } public int getMultinameAtPos(int pos) { return getMultinameAtPos(pos, false); } public int getMultinameAtPos(int pos, boolean codeOnly) { Highlighting tm = Highlighting.search(methodHighlights, pos); Trait currentTrait = null; int currentMethod = -1; if (tm != null) { int mi = (int) (long) tm.getPropertyLong("index"); currentMethod = mi; int bi = abc.findBodyIndex(mi); Highlighting h = Highlighting.search(highlights, pos); if (h != null) { List list = abc.bodies.get(bi).getCode().code; AVM2Instruction lastIns = null; long inspos = 0; AVM2Instruction selIns = null; for (AVM2Instruction ins : list) { if (h.getPropertyLong("offset") == ins.getOffset()) { selIns = ins; break; } if (ins.getOffset() > h.getPropertyLong("offset")) { inspos = h.getPropertyLong("offset") - lastIns.offset; selIns = lastIns; break; } lastIns = ins; } if (selIns != null) { if (!codeOnly && ((selIns.definition instanceof ConstructSuperIns) || (selIns.definition instanceof CallSuperIns)|| (selIns.definition instanceof CallSuperVoidIns))) { Highlighting tc = Highlighting.search(classHighlights, pos); if(tc!=null){ int cindex = (int)(long)tc.getPropertyLong("index"); if(cindex>-1){ return abc.instance_info.get(cindex).super_index; } } } else { for (int i = 0; i < selIns.definition.operands.length; i++) { if (selIns.definition.operands[i] == AVM2Code.DAT_MULTINAME_INDEX) { return selIns.operands[i]; } } } } } } if(codeOnly){ return -1; } Highlighting ch = Highlighting.search(classHighlights, pos); if(ch!=null){ Highlighting th = Highlighting.search(traitHighlights, pos); if (th != null) { currentTrait = abc.findTraitByTraitId((int) (long) ch.getPropertyLong("index"), (int) (long) th.getPropertyLong("index")); } } if (currentTrait instanceof TraitMethodGetterSetter) { currentMethod = ((TraitMethodGetterSetter) currentTrait).method_info; } Highlighting sh = Highlighting.search(specialHighlights, pos); if (sh != null) { switch (sh.getPropertyString("subtype")) { case "typename": String typeName = sh.getPropertyString("index"); for (int i = 1; i < abc.constants.constant_multiname.size(); i++) { Multiname m = abc.constants.constant_multiname.get(i); if (m != null) { if (typeName.equals(m.getNameWithNamespace(abc.constants, true))) { return i; } } } case "traittypename": if (currentTrait instanceof TraitSlotConst) { TraitSlotConst ts = (TraitSlotConst) currentTrait; return ts.type_index; } break; case "traitname": if (currentTrait != null) { //return currentTrait.name_index; } break; case "returns": if (currentMethod > -1) { return abc.method_info.get(currentMethod).ret_type; } break; case "param": if (currentMethod > -1) { return abc.method_info.get(currentMethod).param_types[(int) (long) sh.getPropertyLong("index")]; } break; } } return -1; } @Override public void caretUpdate(final CaretEvent e) { if (!SwingUtilities.isEventDispatchThread()) { View.execInEventDispatch(new Runnable() { @Override public void run() { caretUpdate(e); } }); return; } if (abc == null) { return; } if (ignoreCarret) { return; } getCaret().setVisible(true); int pos = getCaretPosition(); abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setIgnoreCarret(true); try { classIndex = -1; Highlighting cm = Highlighting.search(classHighlights, pos); if (cm != null) { classIndex = (int) (long) cm.getPropertyLong("index"); displayClass(classIndex, script.scriptIndex); } Highlighting tm = Highlighting.search(methodHighlights, pos); if (tm != null) { String name = ""; if (abc != null) { if (classIndex > -1) { name = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, false); } } Trait currentTrait = null; currentTraitHighlight = Highlighting.search(traitHighlights, pos); if (currentTraitHighlight != null) { lastTraitIndex = (int) (long) currentTraitHighlight.getPropertyLong("index"); if ((abc != null) && (classIndex != -1)) { currentTrait = getCurrentTrait(); isStatic = abc.isStaticTraitId(classIndex, lastTraitIndex); if (currentTrait != null) { name += ":" + currentTrait.getName(abc).getName(abc.constants, new ArrayList(), false); } } } displayMethod(pos, (int) (long) tm.getPropertyLong("index"), name, currentTrait, isStatic); currentMethodHighlight = tm; return; } if (classIndex == -1) { setNoTrait(); return; } Trait currentTrait = null; currentTraitHighlight = Highlighting.search(traitHighlights, pos); if (currentTraitHighlight != null) { lastTraitIndex = (int) (long) currentTraitHighlight.getPropertyLong("index"); currentTrait = getCurrentTrait(); if (currentTrait != null) { if (currentTrait instanceof TraitSlotConst) { abcPanel.detailPanel.slotConstTraitPanel.load((TraitSlotConst) currentTrait, abc, abc.isStaticTraitId(classIndex, lastTraitIndex)); abcPanel.detailPanel.showCard(DetailPanel.SLOT_CONST_TRAIT_CARD, currentTrait); abcPanel.detailPanel.setEditMode(false); currentMethodHighlight = null; Highlighting spec = Highlighting.search(specialHighlights, pos, null, null, currentTraitHighlight.startPos, currentTraitHighlight.startPos + currentTraitHighlight.len); if (spec != null) { abcPanel.detailPanel.slotConstTraitPanel.hilightSpecial(spec); } return; } } currentMethodHighlight = null; String name = ""; currentTrait = null; if (abc != null) { name = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, false); currentTrait = getCurrentTrait(); isStatic = abc.isStaticTraitId(classIndex, lastTraitIndex); if (currentTrait != null) { name += ":" + currentTrait.getName(abc).getName(abc.constants, new ArrayList(), false); } } displayMethod(pos, abc.findMethodIdByTraitId(classIndex, lastTraitIndex), name, currentTrait, isStatic); return; } setNoTrait(); } finally { abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setIgnoreCarret(false); } } public void uncache(ScriptPack pack) { cache.remove(pack); } public boolean isCached(ScriptPack pack) { return cache.contains(pack); } private CachedDecompilation getCached(ScriptPack pack) throws InterruptedException { if (!cache.contains(pack)) { cacheScriptPack(pack, abcList); } return (CachedDecompilation) cache.get(pack); } public String getCachedText(ScriptPack pack) throws InterruptedException { return getCached(pack).text; } public void gotoLastTrait() { gotoTrait(lastTraitIndex); } public void gotoTrait(int traitId) { if (traitId == -1) { setCaretPosition(0); return; } Highlighting tc = Highlighting.search(classHighlights, "index", "" + classIndex); if (tc != null) { Highlighting th = Highlighting.search(traitHighlights, "index", "" + traitId, tc.startPos, tc.startPos + tc.len); if (th != null) { ignoreCarret = true; int startPos = th.startPos + th.len - 1; if (startPos <= getDocument().getLength()) { setCaretPosition(startPos); } ignoreCarret = false; final int pos = th.startPos; new Timer().schedule(new TimerTask() { @Override public void run() { if (pos <= getDocument().getLength()) { setCaretPosition(pos); } } }, 100); return; } } setCaretPosition(0); } public DecompiledEditorPane(ABCPanel abcPanel) { super(); setEditable(false); getCaret().setVisible(true); addCaretListener(this); this.abcPanel = abcPanel; } private List abcList; public void clearScriptCache() { cache.clear(); } public void cacheScriptPack(ScriptPack scriptLeaf, List abcList) throws InterruptedException { int maxCacheSize = 50; int scriptIndex = scriptLeaf.scriptIndex; ScriptInfo script = null; ABC abc = scriptLeaf.abc; if (scriptIndex > -1) { script = abc.script_info.get(scriptIndex); } if (!cache.contains(scriptLeaf)) { boolean parallel = Configuration.parallelSpeedUp.get(); HilightedTextWriter writer = new HilightedTextWriter(Configuration.getCodeFormatting(), true); scriptLeaf.toSource(writer, abcList, script.traits.traits, ScriptExportMode.AS, parallel); HilightedText hilightedCode = new HilightedText(writer); cache.put(scriptLeaf, new CachedDecompilation(hilightedCode)); } } public void setScript(ScriptPack scriptLeaf, List abcList) { abcPanel.scriptNameLabel.setText(scriptLeaf.getClassPath().toString()); int scriptIndex = scriptLeaf.scriptIndex; ScriptInfo script = null; ABC abc = scriptLeaf.abc; if (scriptIndex > -1) { script = abc.script_info.get(scriptIndex); } if (script == null) { highlights = new ArrayList<>(); specialHighlights = new ArrayList<>(); traitHighlights = new ArrayList<>(); methodHighlights = new ArrayList<>(); this.script = scriptLeaf; return; } setText("//" + AppStrings.translate("pleasewait") + "..."); this.abc = abc; this.abcList = abcList; this.script = scriptLeaf; CachedDecompilation cd = null; try { cacheScriptPack(scriptLeaf, abcList); cd = getCached(scriptLeaf); } catch (InterruptedException ex) { } if (cd != null) { final String hilightedCode = cd.text; highlights = cd.getInstructionHighlights(); specialHighlights = cd.getSpecialHighligths(); traitHighlights = cd.getTraitHighlights(); methodHighlights = cd.getMethodHighlights(); classHighlights = cd.getClassHighlights(); setContentType("text/actionscript"); setText(hilightedCode); } fireScript(); } public void reloadClass() { int ci = classIndex; uncache(script); if ((script != null) && (abc != null)) { setScript(script, abcList); } setNoTrait(); setClassIndex(ci); } public int getClassIndex() { return classIndex; } public void setABC(ABC abc) { this.abc = abc; cache.clear(); setText(""); } @Override public void setText(String t) { super.setText(t); setCaretPosition(0); } }