15 Commits
9 changed files with 627 additions and 319 deletions
+45
View File
@@ -0,0 +1,45 @@
RABCDAsm Changelog
==================
RABCDAsm v1.4 (2011.02.xx)
--------------------------
* Add support for forward-references for TypeName-kind Multinames
* Correctly order classes by dependencies (extends/implements) and reference
count
* Finish Metadata support
* Documentation updates
RABCDAsm v1.3 (2010.11.11)
--------------------------
* Fixed double precision problem
* This also fixes problems with illegal default values for function
parameters (default values for integer parameters are stored as doubles,
which might become out-of-range due to inadequate double precision)
* Added Changelog
* Documentation markdown fixes
RABCDAsm v1.2 (2010.11.06)
--------------------------
* Fixed ref generation for orphan objects which were only referenced
by other orphans
* Better error handling in `abcexport`; warn when no DoABC tags found
* Documentation updates
RABCDAsm v1.1 (2010.06.30)
--------------------------
* Private namespaces are now referenced by auto-generated names
* Use `:` to delimit namespace and name in QNames for consistency
* Warning: this breaks compatibility with v1.0 disassemblies
* Fixed relative include paths
* Add optional byte offsets to labels, which allows lossless representation
of jumps inside instructions and outside the function bounds
* Documentation updates
RABCDAsm v1.0 (2010.05.05)
--------------------------
* Initial release.
+55 -70
View File
@@ -1,7 +1,7 @@
Robust ABC (ActionScript Bytecode) [Dis-]Assembler
==================================================
[RABCDAsm][] is a collection of utilities including an ActionScript
[RABCDAsm][] is a collection of utilities including an ActionScript 3
assembler/disassembler, and a few tools to manipulate SWF files.
These are:
@@ -31,7 +31,7 @@ Motivation and goals
--------------------
This package was created due to lack of similar software out there.
Particularly, I needed an utility which would allow me to edit ActionScript
Particularly, I needed an utility which would allow me to edit ActionScript 3
bytecode with the following properties:
1. Speed. Less waiting means more productivity. `rabcasm` can assemble large
@@ -102,6 +102,9 @@ To assemble the `.asasm` files back, and update the SWF file:
rabcasm file0/file0.main.asasm
abcreplace file0.swf 0 file0/file0.main.abc
The second `abcreplace` argument represents the index of the ABC block in the
SWF file, and corresponds to the number in the filename created by `abcexport`.
Syntax
======
@@ -122,7 +125,7 @@ use [this handy list][avm2i] as well). You will find it difficult to understand
the disassembly without good understanding of concepts such as namespaces and
multinames.
[avm2]: http://www.adobe.com/devnet/actionscript/articles/avm2overview.pdf
[avm2]: http://www.adobe.com/devnet-archive/actionscript/articles/avm2overview.pdf
[avm2i]: http://www.anotherbigidea.com/javaswf/avm2/AVM2Instructions.html
Overview
@@ -168,7 +171,10 @@ are the trait fields, varying by trait kind:
* `method` / `getter` / `setter` : `dispid` (unsigned integer), `method`
Additionally, all traits may have `flag` fields, describing the trait's
attributes (`FINAL` / `OVERRIDE` / `METADATA`).
attributes (`FINAL` / `OVERRIDE` / `METADATA`), and `metadata` blocks.
`metadata` blocks (which are ignored by the AVM) consist of a name string, and
a series of `item` fields - each item having a key and value string.
`class` blocks have mandatory `instance` and `cinit` fields, defining the class
instance and the class initializer method respectively. They may also have
@@ -294,9 +300,9 @@ be instantiated in two ways:
although variables are defined using a string syntax, they are not
inserted as a string using this syntax. Thus, the code:
#set str "Hello, world!"
...
pushstring $str
#set str "Hello, world!"
...
pushstring $str
will expand to `pushstring Hello, world!`, which will result in an error.
To correct the problem, add escaped quotes around the variable contents
@@ -311,20 +317,20 @@ be instantiated in two ways:
Here's an example of how to use the above features to create a macro which
logs a string literal and the contents of a register:
#set log "
findpropstrict QName(PackageNamespace(\"\"), \"log\")
pushstring $\"1\"
getlocal $2
callpropvoid QName(PackageNamespace(\"\"), \"log\"), 2
"
; ...
pushbyte 2
pushbyte 2
add_i
setlocal1
#call $"log"("two plus two equals", "1")
#set log "
findpropstrict QName(PackageNamespace(\"\"), \"log\")
pushstring $\"1\"
getlocal $2
callpropvoid QName(PackageNamespace(\"\"), \"log\"), 2
"
; ...
pushbyte 2
pushbyte 2
add_i
setlocal1
#call $"log"("two plus two equals", "1")
Highlighting
------------
@@ -344,26 +350,23 @@ instead of indexes, allowing easy manipulation without having to worry about
record order or constant pools. Conversion between various states is done as
follows:
file.abc
| ^
ABCReader | | ABCWriter
v |
ABCFile
| ^
ABCtoAS | | AStoABC
v |
ASProgram
| ^
Disassembler | | Assembler
v |
file.asasm
file.abc
| ^
------ ABCReader | | ABCWriter ----
/ v | \
/ ABCFile \
/ | ^ \
rabcdasm---------- ABCtoAS | | AStoABC --------rabcasm
\ v | /
\ ASProgram /
\ | ^ /
--- Disassembler | | Assembler ----
v |
file.asasm
`AStoABC` will rebuild the constant pools, in a manner similar to Adobe's
compilers (reverse-sorted by reference count). The exact order will almost
surely be different, however. Also, some records (classes and methods) are
currently not sorted by reference count, which may cause `rabcasm` to generate
slightly larger files than the originals (due to variable-length encoding of
integers).
surely be different, however.
Should you need to write an utility to manipulate ABC, you can use the existing
code to load the file to either an `ABCFile` or `ASProgram` instance, and
@@ -394,19 +397,19 @@ RABCDAsm users.
placed in the `OnBeforeResponse` function) will automatically save all SWF
files while preserving the directory structure.
if (oSession.oResponse.headers.ExistsAndContains("Content-Type",
"application/x-shockwave-flash")) {
// Set desired path here
var path:String = "C:\\Temp\\FiddlerCapture\\" +
oSession.host + oSession.PathAndQuery;
if (path.Contains('?'))
path = path.Substring(0, path.IndexOf('?'));
var dir:String = Path.GetDirectoryName(path);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
oSession.utilDecodeResponse();
oSession.SaveResponseBody(path);
}
if (oSession.oResponse.headers.ExistsAndContains("Content-Type",
"application/x-shockwave-flash")) {
// Set desired path here
var path:String = "C:\\Temp\\FiddlerCapture\\" +
oSession.host + oSession.PathAndQuery;
if (path.Contains('?'))
path = path.Substring(0, path.IndexOf('?'));
var dir:String = Path.GetDirectoryName(path);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
oSession.utilDecodeResponse();
oSession.SaveResponseBody(path);
}
Once you have edited a SWF file, you can use Fiddler's [AutoResponder][] to
replace the original file with your modified version.
@@ -417,25 +420,7 @@ RABCDAsm users.
Limitations
===========
* Metadata is currently ignored. I haven't noticed any metadata blocks in any
SWF files I've disassembled.
* Floating point numbers may not be disassembled with adequate precision.
* `rabcasm` may create a broken file due to not ordering classes by ancestry.
The problem originates from the fact that a class's ancestors (extended
class and implemented interfaces) are stored as multinames, and not as
class indices. (This makes sense, since classes may extend objects outside
the current ABC file.) Since `rabcasm` currently doesn't decode multinames,
it is unaware of the class dependencies, and may thus write the classes out
of order. This results in a file that, when opened, will fail to load with
an error message similar to:
`VerifyError: Error #1014: Class AncestorClassName could not be found.`
The simple work-around is to re-order the classes as they are declared in
the `.main.asasm` file, and place ancestors before descendants.
* None known.
License
=======
+22 -16
View File
@@ -21,6 +21,7 @@ module abcexport;
import std.file;
import std.path;
import std.string;
import std.stdio;
import swffile;
void main(string[] args)
@@ -28,22 +29,27 @@ void main(string[] args)
if (args.length == 1)
throw new Exception("No file specified");
foreach (arg; args[1..$])
{
scope swf = SWFFile.read(cast(ubyte[])read(arg));
uint count;
foreach (ref tag; swf.tags)
if ((tag.type == TagType.DoABC || tag.type == TagType.DoABC2))
{
ubyte[] abc;
if (tag.type == TagType.DoABC)
abc = tag.data;
else
try
{
scope swf = SWFFile.read(cast(ubyte[])read(arg));
uint count = 0;
foreach (ref tag; swf.tags)
if ((tag.type == TagType.DoABC || tag.type == TagType.DoABC2))
{
auto p = tag.data.ptr+4; // skip flags
while (*p++) {} // skip name
abc = tag.data[p-tag.data.ptr..$];
ubyte[] abc;
if (tag.type == TagType.DoABC)
abc = tag.data;
else
{
auto p = tag.data.ptr+4; // skip flags
while (*p++) {} // skip name
abc = tag.data[p-tag.data.ptr..$];
}
write(getName(arg) ~ .toString(count++) ~ ".abc", abc);
}
write(getName(arg) ~ .toString(count++) ~ ".abc", abc);
}
}
if (count == 0)
throw new Exception("No DoABC tags found");
}
catch (Object o)
writefln("Error while processing %s: %s", arg, o);
}
+1 -1
View File
@@ -1716,7 +1716,7 @@ private final class ABCWriter
if (v.attr & TraitAttributes.Metadata)
{
writeU30(v.metadata.length);
foreach (ref value; v.metadata)
foreach (value; v.metadata)
writeU30(value);
}
}
+2
View File
@@ -74,10 +74,12 @@
<word name="implements"/>
<word name="initscopedepth"/>
<word name="instance"/>
<word name="item"/>
<word name="localcount"/>
<word name="majorversion"/>
<word name="maxscopedepth"/>
<word name="maxstack"/>
<word name="metadata"/>
<word name="method"/>
<word name="minorversion"/>
<word name="name"/>
+366 -157
View File
@@ -87,6 +87,7 @@ final class ASProgram
}
mixin AutoCompare;
mixin AutoToString;
R processData(R, string prolog, string epilog, H)(ref H handler)
{
@@ -124,6 +125,30 @@ final class ASProgram
}
mixin(epilog);
}
private Multiname[] toQNames()
{
switch (kind)
{
case ASType.QName:
case ASType.QNameA:
return [this];
case ASType.Multiname:
case ASType.MultinameA:
Multiname[] result;
foreach (ns; vMultiname.nsSet)
{
auto n = new Multiname;
n.kind = kind==ASType.Multiname ? ASType.QName : ASType.QNameA;
n.vQName.ns = ns;
n.vQName.name = vMultiname.name;
result ~= n;
}
return result;
default:
throw new .Exception("Can't expand Multiname of this kind");
}
}
}
static class Method
@@ -143,10 +168,16 @@ final class ASProgram
struct Item
{
string key, value;
mixin AutoCompare;
mixin ProcessAllData;
}
string name;
Item[] items;
mixin AutoCompare;
mixin ProcessAllData;
}
static class Instance
@@ -212,6 +243,11 @@ final class ASProgram
Trait[] traits;
Instance instance;
string toString()
{
return instance.name.toString();
}
}
static class Script
@@ -371,7 +407,7 @@ private final class ABCtoAS
return n;
}
ASProgram.Multiname convertMultiname(ref ABCFile.Multiname multiname, uint i)
ASProgram.Multiname convertMultiname(ref ABCFile.Multiname multiname)
{
auto n = new ASProgram.Multiname();
n.kind = multiname.kind;
@@ -399,16 +435,7 @@ private final class ABCtoAS
n.vMultinameL.nsSet = namespaceSets[multiname.MultinameL.nsSet];
break;
case ASType.TypeName:
if (multiname.TypeName.name >= i)
throw new Exception("Forward Multiname/TypeName reference");
n.vTypeName.name = multinames[multiname.TypeName.name];
n.vTypeName.params.length = multiname.TypeName.params.length;
foreach (j, param; multiname.TypeName.params)
{
if (param >= i)
throw new Exception("Forward Multiname/TypeName parameter reference");
n.vTypeName.params[j] = multinames[param];
}
// handled in postConvertMultiname
break;
default:
throw new Exception("Unknown Multiname kind");
@@ -416,6 +443,20 @@ private final class ABCtoAS
return n;
}
void postConvertMultiname(ref ABCFile.Multiname multiname, ASProgram.Multiname n)
{
switch (multiname.kind)
{
case ASType.TypeName:
n.vTypeName.name = multinames[multiname.TypeName.name];
n.vTypeName.params.length = multiname.TypeName.params.length;
foreach (j, param; multiname.TypeName.params)
n.vTypeName.params[j] = multinames[param];
default:
break;
}
}
ASProgram.Method convertMethod(ref ABCFile.MethodInfo method)
{
auto n = new ASProgram.Method();
@@ -482,6 +523,9 @@ private final class ABCtoAS
default:
throw new Exception("Unknown trait kind");
}
r[i].metadata.length = trait.metadata.length;
foreach (j, index; trait.metadata)
r[i].metadata[j] = metadata[index];
}
return r;
}
@@ -626,7 +670,10 @@ private final class ABCtoAS
multinames.length = abc.multinames.length;
foreach (i, ref multiname; abc.multinames)
if (i)
multinames[i] = convertMultiname(multiname, i);
multinames[i] = convertMultiname(multiname);
foreach (i, ref multiname; abc.multinames)
if (i)
postConvertMultiname(multiname, multinames[i]);
methods.length = methodAdded.length = abc.methods.length;
foreach (i, ref method; abc.methods)
@@ -686,19 +733,36 @@ private final class AStoABC
}
/// Maintain an unordered set of values; sort/index by usage count
struct Constant(T)
struct ConstantPool(T, bool haveNull = true)
{
static Constant!(T)[T] pool;
static T[] values;
static bool add(T value) // return true if added
struct Entry
{
if (isNull(value))
uint hits;
T value;
uint index;
mixin AutoCompare;
R processData(R, string prolog, string epilog, H)(ref H handler)
{
mixin(prolog);
mixin(addAutoField("hits", true));
mixin(addAutoField("value"));
mixin(epilog);
}
}
Entry[T] pool;
T[] values;
bool add(T value) // return true if added
{
if (haveNull && isNull(value))
return false;
auto cp = value in pool;
if (cp is null)
{
pool[value] = Constant!(T)(1, value);
pool[value] = Entry(1, value);
return true;
}
else
@@ -708,113 +772,196 @@ private final class AStoABC
}
}
static bool notAdded(T value)
bool notAdded(T value)
{
return !(isNull(value) || value in pool);
auto ep = value in pool;
if (ep)
ep.hits++;
return !((haveNull && isNull(value)) || ep);
}
static void sort()
void finalize()
{
auto all = pool.values;
all.sort;
values.length = all.length+1;
enum { NullOffset = haveNull ? 1 : 0 }
values.length = all.length + NullOffset;
foreach (i, ref c; all)
{
pool[c.value].index = i+1;
values[i+1] = c.value;
pool[c.value].index = i + NullOffset;
values[i + NullOffset] = c.value;
}
}
static uint get(T value)
uint get(T value)
{
if (isNull(value))
if (haveNull && isNull(value))
return 0;
return pool[value].index;
}
mixin AutoCompare;
R processData(R, string prolog, string epilog, H)(ref H handler)
{
mixin(prolog);
mixin(addAutoField("hits", true));
mixin(addAutoField("value"));
mixin(epilog);
}
uint hits;
T value;
uint index;
}
/// Pair an index with class instances
struct Reference(T)
struct ReferencePool(T : Object)
{
static Reference[void*] references;
static T[] objects;
struct Entry
{
uint hits;
void* object;
uint addIndex, index;
void*[] parents;
static bool add(T obj) // return true if added
int opCmp(Entry* o)
{
if (this.parents.contains(o.object))
return 1;
if (o.parents.contains(this.object))
return -1;
if (o.hits==hits)
return addIndex-o.addIndex;
else
return o.hits-hits;
}
}
Entry[void*] pool;
T[] objects;
bool add(T obj) // return true if added
{
if (obj is null)
return false;
auto p = cast(void*)obj;
auto rp = p in references;
auto rp = p in pool;
if (rp is null)
{
references[p] = Reference!(T)(1); //, objects.length
objects ~= obj;
pool[p] = Entry(1, p, pool.length);
return true;
}
else
{
rp.hits++;
return false;
}
}
static bool notAdded(T obj)
bool notAdded(T obj)
{
return !(obj is null || (cast(void*)obj) in references);
auto ep = (cast(void*)obj) in pool;
if (ep)
ep.hits++;
return !(obj is null || ep);
}
static void reindex()
void registerDependency(T from, T to)
{
foreach (i, o; objects)
references[cast(void*)o].index = i;
auto pfrom = (cast(void*)from) in pool;
assert(pfrom, "Unknown dependency source");
auto vto = cast(void*)to;
auto pto = vto in pool;
assert(pto, "Unknown dependency target");
assert(!pfrom.parents.contains(vto), "Dependency already set");
pfrom.parents ~= vto;
}
static uint get(T obj)
T[] getPreliminaryObjects()
{
return cast(T[])pool.keys;
}
void finalize()
{
// assign unique index
int i=0;
foreach (ref e; pool)
e.index = i++;
// topographical sort
bool done;
while (!done)
{
done = true;
foreach (ref a; pool)
foreach (parent; a.parents)
{
auto pb = parent in pool;
assert(pb !is null, "Can't find referenced object");
if (pb.index > a.index)
{
auto t = pb.index;
pb.index = a.index;
a.index = t;
done = false;
break;
}
}
}
// copy to array
auto all = new Entry[i];
foreach (ref e; pool)
all[e.index] = e;
// sort
//all.sort; // sort preserving topological integrity demands compared items to always be adjacent
done = false;
while (!done)
{
done = true;
for (int j=0; j<all.length-1; j++)
if (all[j] > &all[j+1])
{
auto t = all[j];
all[j] = all[j+1];
all[j+1] = t;
done = false;
}
}
objects.length = i;
foreach (j, ref e; all)
{
pool[e.object].index = j;
objects[j] = cast(T)e.object;
}
}
uint get(T obj)
{
assert(obj !is null, "Trying to get index of null object");
return references[cast(void*)obj].index;
return pool[cast(void*)obj].index;
}
uint index;
}
typedef Constant!(long) IntC;
typedef Constant!(ulong) UIntC;
typedef Constant!(double) DoubleC;
typedef Constant!(string) StringC;
typedef Constant!(ASProgram.Namespace) NamespaceC;
typedef Constant!(ASProgram.Namespace[]) NamespaceSetC;
typedef Constant!(ASProgram.Multiname) MultinameC;
typedef Reference!(ASProgram.Class) ClassR;
typedef Reference!(ASProgram.Method) MethodR;
ConstantPool!(long) ints;
ConstantPool!(ulong) uints;
ConstantPool!(double) doubles;
ConstantPool!(string) strings;
ConstantPool!(ASProgram.Namespace) namespaces;
ConstantPool!(ASProgram.Namespace[]) namespaceSets;
ConstantPool!(ASProgram.Multiname) multinames;
ConstantPool!(ASProgram.Metadata, false) metadatas;
ReferencePool!(ASProgram.Class) classes;
ReferencePool!(ASProgram.Method) methods;
void visitNamespace(ASProgram.Namespace ns)
{
if (NamespaceC.add(ns))
StringC.add(ns.name);
if (namespaces.add(ns))
strings.add(ns.name);
}
void visitNamespaceSet(ASProgram.Namespace[] nsSet)
{
if (NamespaceSetC.add(nsSet))
if (namespaceSets.add(nsSet))
foreach (ns; nsSet)
visitNamespace(ns);
}
void visitMultiname(ASProgram.Multiname multiname)
{
if (MultinameC.notAdded(multiname))
if (multinames.notAdded(multiname))
{
with (multiname)
switch (kind)
@@ -822,18 +969,18 @@ private final class AStoABC
case ASType.QName:
case ASType.QNameA:
visitNamespace(vQName.ns);
StringC.add(vQName.name);
strings.add(vQName.name);
break;
case ASType.RTQName:
case ASType.RTQNameA:
StringC.add(vRTQName.name);
strings.add(vRTQName.name);
break;
case ASType.RTQNameL:
case ASType.RTQNameLA:
break;
case ASType.Multiname:
case ASType.MultinameA:
StringC.add(vMultiname.name);
strings.add(vMultiname.name);
visitNamespaceSet(vMultiname.nsSet);
break;
case ASType.MultinameL:
@@ -848,7 +995,7 @@ private final class AStoABC
default:
throw new .Exception("Unknown Multiname kind");
}
bool r = MultinameC.add(multiname);
bool r = multinames.add(multiname);
assert(r);
}
}
@@ -885,6 +1032,21 @@ private final class AStoABC
default:
throw new Exception("Unknown trait kind");
}
foreach (metadata; trait.metadata)
visitMetadata(metadata);
}
}
void visitMetadata(ASProgram.Metadata metadata)
{
if (metadatas.add(metadata))
{
strings.add(metadata.name);
foreach (ref item; metadata.items)
{
strings.add(item.key);
strings.add(item.value);
}
}
}
@@ -893,16 +1055,16 @@ private final class AStoABC
switch (value.vkind)
{
case ASType.Integer:
IntC.add(value.vint);
ints.add(value.vint);
break;
case ASType.UInteger:
UIntC.add(value.vuint);
uints.add(value.vuint);
break;
case ASType.Double:
DoubleC.add(value.vdouble);
doubles.add(value.vdouble);
break;
case ASType.Utf8:
StringC.add(value.vstring);
strings.add(value.vstring);
break;
case ASType.Namespace:
case ASType.PackageNamespace:
@@ -925,31 +1087,31 @@ private final class AStoABC
void visitClass(ASProgram.Class vclass)
{
if (ClassR.notAdded(vclass))
if (classes.notAdded(vclass))
{
visitMethod(vclass.cinit);
visitTraits(vclass.traits);
visitInstance(vclass.instance);
bool r = ClassR.add(vclass);
bool r = classes.add(vclass);
assert(r);
}
}
void visitMethod(ASProgram.Method method)
{
if (MethodR.add(method))
if (methods.add(method))
with (method)
{
foreach (type; paramTypes)
visitMultiname(type);
visitMultiname(returnType);
StringC.add(name);
strings.add(name);
foreach (ref value; options)
visitValue(value);
foreach (name; paramNames)
StringC.add(name);
strings.add(name);
if (vbody)
visitMethodBody(vbody);
@@ -971,16 +1133,16 @@ private final class AStoABC
break;
case OpcodeArgumentType.Int:
IntC.add(instruction.arguments[i].intv);
ints.add(instruction.arguments[i].intv);
break;
case OpcodeArgumentType.UInt:
UIntC.add(instruction.arguments[i].uintv);
uints.add(instruction.arguments[i].uintv);
break;
case OpcodeArgumentType.Double:
DoubleC.add(instruction.arguments[i].doublev);
doubles.add(instruction.arguments[i].doublev);
break;
case OpcodeArgumentType.String:
StringC.add(instruction.arguments[i].stringv);
strings.add(instruction.arguments[i].stringv);
break;
case OpcodeArgumentType.Namespace:
visitNamespace(instruction.arguments[i].namespacev);
@@ -1010,6 +1172,7 @@ private final class AStoABC
visitMultiname(exception.varName);
}
visitMethod(vbody.method);
visitTraits(vbody.traits);
}
@@ -1029,13 +1192,13 @@ private final class AStoABC
switch (value.vkind)
{
case ASType.Integer:
return IntC.get(value.vint);
return ints.get(value.vint);
case ASType.UInteger:
return UIntC.get(value.vuint);
return uints.get(value.vuint);
case ASType.Double:
return DoubleC.get(value.vdouble);
return doubles.get(value.vdouble);
case ASType.Utf8:
return StringC.get(value.vstring);
return strings.get(value.vstring);
case ASType.Namespace:
case ASType.PackageNamespace:
case ASType.PackageInternalNs:
@@ -1043,7 +1206,7 @@ private final class AStoABC
case ASType.ExplicitNamespace:
case ASType.StaticProtectedNs:
case ASType.PrivateNamespace:
return NamespaceC.get(value.vnamespace);
return namespaces.get(value.vnamespace);
case ASType.True:
case ASType.False:
case ASType.Null:
@@ -1054,6 +1217,28 @@ private final class AStoABC
}
}
void registerClassDependencies()
{
ASProgram.Class[ASProgram.Multiname] classByName;
ASProgram.Class[] classObjects = classes.getPreliminaryObjects();
foreach (c; classObjects)
{
assert(!(c.instance.name in classByName), "Duplicate class name " ~ c.instance.name.toString());
classByName[c.instance.name] = c;
}
foreach (c; classObjects)
foreach (dependency; [c.instance.superName] ~ c.instance.interfaces)
if (dependency)
foreach (dependencyName; dependency.toQNames())
{
auto pp = dependencyName in classByName;
if (pp)
classes.registerDependency(c, *pp);
}
}
this(ASProgram as)
{
this.abc = new ABCFile();
@@ -1069,43 +1254,43 @@ private final class AStoABC
foreach (method; as.orphanMethods)
visitMethod(method);
IntC.sort();
UIntC.sort();
DoubleC.sort();
StringC.sort();
NamespaceC.sort();
NamespaceSetC.sort();
MultinameC.sort();
//MultinameC.flip();
//MultinameC.reindex();
//ClassR.flip();
ClassR.reindex();
MethodR.reindex();
registerClassDependencies();
abc.ints = IntC.values;
abc.uints = UIntC.values;
abc.doubles = DoubleC.values;
abc.strings = StringC.values;
ints.finalize();
uints.finalize();
doubles.finalize();
strings.finalize();
namespaces.finalize();
namespaceSets.finalize();
multinames.finalize();
metadatas.finalize();
classes.finalize();
methods.finalize();
abc.namespaces.length = NamespaceC.values.length;
foreach (i, v; NamespaceC.values[1..$])
abc.ints = ints.values;
abc.uints = uints.values;
abc.doubles = doubles.values;
abc.strings = strings.values;
abc.namespaces.length = namespaces.values.length;
foreach (i, v; namespaces.values[1..$])
{
auto n = &abc.namespaces[i+1];
n.kind = v.kind;
n.name = StringC.get(v.name);
n.name = strings.get(v.name);
}
abc.namespaceSets.length = NamespaceSetC.values.length;
foreach (i, v; NamespaceSetC.values[1..$])
abc.namespaceSets.length = namespaceSets.values.length;
foreach (i, v; namespaceSets.values[1..$])
{
auto n = new uint[v.length];
foreach (j, ns; v)
n[j] = NamespaceC.get(ns);
n[j] = namespaces.get(ns);
abc.namespaceSets[i+1] = n;
}
abc.multinames.length = MultinameC.values.length;
foreach (i, v; MultinameC.values[1..$])
abc.multinames.length = multinames.values.length;
foreach (i, v; multinames.values[1..$])
{
auto n = &abc.multinames[i+1];
n.kind = v.kind;
@@ -1113,47 +1298,60 @@ private final class AStoABC
{
case ASType.QName:
case ASType.QNameA:
n.QName.ns = NamespaceC.get(v.vQName.ns);
n.QName.name = StringC.get(v.vQName.name);
n.QName.ns = namespaces.get(v.vQName.ns);
n.QName.name = strings.get(v.vQName.name);
break;
case ASType.RTQName:
case ASType.RTQNameA:
n.RTQName.name = StringC.get(v.vRTQName.name);
n.RTQName.name = strings.get(v.vRTQName.name);
break;
case ASType.RTQNameL:
case ASType.RTQNameLA:
break;
case ASType.Multiname:
case ASType.MultinameA:
n.Multiname.name = StringC.get(v.vMultiname.name);
n.Multiname.nsSet = NamespaceSetC.get(v.vMultiname.nsSet);
n.Multiname.name = strings.get(v.vMultiname.name);
n.Multiname.nsSet = namespaceSets.get(v.vMultiname.nsSet);
break;
case ASType.MultinameL:
case ASType.MultinameLA:
n.MultinameL.nsSet = NamespaceSetC.get(v.vMultinameL.nsSet);
n.MultinameL.nsSet = namespaceSets.get(v.vMultinameL.nsSet);
break;
case ASType.TypeName:
n.TypeName.name = MultinameC.get(v.vTypeName.name);
n.TypeName.name = multinames.get(v.vTypeName.name);
n.TypeName.params.length = v.vTypeName.params.length;
foreach (j, param; v.vTypeName.params)
n.TypeName.params[j] = MultinameC.get(param);
n.TypeName.params[j] = multinames.get(param);
break;
default:
throw new Exception("Unknown Multiname kind");
}
}
abc.metadata.length = metadatas.values.length;
foreach (i, m; metadatas.values)
{
auto n = &abc.metadata[i];
n.name = strings.get(m.name);
n.items.length = m.items.length;
foreach (j, ref item; m.items)
{
n.items[j].key = strings.get(m.items[j].key);
n.items[j].value = strings.get(m.items[j].value);
}
}
ASProgram.MethodBody[] bodies;
abc.methods.length = MethodR.objects.length;
foreach (i, o; MethodR.objects)
abc.methods.length = methods.objects.length;
foreach (i, o; methods.objects)
{
auto n = &abc.methods[i];
n.paramTypes.length = o.paramTypes.length;
foreach (j, p; o.paramTypes)
n.paramTypes[j] = MultinameC.get(p);
n.returnType = MultinameC.get(o.returnType);
n.name = StringC.get(o.name);
n.paramTypes[j] = multinames.get(p);
n.returnType = multinames.get(o.returnType);
n.name = strings.get(o.name);
n.flags = o.flags;
n.options.length = o.options.length;
foreach (j, ref value; o.options)
@@ -1163,34 +1361,34 @@ private final class AStoABC
}
n.paramNames.length = o.paramNames.length;
foreach (j, name; o.paramNames)
n.paramNames[j] = StringC.get(name);
n.paramNames[j] = strings.get(name);
if (o.vbody)
bodies ~= o.vbody;
}
abc.instances.length = ClassR.objects.length;
foreach (i, c; ClassR.objects)
abc.instances.length = classes.objects.length;
foreach (i, c; classes.objects)
{
auto o = c.instance;
auto n = &abc.instances[i];
n.name = MultinameC.get(o.name);
n.superName = MultinameC.get(o.superName);
n.name = multinames.get(o.name);
n.superName = multinames.get(o.superName);
n.flags = o.flags;
n.protectedNs = NamespaceC.get(o.protectedNs);
n.protectedNs = namespaces.get(o.protectedNs);
n.interfaces.length = o.interfaces.length;
foreach (j, intf; o.interfaces)
n.interfaces[j] = MultinameC.get(intf);
n.iinit = MethodR.get(o.iinit);
n.interfaces[j] = multinames.get(intf);
n.iinit = methods.get(o.iinit);
n.traits = convertTraits(o.traits);
}
abc.classes.length = ClassR.objects.length;
foreach (i, o; ClassR.objects)
abc.classes.length = classes.objects.length;
foreach (i, o; classes.objects)
{
auto n = &abc.classes[i];
n.cinit = MethodR.get(o.cinit);
n.cinit = methods.get(o.cinit);
n.traits = convertTraits(o.traits);
}
@@ -1198,7 +1396,7 @@ private final class AStoABC
foreach (i, o; as.scripts)
{
auto n = &abc.scripts[i];
n.sinit = MethodR.get(o.sinit);
n.sinit = methods.get(o.sinit);
n.traits = convertTraits(o.traits);
}
@@ -1206,7 +1404,7 @@ private final class AStoABC
foreach (i, o; bodies)
{
auto n = &abc.bodies[i];
n.method = MethodR.get(o.method);
n.method = methods.get(o.method);
n.maxStack = o.maxStack;
n.localCount = o.localCount;
n.initScopeDepth = o.initScopeDepth;
@@ -1221,8 +1419,8 @@ private final class AStoABC
ne.from = oe.from;
ne.to = oe.to;
ne.target = oe.target;
ne.excType = MultinameC.get(oe.excType);
ne.varName = MultinameC.get(oe.varName);
ne.excType = multinames.get(oe.excType);
ne.varName = multinames.get(oe.varName);
}
n.traits = convertTraits(o.traits);
}
@@ -1233,7 +1431,7 @@ private final class AStoABC
auto r = new ABCFile.TraitsInfo[traits.length];
foreach (i, ref trait; traits)
{
r[i].name = MultinameC.get(trait.name);
r[i].name = multinames.get(trait.name);
r[i].kind = trait.kind;
r[i].attr = trait.attr;
switch (trait.kind)
@@ -1241,27 +1439,30 @@ private final class AStoABC
case TraitKind.Slot:
case TraitKind.Const:
r[i].Slot.slotId = trait.vSlot.slotId;
r[i].Slot.typeName = MultinameC.get(trait.vSlot.typeName);
r[i].Slot.typeName = multinames.get(trait.vSlot.typeName);
r[i].Slot.vkind = trait.vSlot.value.vkind;
r[i].Slot.vindex = getValueIndex(trait.vSlot.value);
break;
case TraitKind.Class:
r[i].Class.slotId = trait.vClass.slotId;
r[i].Class.classi = ClassR.get(trait.vClass.vclass);
r[i].Class.classi = classes.get(trait.vClass.vclass);
break;
case TraitKind.Function:
r[i].Function.slotId = trait.vFunction.slotId;
r[i].Function.functioni = MethodR.get(trait.vFunction.vfunction);
r[i].Function.functioni = methods.get(trait.vFunction.vfunction);
break;
case TraitKind.Method:
case TraitKind.Getter:
case TraitKind.Setter:
r[i].Method.dispId = trait.vMethod.dispId;
r[i].Method.method = MethodR.get(trait.vMethod.vmethod);
r[i].Method.method = methods.get(trait.vMethod.vmethod);
break;
default:
throw new Exception("Unknown trait kind");
}
r[i].metadata.length = trait.metadata.length;
foreach (j, ref m; trait.metadata)
r[i].metadata[j] = metadatas.get(m);
}
return r;
}
@@ -1289,28 +1490,28 @@ private final class AStoABC
break;
case OpcodeArgumentType.Int:
r.arguments[i].index = IntC.get(instruction.arguments[i].intv);
r.arguments[i].index = ints.get(instruction.arguments[i].intv);
break;
case OpcodeArgumentType.UInt:
r.arguments[i].index = UIntC.get(instruction.arguments[i].uintv);
r.arguments[i].index = uints.get(instruction.arguments[i].uintv);
break;
case OpcodeArgumentType.Double:
r.arguments[i].index = DoubleC.get(instruction.arguments[i].doublev);
r.arguments[i].index = doubles.get(instruction.arguments[i].doublev);
break;
case OpcodeArgumentType.String:
r.arguments[i].index = StringC.get(instruction.arguments[i].stringv);
r.arguments[i].index = strings.get(instruction.arguments[i].stringv);
break;
case OpcodeArgumentType.Namespace:
r.arguments[i].index = NamespaceC.get(instruction.arguments[i].namespacev);
r.arguments[i].index = namespaces.get(instruction.arguments[i].namespacev);
break;
case OpcodeArgumentType.Multiname:
r.arguments[i].index = MultinameC.get(instruction.arguments[i].multinamev);
r.arguments[i].index = multinames.get(instruction.arguments[i].multinamev);
break;
case OpcodeArgumentType.Class:
r.arguments[i].index = ClassR.get(instruction.arguments[i].classv);
r.arguments[i].index = classes.get(instruction.arguments[i].classv);
break;
case OpcodeArgumentType.Method:
r.arguments[i].index = MethodR.get(instruction.arguments[i].methodv);
r.arguments[i].index = methods.get(instruction.arguments[i].methodv);
break;
case OpcodeArgumentType.JumpTarget:
@@ -1376,3 +1577,11 @@ class ASTraitsVisitor
}
}
}
private bool contains(T)(T[] arr, T val)
{
foreach (v; arr)
if (v == val)
return true;
return false;
}
+29
View File
@@ -656,6 +656,9 @@ final class Assembler
case "value":
t.vSlot.value = readValue();
break;
case "metadata":
t.metadata ~= readMetadata();
break;
case "end":
return t;
default:
@@ -678,6 +681,9 @@ final class Assembler
mustBeNull(t.vClass.vclass);
t.vClass.vclass = readClass();
break;
case "metadata":
t.metadata ~= readMetadata();
break;
case "end":
return t;
default:
@@ -700,6 +706,9 @@ final class Assembler
mustBeNull(t.vFunction.vfunction);
t.vFunction.vfunction = readMethod();
break;
case "metadata":
t.metadata ~= readMetadata();
break;
case "end":
return t;
default:
@@ -724,6 +733,9 @@ final class Assembler
mustBeNull(t.vMethod.vmethod);
t.vMethod.vmethod = readMethod();
break;
case "metadata":
t.metadata ~= readMetadata();
break;
case "end":
return t;
default:
@@ -735,6 +747,23 @@ final class Assembler
}
}
ASProgram.Metadata readMetadata()
{
auto metadata = new ASProgram.Metadata;
metadata.name = readString();
while (true)
switch (readWord())
{
case "item":
metadata.items ~= ASProgram.Metadata.Item(readString(), readString());
break;
case "end":
return metadata;
default:
throw new Exception("Expected item or end");
}
}
ASProgram.Method readMethod()
{
ASProgram.Method m = new ASProgram.Method;
+40 -8
View File
@@ -109,6 +109,10 @@ final class RefBuilder : ASTraitsVisitor
{
foreach (i, ref v; as.scripts)
addMethod(v.sinit, "script" ~ .toString(i) ~ "_sinit");
foreach (vclass; as.orphanClasses)
addClass(vclass, "orphan");
foreach (method; as.orphanMethods)
addMethod(method, "orphan");
super.run();
}
@@ -257,9 +261,9 @@ final class RefBuilder : ASTraitsVisitor
return uniqueName;
}
void addClass(ASProgram.Class vclass)
void addClass(ASProgram.Class vclass, string field = null)
{
addObject(vclass, classByName, string.init);
addObject(vclass, classByName, field);
addMethod(vclass.cinit, "cinit");
addMethod(vclass.instance.iinit, "iinit");
}
@@ -412,7 +416,7 @@ final class Disassembler
if (v == ABCFile.NULL_DOUBLE)
sb ~= "null";
else
sb ~= .toString(v);
sb ~= format("%.18g", v);
}
void dumpString(StringBuilder sb, string str)
@@ -547,6 +551,7 @@ final class Disassembler
dumpMultiname(sb, trait.name);
if (trait.attr)
dumpFlags!(true)(sb, trait.attr, TraitAttributeNames);
bool inLine = false;
switch (trait.kind)
{
case TraitKind.Slot:
@@ -566,8 +571,7 @@ final class Disassembler
sb ~= " value ";
dumpValue(sb, trait.vSlot.value);
}
sb ~= " end";
sb.newLine();
inLine = true;
break;
case TraitKind.Class:
if (trait.vClass.slotId)
@@ -578,7 +582,6 @@ final class Disassembler
sb.indent++; sb.newLine();
sb ~= "class";
dumpClass(sb, trait.vClass.vclass);
sb.indent--; sb ~= "end ; trait"; sb.newLine();
break;
case TraitKind.Function:
if (trait.vFunction.slotId)
@@ -589,7 +592,6 @@ final class Disassembler
sb.indent++; sb.newLine();
sb ~= "method";
dumpMethod(sb, trait.vFunction.vfunction);
sb.indent--; sb ~= "end ; trait"; sb.newLine();
break;
case TraitKind.Method:
case TraitKind.Getter:
@@ -602,14 +604,44 @@ final class Disassembler
sb.indent++; sb.newLine();
sb ~= "method";
dumpMethod(sb, trait.vMethod.vmethod);
sb.indent--; sb ~= "end ; trait"; sb.newLine();
break;
default:
throw new Exception("Unknown trait kind");
}
foreach (metadata; trait.metadata)
{
if (inLine)
{
sb.indent++; sb.newLine();
inLine = false;
}
dumpMetadata(sb, metadata);
}
if (inLine)
{ sb ~= " end"; sb.newLine(); }
else
{ sb.indent--; sb ~= "end ; trait"; sb.newLine(); }
}
}
void dumpMetadata(StringBuilder sb, ASProgram.Metadata metadata)
{
sb ~= "metadata ";
dumpString(sb, metadata.name);
sb.indent++; sb.newLine();
foreach (ref item; metadata.items)
{
sb ~= "item ";
dumpString(sb, item.key);
sb ~= " ";
dumpString(sb, item.value);
sb.newLine();
}
sb.indent--; sb ~= "end ; metadata"; sb.newLine();
}
void dumpFlags(bool oneLine = false)(StringBuilder sb, ubyte flags, string[] names)
{
for (int i=0; flags; i++, flags>>=1)
+67 -67
View File
@@ -152,73 +152,73 @@ private final class SWFReader
enum TagType
{
End = 0,
ShowFrame = 1,
DefineShape = 2,
FreeCharacter = 3,
PlaceObject = 4,
RemoveObject = 5,
DefineBits = 6,
DefineButton = 7,
JPEGTables = 8,
SetBackgroundColor = 9,
DefineFont = 10,
DefineText = 11,
DoAction = 12,
DefineFontInfo = 13,
DefineSound = 14,
StartSound = 15,
DefineButtonSound = 17,
SoundStreamHead = 18,
SoundStreamBlock = 19,
DefineBitsLossless = 20,
DefineBitsJPEG2 = 21,
DefineShape2 = 22,
DefineButtonCxform = 23,
Protect = 24,
PathsArePostScript = 25,
PlaceObject2 = 26,
RemoveObject2 = 28,
DefineShape3 = 32,
DefineText2 = 33,
DefineButton2 = 34,
DefineBitsJPEG3 = 35,
DefineBitsLossless2 = 36,
DefineSprite = 39,
ProductInfo = 41,
FrameLabel = 43,
SoundStreamHead2 = 45,
DefineMorphShape = 46,
DefineFont2 = 48,
DefineEditText = 37,
ExportAssets = 56,
ImportAssets = 57,
EnableDebugger = 58,
DoInitAction = 59,
DefineVideoStream = 60,
VideoFrame = 61,
DefineFontInfo2 = 62,
DebugID = 63,
EnableDebugger2 = 64,
ScriptLimits = 65,
SetTabIndex = 66,
FileAttributes = 69,
PlaceObject3 = 70,
ImportAssets2 = 71,
DoABC = 72,
DefineFontAlignZones = 73,
CSMTextSettings = 74,
DefineFont3 = 75,
SymbolClass = 76,
Metadata = 77,
DefineScalingGrid = 78,
DoABC2 = 82,
DefineShape4 = 83,
DefineMorphShape2 = 84,
DefineSceneAndFrameLabelData = 86,
DefineBinaryData = 87,
DefineFontName = 88,
DefineFont4 = 91
End = 0,
ShowFrame = 1,
DefineShape = 2,
FreeCharacter = 3,
PlaceObject = 4,
RemoveObject = 5,
DefineBits = 6,
DefineButton = 7,
JPEGTables = 8,
SetBackgroundColor = 9,
DefineFont = 10,
DefineText = 11,
DoAction = 12,
DefineFontInfo = 13,
DefineSound = 14,
StartSound = 15,
DefineButtonSound = 17,
SoundStreamHead = 18,
SoundStreamBlock = 19,
DefineBitsLossless = 20,
DefineBitsJPEG2 = 21,
DefineShape2 = 22,
DefineButtonCxform = 23,
Protect = 24,
PathsArePostScript = 25,
PlaceObject2 = 26,
RemoveObject2 = 28,
DefineShape3 = 32,
DefineText2 = 33,
DefineButton2 = 34,
DefineBitsJPEG3 = 35,
DefineBitsLossless2 = 36,
DefineSprite = 39,
ProductInfo = 41,
FrameLabel = 43,
SoundStreamHead2 = 45,
DefineMorphShape = 46,
DefineFont2 = 48,
DefineEditText = 37,
ExportAssets = 56,
ImportAssets = 57,
EnableDebugger = 58,
DoInitAction = 59,
DefineVideoStream = 60,
VideoFrame = 61,
DefineFontInfo2 = 62,
DebugID = 63,
EnableDebugger2 = 64,
ScriptLimits = 65,
SetTabIndex = 66,
FileAttributes = 69,
PlaceObject3 = 70,
ImportAssets2 = 71,
DoABC = 72,
DefineFontAlignZones = 73,
CSMTextSettings = 74,
DefineFont3 = 75,
SymbolClass = 76,
Metadata = 77,
DefineScalingGrid = 78,
DoABC2 = 82,
DefineShape4 = 83,
DefineMorphShape2 = 84,
DefineSceneAndFrameLabelData = 86,
DefineBinaryData = 87,
DefineFontName = 88,
DefineFont4 = 91
}
private final class SWFWriter