Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
package com.swfwire.decompiler.utils
{
import com.swfwire.decompiler.abc.ABCFile;
import com.swfwire.decompiler.abc.AVM2;
import com.swfwire.decompiler.abc.LocalRegisters;
import com.swfwire.decompiler.abc.OperandStack;
import com.swfwire.decompiler.abc.ScopeStack;
import com.swfwire.decompiler.abc.instructions.*;
import com.swfwire.decompiler.abc.tokens.*;
import com.swfwire.decompiler.abc.tokens.cpool.CPoolIndex;
import com.swfwire.decompiler.abc.tokens.multinames.*;
import com.swfwire.decompiler.abc.tokens.traits.*;
import com.swfwire.utils.Debug;
import com.swfwire.utils.ObjectUtil;
import com.swfwire.utils.StringUtil;
import flash.utils.Dictionary;
import flash.utils.describeType;
import flash.utils.getTimer;
public class ABCToActionScript
{
private var abcFile:ABCFile;
private var offsetLookup:Object;
public var showByteCode:Boolean = true;
public var showActionScript:Boolean = true;
public var showStack:Boolean = true;
public var showDebug:Boolean = false;
public var showBranchInfo:Boolean = false;
private var methodLookupCache:Array;
private var customNamespaces:Object;
public function ABCToActionScript(abcFile:ABCFile, offsetLookup:Object = null, customNamespaces:Object = null)
{
this.abcFile = abcFile;
this.offsetLookup = offsetLookup;
methodLookupCache = new Array();
for(var iter:uint = 0; iter < abcFile.methodBodyCount; iter++)
{
methodLookupCache[abcFile.methodBodies[iter].method] = iter;
}
if(!customNamespaces)
{
customNamespaces = {};
}
this.customNamespaces = customNamespaces;
}
public function getReadableMultinameRuntime(index:uint, readable:ReadableMultiname, stack:OperandStack):void
{
var cpool:ConstantPoolToken = abcFile.cpool;
var multiname:MultinameToken = cpool.multinames[index];
switch(multiname.kind)
{
case MultinameToken.KIND_RTQName:
case MultinameToken.KIND_RTQNameA:
var rtqn:MultinameRTQNameToken = multiname.data as MultinameRTQNameToken;
readable.namespace = uncoerce(stack.pop(), 'Namespace', true).replace('::', '.');
readable.name = cpool.strings[rtqn.name].utf8;
break;
case MultinameToken.KIND_MultinameL:
case MultinameToken.KIND_MultinameLA:
readable.namespace = '';
readable.name = stack.pop();
break;
default:
getReadableMultiname(index, readable);
break;
}
}
public function getReadableMultiname(index:uint, readable:ReadableMultiname):void
{
var cpool:ConstantPoolToken = abcFile.cpool;
var multiname:MultinameToken = cpool.multinames[index];
readable.namespace = '';
readable.name = '?';
var isAttribute:Boolean = false;
if(index == 0)
{
readable.name = '*';
}
else
{
switch(multiname.kind)
{
case MultinameToken.KIND_QName:
case MultinameToken.KIND_QNameA:
var mq:MultinameQNameToken = multiname.data as MultinameQNameToken;
readable.namespace = namespaceToString(mq.ns);
readable.name = cpool.strings[mq.name].utf8;
break;
case MultinameToken.KIND_RTQName:
case MultinameToken.KIND_RTQNameA:
var rtqn:MultinameRTQNameToken = multiname.data as MultinameRTQNameToken;
readable.name = cpool.strings[rtqn.name].utf8;
break;
case MultinameToken.KIND_Multiname:
case MultinameToken.KIND_MultinameA:
var mm:MultinameMultinameToken = multiname.data as MultinameMultinameToken;
readable.namespace = '';
readable.name = cpool.strings[mm.name].utf8;
break;
case MultinameToken.KIND_TypeName:
var tn:MultinameTypeNameToken = multiname.data as MultinameTypeNameToken;
var mq1:MultinameQNameToken = cpool.multinames[tn.name].data as MultinameQNameToken;
var mq2:MultinameQNameToken = cpool.multinames[tn.subType].data as MultinameQNameToken;
var rSub:ReadableMultiname = new ReadableMultiname();
getReadableMultiname(tn.subType, rSub);
readable.name = cpool.strings[mq1.name].utf8 + '.<' + multinameTypeToString(rSub) + '>';
break;
default:
readable.name = '#'+index+'/'+cpool.multinames.length+'('+multiname.kind.toString(16)+')';
break;
}
if(multiname.kind == MultinameToken.KIND_MultinameA)
{
readable.name = '@'+readable.name;
}
}
}
public function getReadableClass(index:uint, rc:ReadableClass):void
{
var classInfo:ClassInfoToken = abcFile.classes[index];
var instance:InstanceToken = abcFile.instances[index];
rc.type = instance.flags & InstanceToken.FLAG_CLASS_INTERFACE ? ReadableClass.INTERFACE : ReadableClass.CLASS;
rc.className = new ReadableMultiname();
getReadableMultiname(instance.name, rc.className);
rc.superName = new ReadableMultiname();
getReadableMultiname(instance.superName, rc.superName);
rc.traits = new Array();
rc.interfaces = new Array();
var iter2:uint;
var trait:TraitsInfoToken;
var r:ReadableTrait;
/*
r = new ReadableTrait();
translator.getMethodBody(instance.name, classInfo.cinit, r);
rc.traits.push(r);
*/
r = new ReadableTrait();
getMethodBody(instance.name, instance.iinit, r);
r.type.name = '';
r.declaration.namespace = 'public';
rc.traits.push(r);
for(iter2 = 0; iter2 < classInfo.traitCount; iter2++)
{
trait = classInfo.traits[iter2];
r = new ReadableTrait();
r.isStatic = true;
getReadableTrait(trait, r);
rc.traits.push(r);
}
for(iter2 = 0; iter2 < instance.traitCount; iter2++)
{
trait = instance.traits[iter2];
r = new ReadableTrait();
getReadableTrait(trait, r);
rc.traits.push(r);
}
for(iter2 = 0; iter2 < instance.interfaceCount; iter2++)
{
rc.interfaces.push(instance.interfaces[iter2]);
}
}
public function getMethodBody(name:uint, methodId:uint, r:ReadableTrait):void
{
r.arguments = new Vector.<ReadableMultiname>();
r.argumentNames = new Vector.<String>();
r.declaration = new ReadableMultiname();
r.traitType = ReadableTrait.TYPE_METHOD;
multinameTraitToString(name, r.declaration);
var methodInfo:MethodInfoToken = abcFile.methods[methodId];
for(var iter:uint = 0; iter < methodInfo.paramCount; iter++)
{
var paramType:uint = methodInfo.paramTypes[iter];
var readableArg:ReadableMultiname = new ReadableMultiname();
getReadableMultiname(paramType, readableArg);
r.arguments[iter] = readableArg;
if(methodInfo.paramNames[iter])
{
r.argumentNames[iter] = abcFile.cpool.strings[methodInfo.paramNames[iter].value].utf8;
}
else
{
r.argumentNames[iter] = 'arg'+iter;
}
//args.push('arg'+iter+':'+multinameTypeToString(cpool, paramType));
}
var bodyId:int = getBodyIdFromMethodId(methodId);
if(bodyId >= 0)
{
var methodBody:MethodBodyInfoToken = abcFile.methodBodies[bodyId];
r.instructions = methodBody.instructions;
r.localCount = methodBody.localCount;
r.slots = {};
for(var iterTraits:uint = 0; iterTraits < methodBody.traits.length; iterTraits++)
{
var trait:TraitsInfoToken = methodBody.traits[iterTraits];
if(trait.kind == TraitsInfoToken.KIND_TRAIT_SLOT)
{
var slotToken:TraitSlotToken = trait.data as TraitSlotToken;
r.slots[slotToken.slotId] = abcFile.cpool.strings[MultinameQNameToken(abcFile.cpool.multinames[trait.name].data).name].utf8;
}
}
}
r.type = new ReadableMultiname();
getReadableMultiname(methodInfo.returnType, r.type);
}
public function getReadableTrait(traitInfo:TraitsInfoToken, r:ReadableTrait):void
{
r.arguments = new Vector.<ReadableMultiname>();
r.argumentNames = new Vector.<String>();
var args:Array = [];
var cpool:ConstantPoolToken = abcFile.cpool;
r.declaration = new ReadableMultiname();
multinameTraitToString(traitInfo.name, r.declaration);
if(traitInfo.kind == TraitsInfoToken.KIND_TRAIT_CLASS)
{
r.traitType = ReadableTrait.TYPE_CLASS;
r.classInfo = new ReadableClass();
var traitClass:TraitClassToken = traitInfo.data as TraitClassToken;
getReadableClass(traitClass.classId, r.classInfo);
}
else if(traitInfo.kind == TraitsInfoToken.KIND_TRAIT_SLOT)
{
var slotInfo:TraitSlotToken = TraitSlotToken(traitInfo.data);
r.traitType = ReadableTrait.TYPE_PROPERTY;
r.type = new ReadableMultiname();
getReadableMultiname(slotInfo.typeName, r.type);
//result = traitName+':'+multinameTypeToString(TraitSlotToken(traitInfo.data).typeName, r);
}
else if(traitInfo.kind == TraitsInfoToken.KIND_TRAIT_METHOD || traitInfo.kind == TraitsInfoToken.KIND_TRAIT_GETTER || traitInfo.kind == TraitsInfoToken.KIND_TRAIT_SETTER)
{
r.traitType = ReadableTrait.TYPE_METHOD;
r.slots = {};
var traitMethod:TraitMethodToken = TraitMethodToken(traitInfo.data);
var methodInfo:MethodInfoToken = abcFile.methods[traitMethod.methodId];
for(var iter:uint = 0; iter < methodInfo.paramCount; iter++)
{
var paramType:uint = methodInfo.paramTypes[iter];
var readableArg:ReadableMultiname = new ReadableMultiname();
getReadableMultiname(paramType, readableArg);
r.arguments[iter] = readableArg;
if(methodInfo.paramNames[iter])
{
r.argumentNames[iter] = abcFile.cpool.strings[methodInfo.paramNames[iter].value].utf8;
}
else
{
r.argumentNames[iter] = 'arg'+iter;
}
//args.push('arg'+iter+':'+multinameTypeToString(cpool, paramType));
}
var bodyId:int = getBodyIdFromMethodId(traitMethod.methodId);
if(bodyId >= 0)
{
var methodBody:MethodBodyInfoToken = abcFile.methodBodies[bodyId];
r.instructions = methodBody.instructions;
r.localCount = methodBody.localCount;
for(var iterTraits:uint = 0; iterTraits < methodBody.traits.length; iterTraits++)
{
var trait:TraitsInfoToken = methodBody.traits[iterTraits];
if(trait.kind == TraitsInfoToken.KIND_TRAIT_SLOT)
{
var slotToken:TraitSlotToken = trait.data as TraitSlotToken;
r.slots[slotToken.slotId] = namespaceToString(MultinameQNameToken(abcFile.cpool.multinames[trait.name].data).ns) + "::" + abcFile.cpool.strings[MultinameQNameToken(abcFile.cpool.multinames[trait.name].data).name].utf8;
}
}
}
r.type = new ReadableMultiname();
getReadableMultiname(methodInfo.returnType, r.type);
}
else if(traitInfo.kind == TraitsInfoToken.KIND_TRAIT_CONST)
{
r.traitType = ReadableTrait.TYPE_PROPERTY;
r.type = new ReadableMultiname();
getReadableMultiname(TraitSlotToken(traitInfo.data).typeName, r.type);
r.isConst = true;
}
if(traitInfo.kind == TraitsInfoToken.KIND_TRAIT_GETTER)
{
r.declaration.name = 'get '+r.declaration.name;
}
if(traitInfo.kind == TraitsInfoToken.KIND_TRAIT_SETTER)
{
r.declaration.name = 'set '+r.declaration.name;
}
if(traitInfo.kind == TraitsInfoToken.KIND_TRAIT_SLOT || traitInfo.kind == TraitsInfoToken.KIND_TRAIT_CONST)
{
var slotInfo2:TraitSlotToken = TraitSlotToken(traitInfo.data);
switch(slotInfo2.vKind)
{
case 0x01:
r.initializer = '"' + String(abcFile.cpool.strings[slotInfo2.vIndex].utf8) + '"';
break;
case 0x03:
r.initializer = String(abcFile.cpool.integers[slotInfo2.vIndex]);
break;
case 0x08:
var ns:String = namespaceToString(slotInfo2.vIndex);
r.traitType = ReadableTrait.TYPE_NAMESPACE;
r.initializer = '"' + ns + '"';
break;
}
/*
CONSTANT_Int 0x03 integer
CONSTANT_UInt 0x04 uinteger
CONSTANT_Double 0x06 double
CONSTANT_Utf8 0x01 string
CONSTANT_True 0x0B -
CONSTANT_False 0x0A -
CONSTANT_Null 0x0C -
CONSTANT_Undefined 0x00 -
CONSTANT_Namespace 0x08 namespace
CONSTANT_PackageNamespace 0x16 namespace
CONSTANT_PackageInternalNs 0x17 Namespace
CONSTANT_ProtectedNamespace 0x18 Namespace
CONSTANT_ExplicitNamespace 0x19 Namespace
CONSTANT_StaticProtectedNs 0x1A Namespace
CONSTANT_PrivateNs 0x05 namespace
*/
}
}
private function getBodyIdFromMethodId(methodId:uint):int
{
return methodLookupCache[methodId];
}
public function namespaceToString(index:uint):String
{
var result:String = '';
var ns:NamespaceToken = abcFile.cpool.namespaces[index];
if(ns.kind == NamespaceToken.KIND_PrivateNs)
{
result = 'private';
}
else if(ns.kind == NamespaceToken.KIND_PackageInternalNs)
{
result = 'internal';
}
else if(ns.kind == NamespaceToken.KIND_ProtectedNamespace || ns.kind == NamespaceToken.KIND_StaticProtectedNs)
{
result = 'protected';
}
else
{
result = abcFile.cpool.strings[ns.name].utf8;
}
return result;
}
private function cloneObject(source:Object):Object
{
var clone:Object = new Object();
for(var iter:String in source)
{
clone[iter] = source[iter];
}
return clone;
}
private function uncoerce(value:String, pattern:String = '[\w]+', leaveParenthesis:Boolean = false):String
{
var matches:Array = value.match(new RegExp('^'+pattern+'\\((.*)\\)$'))
if(matches)
{
value = leaveParenthesis ? '('+matches[1]+')' : matches[1];
}
return value;
}
private function instructionsToString(methodName:String,
startTime:int,
instructions:Vector.<IInstruction>,
argumentNames:Vector.<String>,
slotNames:Object,
localCount:uint,
start:uint = 0,
cache2:Object = null,
hitmap:Object = null,
hitmapWithStack:Object = null,
knownConditions:Object = null,
positionLookup:Dictionary = null,
stopOnJump:Boolean = false,
scope:ScopeStack = null,
locals:LocalRegisters = null,
stack:OperandStack = null,
target:int = -1,
depth:int = 0,
definedLocals:Object = null):Object
{
var lines:Array = [];
trace('depth: '+depth+' '+start);
if(!scope)
{
scope = ScopeStack.buildstack();
}
if(!definedLocals)
{
definedLocals = {};
}
if(!locals)
{
locals = new LocalRegisters();
locals.setName(0, 'this');
locals.setValue(0, 'this');
var iterArg:uint;
var argumentCount:uint = argumentNames.length;
for(iterArg = 0; iterArg < argumentCount; iterArg++)
{
locals.setName(iterArg + 1, argumentNames[iterArg]);
locals.setValue(iterArg + 1, argumentNames[iterArg]);
definedLocals[iterArg + 1] = false;
}
for(; iterArg < localCount; iterArg++)
{
locals.setName(iterArg + 1, 'local'+(iterArg - argumentCount));
locals.setValue(iterArg + 1, 'null');
definedLocals[iterArg + 1] = false;
}
}
if(!stack)
{
stack = OperandStack.buildstack();
}
var localCount:uint = 0;
if(!hitmap)
{
hitmap = {};
}
if(!hitmapWithStack)
{
hitmapWithStack = {};
}
if(!knownConditions)
{
knownConditions = {};
}
if(!positionLookup)
{
positionLookup = new Dictionary();
for(var iter1:uint = 0; iter1 < instructions.length; iter1++)
{
positionLookup[instructions[iter1]] = iter1;
}
}
if(!cache2)
{
cache2 = {};
}
var subflow:Object = {};
var flow:Array = [];
var sourceUntil:Object = {};
var breakOn:int = -1;
var firstNextName:String = null;
var firstNextValue:String = null;
var firstWasNextName:Boolean = false;
var firstWasNextValue:Boolean = false;
var resultObj2:Object;
var importantFlow:Array = [];
var hassource:Boolean = false;
/*
if(getTimer() - startTime > 10000)
{
trace('Script timed out.');
return {result: '//ERROR: DECOMPILE TIMEOUT', flow: flow, breakOn: breakOn, sourceUntil: sourceUntil, firstNextName: firstNextName};
}
*/
for(var iter:uint = start; iter < instructions.length; iter++)
{
sourceUntil[iter] = lines.join('\n');
var key:String = iter+'$'+String(stack);
//trace('for: '+key+' ('+stack.values.length+')');
if(hitmap[iter])
{
//trace('hitmap hit: '+iter);
//lines.push('HITMAP HIT');
breakOn = iter;
break;
}
/*
if(cache[key])
{
trace('cache hit 13');
resultObj2 = {result: lines.join('\n'), flow: flow, breakOn: breakOn, sourceUntil: sourceUntil, firstNextName: firstNextName};
var cached:Object = cache[key];
resultObj2.result += '\n--JOINED FROM '+key+'--\n' + cached.result;
resultObj2.flow = resultObj2.flow.concat(cached.flow);
for(var iter5:String in cached.sourceUntil)
{
resultObj2[iter5] = cached.sourceUntil[iter5];
}
resultObj2.breakOn = cached.breakOn;
break;
}
*/
if(hitmapWithStack[key])
{
//trace('hitmapWithStack hit: '+key);
//lines.push('HITMAPSTACK HIT');
break;
}
if(iter == target)
{
//trace('target hit: '+iter);
breakOn = iter;
break;
}
if(op is EndInstruction)
{
continue;
}
subflow[iter] = 1;
hitmap[iter] = 1;
hitmapWithStack[key] = 1;
flow.push(key);
var op:IInstruction = instructions[iter];
var params:Array = [];
var line:String = '';
var args:Array = [];
var tempInt:int;
var tempInt2:int;
var tempArr:Array;
var tempStr:String;
var tempStr2:String;
var tempStr3:String;
var mn:MultinameToken;
var rmn:ReadableMultiname;
var source:String = '';
var sourceStarted:Boolean = false;
var exit:Boolean = false;
var tempStr4:String;
var b:Object;
var i:int;
if(showByteCode)
{
var description:XML = describeType(op);
var string:String = ' '
string += StringUtil.padEnd('#'+iter, ' ');
/*
if(offsetLookup && offsetLookup[iter])
{
string += '#'+offsetLookup[iter]+' ';
}
*/
if(!showDebug && (op is Instruction_debug || op is Instruction_debugfile || op is Instruction_debugline))
{
continue;
}
else
{
string += String(description.@name).replace(/.*Instruction_/, '');
}
for each(var name:String in description.variable.@name)
{
if(name.toLowerCase().indexOf('offset') != -1)
{
continue;
}
//multiname index
if(name == 'index' &&
(
op is Instruction_getlex ||
op is Instruction_callproperty ||
op is Instruction_callpropvoid ||
op is Instruction_coerce ||
op is Instruction_findpropstrict ||
op is Instruction_getproperty ||
op is Instruction_setproperty ||
op is Instruction_findproperty ||
op is Instruction_initproperty
))
{
var r:ReadableMultiname = new ReadableMultiname();
this.getReadableMultiname(op['index'], r);
var mnString:String = this.multinameTypeToString(r);
if(mnString == '?')
{
mnString = '(#'+op['index']+'/'+abcFile.cpool.multinames.length+')';
}
params.push(mnString);
}
//string index
else if(name == 'index' && (op is Instruction_pushstring))
{
params.push('"'+abcFile.cpool.strings[op['index']].utf8+'"');
}
else
{
var prop:* = op[name];
if(prop is IInstruction)
{
prop = '#'+positionLookup[prop];
}
else if(prop is Vector.<IInstruction>)
{
var props:Array = [];
for(tempInt = 0; tempInt < prop.length; tempInt++)
{
props.push('#'+positionLookup[prop[tempInt]]);
}
prop = props.join(', ');
}
params.push(name+': '+prop);
}
//props[name] = variable[name];
}
string += ' ' + params.join(', ');
lines.push(string);
}
if(showActionScript)
{
function branch(target1:int, target2:int, knownConditions1:Object, knownConditions2:Object):Object
{
var tempStr1:String = '';
var tempStr2:String = '';
var hitmapCopy1:Object = cloneObject(hitmapWithStack);
var hitmapCopy2:Object = cloneObject(hitmapWithStack);
var hitmapCopy3:Object = cloneObject(hitmap);
var hitmapCopy4:Object = cloneObject(hitmap);
var stackCopy1:OperandStack = stack.clone();
var stackCopy2:OperandStack = stack.clone();
var scope1:ScopeStack = scope.clone();
var scope2:ScopeStack = scope.clone();
trace(' branch point: '+iter);
trace(' start branch from: '+target1);
var r1:Object = instructionsToString(methodName, startTime, instructions,
argumentNames, slotNames, localCount, target1, cache2, hitmapCopy3, hitmapCopy1,
knownConditions1, positionLookup, true, scope1, locals, stackCopy1, -1, depth + 1, definedLocals);
trace(' end branch from: '+target1);
trace(' start branch from: '+target2);
var r2:Object = instructionsToString(methodName, startTime, instructions,
argumentNames, slotNames, localCount, target2, cache2, hitmapCopy4, hitmapCopy2,
knownConditions2, positionLookup, true, scope2, locals, stackCopy2, -1, depth + 1, definedLocals);
trace(' end branch from: '+target2);
var isWhile:Boolean = false;
var isForIn:Boolean = false;
var isForEachIn:Boolean = false;
var a1:int = -1;
var a2:String = '';
var id:int = stack.getid;
//Debug.dump({flow1: r1.flow, flow2: r2.flow});
outer:
for(var iter4:int = 0; iter4 < r1.flow.length; iter4++)
{
for(var iter5:int = 0; iter5 < r2.flow.length; iter5++)
{
if(r1.flow[iter4] == r2.flow[iter5])
{
a2 = r1.flow[iter4];
var splitPoint:int = a2.indexOf('$');
a1 = int(a2.substr(0, splitPoint));
var tempidstr:String = a2.substr(splitPoint + 1);
if(tempidstr)
{
id = int(tempidstr);
}
else
{
id = -1;
}
r1.flow.splice(iter4);
r2.flow.splice(iter5);
break outer;
}
}
}
if(showBranchInfo)
{
if(a1 >= 0)
{
lines.push(' MERGE @'+a1);
lines.push(' FLOW1 LENGTH: '+r1.flow.length+', BREAK @'+r1.breakOn);
lines.push(' FLOW2 LENGTH: '+r2.flow.length+', BREAK @'+r2.breakOn);
}
else
{
lines.push(' NO MERGE');
lines.push(' FLOW1 LENGTH: '+r1.flow.length+', BREAK @'+r1.breakOn);
lines.push(' FLOW2 LENGTH: '+r2.flow.length+', BREAK @'+r2.breakOn);
}
}
var localFirstNextName:String;
var localFirstNextValue:String;
if(a1 == -1)
{
//if(r1.breakOn >= 0 && (r1.breakOn == a1 || a1 == -1))
if(r1.breakOn >= 0)
{
for(var iterFlow1:String in subflow)
{
if(iterFlow1 == r1.breakOn)
{
isWhile = true;
r2.flow = [];
localFirstNextName = r1.firstNextName;
localFirstNextValue = r1.firstNextValue;
isForIn = r1.firstNextName != null;
isForEachIn = r1.firstNextValue != null;
break;
}
}
}
//if(r2.breakOn >= 0 && (r2.breakOn == a1 || a1 == -1))
if(r2.breakOn >= 0)
{
for(var iterFlow2:String in subflow)
{
if(iterFlow2 == r2.breakOn)
{
isWhile = true;
r1.flow = [];
localFirstNextName = r2.firstNextName;
localFirstNextValue = r2.firstNextValue;
isForIn = r2.firstNextName != null;
isForEachIn = r2.firstNextValue != null;
break;
}
}
}
}
//Debug.dump(r1.flow);
//Debug.dump(r2.flow);
for(var iterHit2:uint = 0; iterHit2 < r1.flow.length; iterHit2++)
{
if(r1.flow[iterHit2] == a1)
{
break;
}
hitmapWithStack[r1.flow[iterHit2]] = 1;
}
for(var iterHit3:uint = 0; iterHit3 < r2.flow.length; iterHit3++)
{
if(r2.flow[iterHit3] == a1)
{
break;
}
hitmapWithStack[r2.flow[iterHit3]] = 1;
}
if(a1 >= 0)
{
tempStr1 = r1.sourceUntil[a1];
tempStr2 = r2.sourceUntil[a1];
}
else
{
tempStr1 = r1.result;
tempStr2 = r2.result;
}
//trace(tempStr1);
tempStr1 = tempStr1 ? StringUtil.indent(tempStr1, ' ') : '';
tempStr2 = tempStr2 ? StringUtil.indent(tempStr2, ' ') : '';
tempStr1 = (tempStr1 && r1.hassource) ? tempStr1 : '';
tempStr2 = (tempStr2 && r2.hassource) ? tempStr2 : '';
var localResult:Object = {
flow1: r1.flow, flow2: r2.flow,
source1: tempStr1, source2: tempStr2,
merge: a1, isWhile: isWhile,
isForIn: isForIn, isForEachIn: isForEachIn,
firstNextName: localFirstNextName, firstNextValue: localFirstNextValue,
id: id
};
return localResult;
}
var boolrexp:RegExp =/^(!+)?(?:Boolean)?\((.+)\)$/;
function unwrapbool(cond:String):Object {
var ret:Object = { xorexprop:Boolean(false),condexpr:cond};
if (boolrexp.test(cond)){
var condtounwraped:String = cond.slice();
while(true){
var result:Object = boolrexp.exec(condtounwraped);
if (result) {
condtounwraped = '';
if (result[1]&&(result[1].length % 2 == 1)) {
ret.xorexprop = !ret.xorexprop;
}
if (result[2]) condtounwraped += result[2];
}
else break;
}
ret.condexpr = condtounwraped;
}
return ret;
}
function prunedup(cond:String):String {
cond = cond.replace(/^<dup>/, '');
return cond;
}
function xor(x:Boolean, y:Boolean):Boolean {
return !( x && y ) && ( x || y );
}
function conditional(condition:String, inequality:Boolean):void
{
tempInt = positionLookup[Object(op).reference];
condition = prunedup(condition);
var canonicalboolexp:Object= unwrapbool(condition);
var canonicalcondition:String=canonicalboolexp.condexpr;
var isdecidable:Boolean = canonicalcondition && knownConditions.hasOwnProperty(canonicalcondition);
if(isdecidable)
{
var bcondition:Boolean = xor(knownConditions[canonicalcondition], canonicalboolexp.xorexprop);
var execute:Boolean = bcondition == !inequality;
if(execute)
{
iter = tempInt-1;
}
trace('KNOWN CONDITION: ' + canonicalcondition + ' = ' + knownConditions[canonicalcondition] + ', skipping to: ' + iter);
return;
}
var key2:String = iter+'$'+String(stack);
var cached:Object;
if(cache2[key2])
{
trace('CACHE HIT!');
trace(' key: '+key2);
cached = cache2[key2];
//return 'CACHE @'+key2+ '\n' + cached.source;
b = cached.b;
}
else
{
trace('CACHE MISS! EXECUTING EXPENSIVE BRANCH');
trace(' key: '+key2);
var knownConditions1:Object = cloneObject(knownConditions);
var knownConditions2:Object = cloneObject(knownConditions);
knownConditions1[canonicalcondition] = !canonicalboolexp.xorexprop;
knownConditions2[canonicalcondition] = canonicalboolexp.xorexprop;
if(xor(inequality, canonicalboolexp.xorexprop))
{
b = branch(tempInt, iter + 1, knownConditions1, knownConditions2);
}
else
{
b = branch(iter + 1, tempInt, knownConditions2, knownConditions1);
}
}
stack.copyfrom(OperandStack.stacks[b.id]);
var cond:String = b.isWhile ? 'while' : 'if';
var checkHasNext:RegExp = /^<hasnext2>\((.*)\)$/;
if(b.isWhile && b.isForIn)
{
var matches:Array = condition.match(checkHasNext);
if(matches)
{
cond = 'for';
condition = b.firstNextName+' in '+matches[1];
}
}
if(b.isWhile && b.isForEachIn)
{
var matches2:Array = condition.match(checkHasNext);
if(matches2)
{
cond = 'for each';
condition = b.firstNextValue+' in '+matches2[1];
}
}
tempStr2 = '';
var hasstatement:Array = [(b.flow1.length > 0) && (b.source1.length > 0),(b.flow2.length > 0) && (b.source2.length > 0)];
if(hasstatement[0])
{
if(hasstatement[1])
{
var matches3:Array = b.source1.match(/^[\s]*(if\([^)]+\)[\s]*{.*}[\s]*$)/s);
var elseBlock:String;
if(matches3)
{
elseBlock = 'else '+matches3[1].replace(/^\t/gm, '');
}
else
{
elseBlock = 'else\n{\n'+b.source1+'\n}';
}
tempStr2 = cond+'('+condition+')\n{\n'+b.source2+'\n}\n'+elseBlock;
}
else
{
tempStr2 = cond+'(!('+condition+'))\n{\n'+b.source1+'\n}';
}
}
else
{
if(hasstatement[1])
{
tempStr2 = cond+'('+condition+')\n{\n'+b.source2+'\n}';
}
}
source = tempStr2;
if(b.merge > 0)
{
iter = b.merge - 1;
}
if(showBranchInfo)
{
source = '//'+key2+'\n'+source;
}
if(cached)
{
if(showBranchInfo)
{
source = '[CACHED]\n'+source;
}
}
else
{
cache2[key2] = {b: b};
}
}
function localAssign(id:int):void
{
var tempStr:String = stack.pop();
var coercion:RegExp = /^([\w.]+)\((.*)\)$/i;
var result:Array = tempStr.match(coercion);
tempStr2 = result ? result[1] : '';
tempStr3 = result ? result[2] : tempStr;
if(!definedLocals[id] && tempStr2)
{
tempStr4 = tempStr2.split('.').pop();
tempStr4 = tempStr4.substr(0, 1).toLowerCase() + tempStr4.substr(1);
var tempLocal:String = tempStr4;
var iterTempLocal:uint = 1;
tempStr4 = locals.decidenewvarnamewithprefix(tempLocal);
locals.setName(id, tempStr4);
}
var dec:String = locals.getName(id);
var name:String = dec;
var isNextName:Boolean = false;
if(tempStr.match(/^([\w]+\()?<nextname>/))
{
isNextName = true;
firstNextName = dec;
}
var isNextValue:Boolean = false;
if(tempStr.match(/^([\w]+\()?<nextvalue>/))
{
isNextValue = true;
firstNextValue = dec;
}
var addedDeclaration:Boolean = false;
if(definedLocals[id] == false || definedLocals[id] == iter)
{
dec = 'var '+dec;
if(!tempStr2)
{
tempStr2 = '*';
}
dec = dec+':'+tempStr2;
definedLocals[id] = iter;
addedDeclaration = true;
}
if(tempStr3 != '<activation>' && tempStr3 != name && !isNextName && !isNextValue)
{
if(addedDeclaration && tempStr3 == 'undefined')
{
source = dec+';';
}
else
{
source = dec+' = '+tempStr3+';';
}
}
locals.setValue(id, tempStr3);
}
function wrapInParenthesis(code:String):String
{
var alpha:RegExp = /^[\w.]+$/i;
if(!alpha.test(code))
{
var stringLiteral:RegExp = /^"[^"]*"$/i;
if(!stringLiteral.test(code))
{
code = '('+code+')';
}
}
return code;
}
function coerce(type:String):void
{
var operand:String = stack.pop();
if(operand && operand != 'null' && operand != 'undefined')
{
var dup:Boolean = operand.substr(0, 5) == '<dup>';
if(dup)
{
operand = operand.substr(5);
}
var matches:Array = operand.match(new RegExp('^'+type+'\\(.*\\)$'))
if(!matches)
{
operand = type+'('+operand+')';
}
if(dup)
{
operand = '<dup>'+operand;
}
}
stack.push(operand);
}
if(op is EndInstruction)
{
}
else if(op is Instruction_debug || op is Instruction_debugfile || op is Instruction_debugline)
{
}
else if(op is Instruction_hasnext2)
{
tempStr = locals.getName(Instruction_hasnext2(op).objectReg);
stack.push('<hasnext2>('+tempStr+')');
}
else if(op is Instruction_nextname)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
stack.push('<nextname>('+tempStr+', '+tempStr2+')');
if(!sourceStarted)
{
firstWasNextName = true;
}
}
else if(op is Instruction_nextvalue)
{
if(!sourceStarted)
{
firstWasNextValue = true;
}
tempStr = stack.pop();
tempStr2 = stack.pop();
stack.push('<nextvalue>('+tempStr+', '+tempStr2+')');
}
else if(op is Instruction_lookupswitch)
{
tempInt = int(stack.pop());
if(Instruction_lookupswitch(op).caseReferences.hasOwnProperty(tempInt))
{
tempInt2 = positionLookup[Instruction_lookupswitch(op).caseReferences[tempInt]];
}
else
{
tempInt2 = positionLookup[Instruction_lookupswitch(op).defaultReference];
}
iter = tempInt2 - 1;
}
else if(op is Instruction_jump)
{
tempInt = positionLookup[Instruction_jump(op).reference];
iter = tempInt - 1;
}
else if(op is Instruction_greaterthan)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
stack.push(tempStr2 + ' > '+ tempStr);
}
else if(op is Instruction_greaterequals)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
stack.push(tempStr2 + ' >= '+ tempStr);
}
else if(op is Instruction_lessthan)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
stack.push(tempStr2 + ' < '+ tempStr);
}
else if(op is Instruction_lessequals)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
stack.push(tempStr2 + ' <= '+ tempStr);
}
else if(op is Instruction_ifstrictne)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' !== '+tempStr, false);
}
else if(op is Instruction_iftrue)
{
tempStr4 = stack.pop();
if(tempStr4 == 'true')
{
iter = positionLookup[Instruction_iftrue(op).reference] - 1;
}
else {
conditional(tempStr4, false);
}
}
else if(op is Instruction_iffalse)
{
tempStr4 = stack.pop();
if(tempStr4 == 'false')
{
iter = positionLookup[Instruction_iffalse(op).reference] - 1;
}
else
{
conditional(tempStr4, true);
}
}
else if(op is Instruction_ifeq)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' == '+tempStr, false);
}
else if(op is Instruction_ifstricteq)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' === '+tempStr, false);
}
else if(op is Instruction_ifne)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' != '+tempStr, false);
}
else if(op is Instruction_ifstrictne)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' !== '+tempStr, false);
}
else if(op is Instruction_ifgt)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' > '+tempStr, false);
}
else if(op is Instruction_iflt)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' < '+tempStr, false);
}
else if(op is Instruction_ifge)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' >= '+tempStr, false);
}
else if(op is Instruction_ifle)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' <= '+tempStr, false);
}
else if(op is Instruction_ifnge)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' >= '+tempStr, true);
}
else if(op is Instruction_ifnle)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' <= '+tempStr, true);
}
else if(op is Instruction_ifnlt)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' < '+tempStr, true);
}
else if(op is Instruction_ifngt)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
conditional(tempStr2+' > '+tempStr, true);
}
else if(op is Instruction_getlocal0)
{
stack.push(locals.getName(0));
//stack.push(locals.getValue(0));
}
else if(op is Instruction_getlocal1)
{
stack.push(locals.getName(1));
//stack.push(locals.getValue(1));
}
else if(op is Instruction_getlocal2)
{
stack.push(locals.getName(2));
//stack.push(locals.getValue(2));
}
else if(op is Instruction_getlocal3)
{
stack.push(locals.getName(3));
//stack.push(locals.getValue(3));
}
else if(op is Instruction_getlocal)
{
stack.push(locals.getName(Instruction_getlocal(op).index));
//stack.push(locals.getValue(Instruction_getlocal(op).index));
}
else if(op is Instruction_getslot)
{
tempStr = stack.pop();
stack.push(slotNames[Instruction_getslot(op).slotIndex]);
}
else if(op is Instruction_setlocal0)
{
localAssign(0);
}
else if(op is Instruction_setlocal1)
{
localAssign(1);
}
else if(op is Instruction_setlocal2)
{
localAssign(2);
}
else if(op is Instruction_setlocal3)
{
localAssign(3);
}
else if(op is Instruction_setlocal)
{
localAssign(Instruction_setlocal(op).index);
}
else if(op is Instruction_setslot)
{
tempStr = stack.pop();
tempStr3 = stack.pop();
var matches:Array = tempStr.match(/([\w]+)\((.*)\)/s);
tempStr2 = '*';
if(matches)
{
tempStr = matches[2];
tempStr2 = matches[1];
}
tempStr4 = slotNames[Instruction_setslot(op).slotIndex];
if(tempStr4 != tempStr)
{
source = 'var '+tempStr4+':'+tempStr2+' = '+tempStr+';';
}
//source = slotNames[Instruction_setslot(op).slotIndex]+' = '+tempStr+';';
}
else if(op is Instruction_kill)
{
locals.setValue(Instruction_kill(op).index, 'undefined');
}
else if(op is Instruction_dup)
{
tempStr = stack.pop();
if(tempStr.substr(0, 5) == '<dup>')
{
stack.push(tempStr);
}
else
{
stack.push('<dup>'+tempStr);
}
stack.push(tempStr);
}
else if(op is Instruction_swap)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
stack.push(tempStr);
stack.push(tempStr2);
}
else if(op is Instruction_throw)
{
source = 'throw '+stack.pop()+';';
}
/*
else if(op is Instruction_newclass)
{
source = 'throw '+stack.pop()+';';
}
*/
else if(op is Instruction_increment_i)
{
stack.push('int('+stack.pop()+') + 1');
}
else if(op is Instruction_decrement_i)
{
stack.push('int('+stack.pop()+') - 1');
}
else if(op is Instruction_add)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr2+' + '+tempStr);
}
else if(op is Instruction_subtract)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr2+' - '+tempStr);
}
else if(op is Instruction_multiply)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr2+' * '+tempStr);
}
else if(op is Instruction_divide)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr2+' / '+tempStr);
}
else if(op is Instruction_modulo)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr2+' % '+tempStr);
}
else if(op is Instruction_negate)
{
tempStr = wrapInParenthesis(stack.pop());
stack.push('-'+tempStr);
}
else if(op is Instruction_bitand)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr2+' & '+tempStr);
}
else if(op is Instruction_bitor)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr2+' | '+tempStr);
}
else if(op is Instruction_bitxor)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr2+' ^ '+tempStr);
}
else if(op is Instruction_bitnot)
{
tempStr = wrapInParenthesis(stack.pop());
stack.push('~'+tempStr);
}
else if(op is Instruction_lshift)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr2+' << '+tempStr);
}
else if(op is Instruction_rshift)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr2+' >> '+tempStr);
}
else if(op is Instruction_urshift)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr2+' >>> '+tempStr);
}
else if(op is Instruction_equals)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr+' == '+tempStr2);
}
else if(op is Instruction_strictequals)
{
tempStr = wrapInParenthesis(stack.pop());
tempStr2 = wrapInParenthesis(stack.pop());
stack.push(tempStr+' === '+tempStr2);
}
else if(op is Instruction_not)
{
tempStr = wrapInParenthesis(stack.pop());
stack.push('!'+tempStr);
}
else if(op is Instruction_findpropstrict)
{
tempInt = Instruction_findpropstrict(op).index;
mn = abcFile.cpool.multinames[tempInt];
switch(mn.kind)
{
default:
rmn = new ReadableMultiname();
getReadableMultiname(tempInt, rmn);
tempStr = this.multinameTypeToString(rmn);
stack.push(tempStr);
break;
}
}
else if(op is Instruction_findproperty)
{
tempInt = Instruction_findproperty(op).index;
mn = abcFile.cpool.multinames[tempInt];
switch(mn.kind)
{
default:
rmn = new ReadableMultiname();
getReadableMultiname(tempInt, rmn);
tempStr = this.multinameTypeToString(rmn);
stack.push(tempStr);
break;
}
}
else if(op is Instruction_getsuper)
{
tempInt = Instruction_getsuper(op).index;
mn = abcFile.cpool.multinames[tempInt];
var obj:String = stack.pop();
switch(mn.kind)
{
case MultinameToken.KIND_RTQNameL:
case MultinameToken.KIND_RTQNameLA:
case MultinameToken.KIND_MultinameL:
case MultinameToken.KIND_MultinameLA:
tempStr = stack.pop();
tempStr = 'super['+obj+']';
break;
default:
rmn = new ReadableMultiname();
getReadableMultiname(tempInt, rmn);
tempStr = this.multinameTypeToString(rmn);
if(obj != tempStr)
{
tempStr = 'super.'+tempStr;
}
break;
}
stack.push(tempStr);
}
else if(op is Instruction_setsuper)
{
tempInt = Instruction_setsuper(op).index;
mn = abcFile.cpool.multinames[tempInt];
switch(mn.kind)
{
case MultinameToken.KIND_RTQNameL:
case MultinameToken.KIND_RTQNameLA:
case MultinameToken.KIND_MultinameL:
case MultinameToken.KIND_MultinameLA:
tempStr = stack.pop();
tempStr2 = stack.pop();
tempStr3 = stack.pop();
source = 'super['+tempStr2+'] = '+tempStr+';';
break;
default:
var value3:String = stack.pop();
tempStr2 = stack.pop();
rmn = new ReadableMultiname();
getReadableMultiname(tempInt, rmn);
tempStr = this.multinameTypeToString(rmn);
if(tempStr2 != tempStr)
{
tempStr = 'super.'+tempStr;
}
tempStr = tempStr;
source = tempStr+' = '+value3+';';
break;
}
}
else if(op is Instruction_getproperty)
{
tempInt = Instruction_getproperty(op).index;
mn = abcFile.cpool.multinames[tempInt];
switch(mn.kind)
{
case MultinameToken.KIND_RTQNameL:
case MultinameToken.KIND_RTQNameLA:
case MultinameToken.KIND_MultinameL:
case MultinameToken.KIND_MultinameLA:
tempStr2 = stack.pop();
tempStr = stack.pop();
tempStr = tempStr+'['+tempStr2+']';
break;
default:
rmn = new ReadableMultiname();
getReadableMultinameRuntime(tempInt, rmn, stack);
tempStr = this.multinameTypeToString(rmn);
tempStr2 = stack.pop();
if((tempStr != tempStr2) && (tempStr2 != rmn.name))
{
tempStr = this.multinameTypeToString(rmn, '::');
tempStr = tempStr2+'.'+tempStr;
}
else
{
tempStr = this.multinameTypeToString(rmn);
}
break;
}
stack.push(tempStr);
}
else if(op is Instruction_setproperty)
{
tempInt = Instruction_setproperty(op).index;
mn = abcFile.cpool.multinames[tempInt];
switch(mn.kind)
{
case MultinameToken.KIND_RTQNameL:
case MultinameToken.KIND_RTQNameLA:
case MultinameToken.KIND_MultinameL:
case MultinameToken.KIND_MultinameLA:
tempStr = stack.pop();
tempStr2 = stack.pop();
tempStr3 = stack.pop();
source = tempStr3+'['+tempStr2+'] = '+tempStr+';';
break;
default:
tempStr4 = stack.pop();
rmn = new ReadableMultiname();
getReadableMultinameRuntime(tempInt, rmn, stack);
tempStr = this.multinameTypeToString(rmn);
tempStr2 = stack.pop();
if((tempStr2 != tempStr) && (tempStr2 != rmn.name))
{
tempStr = this.multinameTypeToString(rmn, '::');
tempStr = tempStr2+'.'+tempStr;
}
else
{
tempStr = this.multinameTypeToString(rmn);
}
source = tempStr+' = '+tempStr4+';';
break;
}
}
else if(op is Instruction_initproperty)
{
tempInt = Instruction_initproperty(op).index;
mn = abcFile.cpool.multinames[tempInt];
switch(mn.kind)
{
case MultinameToken.KIND_RTQNameL:
case MultinameToken.KIND_RTQNameLA:
case MultinameToken.KIND_MultinameL:
case MultinameToken.KIND_MultinameLA:
tempStr = stack.pop();
tempStr2 = stack.pop();
tempStr3 = stack.pop();
source = tempStr3+'['+tempStr2+'] = '+tempStr+';';
break;
default:
var value2:String = stack.pop();
tempStr2 = stack.pop();
rmn = new ReadableMultiname();
getReadableMultiname(tempInt, rmn);
tempStr = this.multinameTypeToString(rmn);
if(tempStr2 != tempStr)
{
tempStr = tempStr2+'.'+tempStr;
}
tempStr = tempStr;
source = tempStr+' = '+value2+';';
break;
}
}
else if(op is Instruction_getlex)
{
tempInt = Instruction_getlex(op).index;
mn = abcFile.cpool.multinames[tempInt];
switch(mn.kind)
{
default:
rmn = new ReadableMultiname();
getReadableMultiname(tempInt, rmn);
tempStr = this.multinameTypeToString(rmn);
tempStr = tempStr;
stack.push(tempStr);
//source = 'getprop - '+tempStr;
break;
}
}
else if(op is Instruction_getdescendants)
{
tempInt = Instruction_getdescendants(op).index;
mn = abcFile.cpool.multinames[tempInt];
rmn = new ReadableMultiname();
getReadableMultinameRuntime(tempInt, rmn, stack);
tempStr2 = stack.pop();
tempStr = this.multinameTypeToString(rmn);
tempStr = tempStr2+'..'+tempStr;
stack.push(tempStr);
}
else if(op is Instruction_call)
{
args = [];
for(tempInt2 = Instruction_call(op).argCount - 1; tempInt2 >= 0; tempInt2--)
{
args.unshift(stack.pop());
}
tempStr2 = stack.pop();
tempStr3 = stack.pop();
if(tempStr2 == '<global>' || tempStr2 == 'null')
{
tempStr = tempStr3+'('+args.join(', ')+')';
}
else
{
args.unshift(tempStr2);
tempStr = tempStr3+'.call('+args.join(', ')+')';
}
stack.push(tempStr);
}
else if(op is Instruction_callproperty)
{
tempInt = Instruction_callproperty(op).index;
mn = abcFile.cpool.multinames[tempInt];
switch(mn.kind)
{
default:
args = [];
for(tempInt2 = Instruction_callproperty(op).argCount - 1; tempInt2 >= 0; tempInt2--)
{
args.unshift(stack.pop());
}
tempStr2 = stack.pop();
rmn = new ReadableMultiname();
getReadableMultiname(tempInt, rmn);
tempStr = this.multinameTypeToString(rmn);
if(tempStr2 != tempStr)
{
tempStr = tempStr2+'.'+tempStr;
}
tempStr = tempStr+'('+args.join(', ')+')';
localCount++;
/*
stack.push('temp'+localCount);
source = 'var temp'+localCount+':* = '+tempStr+';';
*/
stack.push(tempStr);
break;
}
}
else if(op is Instruction_callpropvoid)
{
tempInt = Instruction_callpropvoid(op).index;
mn = abcFile.cpool.multinames[tempInt];
switch(mn.kind)
{
default:
args = [];
for(tempInt2 = Instruction_callpropvoid(op).argCount - 1; tempInt2 >= 0; tempInt2--)
{
args.unshift(stack.pop());
}
tempStr2 = stack.pop();
rmn = new ReadableMultiname();
getReadableMultiname(tempInt, rmn);
tempStr = this.multinameTypeToString(rmn);
if(tempStr2 != tempStr)
{
tempStr = tempStr2+'.'+tempStr;
}
tempStr = tempStr+'('+args.join(', ')+')';
source = tempStr+';';
break;
}
}
else if(op is Instruction_callsuper)
{
args = [];
for(tempInt2 = Instruction_callsuper(op).argCount - 1; tempInt2 >= 0; tempInt2--)
{
args.unshift(stack.pop());
}
tempStr = 'super.'+methodName+'('+args.join(', ')+')';
source = tempStr+';';
}
else if(op is Instruction_callsupervoid)
{
if(!sourceStarted)
{
source = 'super();';
}
}
else if(op is Instruction_constructprop)
{
tempInt = Instruction_constructprop(op).index;
mn = abcFile.cpool.multinames[tempInt];
switch(mn.kind)
{
default:
args = [];
for(tempInt2 = Instruction_constructprop(op).argCount - 1; tempInt2 >= 0; tempInt2--)
{
args.unshift(stack.pop());
}
tempStr2 = stack.pop();
rmn = new ReadableMultiname();
getReadableMultiname(tempInt, rmn);
tempStr = this.multinameTypeToString(rmn);
if(tempStr2 != tempStr)
{
tempStr = tempStr2+'.'+tempStr;
}
tempStr = tempStr+'('+args.join(', ')+')';
localCount++;
/*
stack.push('temp'+localCount);
source = 'var temp'+localCount+':* = new '+tempStr+';';
*/
stack.push('new '+tempStr);
break;
}
}
else if(op is Instruction_constructsuper)
{
tempInt = Instruction_constructsuper(op).argCount;
args = [];
for(tempInt2 = 0; tempInt2 < tempInt; tempInt2++)
{
args.push(stack.pop());
}
//Not sure why this exists... should always be 'this'
stack.pop();
if(args.length != 0 || sourceStarted)
{
source = 'super('+args.join(', ')+');';
}
}
else if(op is Instruction_coerce)
{
tempStr = stack.pop();
tempInt = Instruction_coerce(op).index;
rmn = new ReadableMultiname();
getReadableMultiname(tempInt, rmn);
tempStr2 = this.multinameTypeToString(rmn);
stack.push(tempStr2+'('+tempStr+')');
}
else if(op is Instruction_coerce_s)
{
coerce('String');
}
else if(op is Instruction_coerce_a)
{
}
else if(op is Instruction_convert_b)
{
coerce('Boolean');
}
else if(op is Instruction_convert_d)
{
coerce('Number');
}
else if(op is Instruction_convert_i)
{
coerce('int');
}
else if(op is Instruction_convert_u)
{
coerce('uint');
}
else if(op is Instruction_astypelate)
{
tempStr = stack.pop();
coerce(tempStr);
}
else if(op is Instruction_applytype)
{
tempArr = [];
for(i = 0; i < Instruction_applytype(op).argCount; i++)
{
tempArr.push(stack.pop());
}
tempArr.reverse();
tempStr2 = tempArr.join(', ');
tempStr = stack.pop();
stack.push(tempStr+'.<'+tempStr2+'>');
}
else if(op is Instruction_istypelate)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
stack.push(tempStr2+' is '+tempStr);
}
else if(op is Instruction_inclocal_i)
{
var index:int = Instruction_inclocal_i(op).index;
source += ["++",locals.getName(index),";"].join("");
locals.setValue(index, [locals.getValue(index), "+1"].join(""));
}
else if(op is Instruction_in)
{
tempStr = stack.pop();
tempStr2 = stack.pop();
stack.push(tempStr2+' in '+tempStr);
}
else if(op is Instruction_construct)
{
tempArr = [];
for(i = 0; i < Instruction_construct(op).argCount; i++)
{
tempArr.push(stack.pop());
}
tempArr.reverse();
tempStr2 = tempArr.join(', ');
tempStr = stack.pop();
stack.push('new '+tempStr+'('+tempStr2+')');
}
else if(op is Instruction_typeof)
{
stack.push('typeof('+stack.pop()+')');
}
else if(op is Instruction_increment)
{
stack.push(stack.pop()+' + 1');
}
else if(op is Instruction_decrement)
{
stack.push(stack.pop()+' - 1');
}
else if(op is Instruction_pushwith)
{
scope.push(stack.pop());
}
else if(op is Instruction_pushscope)
{
scope.push(stack.pop());
}
else if(op is Instruction_popscope)
{
scope.pop();
}
else if(op is Instruction_getscopeobject)
{
stack.push(scope.getvalue(scope.length - 1 - Instruction_getscopeobject(op).index));
}
else if(op is Instruction_getglobalscope)
{
stack.push('<global>');
}
else if(op is Instruction_pushnull)
{
stack.push('null');
}
else if(op is Instruction_pushundefined)
{
stack.push('undefined');
}
else if(op is Instruction_pushnan)
{
stack.push('NaN');
}
else if(op is Instruction_pushbyte)
{
stack.push(String(Instruction_pushbyte(op).byteValue));
}
else if(op is Instruction_pushshort)
{
stack.push(String(Instruction_pushshort(op).value));
}
else if(op is Instruction_pushint)
{
stack.push(String(abcFile.cpool.integers[Instruction_pushint(op).index]));
}
else if(op is Instruction_pushuint)
{
stack.push(String(abcFile.cpool.uintegers[Instruction_pushuint(op).index]));
}
else if(op is Instruction_pushdouble)
{
stack.push(String(abcFile.cpool.doubles[Instruction_pushdouble(op).index]));
}
else if(op is Instruction_pushstring)
{
tempStr = abcFile.cpool.strings[Instruction_pushstring(op).index].utf8;
tempStr = tempStr.replace(/\r/g, '\\r');
tempStr = tempStr.replace(/\n/g, '\\n');
tempStr = tempStr.replace(/'/g, '\\\'');
stack.push('\''+tempStr+'\'');
}
else if(op is Instruction_pushtrue)
{
stack.push('true');
}
else if(op is Instruction_pushfalse)
{
stack.push('false');
}
else if(op is Instruction_pop)
{
tempStr = stack.pop();
if(tempStr != '<wasdeleted>')
{
if(tempStr && tempStr.substr(0, 5) != '<dup>')
{
source = tempStr+';';
}
}
}
else if(op is Instruction_newarray)
{
var argCountA:uint = Instruction_newarray(op).argCount;
var props2A:Array = [];
for(var iter2A:int = 0; iter2A < argCountA; iter2A++)
{
var valNA:* = stack.pop();
props2A.push(valNA);
}
props2A.reverse();
line += '['+props2A.join(', ')+']';
stack.push(line);
}
else if(op is Instruction_newobject)
{
var argCount:uint = Instruction_newobject(op).argCount;
var props2:Array = [];
for(var iter2:int = 0; iter2 < argCount; iter2++)
{
var valN:* = stack.pop();
var nameN:* = stack.pop();
props2.push(nameN+': '+valN);
}
props2.reverse();
line += '{'+props2.join(', ')+'}';
stack.push(line);
}
else if(op is Instruction_newactivation)
{
stack.push('<activation>');
}
else if(op is Instruction_newfunction)
{
var r2:ReadableTrait = new ReadableTrait();
getMethodBody(0, Instruction_newfunction(op).index, r2);
stack.push(traitToString(r2, true, false, false, 0));
}
else if(op is Instruction_deleteproperty)
{
tempStr2 = stack.pop();
rmn = new ReadableMultiname();
getReadableMultinameRuntime(Instruction_deleteproperty(op).index, rmn, stack);
tempStr = this.multinameTypeToString(rmn);
source = 'delete '+tempStr+'['+tempStr2+'];';
stack.push('<wasdeleted>');
}
else if(op is Instruction_returnvalue)
{
var str:String = stack.pop();
source = 'return '+str+';';
exit = true;
}
else if(op is Instruction_returnvoid)
{
if(!(instructions[iter + 1] is EndInstruction))
{
source = 'return;';
}
exit = true;
}
else if(op is Instruction_label)
{
}
else if(op is Instruction_checkfilter)
{
}
else
{
lines.push(' //UNKNOWN OP: '+op);
}
if(showStack)
{
lines.push(' stack: ' + stack.stackinfo);
lines.push(' local: ' + locals.names.join(', '));
}
var hassourcefornow:Boolean = (source.length > 0) && source.split('\n').some((function(a:String, ...args):Boolean { return (a.length>0) && !/^\/\//.test(a); } ));
if(hassourcefornow)
{
if (!hassource)
hassource = true;
lines.push(source);
}
if(!sourceStarted)
{
if(!source.match(/\s*#/))
{
sourceStarted = true;
}
}
if(exit)
{
break;
}
}
}
var resultObj:Object;
resultObj = {
result: lines.join('\n'), flow: flow,
breakOn: breakOn, sourceUntil: sourceUntil,
firstNextName: firstNextName, firstNextValue: firstNextValue, hassource:hassource
};
return resultObj;
}
public function scriptTraitToString(r:ReadableTrait):String
{
var result:String = '';
if(r.traitType == ReadableTrait.TYPE_CLASS)
{
result = classToString(r.classInfo);
}
else if(r.traitType == ReadableTrait.TYPE_NAMESPACE)
{
result =
'package '+r.declaration.namespace+'\n'+
'{\n' +
' public namespace '+r.declaration.name+' = '+r.initializer+';\n' +
'}';
}
return result;
}
public function traitToString(r:ReadableTrait, showMethodBody:Boolean = true, showNamespace:Boolean = true, showName:Boolean = true, methodIndents:int = 2):String
{
var pieces:Array = [];
if(showNamespace)
{
var ns:String = customNamespaces.hasOwnProperty(r.declaration.namespace) ? customNamespaces[r.declaration.namespace] : r.declaration.namespace;
pieces.push(ns+' ');
}
if(r.traitType == ReadableTrait.TYPE_METHOD)
{
if(r.isStatic)
{
pieces.push('static ');
}
}
else if(r.traitType == ReadableTrait.TYPE_CLASS)
{
pieces.push(classToString(r.classInfo));
}
else if(r.traitType == ReadableTrait.TYPE_NAMESPACE)
{
pieces.push('namespace ');
pieces.push(r.declaration.name);
if(r.initializer)
{
pieces.push(' = '+r.initializer);
}
pieces.push(';');
}
else if(r.traitType == ReadableTrait.TYPE_PROPERTY)
{
if(r.isStatic)
{
pieces.push('static ');
}
if(r.isConst)
{
pieces.push('const ');
}
else
{
pieces.push('var ');
}
pieces.push(r.declaration.name+':'+multinameTypeToString(r.type));
if(r.initializer)
{
pieces.push(' = '+r.initializer);
}
pieces.push(';');
}
else
{
trace('undefined trait type: '+r.traitType);
pieces = [];
}
if(r.traitType == ReadableTrait.TYPE_METHOD)
{
var args:Array = [];
for(var iter:uint = 0; iter < r.arguments.length; iter++)
{
args.push(r.argumentNames[iter] + ':' + multinameTypeToString(r.arguments[iter]));
}
if(showName)
{
pieces.push('function ');
pieces.push(r.declaration.name);
}
else
{
pieces.push('function');
}
pieces.push('('+args.join(', ')+')');
var type:String = multinameTypeToString(r.type);
if(type != '')
{
pieces.push(':'+type);
}
if(showMethodBody)
{
if(r.instructions && r.instructions.length > 0)
{
var indent:String = StringUtil.repeat('\t', methodIndents);
pieces.push('\n'+indent+'{\n'+StringUtil.indent(instructionsToString(r.declaration.name, getTimer(), r.instructions, r.argumentNames, r.slots, r.localCount).result, indent+' ')+'\n'+indent+'}');
}
else
{
pieces.push(' {}');
}
}
else
{
pieces.push(';');
}
}
return pieces.join('');
}
public function multinameTraitToString(index:uint, r:ReadableMultiname):void
{
getReadableMultiname(index, r);
if(customNamespaces[r.namespace])
{
r.namespace = customNamespaces[r.namespace];
}
if(r.namespace == '')
{
r.namespace = 'public';
}
}
public function multinameTypeToString(r:ReadableMultiname, seperator:String = '.'):String
{
var result:String = '';
if(customNamespaces[r.namespace])
{
r.namespace = customNamespaces[r.namespace];
}
if(r.namespace == '' || r.namespace == 'http://adobe.com/AS3/2006/builtin' || r.namespace == 'private' || r.namespace == 'protected' || r.namespace == 'internal')
{
result = r.name;
}
else
{
result = r.namespace + seperator + r.name;
}
return result;
}
public static function resetstackcount():void {
OperandStack.init();
ScopeStack.init();
}
public function classToString(c:ReadableClass):String
{
resetstackcount();
var properties:Array = [];
var type:String = c.type;
var isClass:Boolean = type == ReadableClass.CLASS;
for(var iter:String in c.traits)
{
var rt:ReadableTrait = c.traits[iter];
if(rt.traitType == ReadableTrait.TYPE_NAMESPACE)
{
var uri:String = rt.initializer.replace(/^"(.*)"$/, '$1');
trace(uri);
customNamespaces[uri] = multinameTypeToString(rt.declaration);
}
}
for(var iter3:String in c.traits)
{
var rt2:ReadableTrait = c.traits[iter3];
if(!isClass)
{
if(rt2.declaration.name == c.className.name && rt2.declaration.namespace == 'public')
{
continue;
}
}
var str:String = traitToString(rt2, isClass, isClass);
if(str != '')
{
properties.push(str);
}
}
function startsWith(str:String, substr:String):Boolean
{
return str.substr(0, substr.length) == substr;
}
function getRanking(str:String):int
{
var offset:int = 0;
if(startsWith(str, 'public '))
{
offset = 3;
}
else if(startsWith(str, 'protected '))
{
offset = 2;
}
else if(startsWith(str, 'private '))
{
offset = 1;
}
if(!startsWith(str, 'function '))
{
str = str.substr(str.indexOf(' ') + 1);
}
var typeOrder:Array = [
'static const ',
'static var ',
'static function ',
'function '+c.className.name+'(',
'const ',
'var ',
'function get ',
'function set ',
'function ',
];
for(var iter:uint = 0; iter < typeOrder.length; iter++)
{
if(startsWith(str, typeOrder[iter]))
{
return (typeOrder.length - iter) * 4 + offset;
}
}
return 0;
}
properties.sort(function(a:String, b:String):int
{
if(a == b)
{
return 0;
}
var aRank:int = getRanking(a);
var bRank:int = getRanking(b);
if(aRank == bRank)
{
return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
}
return aRank < bRank ? 1 : -1;
});
var interfaces:Array = [];
for(var iter2:uint = 0; iter2 < c.interfaces.length; iter2++)
{
var r:ReadableMultiname = new ReadableMultiname();
getReadableMultiname(c.interfaces[iter2], r);
interfaces.push(multinameTypeToString(r));
}
var interfaceString:String = '';
if(interfaces.length > 0)
{
interfaceString = ' implements '+interfaces.join(', ');
}
var inheritanceString:String = '';
var superName:String = multinameTypeToString(c.superName);
if(superName != '*' && superName != '' && superName != 'Object')
{
inheritanceString = ' extends '+superName;
}
var result:String =
'package '+c.className.namespace+'\n' +
'{\n' +
' public '+type+' '+c.className.name+inheritanceString+interfaceString+'\n' +
' {\n' +
' ' + properties.join('\n ') + '\n' +
' }\n' +
'}';
return result;
}
}
}