More documentation.

This commit is contained in:
Jindra Petřík
2024-08-08 19:27:14 +02:00
parent 68954e714d
commit b57e38e387
286 changed files with 11752 additions and 3576 deletions
@@ -19,14 +19,22 @@ package com.jpexs.decompiler.graph;
import java.util.Collection;
/**
*
* Abstract graph target visitor.
* @author JPEXS
*/
public abstract class AbstractGraphTargetVisitor implements GraphTargetVisitorInterface {
/**
* Visits a graph target item.
* @param item Graph target item
*/
@Override
public abstract void visit(GraphTargetItem item);
/**
* Visits all graph target items.
* @param items Collection of graph target items
*/
@Override
public final void visitAll(Collection<GraphTargetItem> items) {
for (GraphTargetItem item : items) {
@@ -17,15 +17,25 @@
package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.graph.model.ContinueItem;
import java.util.List;
/**
*
* Block interface.
* For example, a block can be a loop, if statement, or a function.
* @author JPEXS
*/
public interface Block {
/**
* Gets all sub continues.
* @return List of continues
*/
public List<ContinueItem> getContinues();
/**
* Gets all sub blocks.
* @return List of blocks
*/
public List<List<GraphTargetItem>> getSubs();
}
@@ -17,15 +17,26 @@
package com.jpexs.decompiler.graph;
/**
*
* Compilation exception.
* @author JPEXS
*/
public class CompilationException extends Exception {
/**
* Line number.
*/
public int line;
/**
* Error message.
*/
public String text;
/**
* Constructs a new compilation exception.
* @param message Error message
* @param line Line number
*/
public CompilationException(String message, int line) {
super("Compilation error on line " + line + ": " + message);
this.line = line;
@@ -18,6 +18,7 @@ package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.flash.IdentifiersDeobfuscation;
import com.jpexs.helpers.Helper;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
@@ -26,11 +27,14 @@ import java.util.List;
import java.util.Objects;
/**
*
* Dotted chain class.
* Represents a chain of names separated by dots.
* @author JPEXS
*/
public class DottedChain implements Serializable, Comparable<DottedChain> {
//Basic dotted chains
public static final DottedChain EMPTY = new DottedChain(true);
public static final DottedChain UNBOUNDED = new DottedChain(new String[]{"*"});
@@ -65,14 +69,29 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
public static final DottedChain ALL = new DottedChain(new String[]{"*"});
/**
* Parts of the chain.
*/
private List<PathPart> parts;
/**
* Is this chain null?
*/
private boolean isNull = false;
/**
* Get the namespace suffix of the part at the given index.
* @param index Index
* @return Namespace suffix
*/
public String getNamespaceSuffix(int index) {
return parts.get(index).namespaceSuffix;
}
/**
* Gets the last namespace suffix.
* @return Last namespace suffix
*/
public String getLastNamespaceSuffix() {
if (parts.isEmpty()) {
return "";
@@ -80,6 +99,11 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return parts.get(parts.size() - 1).namespaceSuffix;
}
/**
* Parses a dotted chain from a string without suffix.
* @param name Name
* @return Dotted chain
*/
public static final DottedChain parseNoSuffix(String name) {
if (name == null) {
return DottedChain.EMPTY;
@@ -96,6 +120,11 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
}
}
/**
* Parses a dotted chain from a string with suffix.
* @param name Name
* @return Dotted chain
*/
public static final DottedChain parseWithSuffix(String name) {
if (name == null) {
return DottedChain.EMPTY;
@@ -118,25 +147,46 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
}
}
/**
* Constructs a new dotted chain.
* @param isNull Whether the chain is null
*/
private DottedChain(boolean isNull) {
this.isNull = isNull;
this.parts = new ArrayList<>();
}
/**
* Constructs a new dotted chain.
* @param src Source chain
*/
public DottedChain(DottedChain src) {
this.parts = new ArrayList<>(src.parts);
this.isNull = src.isNull;
}
/**
* Constructs a new dotted chain.
* @param parts Parts
*/
public DottedChain(String[] parts) {
this(Arrays.asList(parts));
}
/**
* Constructs a new dotted chain.
* @param parts Parts
* @param isNull Whether the chain is null
*/
private DottedChain(List<PathPart> parts, boolean isNull) {
this.parts = parts;
this.isNull = isNull;
}
/**
* Constructs a new dotted chain.
* @param parts Parts
*/
public DottedChain(List<String> parts) {
List<PathPart> newParts = new ArrayList<>();
for (String part : parts) {
@@ -145,10 +195,21 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
this.parts = newParts;
}
/**
* Constructs a new dotted chain.
* @param parts Parts
* @param namespaceSuffixes Namespace suffixes
*/
public DottedChain(String[] parts, String[] namespaceSuffixes) {
this(new boolean[parts.length], parts, namespaceSuffixes);
}
/**
* Constructs a new dotted chain.
* @param attributes Attributes
* @param parts Parts
* @param namespaceSuffixes Namespace suffixes
*/
public DottedChain(boolean[] attributes, String[] parts, String[] namespaceSuffixes) {
List<PathPart> newParts = new ArrayList<>();
for (int i = 0; i < attributes.length; i++) {
@@ -158,18 +219,35 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
}
/**
* Checks whether this chain is top-level.
* @return Whether this chain is top-level
*/
public boolean isTopLevel() {
return !isNull && parts.isEmpty();
}
/**
* Checks whether this chain is empty.
* @return Whether this chain is empty
*/
public boolean isEmpty() {
return isNull;
}
/**
* Gets the number of parts in this chain.
* @return Number of parts
*/
public int size() {
return parts.size();
}
/**
* Gets the part at the given index.
* @param index Index
* @return Part
*/
public String get(int index) {
if (index >= parts.size()) {
throw new ArrayIndexOutOfBoundsException();
@@ -178,6 +256,11 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return parts.get(index).name;
}
/**
* Checks whether the part at the given index is an attribute.
* @param index Index
* @return Whether the part at the given index is an attribute
*/
public boolean isAttribute(int index) {
if (index >= parts.size()) {
throw new ArrayIndexOutOfBoundsException();
@@ -185,6 +268,11 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return parts.get(index).attribute;
}
/**
* Gets the sub-chain of specific length.
* @param count Length
* @return
*/
public DottedChain subChain(int count) {
if (count > parts.size()) {
throw new ArrayIndexOutOfBoundsException();
@@ -193,6 +281,10 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return new DottedChain(new ArrayList<>(parts.subList(0, count)), isNull);
}
/**
* Gets last part.
* @return Last part
*/
public String getLast() {
if (isNull) {
return null;
@@ -204,6 +296,10 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
}
}
/**
* Checks whether the last part is an attribute.
* @return Whether the last part is an attribute
*/
public boolean isLastAttribute() {
if (isNull) {
return false;
@@ -215,6 +311,10 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
}
}
/**
* Gets the chain without the last part.
* @return Chain without the last part
*/
public DottedChain getWithoutLast() {
if (isNull) {
return null;
@@ -226,6 +326,11 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return subChain(parts.size() - 1);
}
/**
* Adds a part to the chain with a suffix.
* @param name Name
* @return New chain
*/
public DottedChain addWithSuffix(String name) {
String addedNameNoSuffix = name;
String addedNamespaceSuffix = "";
@@ -236,10 +341,23 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return add(addedNameNoSuffix, addedNamespaceSuffix);
}
/**
* Adds a part to the chain.
* @param name Name
* @param namespaceSuffix Namespace suffix
* @return New chain
*/
public DottedChain add(String name, String namespaceSuffix) {
return add(false, name, namespaceSuffix);
}
/**
* Adds a part to the chain.
* @param attribute Whether the part is an attribute
* @param name Name
* @param namespaceSuffix Namespace suffix
* @return New chain
*/
public DottedChain add(boolean attribute, String name, String namespaceSuffix) {
if (name == null) {
return new DottedChain(this);
@@ -249,10 +367,23 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return new DottedChain(newParts, false);
}
/**
* Adds prefix to the chain.
* @param name Name
* @param namespaceSuffix Namespace suffix
* @return New chain
*/
public DottedChain preAdd(String name, String namespaceSuffix) {
return preAdd(false, name, namespaceSuffix);
}
/**
* Adds prefix to the chain.
* @param attribute Whether the part is an attribute
* @param name Name
* @param namespaceSuffix Namespace suffix
* @return New chain
*/
public DottedChain preAdd(boolean attribute, String name, String namespaceSuffix) {
if (name == null) {
return new DottedChain(this);
@@ -262,11 +393,22 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return new DottedChain(newParts, false);
}
/**
* To string.
* @return String
*/
@Override
public String toString() {
return toRawString();
}
/**
* To string.
* @param as3 Whether to print as AS3
* @param raw Whether to print raw (without deobfuscation)
* @param withSuffix Whether to print with suffix
* @return String
*/
protected String toString(boolean as3, boolean raw, boolean withSuffix) {
if (isNull) {
return "";
@@ -294,6 +436,10 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return ret.toString();
}
/**
* To file path.
* @return File path
*/
public String toFilePath() {
if (isNull) {
return "";
@@ -313,6 +459,10 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return ret.toString();
}
/**
* To list.
* @return List
*/
public List<String> toList() {
List<String> ret = new ArrayList<>();
for (PathPart p : parts) {
@@ -321,14 +471,27 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return ret;
}
/**
* To printable string.
* @param as3 Whether to print as AS3
* @return Printable string
*/
public String toPrintableString(boolean as3) {
return toString(as3, false, true);
}
/**
* To raw string. (without deobfuscation)
* @return Raw string
*/
public String toRawString() { //Is SUFFIX correctly handled?
return toString(false/*ignored*/, true, true);
}
/**
* Hash code.
* @return Hash code
*/
@Override
public int hashCode() {
int hash = 7;
@@ -337,6 +500,11 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return hash;
}
/**
* Equals.
* @param obj Object
* @return Whether this object is equal to the given object
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -355,23 +523,52 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return Objects.equals(this.parts, other.parts);
}
/**
* Compare to.
* @param o the object to be compared.
* @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
*/
@Override
public int compareTo(DottedChain o) {
return toRawString().compareTo(o.toRawString());
}
/**
* Path part class.
*/
private static class PathPart implements Serializable {
/**
* Name.
*/
public String name;
/**
* Is this part an attribute?
*/
public boolean attribute;
/**
* Namespace suffix.
*/
public String namespaceSuffix;
/**
* Constructs a new path part.
* @param name Name
* @param attribute Whether this part is an attribute
* @param namespaceSuffix Namespace suffix
*/
public PathPart(String name, boolean attribute, String namespaceSuffix) {
this.name = name;
this.attribute = attribute;
this.namespaceSuffix = namespaceSuffix;
}
/**
* Hash code.
* @return
*/
@Override
public int hashCode() {
int hash = 5;
@@ -381,6 +578,11 @@ public class DottedChain implements Serializable, Comparable<DottedChain> {
return hash;
}
/**
* Equals.
* @param obj Object
* @return Whether this object is equal to the given object
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -19,7 +19,7 @@ package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.graph.model.BinaryOp;
/**
*
* Equals type item.
* @author JPEXS
*/
public interface EqualsTypeItem extends BinaryOp {
File diff suppressed because it is too large Load Diff
@@ -17,15 +17,31 @@
package com.jpexs.decompiler.graph;
/**
*
* Exception thrown in the graph.
* @author JPEXS
*/
public class GraphException {
/**
* Start IP of thrown block
*/
public int start;
/**
* End IP of thrown block
*/
public int end;
/**
* Target IP to go when exception is thrown
*/
public int target;
/**
* Constructs a new exception
* @param start Start IP
* @param end End IP
* @param target Target IP
*/
public GraphException(int start, int end, int target) {
this.start = start;
this.end = end;
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.graph;
/**
*
* @author JPEXS
*/
public class GraphExceptionParts {
public GraphPart start;
public GraphPart end;
public GraphPart target;
public GraphExceptionParts(GraphPart start, GraphPart end, GraphPart target) {
this.start = start;
this.end = end;
this.target = target;
}
}
@@ -17,6 +17,7 @@
package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.flash.BaseLocalData;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
@@ -24,90 +25,80 @@ import java.util.List;
import java.util.Stack;
/**
*
* Represents a part of a graph.
* Block of instructions which are executed in sequence.
* No jumps or branches are allowed inside a GraphPart.
* @author JPEXS
*/
public class GraphPart implements Serializable {
public static final int TYPE_NONE = 0;
public static final int TYPE_LOOP_HEADER = 1;
public static final int TYPE_PRELOOP = 3;
public static final int TYPE_REENTRY = 2;
public boolean traversed = false;
public int DFSP_pos = 0;
public GraphPart iloop_header;
public int type = TYPE_NONE;
public boolean irreducible = false;
/**
* Start IP
*/
public int start = 0;
/**
* End IP
*/
public int end = 0;
public int instanceCount = 0;
/**
* Next parts
*/
public List<GraphPart> nextParts = new ArrayList<>();
public int posX = -1;
public int posY = -1;
/**
* Path
*/
public GraphPath path = new GraphPath();
/**
* Previous parts
*/
public List<GraphPart> refs = new ArrayList<>();
public boolean ignored = false;
/**
* Level in the graph
*/
public int level;
/**
* Discovered time in DFS
*/
public int discoveredTime;
/**
* Finished time in DFS
* Calculated in setTime.
*/
public int finishedTime;
/**
* Closed time.
* The node is closed when all its input edges are already visited.
* Calculated in Graph.calculateClosedTime.
*/
public int closedTime;
/**
* Order in DFS.
* Calculated in setTime.
*/
public int order;
/**
* Number of parts following this part.
* Calculated in setNumblocks.
*/
public int numBlocks = Integer.MAX_VALUE;
//public List<GraphPart> throwParts = new ArrayList<>();
public enum StopPartType {
NONE, AND_OR, COMMONPART
}
//public StopPartType stopPartType = StopPartType.NONE;
//public TranslateStack andOrStack; // Stores stack when AND_OR stopPart has been reached
/*public class CommonPartStack { // Stores stack when COMMONPART stopPart has been reached
boolean isTrueStack;
TranslateStack trueStack;
TranslateStack falseStack;
}/
//public ArrayList<CommonPartStack> commonPartStacks;
/* public void setAndOrStack(TranslateStack stack) {
andOrStack = stack;
}
public void setCommonPartStack(TranslateStack stack) {
CommonPartStack currentStack = commonPartStacks.get(commonPartStacks.size() - 1);
if (currentStack.isTrueStack) {
currentStack.trueStack = stack;
} else {
currentStack.falseStack = stack;
}
}*/
/**
* Sets the time of this part in DFS.
* @param time Time
* @param ordered Ordered parts
* @param visited Visited parts
* @return Time
*/
public int setTime(int time, List<GraphPart> ordered, List<GraphPart> visited) {
if (visited.contains(this)) {
return time;
@@ -126,6 +117,10 @@ public class GraphPart implements Serializable {
return time;
}
/**
* Sets the number of blocks following this part.
* @param numBlocks Number of blocks
*/
public void setNumblocks(int numBlocks) {
this.numBlocks = numBlocks;
numBlocks++;
@@ -136,72 +131,20 @@ public class GraphPart implements Serializable {
}
}
private boolean leadsToRecursive(BaseLocalData localData, Graph gr, GraphSource code, GraphPart prev, GraphPart part, HashSet<GraphPart> visited, List<Loop> loops, List<ThrowState> throwStates, boolean useThrow) throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
GraphPart tpart = gr.checkPart(null, localData, prev, this, null);
if (tpart == null) {
return false;
}
if (tpart != this) {
return tpart.leadsTo(localData, gr, code, null, part, visited, loops, throwStates, useThrow);
}
Loop currentLoop = null;
for (Loop l : loops) {
/*if(l.phase==0){
if(l.loopContinue==this){
l.leadsToMark = 1;
next = l.loopBreak;
currentLoop = l;
continue;
}
}*/
if (l.phase == 1) {
if (l.loopContinue == this) {
return false;
}
if (l.loopPreContinue == this) {
return false;
}
if (l.loopBreak == this) {
//return false; //?
}
}
}
if (visited.contains(this)) {
return false;
}
/*if (loops.contains(this)) {
return false;
}*/
visited.add(this);
if (end < code.size() && code.get(end).isBranch() && (code.get(end).ignoredLoops())) {
return false;
}
for (GraphPart p : nextParts) {
if (p == part) {
return true;
} else if (p.leadsTo(localData, gr, code, this, part, visited, loops, throwStates, useThrow)) {
return true;
}
}
for (ThrowState ts : throwStates) {
if (ts.state != 1) {
if (ts.throwingParts.contains(this)) {
GraphPart p = ts.targetPart;
if (p == part) {
return true;
} else if (p.leadsTo(localData, gr, code, this, part, visited, loops, throwStates, useThrow)) {
return true;
}
}
}
}
return false;
}
/**
* Checks if this part leads to another part.
* @param localData Local data
* @param gr Graph
* @param code Code
* @param prev Previous part
* @param part Part to check
* @param visited Visited parts
* @param loops Loops
* @param throwStates Throw states
* @param useThrow Use throw
* @return True if this part leads to the other part
* @throws InterruptedException
*/
private boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart prev, GraphPart part, HashSet<GraphPart> visited, List<Loop> loops, List<ThrowState> throwStates, boolean useThrow) throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
@@ -271,6 +214,18 @@ public class GraphPart implements Serializable {
return false;
}
/**
* Checks if this part leads to another part.
* @param localData Local data
* @param gr Graph
* @param code Code
* @param part Part to check
* @param loops Loops
* @param throwStates Throw states
* @param useThrow Use throw
* @return True if this part leads to the other part
* @throws InterruptedException
*/
public boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart part, List<Loop> loops, List<ThrowState> throwStates, boolean useThrow) throws InterruptedException {
for (Loop l : loops) {
l.leadsToMark = 0;
@@ -278,71 +233,20 @@ public class GraphPart implements Serializable {
return leadsTo(localData, gr, code, null /*???*/, part, new HashSet<>(), loops, throwStates, useThrow);
}
/**
* Constructs a new GraphPart.
* @param start Start IP
* @param end End IP
*/
public GraphPart(int start, int end) {
this.start = start;
this.end = end;
}
private GraphPart getNextPartPath(GraphPart original, GraphPath path, List<GraphPart> visited) {
if (visited.contains(this) && (this == original)) {
return null;
}
if (visited.contains(this) && (this != original)) {
return null;
}
visited.add(this);
for (GraphPart p : nextParts) {
if (p == original) {
continue;
}
if (p.path.equals(path)) {
return p;
} else if (p.path.length() >= path.length()) {
GraphPart gp = p.getNextPartPath(original, path, visited);
if (gp != null) {
return gp;
}
}
}
return null;
}
public GraphPart getNextPartPath(List<GraphPart> ignored) {
List<GraphPart> visited = new ArrayList<>();
visited.addAll(ignored);
if (visited.contains(this)) {
visited.remove(this);
}
return getNextPartPath(this, path, visited);
}
public GraphPart getNextSuperPartPath(List<GraphPart> ignored) {
List<GraphPart> visited = new ArrayList<>();
visited.addAll(ignored);
return getNextSuperPartPath(this, path, visited);
}
private GraphPart getNextSuperPartPath(GraphPart original, GraphPath path, List<GraphPart> visited) {
if (visited.contains(this)) {
return null;
}
visited.add(this);
for (GraphPart p : nextParts) {
if (p == original) {
continue;
}
if (p.path.length() < path.length()) {
return p;
} else {
GraphPart gp = p.getNextSuperPartPath(original, path, visited);
if (gp != null) {
return gp;
}
}
}
return null;
}
/**
* To string.
* @return String representation
*/
@Override
public String toString() {
if (end < start) {
@@ -352,47 +256,48 @@ public class GraphPart implements Serializable {
int printEnd = end + 1;
return "" + (printStart < 0 ? "(" : "") + printStart + (printStart < 0 ? ")" : "")
+ "-" + (printEnd < 0 ? "(" : "") + printEnd + (printEnd < 0 ? ")" : "") + (instanceCount > 1 ? "(" + instanceCount + " links)" : "");
+ "-" + (printEnd < 0 ? "(" : "") + printEnd + (printEnd < 0 ? ")" : "");
}
/**
* Checks if this part contains an IP.
* @param ip IP
* @return True if this part contains the IP
*/
public boolean containsIP(int ip) {
return (ip >= start) && (ip <= end);
}
/**
* Gets the height of this part - number of instructions in this part.
* @return Height
*/
public int getHeight() {
return end - start + 1;
}
/**
* Gets IP at offset from start.
* @param offset Offset
* @return IP
*/
public int getPosAt(int offset) {
return start + offset;
}
private boolean containsPart(GraphPart part, GraphPart what, List<GraphPart> used) {
if (used.contains(part)) {
return false;
}
used.add(part);
for (GraphPart subpart : part.nextParts) {
if (subpart == what) {
return true;
}
if (containsPart(subpart, what, used)) {
return true;
}
}
return false;
}
public boolean containsPart(GraphPart what) {
return containsPart(this, what, new ArrayList<>());
}
/**
* Gets sub parts. Currently only self is allowed.
*/
public List<GraphPart> getSubParts() {
List<GraphPart> ret = new ArrayList<>();
ret.add(this);
return ret;
}
/**
* Hash code.
* @return Hash code
*/
@Override
public int hashCode() {
int hash = 3;
@@ -400,6 +305,11 @@ public class GraphPart implements Serializable {
return hash;
}
/**
* Equals.
* @param obj Object
* @return True if equals
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
@@ -19,23 +19,43 @@ package com.jpexs.decompiler.graph;
import java.util.List;
/**
*
* Exception when part of the graph is changed.
* @author JPEXS
*/
public class GraphPartChangeException extends Exception {
/**
* IP
*/
private final int ip;
/**
* Output
*/
private final List<GraphTargetItem> output;
/**
* Constructs a new GraphPartChangeException
* @param output Output
* @param ip IP
*/
public GraphPartChangeException(List<GraphTargetItem> output, int ip) {
this.output = output;
this.ip = ip;
}
/**
* Gets the IP
* @return IP
*/
public int getIp() {
return ip;
}
/**
* Gets the output
* @return Output
*/
public List<GraphTargetItem> getOutput() {
return output;
}
@@ -1,69 +0,0 @@
/*
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.graph;
import java.util.Objects;
/**
*
* @author JPEXS
*/
public class GraphPartDecision {
public GraphPart part;
public int way;
public GraphPartDecision(GraphPart part, int way) {
this.part = part;
this.way = way;
}
@Override
public String toString() {
return part.toString() + ":" + way;
}
@Override
public int hashCode() {
int hash = 3;
hash = 59 * hash + Objects.hashCode(this.part);
hash = 59 * hash + this.way;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final GraphPartDecision other = (GraphPartDecision) obj;
if (this.way != other.way) {
return false;
}
if (!Objects.equals(this.part, other.part)) {
return false;
}
return true;
}
}
@@ -19,19 +19,35 @@ package com.jpexs.decompiler.graph;
import java.util.Objects;
/**
*
* Edge of a graph. Represents a connection between two GraphParts.
* @author JPEXS
*/
public class GraphPartEdge {
/**
* From part
*/
public GraphPart from;
/**
* To part
*/
public GraphPart to;
/**
* Constructs a new edge
* @param from From
* @param to To
*/
public GraphPartEdge(GraphPart from, GraphPart to) {
this.from = from;
this.to = to;
}
/**
* Hash code
* @return Hash code
*/
@Override
public int hashCode() {
int hash = 3;
@@ -40,6 +56,11 @@ public class GraphPartEdge {
return hash;
}
/**
* Equals
* @param obj Object to compare
* @return True if equals
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -61,6 +82,10 @@ public class GraphPartEdge {
return true;
}
/**
* To string
* @return String representation
*/
@Override
public String toString() {
return from.toString() + " -> " + to.toString();
@@ -21,14 +21,25 @@ import java.util.Collection;
import java.util.List;
/**
*
* List which can store parts of the graph for each element.
* @author JPEXS
*/
public class GraphPartMarkedArrayList<E> extends ArrayList<E> {
/**
* List of parts for each element.
*/
private List<List<GraphPart>> listParts = new ArrayList<>();
/**
* Current parts.
*/
private List<GraphPart> currentParts = new ArrayList<>();
/**
* Constructs GraphPartMarkedArrayList from another collection.
* @param collection
*/
@SuppressWarnings("unchecked")
public GraphPartMarkedArrayList(Collection<? extends E> collection) {
super(collection);
@@ -44,33 +55,63 @@ public class GraphPartMarkedArrayList<E> extends ArrayList<E> {
}
}
/**
* Constructs GraphPartMarkedArrayList.
*/
public GraphPartMarkedArrayList() {
}
/**
* Starts new part.
* @param part Part
*/
public void startPart(GraphPart part) {
currentParts.add(part);
}
/**
* Clears current parts.
*/
public void clearCurrentParts() {
currentParts = new ArrayList<>();
}
/**
* Adds element to the collection.
* @param e element whose presence in this collection is to be ensured
* @return true if this collection changed as a result of the call
*/
@Override
public boolean add(E e) {
listParts.add(currentParts);
return super.add(e);
}
/**
* Inserts the specified element at the specified position in this list.
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
*/
@Override
public void add(int index, E element) {
listParts.add(index, currentParts);
super.add(index, element);
}
/**
* Returns the parts at the specified index.
* @param index index of the element
* @return parts at the specified index
*/
public List<GraphPart> getPartsAt(int index) {
return listParts.get(index);
}
/**
* Gets the index of the part in the list.
* @param part
* @return
*/
public int indexOfPart(GraphPart part) {
for (int i = 0; i < listParts.size(); i++) {
List<GraphPart> list = listParts.get(i);
@@ -81,6 +122,11 @@ public class GraphPartMarkedArrayList<E> extends ArrayList<E> {
return -1;
}
/**
* Adds all elements from the collection to this collection.
* @param c collection containing elements to be added to this collection
* @return true if this collection changed as a result of the call
*/
@SuppressWarnings("unchecked")
@Override
public boolean addAll(Collection<? extends E> c) {
@@ -96,6 +142,13 @@ public class GraphPartMarkedArrayList<E> extends ArrayList<E> {
return super.addAll(c);
}
/**
* Inserts all elements in the specified collection into this list at the
* @param index index at which to insert the first element from the
* specified collection
* @param c collection containing elements to be added to this list
* @return true if this list changed as a result of the call
*/
@SuppressWarnings("unchecked")
@Override
public boolean addAll(int index, Collection<? extends E> c) {
@@ -111,6 +164,11 @@ public class GraphPartMarkedArrayList<E> extends ArrayList<E> {
return super.addAll(index, c);
}
/**
* Removes the first occurrence of the specified element from this list, if
* @param o element to be removed from this list, if present
* @return true if an element was removed as a result of this call
*/
@Override
public boolean remove(Object o) {
if (contains(o)) {
@@ -119,18 +177,32 @@ public class GraphPartMarkedArrayList<E> extends ArrayList<E> {
return super.remove(o);
}
/**
* Removes the element at the specified position in this list. Shifts any
* @param index the index of the element to be removed
* @return the element that was removed from the list
*/
@Override
public E remove(int index) {
listParts.remove(index);
return super.remove(index);
}
/**
* Clears the list.
*/
@Override
public void clear() {
listParts.clear();
super.clear();
}
/**
* Returns a view of the portion of this list between the specified
* @param fromIndex low endpoint (inclusive) of the subList
* @param toIndex high endpoint (exclusive) of the subList
* @return a view of the specified range within this list
*/
@Override
public List<E> subList(int fromIndex, int toIndex) {
GraphPartMarkedArrayList<E> ret = new GraphPartMarkedArrayList<E>(this);
@@ -143,11 +215,20 @@ public class GraphPartMarkedArrayList<E> extends ArrayList<E> {
return ret;
}
/**
* Returns a shallow copy of this ArrayList instance.
* @return a clone of this ArrayList instance
*/
@Override
public Object clone() {
return new GraphPartMarkedArrayList<>(this);
}
/**
* Removes from this list all of its elements that are contained in the
* @param c collection containing elements to be removed from this list
* @return true if this list changed as a result of the call
*/
@Override
public boolean removeAll(Collection<?> c) {
for (Object o : c) {
@@ -20,17 +20,24 @@ import java.util.Collection;
import java.util.LinkedList;
/**
*
* Queue of GraphPart objects.
* @author JPEXS
*/
public class GraphPartQueue extends LinkedList<GraphPart> {
public Loop currentLoop;
/**
* Constructs a GraphPartQueue containing the elements of the specified collection.
* @param c
*/
public GraphPartQueue(Collection<? extends GraphPart> c) {
super(c);
}
/**
* Constructs an empty GraphPartQueue.
*/
public GraphPartQueue() {
}
}
@@ -22,93 +22,159 @@ import java.util.List;
import java.util.Objects;
/**
*
* Represents a path in a graph.
* @author JPEXS
*/
public class GraphPath implements Serializable {
private final List<Integer> keys = new ArrayList<>();
/**
* List of IPs where the Path branched
*/
private final List<Integer> branchIps = new ArrayList<>();
private final List<Integer> vals = new ArrayList<>();
/**
* List of branch indices of which way the path went
*/
private final List<Integer> branchIndices = new ArrayList<>();
/**
* Name of the root
*/
public final String rootName;
public GraphPath(String rootName, List<Integer> keys, List<Integer> vals) {
/**
* Constructs a GraphPath with the given root name, branch IPs and branch indices.
* @param rootName Root name
* @param branchIps Branch IPs
* @param branchIndices Branch indices
*/
public GraphPath(String rootName, List<Integer> branchIps, List<Integer> branchIndices) {
this.rootName = rootName;
this.keys.addAll(keys);
this.vals.addAll(vals);
this.branchIps.addAll(branchIps);
this.branchIndices.addAll(branchIndices);
}
public GraphPath(List<Integer> keys, List<Integer> vals) {
/**
* Constructs a GraphPath with the given branch IPs and branch indices.
* @param branchIps Branch IPs
* @param branchIndices Branch indices
*/
public GraphPath(List<Integer> branchIps, List<Integer> branchIndices) {
rootName = "";
this.keys.addAll(keys);
this.vals.addAll(vals);
this.branchIps.addAll(branchIps);
this.branchIndices.addAll(branchIndices);
}
/**
* Constructs a GraphPath
*/
public GraphPath() {
rootName = "";
}
/**
* Checks whether the path starts with the given path.
* @param p
* @return True if the path starts with the given path, false otherwise
*/
public boolean startsWith(GraphPath p) {
if (p.length() > length()) {
return false;
}
List<Integer> otherKeys = new ArrayList<>(p.keys);
List<Integer> otherVals = new ArrayList<>(p.vals);
List<Integer> otherKeys = new ArrayList<>(p.branchIps);
List<Integer> otherVals = new ArrayList<>(p.branchIndices);
for (int i = 0; i < p.length(); i++) {
if (!Objects.equals(keys.get(i), otherKeys.get(i))) {
if (!Objects.equals(branchIps.get(i), otherKeys.get(i))) {
return false;
}
if (!Objects.equals(vals.get(i), otherVals.get(i))) {
if (!Objects.equals(branchIndices.get(i), otherVals.get(i))) {
return false;
}
}
return true;
}
/**
* Returns a new parent GraphPath with the given length.
* @param len Length
* @return
*/
public GraphPath parent(int len) {
GraphPath par = new GraphPath(rootName);
for (int i = 0; i < len; i++) {
par.keys.add(keys.get(i));
par.vals.add(vals.get(i));
par.branchIps.add(branchIps.get(i));
par.branchIndices.add(branchIndices.get(i));
}
return par;
}
public GraphPath sub(int part, int codePos) {
GraphPath next = new GraphPath(rootName, this.keys, this.vals);
next.keys.add(codePos);
next.vals.add(part);
/**
* Returns a new sub GraphPath with the given branch index and code position.
* @param branchIndex Branch index
* @param codePos Code position
* @return New sub GraphPath
*/
public GraphPath sub(int branchIndex, int codePos) {
GraphPath next = new GraphPath(rootName, this.branchIps, this.branchIndices);
next.branchIps.add(codePos);
next.branchIndices.add(branchIndex);
return next;
}
/**
* Constructs a GraphPath with the given root name.
* @param rootName Root name
*/
public GraphPath(String rootName) {
this.rootName = rootName;
}
/**
* Gets length of the path.
* @return Length
*/
public int length() {
return vals.size();
return branchIndices.size();
}
/**
* Gets the branch index at the given index.
* @param index Index
* @return Branch index
*/
public int get(int index) {
return vals.get(index);
return branchIndices.get(index);
}
/**
* Gets the IP at the given index.
* @param index Index
* @return IP
*/
public int getKey(int index) {
return keys.get(index);
return branchIps.get(index);
}
/**
* Hash code.
* @return Hash code
*/
@Override
public int hashCode() {
int hash = 5;
hash = 23 * hash + arrHashCode(keys);
hash = 23 * hash + arrHashCode(vals);
hash = 23 * hash + arrHashCode(branchIps);
hash = 23 * hash + arrHashCode(branchIndices);
hash = 23 * hash + Objects.hashCode(rootName);
return hash;
}
/**
* Equals.
* @param obj Eq
* @return
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
@@ -126,17 +192,22 @@ public class GraphPath implements Serializable {
return false;
}
if (!arrMatch(keys, other.keys)) {
if (!arrMatch(branchIps, other.branchIps)) {
return false;
}
if (!arrMatch(vals, other.vals)) {
if (!arrMatch(branchIndices, other.branchIndices)) {
return false;
}
return true;
}
/**
* Hash code for a list of integers.
* @param arr List of integers
* @return Hash code
*/
private static int arrHashCode(List<Integer> arr) {
if (arr == null || arr.isEmpty()) {
return 0;
@@ -150,6 +221,12 @@ public class GraphPath implements Serializable {
return hash;
}
/**
* Checks whether two lists of integers match.
* @param arr List of integers
* @param arr2 List of integers
* @return True if the lists match, false otherwise
*/
private static boolean arrMatch(List<Integer> arr, List<Integer> arr2) {
if (arr.size() != arr2.size()) {
return false;
@@ -162,11 +239,15 @@ public class GraphPath implements Serializable {
return true;
}
/**
* Returns a string representation of the GraphPath.
* @return String representation
*/
@Override
public String toString() {
String ret = rootName;
for (int i = 0; i < keys.size(); i++) {
ret += "/" + keys.get(i) + ":" + vals.get(i);
for (int i = 0; i < branchIps.size(); i++) {
ret += "/" + branchIps.get(i) + ":" + branchIndices.get(i);
}
return ret;
}
@@ -18,6 +18,7 @@ package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.flash.BaseLocalData;
import com.jpexs.decompiler.flash.action.Action;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
@@ -25,23 +26,67 @@ import java.util.List;
import java.util.Set;
/**
*
* Graph source abstract class
* @author JPEXS
*/
public abstract class GraphSource implements Serializable {
/**
* Gets the size of the graph source
* @return The size of the graph source
*/
public abstract int size();
/**
* Gets the graph source item at the specified position
* @param pos Position of the graph source item
* @return The graph source item at the specified position
*/
public abstract GraphSourceItem get(int pos);
/**
* Checks if the graph source is empty
* @return True if the graph source is empty, false otherwise
*/
public abstract boolean isEmpty();
/**
* Translates the part of the graph source
* @param graph Graph
* @param part Graph part
* @param localData Local data
* @param stack Translate stack
* @param start Start position
* @param end End position
* @param staticOperation Unused
* @param path Path
* @return List of graph target items
* @throws InterruptedException
* @throws GraphPartChangeException
*/
public abstract List<GraphTargetItem> translatePart(Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException, GraphPartChangeException;
/**
* Gets the important addresses
* @return Set of important addresses
*/
public abstract Set<Long> getImportantAddresses();
/**
* Converts instruction at the specified position to string
* @param pos Position of the instruction
* @return Instruction as string
*/
public abstract String insToString(int pos);
/**
* Visits the code
* @param ip Start position
* @param lastIp Last position
* @param refs References
* @param endIp End position
* @throws InterruptedException
*/
private void visitCode(int ip, int lastIp, HashMap<Integer, List<Integer>> refs, int endIp) throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
@@ -96,6 +141,12 @@ public abstract class GraphSource implements Serializable {
}
}
/**
* Visits the code
* @param alternateEntries Alternate entries
* @return References
* @throws InterruptedException
*/
public HashMap<Integer, List<Integer>> visitCode(List<Integer> alternateEntries) throws InterruptedException {
HashMap<Integer, List<Integer>> refs = new HashMap<>();
int siz = size();
@@ -111,10 +162,25 @@ public abstract class GraphSource implements Serializable {
return refs;
}
/**
* Converts address to position
* @param adr Address
* @return Position
*/
public abstract int adr2pos(long adr);
/**
* Converts address to position
* @param adr Address
* @param nearest Nearest
* @return Position
*/
public abstract int adr2pos(long adr, boolean nearest);
/**
* Gets the address after the code
* @return Address after the code
*/
public long getAddressAfterCode() {
if (isEmpty()) {
return 0;
@@ -123,8 +189,19 @@ public abstract class GraphSource implements Serializable {
return lastAddr + get(size() - 1).getBytesLength();
}
/**
* Converts position to address
* @param pos Position
* @return Address
*/
public abstract long pos2adr(int pos);
/**
* Converts position to address
* @param pos Position
* @param allowPosAfterCode Allow position after code
* @return Address
*/
public final long pos2adr(int pos, boolean allowPosAfterCode) {
if (pos == size() && allowPosAfterCode) {
return getAddressAfterCode();
@@ -17,62 +17,144 @@
package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.flash.BaseLocalData;
import java.io.Serializable;
import java.util.List;
/**
*
* Item of Graph source. Usually an instruction.
* @author JPEXS
*/
public interface GraphSourceItem extends Serializable, Cloneable {
/**
* Translate the item to target items.
* @param localData Local data
* @param stack Stack
* @param output Output list
* @param staticOperation Unused
* @param path Path
* @throws InterruptedException
*/
public void translate(BaseLocalData localData, TranslateStack stack, List<GraphTargetItem> output, int staticOperation, String path) throws InterruptedException;
/**
* Gets the number of stack items that are popped by this item.
* @param localData Local data
* @param stack Stack
* @return Number of stack items that are popped by this item
*/
public int getStackPopCount(BaseLocalData localData, TranslateStack stack);
/**
* Gets the number of stack items that are pushed by this item.
* @param localData Local data
* @param stack Stack
* @return Number of stack items that are pushed by this item
*/
public int getStackPushCount(BaseLocalData localData, TranslateStack stack);
/**
* Gets file offset.
* @return File offset
*/
public long getFileOffset();
/**
* Checks whether this item is a jump.
* @return True if this item is a jump, false otherwise
*/
public boolean isJump();
/**
* Checks whether this item is a branch.
* @return True if this item is a branch, false otherwise
*/
public boolean isBranch();
/**
* Checks whether this item is an exit (throw, return, etc.).
* @return True if this item is an exit, false otherwise
*/
public boolean isExit();
/**
* Gets the address.
* @return Address
*/
public long getAddress();
/**
* Gets the line offset.
* @return Line offset
*/
public long getLineOffset();
/**
* Checks whether the loops are ignored.
* FIXME: What is this, how to use it?
* @return True if the loops are ignored, false otherwise
*/
public boolean ignoredLoops();
/**
* Gets branches
* @param code Code
* @return List of IPs to branch to
*/
public List<Integer> getBranches(GraphSource code);
/**
* Checks whether this item is ignored.
* @return True if this item is ignored, false otherwise
*/
public boolean isIgnored();
/**
* Sets whether this item is ignored.
* @param ignored True if this item is ignored, false otherwise
* @param pos Sub position
*/
public void setIgnored(boolean ignored, int pos);
/**
* Checks whether this item is a DeobfuscatePop instruction.
* It is a special instruction for deobfuscation.
* @return True if this item is a DeobfuscatePop instruction, false otherwise
*/
public boolean isDeobfuscatePop();
/**
* Gets the line in the high level source code.
* @return Line
*/
public int getLine();
/**
* Gets the high level source code file name.
* @return File name
*/
public String getFile();
/**
* Gets length of the item in bytes.
* @return Length of the item in bytes
*/
public abstract int getBytesLength();
/**
* Gets virtual address. A virtual adress can be used for storing original
* address before applying deobfuscation
* Gets virtual address. A virtual address can be used for storing original
* address before applying deobfuscation.
*
* @return
* @return Virtual address
*/
public long getVirtualAddress();
/**
* Sets virtual address. A virtual adress can be used for storing original
* address before applying deobfuscation
* Sets virtual address. A virtual address can be used for storing original
* address before applying deobfuscation.
*
* @param virtualAddress
* @param virtualAddress Virtual address
*/
public void setVirtualAddress(long virtualAddress);
}
@@ -17,28 +17,71 @@
package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer;
import java.util.HashMap;
import java.util.List;
/**
*
* Container for source items.
* @author JPEXS
*/
public interface GraphSourceItemContainer {
/**
* Gets the size of the header.
* @return The size of the header.
*/
public long getHeaderSize();
/**
* Gets the sizes of the container parts.
* @return List of sizes of the container parts.
*/
public List<Long> getContainerSizes();
/**
* Sets the size of the container part.
* @param index Index of the container part.
* @param size Size of the container part.
*/
public void setContainerSize(int index, long size);
/**
* Gets the ASM source between the specified position.
* @param pos Index of container part
* @return ASM source between the specified position
*/
public String getASMSourceBetween(int pos);
/**
* Parses the division
* @param size Size up to this point
* @param lexer Lexer
* @return
*/
public boolean parseDivision(long size, FlasmLexer lexer);
/**
* Gets the names of the registers.
* @return The names of the registers. Map of register index and register name.
*/
public HashMap<Integer, String> getRegNames();
/**
* Translates the container to high level code.
* @param contents List of contents of the container parts
* @param lineStartItem Start source item of the line
* @param stack Stack
* @param output Output
* @param regNames Register names
* @param variables Variables
* @param functions Functions
*/
public void translateContainer(List<List<GraphTargetItem>> contents, GraphSourceItem lineStartItem, TranslateStack stack, List<GraphTargetItem> output, HashMap<Integer, String> regNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions);
/**
* Gets the name of the container.
* @return The name of the container
*/
public String getName();
}
@@ -19,15 +19,26 @@ package com.jpexs.decompiler.graph;
import java.io.Serializable;
/**
*
* Graph source item with a position.
* @author JPEXS
*/
public class GraphSourceItemPos implements Serializable {
/**
* Source item
*/
public GraphSourceItem item;
/**
* Position
*/
public int pos;
/**
* Constructs a GraphSourceItemPos
* @param item Source item
* @param pos Position
*/
public GraphSourceItemPos(GraphSourceItem item, int pos) {
this.item = item;
this.pos = pos;
@@ -17,44 +17,28 @@
package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.avm2.model.ConvertAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.FloatValueAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NameValuePair;
import com.jpexs.decompiler.flash.abc.avm2.model.NewArrayAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.*;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.ecma.ArrayType;
import com.jpexs.decompiler.flash.ecma.EcmaScript;
import com.jpexs.decompiler.flash.ecma.Null;
import com.jpexs.decompiler.flash.ecma.ObjectType;
import com.jpexs.decompiler.flash.ecma.Undefined;
import com.jpexs.decompiler.flash.ecma.*;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
import com.jpexs.decompiler.flash.helpers.hilight.HighlightData;
import com.jpexs.decompiler.graph.model.BinaryOp;
import com.jpexs.decompiler.graph.model.FalseItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.decompiler.graph.model.NotItem;
import com.jpexs.decompiler.graph.model.TrueItem;
import com.jpexs.decompiler.graph.model.*;
import com.jpexs.helpers.Reference;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
/**
*
* Graph target item - an item in high level representation of the code.
* Decompilation target.
* @author JPEXS
*/
public abstract class GraphTargetItem implements Serializable, Cloneable {
//Precedence levels
public static final int PRECEDENCE_PRIMARY = 0;
public static final int PRECEDENCE_POSTFIX = 1;
@@ -89,24 +73,54 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
public static final int NOPRECEDENCE = 16;
/**
* Source item
*/
private GraphSourceItem src;
/**
* Precedence level
*/
protected int precedence;
/**
* More source items
*/
private List<GraphSourceItemPos> moreSrc;
/**
* First part of the graph
*/
public GraphPart firstPart;
/**
* Value
*/
public GraphTargetItem value;
/**
* Source hilight data
*/
private HighlightData srcData;
/**
* Line start item
*/
public GraphSourceItem lineStartItem;
/**
* Gets the line start item
* @return Line start item
*/
public GraphSourceItem getLineStartItem() {
return lineStartItem;
}
/**
* Converts a value to an item
* @param r Value
* @return Graph target item
*/
protected static GraphTargetItem valToItem(Object r) {
if (r == null) {
return null;
@@ -156,6 +170,12 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return null;
}
/**
* Simplifies something
* @param it Graph target item
* @param implicitCoerce Implicit coerce
* @return Simplified graph target item
*/
public static GraphTargetItem simplifySomething(GraphTargetItem it, String implicitCoerce) {
if ((it instanceof SimpleValue) && implicitCoerce.isEmpty()) {
if (((SimpleValue) it).isSimpleValue()) {
@@ -189,10 +209,19 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return it2;
}
/**
* Simplifies this.
* @param implicitCoerce Implicit coerce
* @return Simplified graph target item
*/
public GraphTargetItem simplify(String implicitCoerce) {
return simplifySomething(this, implicitCoerce);
}
/**
* Gets line.
* @return Line
*/
public int getLine() {
if (src != null) {
return src.getLine();
@@ -200,6 +229,10 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return 0;
}
/**
* Gets file.
* @return File
*/
public String getFile() {
if (src != null) {
return src.getFile();
@@ -207,6 +240,10 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return null;
}
/**
* Gets first graph part.
* @return First graph part
*/
public GraphPart getFirstPart() {
if (value == null) {
return firstPart;
@@ -218,14 +255,30 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return ret;
}
/**
* Constructs GraphTargetItem
*/
public GraphTargetItem() {
this(null, null, NOPRECEDENCE);
}
/**
* Constructs GraphTargetItem
* @param src Source item
* @param lineStartItem Line start item
* @param precedence Precedence
*/
public GraphTargetItem(GraphSourceItem src, GraphSourceItem lineStartItem, int precedence) {
this(src, lineStartItem, precedence, null);
}
/**
* Constructs GraphTargetItem
* @param src Source item
* @param lineStartItem Line start item
* @param precedence Precedence
* @param value Value
*/
public GraphTargetItem(GraphSourceItem src, GraphSourceItem lineStartItem, int precedence, GraphTargetItem value) {
this.src = src;
this.lineStartItem = lineStartItem;
@@ -233,10 +286,18 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
this.value = value;
}
/**
* Gets source item
* @return Source item
*/
public GraphSourceItem getSrc() {
return src;
}
/**
* Gets more source items
* @return More source items
*/
public List<GraphSourceItemPos> getMoreSrc() {
if (moreSrc == null) {
moreSrc = new ArrayList<>();
@@ -245,6 +306,10 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return moreSrc;
}
/**
* Gets highlight src data
* @return
*/
protected HighlightData getSrcData() {
if (srcData == null) {
srcData = new HighlightData();
@@ -253,10 +318,18 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return srcData;
}
/**
* Gets position
* @return Position
*/
protected int getPos() {
return 0;
}
/**
* Gets needed sources
* @return Needed sources
*/
public List<GraphSourceItemPos> getNeededSources() {
List<GraphSourceItemPos> ret = new ArrayList<>();
ret.add(new GraphSourceItemPos(src, getPos()));
@@ -271,6 +344,13 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return ret;
}
/**
* Converts this to string semicoloned.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter toStringSemicoloned(GraphTextWriter writer, LocalData localData) throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
@@ -285,31 +365,74 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return writer;
}
/**
* Checks if semicolon is needed
* @return True if semicolon is needed
*/
public boolean needsSemicolon() {
return true;
}
/**
* Converts this to string as Boolean.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter toStringBoolean(GraphTextWriter writer, LocalData localData) throws InterruptedException {
return toString(writer, localData, "Boolean");
}
/**
* Converts this to string as String.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter toStringString(GraphTextWriter writer, LocalData localData) throws InterruptedException {
return toString(writer, localData, "String");
}
/**
* Converts this to string as int.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter toStringInt(GraphTextWriter writer, LocalData localData) throws InterruptedException {
return toString(writer, localData, "int");
}
/**
* Converts this to string as Number.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter toStringNumber(GraphTextWriter writer, LocalData localData) throws InterruptedException {
return toString(writer, localData, "Number");
}
/**
* Converts this to string.
* @return String
*/
@Override
public String toString() {
return getClass().getName();
}
/**
* Converts this to string.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter toString(GraphTextWriter writer, LocalData localData, String implicitCoerce) throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
@@ -321,10 +444,22 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return writer;
}
/**
* Converts this to string.
* @param localData Local data
* @return String
* @throws InterruptedException
*/
public GraphTextWriter toString(GraphTextWriter writer, LocalData localData) throws InterruptedException {
return toString(writer, localData, "");
}
/**
* Converts this to string.
* @param localData Local data
* @return String
* @throws InterruptedException
*/
public String toString(LocalData localData) throws InterruptedException {
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false);
toString(writer, localData);
@@ -332,12 +467,34 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return writer.toString();
}
/**
* Append this to a writer.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
public abstract GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException;
/**
* Append this to a writer.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter appendTry(GraphTextWriter writer, LocalData localData) throws InterruptedException {
return appendTry(writer, localData, "");
}
/**
* Append this to a writer.
* @param writer Writer
* @param localData Local data
* @param implicitCoerce Implicit coerce
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter appendTry(GraphTextWriter writer, LocalData localData, String implicitCoerce) throws InterruptedException {
GraphTargetItem t = this;
if (!implicitCoerce.isEmpty()) { //if implicit oerce equals explicit
@@ -366,10 +523,18 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
}
/**
* Gets precedence.
* @return Precedence
*/
public int getPrecedence() {
return precedence;
}
/**
* Checks if this can be evaluated statically.
* @return True if this can be evaluated statically
*/
public boolean isCompileTime() {
Set<GraphTargetItem> dependencies = new HashSet<>();
if (!((this instanceof SimpleValue) && ((SimpleValue) this).isSimpleValue())) {
@@ -378,14 +543,29 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return isCompileTime(dependencies);
}
/**
* Checks if this can be evaluated statically.
* @param dependencies Dependencies
* @return True if this can be evaluated statically
*/
public boolean isCompileTime(Set<GraphTargetItem> dependencies) {
return false;
}
/**
* Checks if this can be evaluated statically.
* @param dependencies Dependencies
* @return True if this can be evaluated statically
*/
public boolean isConvertedCompileTime(Set<GraphTargetItem> dependencies) {
return isCompileTime();
}
/**
* Checks whether this has side effects.
* For example function call has side effect, but variable access does not.
* @return True if this has side effects
*/
public boolean hasSideEffect() {
Reference<Boolean> ref = new Reference<>(false);
visitRecursively(new AbstractGraphTargetVisitor() {
@@ -399,26 +579,51 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return ref.getVal();
}
/**
* Checks whether it is computed via variables.
* @return True if it is computed via variables
*/
public boolean isVariableComputed() {
return false;
}
/**
* Computes EcmaScript result.
* @return EcmaScript result
*/
public Object getResult() {
return null;
}
/**
* Computes EcmaScript result as number.
* @return EcmaScript result as number
*/
public Double getResultAsNumber() {
return EcmaScript.toNumberAs2(getResult());
}
/**
* Computes EcmaScript result as string.
* @return EcmaScript result as string
*/
public String getResultAsString() {
return EcmaScript.toString(getResult());
}
/**
* Computes EcmaScript result as boolean.
* @return EcmaScript result as boolean
*/
public Boolean getResultAsBoolean() {
return EcmaScript.toBoolean(getResult());
}
/**
* Converts this to string without quotes.
* @param localData Local data
* @return String
*/
public String toStringNoQuotes(LocalData localData) {
try {
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false);
@@ -431,6 +636,13 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return "";
}
/**
* Converts this to string without quotes.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter toStringNoQuotes(GraphTextWriter writer, LocalData localData) throws InterruptedException {
writer.startOffset(src, getLineStartItem(), getPos(), srcData);
appendToNoQuotes(writer, localData);
@@ -438,30 +650,64 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return writer;
}
/**
* Appends this to a writer without quotes.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter appendToNoQuotes(GraphTextWriter writer, LocalData localData) throws InterruptedException {
return toString(writer, localData);
}
/**
* Gets this without coercion.
* @return This without coercion
*/
public GraphTargetItem getNotCoerced() {
return this;
}
/**
* Gets this without coercion and without duplicates.
* @return This without coercion and without duplicates
*/
public GraphTargetItem getNotCoercedNoDup() {
return getNotCoerced();
}
/**
* Gets this through registers.
* @return This through registers
*/
public GraphTargetItem getThroughRegister() {
return this;
}
/**
* Checks whether this needs a new line.
* @return True if this needs a new line
*/
public boolean needsNewLine() {
return false;
}
/**
* Checks whether this handles new line.
* @return True if this handles new line
*/
public boolean handlesNewLine() {
return false;
}
/**
* Converts this to string with new line.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter toStringNL(GraphTextWriter writer, LocalData localData) throws InterruptedException {
writer.startOffset(src, getLineStartItem(), getPos(), srcData);
appendTry(writer, localData);
@@ -472,26 +718,57 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return writer;
}
/**
* Checks whether this is empty.
* @return True if this is empty
*/
public boolean isEmpty() {
return false;
}
/**
* Gets through items that cannot be statically computed.
* @return Through item that cannot be statically computed
*/
public GraphTargetItem getThroughNotCompilable() {
return this;
}
/**
* Gets item through duplicates.
* @return Item through duplicates
*/
public GraphTargetItem getThroughDuplicate() {
return this;
}
/**
* Checks whether the value equals.
* @param target Target
* @return True if the value equals
*/
public boolean valueEquals(GraphTargetItem target) {
return equals(target);
}
/**
* Converts this to source (low level code).
* @param localData Local data
* @param generator Source generator
* @return Source
* @throws CompilationException
*/
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
return new ArrayList<>();
}
/**
* Converts this to source (low level code) and ignore return value.
* @param localData Local data
* @param generator Source generator
* @return Source
* @throws CompilationException
*/
public List<GraphSourceItem> toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
if (!hasReturnValue()) {
return toSource(localData, generator);
@@ -499,12 +776,26 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return generator.generateDiscardValue(localData, this);
}
/**
* Converts this to source (low level code). with BinaryOp
* @param op Binary operation
* @param action Action
* @return Source
*/
protected List<GraphSourceItem> toSourceBinary(BinaryOp op, GraphSourceItem action) {
List<GraphSourceItem> ret = new ArrayList<>();
return ret;
}
/**
* Merges Object list to one list of GraphTargetItems
* @param localData Local data
* @param gen Source generator
* @param tar Objects
* @return List of GraphTargetItems
* @throws CompilationException
*/
public static List<GraphSourceItem> toSourceMerge(SourceGeneratorLocalData localData, SourceGenerator gen, Object... tar) throws CompilationException {
List<GraphSourceItem> ret = new ArrayList<>();
for (Object o : tar) {
@@ -532,8 +823,16 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return ret;
}
/**
* Checks whether this has return value.
* @return True if this has return value
*/
public abstract boolean hasReturnValue();
/**
* Gets all sub items.
* @return All sub items
*/
public List<GraphTargetItem> getAllSubItems() {
List<GraphTargetItem> ret = new ArrayList<>();
visit(new AbstractGraphTargetVisitor() {
@@ -547,6 +846,10 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return ret;
}
/**
* Gets all sub items recursively.
* @return All sub items recursively
*/
public Set<GraphTargetItem> getAllSubItemsRecursively() {
Set<GraphTargetItem> ret = new HashSet<>();
visitRecursively(new AbstractGraphTargetVisitor() {
@@ -558,6 +861,10 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return ret;
}
/**
* Visits this recursively.
* @param visitor Visitor
*/
public final void visitRecursively(GraphTargetVisitorInterface visitor) {
Set<GraphTargetItem> visitedItems = new HashSet<>();
visit(new AbstractGraphTargetVisitor() {
@@ -572,6 +879,10 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
});
}
/**
* Visits this recursively without using Blocks.
* @param visitor Visitor
*/
public final void visitRecursivelyNoBlock(GraphTargetVisitorInterface visitor) {
Set<GraphTargetItem> visitedItems = new HashSet<>();
visitNoBlock(new AbstractGraphTargetVisitor() {
@@ -586,18 +897,34 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
});
}
/**
* Visits this.
* @param visitor Visitor
*/
public void visit(GraphTargetVisitorInterface visitor) {
if (value != null) {
visitor.visit(value);
}
}
/**
* Visits this without using Blocks.
* @param visitor
*/
public void visitNoBlock(GraphTargetVisitorInterface visitor) {
visit(visitor);
}
/**
* Gets return type.
* @return
*/
public abstract GraphTargetItem returnType();
/**
* Clone this.
* @return Cloned item
*/
@Override
public GraphTargetItem clone() {
try {
@@ -607,10 +934,25 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
}
}
/**
* Inverts this item.
* @param src Source item
* @return Inverted item
*/
public GraphTargetItem invert(GraphSourceItem src) {
return new NotItem(src, getLineStartItem(), this);
}
/**
* Appends this to a writer.
* @param prevLineItem Previous line item
* @param writer Writer
* @param localData Local data
* @param commands Commands
* @param asBlock As block
* @return Writer
* @throws InterruptedException
*/
public GraphTextWriter appendCommands(GraphTargetItem prevLineItem, GraphTextWriter writer, LocalData localData, List<GraphTargetItem> commands, boolean asBlock) throws InterruptedException {
//This may be useful in the future, but we must handle obfuscated SWFs where there is only one debugline instruction on the beggining.
@@ -642,11 +984,24 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return writer;
}
/**
* Append this to a writer as a Block.
* @param prevLineItem
* @param writer
* @param localData
* @param commands
* @return
* @throws InterruptedException
*/
public GraphTextWriter appendBlock(GraphTargetItem prevLineItem, GraphTextWriter writer, LocalData localData, List<GraphTargetItem> commands) throws InterruptedException {
appendCommands(prevLineItem, writer, localData, commands, true);
return writer;
}
/**
* Gets this as long.
* @return
*/
public long getAsLong() {
if (this instanceof DirectValueActionItem) {
DirectValueActionItem dvai = (DirectValueActionItem) this;
@@ -656,10 +1011,21 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
return 0;
}
/**
* Checks whether this is identical to other.
* @param other Other
* @return True if this is identical to other
*/
public boolean isIdentical(GraphTargetItem other) {
return this == other;
}
/**
* Alternative of Objects.equals() for GraphTargetItem.
* @param o1 Object 1
* @param o2 Object 2
* @return True if objects are value equal
*/
public static boolean objectsValueEquals(Object o1, Object o2) {
if (o1 == null && o2 == null) {
return true;
@@ -19,12 +19,20 @@ package com.jpexs.decompiler.graph;
import java.util.Collection;
/**
*
* Graph source visitor interface.
* @author JPEXS
*/
public interface GraphTargetVisitorInterface {
/**
* Visits a graph target item.
* @param item Graph target item
*/
public void visit(GraphTargetItem item);
/**
* Visits all graph target items.
* @param items Collection of graph target items
*/
public void visitAll(Collection<GraphTargetItem> items);
}
@@ -17,48 +17,88 @@
package com.jpexs.decompiler.graph;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
/**
*
* A loop in a graph.
* @author JPEXS
*/
public class Loop implements Serializable {
/**
* Continue part of the loop
*/
public GraphPart loopContinue;
/**
* Break part of the loop
*/
public GraphPart loopBreak;
/**
* Precontinue part of the loop.
* A precontinue is a part of the loop that is executed before the continue part.
* Example of this is a for loop with continue statement.
*/
public GraphPart loopPreContinue;
/**
* Back edges of the loop
*/
public Set<GraphPart> backEdges = new HashSet<>();
/**
* Break candidates of the loop
*/
public List<GraphPart> breakCandidates = new ArrayList<>();
/**
* Levels of the break candidates
*/
public List<Integer> breakCandidatesLevels = new ArrayList<>();
/**
* Unique id of the loop
*/
public final long id;
/**
* Mark for leads to method
*/
public int leadsToMark;
/**
* Mark for reachable method
*/
public int reachableMark;
/**
* Phase of the loop.
* The decompiler marks here whether the loop is already processed or not.
*/
public int phase;
/**
* Break candidates are locked
*/
public int breakCandidatesLocked = 0;
public List<GraphTargetItem> precontinueCommands = null;
/**
* Constructs a loop
* @param id Unique id of the loop
* @param loopContinue Continue part of the loop
* @param loopBreak Break part of the loop
*/
public Loop(long id, GraphPart loopContinue, GraphPart loopBreak) {
this.loopContinue = loopContinue;
this.loopBreak = loopBreak;
this.id = id;
}
/**
* To string method
* @return String representation of the loop
*/
@Override
public String toString() {
Set<String> edgesAsStr = new HashSet<>();
@@ -73,6 +113,10 @@ public class Loop implements Serializable {
return "loop(id:" + id + (loopPreContinue != null ? ",precontinue:" + loopPreContinue : "") + ",continue:" + loopContinue + ", break:" + loopBreak + ", phase:" + phase + ", backedges: " + String.join(",", edgesAsStr) + ", breakCandidates: " + String.join(",", bcAsStr) + ")";
}
/**
* Hash code
* @return Hash code of the loop
*/
@Override
public int hashCode() {
int hash = 3;
@@ -80,6 +124,11 @@ public class Loop implements Serializable {
return hash;
}
/**
* Equals
* @param obj Object to compare
* @return True if the object is equal to this loop
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
@@ -21,37 +21,66 @@ import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.model.LocalData;
/**
*
* Special item for marking a position in the graph.
* @author JPEXS
*/
public class MarkItem extends GraphTargetItem {
/**
* Mark string
*/
private final String mark;
/**
* Constructs a new mark item.
* @param mark Mark string
*/
public MarkItem(String mark) {
super(null, null, NOPRECEDENCE);
this.mark = mark;
}
/**
* Appends this item to the writer.
* @param writer Writer
* @param localData Local data
* @return
*/
@Override
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) {
return writer.append("// ").append(AppResources.translate("decompilerMark")).append(":").append(mark);
}
/**
* Gets the mark string.
* @return
*/
public String getMark() {
return mark;
}
/**
* Checks if this item is empty.
* @return Always true
*/
@Override
public boolean isEmpty() {
return true;
}
/**
* Checks if this item has a return value.
* @return Always false
*/
@Override
public boolean hasReturnValue() {
return false;
}
/**
* Gets the return type of this item.
* @return Unbounded
*/
@Override
public GraphTargetItem returnType() {
return TypeItem.UNBOUNDED;
@@ -18,26 +18,46 @@ package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.Set;
/**
*
* An item that cannot be statically computed.
* @author JPEXS
*/
public class NotCompileTimeItem extends GraphTargetItem {
/**
* Object that cannot be statically computed.
*/
public GraphTargetItem object;
/**
* Constructs a new NotCompileTimeItem.
* @param instruction Instruction
* @param lineStartIns Line start instruction
* @param object Object that cannot be statically computed
*/
public NotCompileTimeItem(GraphSourceItem instruction, GraphSourceItem lineStartIns, GraphTargetItem object) {
super(instruction, lineStartIns, NOPRECEDENCE);
this.object = object;
}
/**
* Whether this item can be computed statically.
* @param dependencies Dependencies
* @return Whether this item can be computed statically
*/
@Override
public boolean isCompileTime(Set<GraphTargetItem> dependencies) {
return false;
}
/**
* Gets through the object that cannot be statically computed.
* @return Through the object that cannot be statically computed
*/
@Override
public GraphTargetItem getThroughNotCompilable() {
if (object == null) {
@@ -46,16 +66,31 @@ public class NotCompileTimeItem extends GraphTargetItem {
return object.getThroughNotCompilable();
}
/**
* Appends this item to the writer.
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
@Override
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
return object.toString(writer, localData);
}
/**
* Whether this item has a return value.
* @return Whether this item has a return value
*/
@Override
public boolean hasReturnValue() {
return object.hasReturnValue();
}
/**
* Gets the return type of this item.
* @return Return type of this item
*/
@Override
public GraphTargetItem returnType() {
return TypeItem.UNBOUNDED;
@@ -19,7 +19,7 @@ package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.graph.model.BinaryOp;
/**
*
* Not equals type item.
* @author JPEXS
*/
public interface NotEqualsTypeItem extends BinaryOp {
@@ -17,15 +17,22 @@
package com.jpexs.decompiler.graph;
/**
*
* Stack for scope.
* @author JPEXS
*/
public class ScopeStack extends TranslateStack {
/**
* Constructs a new ScopeStack.
* @param allowEmpty Whether empty stack is allowed
*/
public ScopeStack(boolean allowEmpty) {
super(allowEmpty ? "scope" : null);
}
/**
* Constructs a new ScopeStack.
*/
public ScopeStack() {
this(true);
}
@@ -20,10 +20,13 @@ import java.util.HashSet;
import java.util.Set;
/**
*
* Second pass data.
* @author JPEXS
*/
public class SecondPassData {
/**
* All switch parts.
*/
public Set<GraphPart> allSwitchParts = new HashSet<>();
}
@@ -17,17 +17,28 @@
package com.jpexs.decompiler.graph;
/**
*
* Second pass exception.
* @author JPEXS
*/
public class SecondPassException extends RuntimeException {
/**
* Second pass data.
*/
private final SecondPassData data;
/**
* Constructs a new SecondPassException.
* @param data Second pass data
*/
public SecondPassException(SecondPassData data) {
this.data = data;
}
/**
* Gets the second pass data.
* @return Second pass data
*/
public SecondPassData getData() {
return data;
}
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.graph;
/**
*
* Simple value interface.
* @author JPEXS
*/
public interface SimpleValue {
@@ -17,68 +17,194 @@
package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.graph.model.AndItem;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.CommaExpressionItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.DoWhileItem;
import com.jpexs.decompiler.graph.model.DuplicateItem;
import com.jpexs.decompiler.graph.model.FalseItem;
import com.jpexs.decompiler.graph.model.ForItem;
import com.jpexs.decompiler.graph.model.IfItem;
import com.jpexs.decompiler.graph.model.NotItem;
import com.jpexs.decompiler.graph.model.OrItem;
import com.jpexs.decompiler.graph.model.PopItem;
import com.jpexs.decompiler.graph.model.PushItem;
import com.jpexs.decompiler.graph.model.SwitchItem;
import com.jpexs.decompiler.graph.model.TernarOpItem;
import com.jpexs.decompiler.graph.model.TrueItem;
import com.jpexs.decompiler.graph.model.WhileItem;
import com.jpexs.decompiler.graph.model.*;
import java.util.List;
/**
*
* Source generator interface. A low-level code generator.
* @author JPEXS
*/
public interface SourceGenerator {
/**
* Generates source code for PushItem.
* @param localData Local data
* @param item PushItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, PushItem item) throws CompilationException;
/**
* Generates source code for PopItem.
* @param localData Local data
* @param item PopItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, PopItem item) throws CompilationException;
/**
* Generates source code for TrueItem.
* @param localData Local data
* @param item TrueItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, TrueItem item) throws CompilationException;
/**
* Generates source code for FalseItem.
* @param localData Local data
* @param item FalseItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, FalseItem item) throws CompilationException;
/**
* Generates source code for AndItem.
* @param localData Local data
* @param item AndItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, AndItem item) throws CompilationException;
/**
* Generates source code for OrItem.
* @param localData Local data
* @param item OrItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, OrItem item) throws CompilationException;
/**
* Generates source code for IfItem.
* @param localData Local data
* @param item IfItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, IfItem item) throws CompilationException;
/**
* Generates source code for TernarOpItem.
* @param localData Local data
* @param item TernarOpItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, TernarOpItem item) throws CompilationException;
/**
* Generates source code for WhileItem.
* @param localData Local data
* @param item WhileItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, WhileItem item) throws CompilationException;
/**
* Generates source code for DoWhileItem.
* @param localData Local data
* @param item DoWhileItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, DoWhileItem item) throws CompilationException;
/**
* Generates source code for ForItem.
* @param localData Local data
* @param item ForItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, ForItem item) throws CompilationException;
/**
* Generates source code for SwitchItem.
* @param localData Local data
* @param item SwitchItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, SwitchItem item) throws CompilationException;
/**
* Generates source code for NotItem.
* @param localData Local data
* @param item NotItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, NotItem item) throws CompilationException;
/**
* Generates source code for DuplicateItem.
* @param localData Local data
* @param item DuplicateItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, DuplicateItem item) throws CompilationException;
/**
* Generates source code for BreakItem.
* @param localData Local data
* @param item BreakItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, BreakItem item) throws CompilationException;
/**
* Generates source code for ContinueItem.
* @param localData Local data
* @param item ContinueItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, ContinueItem item) throws CompilationException;
/**
* Generates source code for commands.
* @param localData Local data
* @param commands List of GraphTargetItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, List<GraphTargetItem> commands) throws CompilationException;
/**
* Generates source code for CommaExpressionItem.
* @param localData Local data
* @param item CommaExpressionItem
* @param withReturnValue If true, the return value is used
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, CommaExpressionItem item, boolean withReturnValue) throws CompilationException;
/**
* Generates source code for TypeItem.
* @param localData Local data
* @param item TypeItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, TypeItem item) throws CompilationException;
/**
* Generates DiscardValue action.
* @param localData Local data
* @param item GraphTargetItem
* @return List of GraphSourceItem
* @throws CompilationException
*/
public List<GraphSourceItem> generateDiscardValue(SourceGeneratorLocalData localData, GraphTargetItem item) throws CompilationException;
}
@@ -17,7 +17,7 @@
package com.jpexs.decompiler.graph;
/**
*
* Kind of stop part.
* @author JPEXS
*/
public enum StopPartKind {
@@ -20,19 +20,45 @@ import java.util.HashSet;
import java.util.Set;
/**
*
* State of throwing exception in a method.
* @author JPEXS
*/
public class ThrowState {
/**
* Exception id
*/
public int exceptionId;
/**
* Throw state
*/
public int state;
/**
* Throwing parts
*/
public Set<GraphPart> throwingParts = new HashSet<>();
/**
* Target part
*/
public GraphPart targetPart;
/**
* Start part
*/
public GraphPart startPart;
/**
* Catch parts
*/
public Set<GraphPart> catchParts = new HashSet<>();
/**
* Hash code
* @return Hash code
*/
@Override
public int hashCode() {
int hash = 3;
@@ -40,6 +66,11 @@ public class ThrowState {
return hash;
}
/**
* Equals
* @param obj Object
* @return True if equals
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -17,11 +17,15 @@
package com.jpexs.decompiler.graph;
/**
*
* Exception thrown when there is a problem with translating the graph.
* @author JPEXS
*/
public class TranslateException extends RuntimeException {
/**
* Constructs new TranslateException with the specified detail message.
* @param s The detail message.
*/
public TranslateException(String s) {
super(s);
}
@@ -17,34 +17,56 @@
package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.graph.model.PopItem;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* Stack for translation.
* @author JPEXS
*/
public class TranslateStack extends Stack<GraphTargetItem> {
/**
* Pop item
*/
private PopItem pop;
/**
* Path
*/
private final String path;
/**
* Simplifies all items in the stack.
*/
public void simplify() {
for (int i = 0; i < size(); i++) {
set(i, get(i).simplify(""));
}
}
/**
* Constructs new TranslateStack with the specified path.
* @param path The path.
*/
public TranslateStack(String path) {
this.path = path;
}
/**
* Gets the path.
* @return The path.
*/
public String getPath() {
return path;
}
/**
* Gets the pop item.
* @return The pop item.
*/
private PopItem getPop() {
if (pop == null) {
pop = new PopItem(null, null); //TODO: linestart?
@@ -53,6 +75,11 @@ public class TranslateStack extends Stack<GraphTargetItem> {
return pop;
}
/**
* Gets the item at the specified index.
* @param index index of the element to return
* @return The element at the specified position in this list
*/
@Override
public synchronized GraphTargetItem get(int index) {
if (path != null) {
@@ -64,6 +91,10 @@ public class TranslateStack extends Stack<GraphTargetItem> {
return super.get(index);
}
/**
* Gets the item at the top of the stack.
* @return The item at the top of the stack.
*/
@Override
public synchronized GraphTargetItem peek() {
if (path != null) {
@@ -75,6 +106,11 @@ public class TranslateStack extends Stack<GraphTargetItem> {
return super.peek();
}
/**
* Gets the item at the specified index from the top of the stack.
* @param index The index.
* @return The item at the specified index from the top of the stack.
*/
public synchronized GraphTargetItem peek(int index) {
if (path != null) {
if (index > this.size()) {
@@ -85,6 +121,10 @@ public class TranslateStack extends Stack<GraphTargetItem> {
return super.get(size() - index);
}
/**
* Pop the item at the top of the stack.
* @return The item at the top of the stack.
*/
@Override
public synchronized GraphTargetItem pop() {
if (path != null) {
@@ -19,29 +19,40 @@ package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.decompiler.graph.model.UnboundedTypeItem;
import java.util.Objects;
/**
*
* Represents a function type.
* @author JPEXS
*/
public class TypeFunctionItem extends GraphTargetItem {
//Basic function items
public static TypeFunctionItem BOOLEAN = new TypeFunctionItem("Boolean");
public static TypeFunctionItem STRING = new TypeFunctionItem("String");
public static TypeFunctionItem ARRAY = new TypeFunctionItem("Array");
public static UnboundedTypeItem UNBOUNDED = TypeItem.UNBOUNDED;
/**
* Full type name
*/
public String fullTypeName;
/**
* Creates a new instance of TypeFunctionItem
* @param fullTypeName Full type name
*/
public TypeFunctionItem(String fullTypeName) {
super(null, null, NOPRECEDENCE);
this.fullTypeName = fullTypeName;
}
/**
* Hash code
* @return Hash code
*/
@Override
public int hashCode() {
int hash = 7;
@@ -49,6 +60,11 @@ public class TypeFunctionItem extends GraphTargetItem {
return hash;
}
/**
* Equals
* @param obj Object to compare
* @return True if equal
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
@@ -61,22 +77,42 @@ public class TypeFunctionItem extends GraphTargetItem {
return Objects.equals(fullTypeName, other.fullTypeName);
}
/**
* Appends to writer
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
@Override
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
writer.append(fullTypeName);
return writer;
}
/**
* Gets the return type
* @return Return type
*/
@Override
public GraphTargetItem returnType() {
return this;
}
/**
* Checks whether this function has a return value
* @return True if has a return value
*/
@Override
public boolean hasReturnValue() {
return true;
}
/**
* Returns a string representation of this function
* @return String representation
*/
@Override
public String toString() {
return "Function[" + fullTypeName + "]";
@@ -22,16 +22,19 @@ import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.decompiler.graph.model.UnboundedTypeItem;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
*
* Type item.
* @author JPEXS
*/
public class TypeItem extends GraphTargetItem {
//Basic type items
public static TypeItem BOOLEAN = new TypeItem(DottedChain.BOOLEAN);
public static TypeItem STRING = new TypeItem(DottedChain.STRING);
@@ -50,34 +53,66 @@ public class TypeItem extends GraphTargetItem {
public static TypeItem UNKNOWN = new TypeItem("--UNKNOWN--");
/**
* Full type name
*/
public final DottedChain fullTypeName;
public boolean printRaw = false;
/**
* Namespace
*/
public String ns;
/**
* Constructs a new instance of TypeItem
* @param s Full type name
*/
public TypeItem(String s) {
this(s, null);
}
/**
* Constructs a new instance of TypeItem
* @param s Full type name
* @param ns Namespace
*/
public TypeItem(String s, String ns) {
this(s == null ? new DottedChain(new String[]{}, new String[]{""}) : DottedChain.parseWithSuffix(s), ns);
}
/**
* Constructs a new instance of TypeItem
* @param fullTypeName Full type name
*/
public TypeItem(DottedChain fullTypeName) {
this(fullTypeName, (String) null);
}
/**
* Constructs a new instance of TypeItem
* @param fullTypeName Full type name
* @param ns Namespace
*/
public TypeItem(DottedChain fullTypeName, String ns) {
this(fullTypeName, new ArrayList<>(), ns);
}
/**
* Constructs a new instance of TypeItem
* @param fullTypeName Full type name
* @param subtypes Subtypes
* @param ns Namespace
*/
public TypeItem(DottedChain fullTypeName, List<GraphTargetItem> subtypes, String ns) {
super(null, null, NOPRECEDENCE);
this.fullTypeName = fullTypeName;
this.ns = ns;
}
/**
* Hash code
* @return Hash code
*/
@Override
public int hashCode() {
int hash = 5;
@@ -86,6 +121,11 @@ public class TypeItem extends GraphTargetItem {
return hash;
}
/**
* Equals
* @param obj Object to compare
* @return True if equal
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -104,6 +144,13 @@ public class TypeItem extends GraphTargetItem {
return Objects.equals(this.fullTypeName, other.fullTypeName);
}
/**
* Appends to writer
* @param writer Writer
* @param localData Local data
* @return Writer
* @throws InterruptedException
*/
@Override
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
boolean as3 = localData.constantsAvm2 != null;
@@ -117,21 +164,40 @@ public class TypeItem extends GraphTargetItem {
return writer;
}
/**
* Gets the return type
* @return Return type
*/
@Override
public GraphTargetItem returnType() {
return this;
}
/**
* Checks whether this function has a return value
* @return True if has a return value
*/
@Override
public boolean hasReturnValue() {
return true;
}
/**
* Returns a string representation of this function
* @return String representation
*/
@Override
public String toString() {
return fullTypeName.toRawString();
}
/**
* Converts this item to low-level source code.
* @param localData Local data
* @param generator Source generator
* @return List of graph source items
* @throws CompilationException
*/
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
return generator.generate(localData, this);
@@ -0,0 +1,4 @@
/**
* High-level model of the graph.
*/
package com.jpexs.decompiler.graph.model;
@@ -0,0 +1,4 @@
/**
* Control flow graph and its decompiling.
*/
package com.jpexs.decompiler.graph;
@@ -0,0 +1,4 @@
/**
* Detection of precontinues in loops.
*/
package com.jpexs.decompiler.graph.precontinues;