blob: 59b0a7edc7a8953ba1050f4acb6ea9447944a4af [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package flash.swf.tools;
import flash.swf.Action;
import flash.swf.ActionHandler;
import flash.swf.ActionConstants;
import flash.swf.actions.Branch;
import flash.swf.actions.ConstantPool;
import flash.swf.actions.DefineFunction;
import flash.swf.actions.GetURL;
import flash.swf.actions.GetURL2;
import flash.swf.actions.GotoFrame;
import flash.swf.actions.GotoFrame2;
import flash.swf.actions.GotoLabel;
import flash.swf.actions.Label;
import flash.swf.actions.Push;
import flash.swf.actions.SetTarget;
import flash.swf.actions.StoreRegister;
import flash.swf.actions.StrictMode;
import flash.swf.actions.Try;
import flash.swf.actions.Unknown;
import flash.swf.actions.WaitForFrame;
import flash.swf.actions.With;
import flash.swf.debug.DebugModule;
import flash.swf.debug.LineRecord;
import flash.swf.debug.RegisterRecord;
import flash.swf.types.ActionList;
import flash.util.FieldFormat;
import java.io.PrintWriter;
import java.util.HashMap;
/**
* This utility supports printing AS2 byte codes.
*
* @author Edwin Smith
*/
public class Disassembler extends ActionHandler
{
protected ConstantPool cpool;
protected int start;
protected int offset;
protected final PrintWriter out;
private boolean showOffset = false;
private boolean showDebugSource = false;
private boolean showLineRecord = true;
private RegisterRecord registerRecord = null;
private int indent;
private int initialIndent;
private String comment;
private String format;
public Disassembler(PrintWriter out, ConstantPool cpool, String comment)
{
this(out, false, 0);
this.cpool = cpool;
this.comment = comment;
}
public Disassembler(PrintWriter out, boolean showOffset, int indent)
{
this.out = out;
this.showOffset = showOffset;
this.indent = indent;
this.initialIndent = indent;
this.comment = "";
}
public void setComment(String comment)
{
this.comment = comment;
}
public void setFormat(String format)
{
this.format = format;
}
public void setShowDebugSource(boolean b)
{
this.showDebugSource = b;
}
public void setShowLineRecord(boolean b)
{
this.showLineRecord = b;
}
public static void disassemble(ActionList list, ConstantPool pool, int startIndex, int endIndex, PrintWriter out)
{
Disassembler d = new Disassembler(out, pool, " ");
d.setFormat(" 0x%08O %a");
d.setShowLineRecord(false);
// probe backward for a register record if any to set up register to variable name mapping
int at = list.lastIndexOf(ActionList.sactionRegisterRecord, startIndex);
if (at > -1)
d.registerRecord = (RegisterRecord)list.getAction(at);
// now dump the contents of our request
list.visit(d, startIndex, endIndex);
out.flush();
}
protected void print(Action action)
{
start(action);
out.println();
}
public void setActionOffset(int offset, Action a)
{
if (this.offset == 0)
{
this.start = offset;
}
this.offset = offset;
}
protected void indent()
{
for (int i=0; i < initialIndent; i++)
out.print(" ");
out.print(comment);
for (int i=initialIndent; i < indent; i++)
out.print(" ");
}
public void registerRecord(RegisterRecord record)
{
// set the active record
registerRecord = record;
}
protected String variableNameForRegister(int regNbr)
{
int at = (registerRecord == null) ? -1 : registerRecord.indexOf(regNbr);
if (at > -1)
return registerRecord.variableNames[at];
else
return null;
}
public void lineRecord(LineRecord line)
{
if (!showLineRecord)
;
else if (showDebugSource)
{
printLines(line, out);
}
else
{
start(line);
out.println(" "+line.module.name +":"+line.lineno);
}
}
public void printLines(LineRecord lr, PrintWriter out)
{
DebugModule script = lr.module;
if (script != null)
{
int lineno = lr.lineno;
if (lineno > 0)
{
while (lineno-1 > 0 && script.offsets[lineno-1] == 0)
{
lineno--;
}
if (lineno == 1)
{
indent();
out.println(script.name);
}
int off = script.index[lineno-1];
int len = script.index[lr.lineno] - off;
out.write(script.text, off, len);
}
}
}
protected void start(Action action)
{
String actionName;
if ((action.code < 0) || (action.code > actionNames.length))
{
actionName = "Unknown";
}
else
{
actionName = actionNames[action.code];
}
if (showOffset)
{
indent();
out.print("absolute=" + offset + ",relative=" + (offset-start) +
",code=" + action.code + "\t" + actionName);
}
else
{
if (format == null)
{
indent();
out.print(actionName);
}
else
{
startFormatted(actionName);
}
}
}
protected void startFormatted(String action)
{
StringBuilder sb = new StringBuilder();
boolean leadingZeros = false;
int width = -1;
for(int i=0; i<format.length(); i++)
{
char c = format.charAt(i);
if (c == '%')
{
c = format.charAt(++i);
if (Character.isDigit(c))
{
// absorb a leading zero, if any
if (c == '0')
{
leadingZeros = true;
c = format.charAt(++i);
}
StringBuilder number = new StringBuilder();
while(Character.isDigit(c))
{
number.append(c);
c = format.charAt(++i);
}
try { width = Integer.parseInt(number.toString()); } catch(NumberFormatException nfe) { width = -1; }
}
if (c == 'O')
{
FieldFormat.formatLongToHex(sb, offset, width, leadingZeros);
}
else if (c == 'o')
{
FieldFormat.formatLong(sb, offset, width, leadingZeros);
}
else if (c == 'a')
{
sb.append(action);
}
}
else
sb.append(c);
}
out.print( sb.toString() );
}
public void nextFrame(Action action)
{
print(action);
}
public void prevFrame(Action action)
{
print(action);
}
public void play(Action action)
{
print(action);
}
public void stop(Action action)
{
print(action);
}
public void toggleQuality(Action action)
{
print(action);
}
public void stopSounds(Action action)
{
print(action);
}
public void add(Action action)
{
print(action);
}
public void subtract(Action action)
{
print(action);
}
public void multiply(Action action)
{
print(action);
}
public void divide(Action action)
{
print(action);
}
public void equals(Action action)
{
print(action);
}
public void less(Action action)
{
print(action);
}
public void and(Action action)
{
print(action);
}
public void or(Action action)
{
print(action);
}
public void not(Action action)
{
print(action);
}
public void stringEquals(Action action)
{
print(action);
}
public void stringLength(Action action)
{
print(action);
}
public void stringExtract(Action action)
{
print(action);
}
public void pop(Action action)
{
print(action);
}
public void toInteger(Action action)
{
print(action);
}
public void getVariable(Action action)
{
print(action);
}
public void setVariable(Action action)
{
print(action);
}
public void setTarget2(Action action)
{
print(action);
}
public void stringAdd(Action action)
{
print(action);
}
public void getProperty(Action action)
{
print(action);
}
public void setProperty(Action action)
{
print(action);
}
public void cloneSprite(Action action)
{
print(action);
}
public void removeSprite(Action action)
{
print(action);
}
public void trace(Action action)
{
print(action);
}
public void startDrag(Action action)
{
print(action);
}
public void endDrag(Action action)
{
print(action);
}
public void stringLess(Action action)
{
print(action);
}
public void randomNumber(Action action)
{
print(action);
}
public void mbStringLength(Action action)
{
print(action);
}
public void charToASCII(Action action)
{
print(action);
}
public void asciiToChar(Action action)
{
print(action);
}
public void getTime(Action action)
{
print(action);
}
public void mbStringExtract(Action action)
{
print(action);
}
public void mbCharToASCII(Action action)
{
print(action);
}
public void mbASCIIToChar(Action action)
{
print(action);
}
public void delete(Action action)
{
print(action);
}
public void delete2(Action action)
{
print(action);
}
public void defineLocal(Action action)
{
print(action);
}
public void callFunction(Action action)
{
print(action);
}
public void returnAction(Action action)
{
print(action);
}
public void modulo(Action action)
{
print(action);
}
public void newObject(Action action)
{
print(action);
}
public void defineLocal2(Action action)
{
print(action);
}
public void initArray(Action action)
{
print(action);
}
public void initObject(Action action)
{
print(action);
}
public void typeOf(Action action)
{
print(action);
}
public void targetPath(Action action)
{
print(action);
}
public void enumerate(Action action)
{
print(action);
}
public void add2(Action action)
{
print(action);
}
public void less2(Action action)
{
print(action);
}
public void equals2(Action action)
{
print(action);
}
public void toNumber(Action action)
{
print(action);
}
public void toString(Action action)
{
print(action);
}
public void pushDuplicate(Action action)
{
print(action);
}
public void stackSwap(Action action)
{
print(action);
}
public void getMember(Action action)
{
print(action);
}
public void setMember(Action action)
{
print(action);
}
public void increment(Action action)
{
print(action);
}
public void decrement(Action action)
{
print(action);
}
public void callMethod(Action action)
{
print(action);
}
public void newMethod(Action action)
{
print(action);
}
public void instanceOf(Action action)
{
print(action);
} // only if object model enabled
public void enumerate2(Action action)
{
print(action);
}
public void bitAnd(Action action)
{
print(action);
}
public void bitOr(Action action)
{
print(action);
}
public void bitXor(Action action)
{
print(action);
}
public void bitLShift(Action action)
{
print(action);
}
public void bitRShift(Action action)
{
print(action);
}
public void bitURShift(Action action)
{
print(action);
}
public void strictEquals(Action action)
{
print(action);
}
public void greater(Action action)
{
print(action);
}
public void stringGreater(Action action)
{
print(action);
}
public void gotoFrame(GotoFrame action)
{
start(action);
out.println(" " + action.frame);
}
public void getURL(GetURL action)
{
start(action);
out.println(" " + action.url + " " + action.target);
}
public void storeRegister(StoreRegister action)
{
start(action);
String variableName = variableNameForRegister(action.register);
out.println(" $" + action.register + ((variableName == null) ? "" : " \t\t; "+variableName) );
}
public void constantPool(ConstantPool action)
{
cpool = action;
start(action);
out.println(" [" + action.pool.length +"]");
}
public void strictMode(StrictMode action)
{
print(action);
}
public void waitForFrame(WaitForFrame action)
{
start(action);
out.println(" " + action.frame + " {");
indent++;
labels.getLabelEntry(action.skipTarget).source = action;
}
public void setTarget(SetTarget action)
{
start(action);
out.println(" " + action.targetName);
}
public void gotoLabel(GotoLabel action)
{
start(action);
out.println(" " + action.label);
}
public void waitForFrame2(WaitForFrame action)
{
start(action);
out.println(" {");
indent++;
labels.getLabelEntry(action.skipTarget).source = action;
}
public void with(With action)
{
start(action);
out.println(" {");
indent++;
labels.getLabelEntry(action.endWith).source = action;
}
public void tryAction(Try action)
{
start(action);
out.println(" {");
indent++;
labels.getLabelEntry(action.endTry).source = action;
if (action.hasCatch())
labels.getLabelEntry(action.endCatch).source = action;
if (action.hasFinally())
labels.getLabelEntry(action.endFinally).source = action;
}
public void throwAction(Action action)
{
print(action);
}
public void castOp(Action action)
{
print(action);
}
public void implementsOp(Action action)
{
print(action);
}
public void extendsOp(Action action)
{
print(action);
}
public void nop(Action action)
{
print(action);
}
public void halt(Action action)
{
print(action);
}
public void push(Push action)
{
start(action);
out.print(" ");
Object value = action.value;
int type = Push.getTypeCode(value);
switch (type)
{
case Push.kPushStringType:
out.print(quoteString(value.toString(),'"'));
break;
case Push.kPushNullType:
out.print("null");
break;
case Push.kPushUndefinedType:
out.print("undefined");
break;
case Push.kPushRegisterType:
String variableName = variableNameForRegister( (((Byte)value).intValue()&0xFF) );
out.print("$" + (((Byte)value).intValue()&0xFF) + ((variableName == null) ? "" : " \t\t; "+variableName) );
break;
case Push.kPushConstant8Type:
case Push.kPushConstant16Type:
int index = ((Number) value).intValue()&0xFFFF;
out.print( ((cpool == null) ? Integer.toString(index) : quoteString(cpool.pool[index],'\'')) );
break;
case Push.kPushFloatType:
out.print(value + "F");
break;
case Push.kPushBooleanType:
case Push.kPushDoubleType:
case Push.kPushIntegerType:
out.print(value);
break;
default:
assert (false);
}
out.println();
}
public void getURL2(GetURL2 action)
{
start(action);
out.println(" " + action.method);
}
public void defineFunction(DefineFunction action)
{
start(action);
out.print(" " + action.name + "(");
for (int i = 0; i < action.params.length; i++)
{
out.print(action.params[i]);
if (i + 1 < action.params.length)
{
out.print(", ");
}
}
out.println(") {");
indent++;
action.actionList.visitAll(this);
indent--;
indent();
out.println("} " + action.name);
}
public void defineFunction2(DefineFunction action)
{
start(action);
out.print(" " + action.name + "(");
for (int i = 0; i < action.params.length; i++)
{
out.print("$"+action.paramReg[i]+"="+action.params[i]);
if (i + 1 < action.params.length)
{
out.print(", ");
}
}
out.print(")");
int regno = 1;
if ((action.flags & DefineFunction.kPreloadThis) != 0)
out.print(" $"+(regno++)+"=this");
if ((action.flags & DefineFunction.kPreloadArguments) != 0)
out.print(" $"+(regno++)+"=arguments");
if ((action.flags & DefineFunction.kPreloadSuper) != 0)
out.print(" $"+(regno++)+"=super");
if ((action.flags & DefineFunction.kPreloadRoot) != 0)
out.print(" $"+(regno++)+"=_root");
if ((action.flags & DefineFunction.kPreloadParent) != 0)
out.print(" $"+(regno++)+"=_parent");
if ((action.flags & DefineFunction.kPreloadGlobal) != 0)
out.print(" $"+(regno)+"=_global");
out.println(" {");
indent++;
action.actionList.visitAll(this);
indent--;
indent();
out.println("} " + action.name);
}
private static class LabelEntry
{
String name;
Action source;
public LabelEntry(String name, Action source)
{
this.name = name;
this.source = source;
}
}
private static class LabelMap extends HashMap<Label, LabelEntry>
{
private static final long serialVersionUID = -7907644739362458461L;
LabelEntry getLabelEntry(Label l)
{
LabelEntry entry = get(l);
if (entry == null)
{
entry = new LabelEntry(null,null);
put(l, entry);
}
return entry;
}
}
private LabelMap labels = new LabelMap();
int labelCount = 0;
public void ifAction(Branch action)
{
printBranch(action);
}
public void jump(Branch action)
{
printBranch(action);
}
protected void printBranch(Branch action)
{
start(action);
LabelEntry entry = labels.getLabelEntry(action.target);
if (entry.name == null)
entry.name = "L"+String.valueOf(labelCount++);
entry.source = action;
out.println(" " + entry.name);
}
public void label(Label label)
{
LabelEntry entry = labels.getLabelEntry(label);
if (entry.source == null)
{
// have not seen any actions that target this label yet, and that
// means the source can only be a backwards branch
entry.name = "L"+String.valueOf(labelCount++);
indent();
out.println(entry.name + ":");
}
else
{
switch (entry.source.code)
{
case ActionConstants.sactionTry:
Try t = (Try) entry.source;
indent--;
indent();
out.println("}");
indent();
if (label == t.endTry && t.hasCatch())
{
out.println("catch("+
(t.hasRegister()?"$"+t.catchReg:t.catchName) +
") {");
indent++;
}
else if ((label == t.endTry || label == t.endCatch) && t.hasFinally())
{
out.println("finally {");
indent++;
}
break;
case ActionConstants.sactionWaitForFrame:
case ActionConstants.sactionWaitForFrame2:
case ActionConstants.sactionWith:
// end of block
indent--;
indent();
out.println("}");
break;
case ActionConstants.sactionIf:
case ActionConstants.sactionJump:
indent();
out.println(entry.name + ":");
break;
default:
assert (false);
break;
}
}
}
public void call(Action action)
{
print(action);
}
public void gotoFrame2(GotoFrame2 action)
{
start(action);
out.println(" " + action.playFlag);
}
public void quickTime(Action action)
{
print(action);
}
public void unknown(Unknown action)
{
print(action);
}
public static String quoteString(String s, char qc)
{
StringBuilder b = new StringBuilder(s.length() + 2);
b.append(qc);
for (int i=0; i < s.length(); i++)
{
char c = s.charAt(i);
switch (c)
{
case 8: b.append("\\v"); break;
case '\f' : b.append("\\f"); break;
case '\r' : b.append("\\r"); break;
case '\t' : b.append("\\t"); break;
case '\n' : b.append("\\n"); break;
case '"' : b.append("\\\""); break;
case '\'' : b.append("\\'"); break;
default: b.append(c); break;
}
}
b.append(qc);
return b.toString();
}
static final public String[] actionNames = {
// 00 0000----
"0x00",
"0x01",
"0x02",
"0x03",
"next", // sactionNextFrame
"prev", // sactionPrevFrame
"play", // sactionPlay
"stop", // sactionStop
"toggle", // sactionToggleQuality
"stopsound", // sactionStopSounds
"add", // sactionAdd
"sub", // sactionSubtract
"mul", // sactionMultiply
"div", // sactionDivide
"eq", // sactionEquals
"lt", // sactionLess
// 0x10 0001----
"and", // sactionAnd
"or", // sactionOr
"not", // sactionNot
"seq", // sactionStringEquals
"slen", // sactionStringLength
"substr", // sactionStringExtract
"0x16",
"pop", // sactionPop
"toint", // sactionToInteger
"0x19",
"0x1A",
"0x1B",
"get", // sactionGetVariable
"set", // sactionGetVariable
"0x1E",
"0x1F",
//0x20 0010----
"settarget2", //sactionSetTarget2
"sadd", // sactionStringAdd
"getprop", // sactionGetProperty
"setprop", // sactoinSetProperty
"csprite", // sactionCloneSprite
"rsprite", // sactionRemoveSprite
"trace", // sactionTrace
"sdrag", // sactionStartDrag
"edrag", // sactionEndDragg
"slt", // sactionStringLess
"0x2A",
"0x2B",
"0x2C",
"0x2D",
"0x2E",
"0x2F",
// 0x30 0011----
"rand", // sactionRandomNumber
"wslen", // sactionMbStringLength
"c2a", // sactionCharToAscii
"a2c", // sactionAscii2Char
"time", // sactionGetTime
"wsubstr", // sactionMBStringExtract
"wc2a", // sactionMbCharToAscii
"wa2c", // sactionMbAsciiToChar
"0x38",
"0x39",
"del", // sactionDelete
"del2", // sactionDelete2
"var", // sactionDefineLoc
"callfun", // sactionCallFunction
"return", // sactionReturn
"mod", // sactionMod
// 0x40 0100----
"newobj", // sactionNewObject
"var2", // sactionDefineLocal2
"initarr", // sactionInitArray
"initobj", // sactionInitObject
"typeof", // sactionTypeOf
"target", // sactionTargetPath
"enum", // sactionEnumerate
"add2", // sactionAdd2
"lt2", // sactionLess2
"eq2", // sactionEquals2
"tonum", // sactionToNumber
"tostr", // sactionToString
"dup", // sactionPushDuplicate
"swap", // sactionStackSwap
"getmem", // sactionGetMember
"setmem", // sactionSetMember
// 0x50 0101----
"inc", // sactionIncrement
"dec", // sactionDecrement
"callmethod", // sactionCallMethod
"newmethod", // sactionNewMethod
"instanceof", // sactionInstanceOf
"enum2", // sactionEnumerate2
"0x56",
"0x57",
"0x58",
"0x59",
"0x5A",
"0x5B",
"0x5C",
"0x5D",
"0x5E",
"halt", // sactionHalt
// 0x60 0110----
"band", // sactionBitAnd
"bor", // sactionBitOr
"bxor", // sactionBitXor
"bls", // sactionBitLShift
"brs", // sactionBitRShift
"burs", // sactionBitURShift
"eqs", // sactionStrictEquals
"gt", // sactionGreater
"sgt", // sactionStringGreater
"extends", // sactionExtends
"0x6A",
"0x6B",
"0x6C",
"0x6D",
"0x6E",
"0x6F",
// 0x70 0111----
"0x70",
"0x71",
"0x72",
"0x73",
"0x74",
"0x75",
"0x76",
"nop", // sactionNop
"0x78",
"0x79",
"0x7A",
"0x7B",
"0x7C",
"0x7D",
"0x7E",
"0x7F",
// 0x80 1000----
"0x80",
"gotoframe", // sactionGotoFrame
"0x82",
"geturl", // sactionGetUrl
"0x84",
"0x85",
"0x86",
"store", // sactionStoreRegister
"cpool", // sactionConstantPool
"strict", // sactionStrictMode
"wait", // sactionWaitForFrame
"settarget", // sactionSetTarget
"gotolabel", // sactoinGotoLabel
"wait2", // sactionWaitForFrame2
"function2", // sactionDefineFunction2
"try", // sactionTry
// 0x90 1001----
"0x90",
"0x91",
"0x92",
"0x93",
"with", // sactionWith
"0x95",
"push", // sactionPush
"0x97",
"0x98",
"jump", // sactionJump
"geturl2", // sactionGetUrl2
"function", // sactionDefineFunction
"0x9C",
"if", // sactionIf
"call", // sactionCall
"gotof2", // sactionGotoFrame2
// 0xA0 1010----
"0xA0",
"0xA1",
"0xA2",
"0xA3",
"0xA4",
"0xA5",
"0xA6",
"0xA7",
"0xA8",
"0xA9",
"quicktime", // sactionQuickTime
"0xAB",
"0xAC",
"0xAD",
"0xAE",
"0xAF",
// 0xB0 1011----
"0xB0",
"0xB1",
"0xB2",
"0xB3",
"0xB4",
"0xB5",
"0xB6",
"0xB7",
"0xB8",
"0xB9",
"0xBA",
"0xBB",
"0xBC",
"0xBD",
"0xBE",
"0xBF",
// 0xC0 1100----
"0xC0",
"0xC1",
"0xC2",
"0xC3",
"0xC4",
"0xC5",
"0xC6",
"0xC7",
"0xC8",
"0xC9",
"0xCA",
"0xCB",
"0xCC",
"0xCD",
"0xCE",
"0xCF",
// 0xD0 1101----
"0xD0",
"0xD1",
"0xD2",
"0xD3",
"0xD4",
"0xD5",
"0xD6",
"0xD7",
"0xD8",
"0xD9",
"0xDA",
"0xDB",
"0xDC",
"0xDD",
"0xDE",
"0xDF",
// 0xE0 1110----
"0xE0",
"0xE1",
"0xE2",
"0xE3",
"0xE4",
"0xE5",
"0xE6",
"0xE7",
"0xE8",
"0xE9",
"0xEA",
"0xEB",
"0xEC",
"0xED",
"0xEE",
"0xEF",
"0xF0",
// 0xF0 1111----
"0xF1",
"0xF2",
"0xF3",
"0xF4",
"0xF5",
"0xF6",
"0xF7",
"0xF8",
"0xF9",
"0xFA",
"0xFB",
"0xFC",
"0xFD",
"0xFE",
"0xFF",
// these are not valid bytecodes, but we use them internally
"label", // 0x100
"line", // 0x101
};
}