AS3: Experimental graph function

This commit is contained in:
Jindra Petk
2010-09-19 10:07:38 +02:00
parent 0c579e1f1c
commit 86f602ff80
11 changed files with 694 additions and 2 deletions

View File

@@ -691,7 +691,7 @@ public class ABC {
"extends", "false", "finally", "for", "function", "if", "implements", "import", "in", "instanceof",
"interface", "internal", "is", "native", "new", "null", "package", "private", "protected", "public",
"return", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while",
"with"};
"with","dynamic","default","final"};
public int unknownCount = 0;
public void cleanOneName(int index) {

View File

@@ -0,0 +1,358 @@
/*
* Copyright (C) 2010 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.jpexs.asdec.abc.avm2.flowgraph;
import com.jpexs.asdec.abc.avm2.AVM2Code;
import com.jpexs.asdec.abc.avm2.ConvertException;
import com.jpexs.asdec.abc.avm2.instructions.IfTypeIns;
import com.jpexs.asdec.abc.avm2.instructions.jumps.IfFalseIns;
import com.jpexs.asdec.abc.avm2.instructions.jumps.IfTrueIns;
import com.jpexs.asdec.abc.avm2.instructions.jumps.JumpIns;
import com.jpexs.asdec.abc.avm2.instructions.localregs.GetLocalTypeIns;
import com.jpexs.asdec.abc.avm2.instructions.localregs.SetLocalTypeIns;
import com.jpexs.asdec.abc.avm2.instructions.other.ReturnValueIns;
import com.jpexs.asdec.abc.avm2.instructions.other.ReturnVoidIns;
import com.jpexs.asdec.abc.avm2.instructions.stack.DupIns;
import com.jpexs.asdec.abc.avm2.instructions.stack.PopIns;
import com.jpexs.asdec.abc.avm2.instructions.stack.PushFalseIns;
import com.jpexs.asdec.abc.avm2.instructions.stack.PushTrueIns;
import com.jpexs.asdec.abc.avm2.instructions.stack.SwapIns;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class Graph {
public List<GraphPart> parts;
public List<Integer> ignored = new ArrayList<Integer>();
private int trueReg = -1;
private int falseReg = -1;
public Graph(AVM2Code code) {
parts = new ArrayList<GraphPart>();
int start = checkSWFSecureStart(code);
makeGraph(new Stack<Boolean>(),code, start, parts, new ArrayList<GraphBlock>());
do {
} while (optimizeDecisions(parts) > 0);
}
private int checkSWFSecureStart(AVM2Code code) {
if (code.code.size() < 2) {
return 0;
}
if (!((code.code.get(0).definition instanceof PushFalseIns) || (code.code.get(0).definition instanceof PushTrueIns))) {
return 0;
}
if (!((code.code.get(1).definition instanceof PushFalseIns) || (code.code.get(1).definition instanceof PushTrueIns))) {
return 0;
}
System.out.println("A");
int pos = 2;
Stack<Boolean> myStack = new Stack<Boolean>();
int ip = 0;
int setCount = 0;
while (ip < code.code.size()) {
if (code.code.get(ip).definition instanceof PushFalseIns) {
myStack.push(Boolean.FALSE);
} else if (code.code.get(ip).definition instanceof PushTrueIns) {
myStack.push(Boolean.TRUE);
} else if (code.code.get(ip).definition instanceof SwapIns) {
Boolean b1 = myStack.pop();
Boolean b2 = myStack.pop();
myStack.push(b1);
myStack.push(b2);
} else if (code.code.get(ip).definition instanceof JumpIns) {
try {
ip = code.adr2pos(code.pos2adr(ip + 1) + code.code.get(ip).operands[0]);
} catch (ConvertException ex) {
Logger.getLogger(Graph.class.getName()).log(Level.SEVERE, null, ex);
}
continue;
} else if (code.code.get(ip).definition instanceof SetLocalTypeIns) {
Boolean val = myStack.pop();
if (val == true) {
trueReg = ((SetLocalTypeIns) code.code.get(ip).definition).getRegisterId(code.code.get(ip));
} else {
falseReg = ((SetLocalTypeIns) code.code.get(ip).definition).getRegisterId(code.code.get(ip));
}
setCount++;
if (setCount == 2) {
return ip + 1;
}
}
ip++;
}
return 0;
}
private int optimizeDecisions(List<GraphPart> parts) {
for (int p = 0; p < parts.size(); p++) {
GraphPart part = parts.get(p);
if (part instanceof GraphDecision) {
/**
* if
* onTrue: nop
* nop
* link A
* onFalse: nop
* A
* B
*
* ==>
*
* if onTrue: nop
* nop
* onFalse: nop
* A
* B
*/
if (((GraphDecision) part).onTrue.size() > 0) {
GraphPart lastTruePart = ((GraphDecision) part).onTrue.get(((GraphDecision) part).onTrue.size() - 1);
if (lastTruePart instanceof GraphLink) {
for (int f = 0; f < ((GraphDecision) part).onFalse.size(); f++) {
if (((GraphDecision) part).onFalse.get(f).start == ((GraphLink) lastTruePart).ip) {
((GraphDecision) part).onFalse.get(f).linkCount--;
((GraphDecision) part).onTrue.remove(((GraphDecision) part).onTrue.size() - 1);
for (int k = f; k < ((GraphDecision) part).onFalse.size(); k++) {
parts.add(p + 1, ((GraphDecision) part).onFalse.remove(k));
}
return optimizeDecisions(parts) + 1;
}
}
}
}
/**
* if
* onTrue: nop
* nop
* A
* B
* onFalse: nop
* link A
*
* ==>
*
* if onTrue: nop
* nop
* onFalse: nop
* A
* B
*/
if (((GraphDecision) part).onFalse.size() > 0) {
GraphPart lastFalsePart = ((GraphDecision) part).onFalse.get(((GraphDecision) part).onFalse.size() - 1);
if (lastFalsePart instanceof GraphLink) {
for (int t = 0; t < ((GraphDecision) part).onTrue.size(); t++) {
if (((GraphDecision) part).onTrue.get(t).start == ((GraphLink) lastFalsePart).ip) {
((GraphDecision) part).onTrue.get(t).linkCount--;
((GraphDecision) part).onFalse.remove(((GraphDecision) part).onFalse.size() - 1);
for (int k = t; k < ((GraphDecision) part).onTrue.size(); k++) {
parts.add(p + 1, ((GraphDecision) part).onTrue.remove(k));
}
return optimizeDecisions(parts) + 1;
}
}
}
}
/*
* if
* onTrue: nop
* link A
* onFalse: nop
* link A
*
* ==>
* if
* onTrue: nop
* onFalse: nop
* link A
*
*/
if ((((GraphDecision) part).onTrue.size() > 0)&&(((GraphDecision) part).onFalse.size() > 0)) {
GraphPart lastFalsePart = ((GraphDecision) part).onFalse.get(((GraphDecision) part).onFalse.size() - 1);
GraphPart lastTruePart = ((GraphDecision) part).onTrue.get(((GraphDecision) part).onTrue.size() - 1);
if((lastFalsePart instanceof GraphLink)&&(lastTruePart instanceof GraphLink)){
if(((GraphLink)lastFalsePart).ip==((GraphLink)lastTruePart).ip){
((GraphDecision) part).onFalse.remove(((GraphDecision) part).onFalse.size()-1);
((GraphDecision) part).onTrue.remove(((GraphDecision) part).onTrue.size()-1);
parts.add(p+1,lastTruePart);
return optimizeDecisions(parts) + 1;
}
}
}
}
}
int optcount = 0;
for (int p = 0; p < parts.size(); p++) {
GraphPart part = parts.get(p);
if (part instanceof GraphDecision) {
optcount += optimizeDecisions(((GraphDecision) part).onTrue);
optcount += optimizeDecisions(((GraphDecision) part).onFalse);
}
}
return optcount;
}
private GraphBlock splitBlock(GraphBlock block, int ip, List<GraphBlock> allBlocks) {
return processSplit(block, parts, ip, allBlocks);
}
private GraphBlock processSplit(GraphBlock block, List<GraphPart> parts, int ip, List<GraphBlock> allBlocks) {
for (int i = 0; i < parts.size(); i++) {
if (parts.get(i) == block) {
parts.remove(i);
GraphBlock gr1 = new GraphBlock(block.start, ip - 1);
parts.add(i, gr1);
gr1.linkCount=block.linkCount;
GraphBlock gr2 = new GraphBlock(ip, block.end);
parts.add(i + 1, gr2);
allBlocks.remove(block);
allBlocks.add(gr1);
allBlocks.add(gr2);
return (GraphBlock) parts.get(i + 1);
} else if (parts.get(i) instanceof GraphDecision) {
GraphBlock gr = processSplit(block, ((GraphDecision) parts.get(i)).onTrue, ip, allBlocks);
if (gr != null) {
return gr;
}
gr = processSplit(block, ((GraphDecision) parts.get(i)).onFalse, ip, allBlocks);
if (gr != null) {
return gr;
}
}
}
return null;
}
private void makeGraph(Stack<Boolean> myStack,AVM2Code code, int start, List<GraphPart> parts, List<GraphBlock> allBlocks) {
try {
int ip = start;
while (ip < code.code.size()) {
for (GraphBlock block : allBlocks) {
if (block.contains(ip)) {
if (block.start < ip) {
block = splitBlock(block, ip, allBlocks);
}
if (ip - 1 >= start) {
GraphBlock bl = new GraphBlock(start, ip - 1);
parts.add(bl);
allBlocks.add(bl);
}
parts.add(new GraphLink(block));
return;
}
}
boolean forceJump = false;
boolean forceSkip = false;
if (code.code.get(ip).definition instanceof IfTrueIns) {
if (!myStack.empty()) {
if (myStack.pop() == true) {
forceJump = true;
} else {
forceSkip = true;
}
}
}
if (code.code.get(ip).definition instanceof IfFalseIns) {
if (!myStack.empty()) {
if (myStack.pop() == false) {
forceJump = true;
} else {
forceSkip = true;
}
}
}
if (code.code.get(ip).definition instanceof GetLocalTypeIns) {
int locreg = ((GetLocalTypeIns) code.code.get(ip).definition).getRegisterId(code.code.get(ip));
if (locreg == trueReg) {
myStack.push(Boolean.TRUE);
ignored.add(ip);
}
if (locreg == falseReg) {
myStack.push(Boolean.FALSE);
ignored.add(ip);
}
} else if (code.code.get(ip).definition instanceof PopIns) {
if (!myStack.empty()) {
myStack.pop();
ignored.add(ip);
}
} else if (code.code.get(ip).definition instanceof SwapIns) {
if (myStack.size() >= 2) {
Boolean b1 = myStack.pop();
Boolean b2 = myStack.pop();
myStack.push(b1);
myStack.push(b2);
ignored.add(ip);
}
} else if (code.code.get(ip).definition instanceof DupIns) {
if (!myStack.empty()) {
Boolean b = myStack.pop();
myStack.push(b);
myStack.push(b);
ignored.add(ip);
}
} else if ((code.code.get(ip).definition instanceof JumpIns) || forceJump) {
if (ip - 1 >= start) {
GraphBlock bl = new GraphBlock(start, ip - 1);
parts.add(bl);
allBlocks.add(bl);
}
int jumpIp = code.adr2pos(code.pos2adr(ip + 1) + code.code.get(ip).operands[0]);
makeGraph(myStack,code, jumpIp, parts, allBlocks);
return;
} else if (code.code.get(ip).definition instanceof IfTypeIns) {
if (forceSkip) {
ip++;
continue;
}
if (ip - 1 >= start) {
GraphBlock bl = new GraphBlock(start, ip - 1);
parts.add(bl);
allBlocks.add(bl);
}
int jumpIp = code.adr2pos(code.pos2adr(ip + 1) + code.code.get(ip).operands[0]);
GraphDecision dec = new GraphDecision();
parts.add(dec);
dec.start = ip;
makeGraph(myStack,code, jumpIp, dec.onTrue, allBlocks);
makeGraph(myStack,code, ip + 1, dec.onFalse, allBlocks);
return;
} else if ((code.code.get(ip).definition instanceof ReturnValueIns) || (code.code.get(ip).definition instanceof ReturnVoidIns)) {
ip++;
break;
}
ip++;
}
GraphFinalBlock bl = new GraphFinalBlock(start, ip - 1);
parts.add(bl);
allBlocks.add(bl);
} catch (ConvertException ex) {
Logger.getLogger(Graph.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2010 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.jpexs.asdec.abc.avm2.flowgraph;
/**
*
* @author JPEXS
*/
public class GraphBlock extends GraphPart {
public int end;
public GraphBlock(int start, int end) {
this.start = start;
this.end = end;
}
public boolean contains(int ip){
return ip>=start && ip<=end;
}
@Override
public String toString() {
return "Block "+(start+1)+"-"+(end+1)+(linkCount>0?" ("+linkCount+" links)":"");
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2010 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.jpexs.asdec.abc.avm2.flowgraph;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
public class GraphDecision extends GraphPart {
public List<GraphPart> onTrue=new ArrayList<GraphPart>();
public List<GraphPart> onFalse=new ArrayList<GraphPart>();
@Override
public String toString() {
return "Decision "+(start+1);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2010 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.jpexs.asdec.abc.avm2.flowgraph;
/**
*
* @author JPEXS
*/
public class GraphFinalBlock extends GraphBlock {
public GraphFinalBlock(int start,int end) {
super(start,end);
}
@Override
public String toString() {
return "Final"+super.toString();
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2010 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.jpexs.asdec.abc.avm2.flowgraph;
/**
*
* @author JPEXS
*/
public class GraphLink extends GraphPart {
public int ip=0;
public GraphLink(GraphPart linked) {
ip=linked.start;
linked.linkCount++;
}
@Override
public String toString() {
return "Link to "+(ip+1);
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2010 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.jpexs.asdec.abc.avm2.flowgraph;
/**
*
* @author JPEXS
*/
public class GraphPart {
public int start=0;
public int linkCount=0;
}

View File

@@ -23,6 +23,7 @@ import com.jpexs.asdec.abc.ABC;
import com.jpexs.asdec.abc.avm2.AVM2Code;
import com.jpexs.asdec.abc.avm2.ConstantPool;
import com.jpexs.asdec.abc.avm2.ConvertException;
import com.jpexs.asdec.abc.avm2.flowgraph.Graph;
import com.jpexs.asdec.abc.avm2.parser.ASM3Parser;
import com.jpexs.asdec.abc.avm2.parser.ParseException;
@@ -46,6 +47,10 @@ public class ASMSourceEditorPane extends JEditorPane {
setText(abc.bodies[bodyIndex].code.toASMSource(abc.constants));
}
public void graph(){
(new GraphFrame(new Graph(abc.bodies[bodyIndex].code))).setVisible(true);
}
public void save(ConstantPool constants) {
try {
AVM2Code acode = ASM3Parser.parse(new ByteArrayInputStream(getText().getBytes()), constants, new DialogMissingSymbolHandler());

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2010 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.jpexs.asdec.abc.gui;
import com.jpexs.asdec.abc.avm2.flowgraph.Graph;
import com.jpexs.asdec.abc.avm2.flowgraph.GraphDecision;
import java.awt.BorderLayout;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
/**
*
* @author JPEXS
*/
public class GraphFrame extends JFrame {
public JTree graphTree;
private class DecisionItem{
public GraphDecision decision;
public boolean isTrue;
public DecisionItem(GraphDecision decision, boolean isTrue) {
this.decision = decision;
this.isTrue = isTrue;
}
@Override
public String toString() {
if(isTrue){
return "onTrue";
}else{
return "onFalse";
}
}
}
public GraphFrame(final Graph graph){
setSize(400,400);
graphTree=new JTree(new TreeModel(){
private String root="root";
public Object getRoot() {
return root;
}
public Object getChild(Object parent, int index) {
if(parent==root){
return graph.parts.get(index);
}else{
if(parent instanceof GraphDecision){
if(index==0) return new DecisionItem((GraphDecision)parent,true);
return new DecisionItem((GraphDecision)parent,false);
}
if(parent instanceof DecisionItem){
DecisionItem di=(DecisionItem)parent;
if(di.isTrue) return di.decision.onTrue.get(index);
return di.decision.onFalse.get(index);
}
}
return null;
}
public int getChildCount(Object parent) {
if(parent==root){
return graph.parts.size();
}
if(parent instanceof GraphDecision){
return 2;
}
if(parent instanceof DecisionItem){
DecisionItem di=(DecisionItem)parent;
if(di.isTrue) return di.decision.onTrue.size();
return di.decision.onFalse.size();
}
return 0;
}
public boolean isLeaf(Object node) {
return getChildCount(node)==0;
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public int getIndexOfChild(Object parent, Object child) {
if(parent==root){
return graph.parts.indexOf(child);
}else{
if(parent instanceof GraphDecision){
if(child instanceof DecisionItem){
DecisionItem di=(DecisionItem)child;
if(di.isTrue) return 0;
return 1;
}
}
if(parent instanceof DecisionItem){
DecisionItem di=(DecisionItem)parent;
if(di.isTrue) return di.decision.onTrue.indexOf(child);
return di.decision.onFalse.indexOf(child);
}
}
return -1;
}
public void addTreeModelListener(TreeModelListener l) {
}
public void removeTreeModelListener(TreeModelListener l) {
}
});
Container cnt=getContentPane();
cnt.setLayout(new BorderLayout());
cnt.add(graphTree,BorderLayout.CENTER);
}
}

View File

@@ -182,7 +182,11 @@ public class MainFrame extends JFrame implements ActionListener, ItemListener {
saveButton.setActionCommand("SAVEBODY");
saveButton.addActionListener(this);
JButton graphButton = new JButton("Graph");
graphButton.setActionCommand("GRAPH");
graphButton.addActionListener(this);
//buttonsPan.add(graphButton);
buttonsPan.add(saveButton);
rightPanel.add(buttonsPan, BorderLayout.SOUTH);
decompiledTextArea = new DecompiledEditorPane();
@@ -332,6 +336,9 @@ public class MainFrame extends JFrame implements ActionListener, ItemListener {
Main.exit();
}
if (Main.isWorking()) return;
if (e.getActionCommand().equals("GRAPH")) {
sourceTextArea.graph();
}
if (e.getActionCommand().equals("SHOWPROXY")) {
Main.showProxy();
}

View File

@@ -181,7 +181,7 @@ public class MainFrame extends JFrame implements TreeSelectionListener, ActionLi
@Override
public void run() {
editor.setText(asm.getASMSource(10)); //TODO: Ensure correct version here
editor.setText(asm.getASMSource(10)); //TODO:Ensure correct version here
decompiledEditor.setText(Highlighting.stripHilights(com.jpexs.asdec.action.Action.actionsToSource(asm.getActions(), 10))); //TODO:Ensure correct version here
Main.stopWork();
}