23 Commits
Author SHA1 Message Date
Vladimir Panteleev 413aa9b5f0 Update Changelog 2011-04-14 05:28:22 +03:00
Vladimir Panteleev 5da1e26835 Add support for memory-access and sign-extend opcodes 2011-04-14 05:02:03 +03:00
Vladimir Panteleev 53a4173fd3 Improve exception handling code 2011-04-14 05:01:40 +03:00
Vladimir Panteleev 5737243606 Update Changelog 2011-03-22 12:33:46 +02:00
Vladimir Panteleev be727e67c6 Documentation: remove -release from compilation instructions 2011-03-22 10:37:43 +02:00
Vladimir Panteleev 9d67c46835 abcfile: Use preallocated initial buffer for writeMethodBody 2011-03-22 09:50:27 +02:00
Vladimir Panteleev 756a2fd060 assembler: Remove a TODO for a bad idea 2011-03-22 09:47:30 +02:00
Vladimir Panteleev 8ad1f85a45 asprogram: sort faster 2011-03-22 09:18:14 +02:00
Vladimir Panteleev ad6ae5d087 autodata: Fix grouping of array fields (D2 porting bug) 2011-03-22 05:46:42 +02:00
Vladimir Panteleev 580a1364d0 Clarify some asserts 2011-03-22 04:39:25 +02:00
Vladimir Panteleev d4f90f9c01 Documentation: compile with optimization 2011-03-22 04:33:50 +02:00
Vladimir Panteleev cecff913f1 Update Changelog 2011-03-07 20:26:52 +02:00
Vladimir Panteleev 7a4c78dac3 Documentation: svn no longer required 2011-03-07 20:26:37 +02:00
Vladimir Panteleev ee47075fda Merge branch 'd2' 2011-03-07 14:11:20 +02:00
Vladimir Panteleev 100f6b6bf4 Port to D2 2011-02-20 20:48:24 +02:00
Vladimir Panteleev 2e48e8deef Update Changelog 2011-02-20 20:44:31 +02:00
Vladimir Panteleev f49fe820b6 Whitespace 2011-02-20 20:44:05 +02:00
Vladimir Panteleev 52a2cde1b0 Finish metadata support 2011-02-17 06:48:40 +02:00
Vladimir Panteleev 608a8ca51d Correctly order classes by dependencies (extends/implements) and reference count 2011-02-17 05:25:49 +02:00
Vladimir Panteleev d11270a553 A bit of debugging code 2011-02-17 05:25:01 +02:00
Vladimir Panteleev 68a0845c53 Support forward references of typenames 2011-02-17 04:31:04 +02:00
Vladimir Panteleev 824a945247 asprogram: Refactor constant/reference pool code 2011-02-17 02:52:00 +02:00
Vladimir Panteleev c89f2bbe70 Documentation: update link to AVM2 reference PDF 2010-12-20 19:28:49 +02:00
12 changed files with 694 additions and 453 deletions
+18
View File
@@ -1,6 +1,24 @@
RABCDAsm Changelog
==================
RABCDAsm v1.5 (2011.04.14)
--------------------------
* Fixed v1.4 constant pool regression
* Added support for memory-access and sign-extend opcodes
* Speed optimizations
* Documentation updates
RABCDAsm v1.4 (2011.03.07)
--------------------------
* Source code ported to D2
* 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)
--------------------------
+17 -37
View File
@@ -48,27 +48,23 @@ bytecode with the following properties:
Compiling from source
---------------------
RABCDAsm is written in the [D programming language, version 1][d1]. It uses one
third-party library, [std2][] (for some fancy template stuff).
RABCDAsm is written in the [D programming language, version 2][d2].
Assuming you have [dmd][], [git][] and [svn][] installed, compiling should be
as straight-forward as:
Assuming you have [dmd][] and [git][] installed, compiling should be as
straight-forward as:
git clone git://github.com/CyberShadow/RABCDAsm.git
cd RABCDAsm
svn co http://svn.dsource.org/projects/std2/trunk/std2/std2
dmd rabcdasm abcfile asprogram disassembler autodata murmurhash2a
dmd rabcasm abcfile asprogram assembler autodata murmurhash2a
dmd abcexport swffile zlibx
dmd abcreplace swffile zlibx
dmd swfdecompress swffile zlibx
dmd swf7zcompress swffile zlibx
dmd -O -inline rabcdasm abcfile asprogram disassembler autodata murmurhash2a
dmd -O -inline rabcasm abcfile asprogram assembler autodata murmurhash2a
dmd -O -inline abcexport swffile zlibx
dmd -O -inline abcreplace swffile zlibx
dmd -O -inline swfdecompress swffile zlibx
dmd -O -inline swf7zcompress swffile zlibx
[d1]: http://www.digitalmars.com/d/1.0/
[std2]: http://dsource.org/projects/std2
[d2]: http://www.digitalmars.com/d/2.0/
[dmd]: http://www.digitalmars.com/d/download.html
[git]: http://git-scm.com/
[svn]: http://subversion.apache.org/
Pre-compiled binaries
---------------------
@@ -125,7 +121,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
@@ -171,7 +167,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
@@ -363,10 +362,7 @@ follows:
`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
@@ -420,23 +416,7 @@ RABCDAsm users.
Limitations
===========
* Metadata is currently ignored. I haven't noticed any metadata blocks in any
SWF files I've disassembled.
* `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
=======
+4 -4
View File
@@ -20,7 +20,7 @@ module abcexport;
import std.file;
import std.path;
import std.string;
import std.conv;
import std.stdio;
import swffile;
@@ -45,11 +45,11 @@ void main(string[] args)
while (*p++) {} // skip name
abc = tag.data[p-tag.data.ptr..$];
}
write(getName(arg) ~ .toString(count++) ~ ".abc", abc);
std.file.write(getName(arg) ~ to!string(count++) ~ ".abc", abc);
}
if (count == 0)
throw new Exception("No DoABC tags found");
}
catch (Object o)
writefln("Error while processing %s: %s", arg, o);
catch (Exception e)
writefln("Error while processing %s: %s", arg, e);
}
+28 -25
View File
@@ -19,6 +19,7 @@
module abcfile;
import std.string : format; // exception formatting
import std.exception;
/**
* Implements a shallow representation of an .abc file.
@@ -181,7 +182,7 @@ class ABCFile
// TraitAttributes bitmask
ubyte attr() { return cast(ubyte)(kindAttr >> 4); }
void attr(ubyte value) { kindAttr = (kindAttr&0xF) | (value<<4); }
void attr(ubyte value) { kindAttr = cast(ubyte)((kindAttr&0xF) | (value<<4)); }
}
struct Class
@@ -708,16 +709,16 @@ const OpcodeInfo[256] opcodeInfo = [
/* 0x32 */ {"hasnext2", [OpcodeArgumentType.UIntLiteral, OpcodeArgumentType.UIntLiteral]},
/* 0x33 */ {"pushdecimal", [OpcodeArgumentType.Unknown]},
/* 0x34 */ {"pushdnan", [OpcodeArgumentType.Unknown]},
/* 0x35 */ {"li8", [OpcodeArgumentType.Unknown]},
/* 0x36 */ {"li16", [OpcodeArgumentType.Unknown]},
/* 0x37 */ {"li32", [OpcodeArgumentType.Unknown]},
/* 0x38 */ {"lf32", [OpcodeArgumentType.Unknown]},
/* 0x39 */ {"lf64", [OpcodeArgumentType.Unknown]},
/* 0x3A */ {"si8", [OpcodeArgumentType.Unknown]},
/* 0x3B */ {"si16", [OpcodeArgumentType.Unknown]},
/* 0x3C */ {"si32", [OpcodeArgumentType.Unknown]},
/* 0x3D */ {"sf32", [OpcodeArgumentType.Unknown]},
/* 0x3E */ {"sf64", [OpcodeArgumentType.Unknown]},
/* 0x35 */ {"li8", []},
/* 0x36 */ {"li16", []},
/* 0x37 */ {"li32", []},
/* 0x38 */ {"lf32", []},
/* 0x39 */ {"lf64", []},
/* 0x3A */ {"si8", []},
/* 0x3B */ {"si16", []},
/* 0x3C */ {"si32", []},
/* 0x3D */ {"sf32", []},
/* 0x3E */ {"sf64", []},
/* 0x3F */ {"0x3F", [OpcodeArgumentType.Unknown]},
/* 0x40 */ {"newfunction", [OpcodeArgumentType.Method]},
/* 0x41 */ {"call", [OpcodeArgumentType.UIntLiteral]},
@@ -735,9 +736,9 @@ const OpcodeInfo[256] opcodeInfo = [
/* 0x4D */ {"callinterface", [OpcodeArgumentType.Unknown]},
/* 0x4E */ {"callsupervoid", [OpcodeArgumentType.Multiname, OpcodeArgumentType.UIntLiteral]},
/* 0x4F */ {"callpropvoid", [OpcodeArgumentType.Multiname, OpcodeArgumentType.UIntLiteral]},
/* 0x50 */ {"sxi1", [OpcodeArgumentType.Unknown]},
/* 0x51 */ {"sxi8", [OpcodeArgumentType.Unknown]},
/* 0x52 */ {"sxi16", [OpcodeArgumentType.Unknown]},
/* 0x50 */ {"sxi1", []},
/* 0x51 */ {"sxi8", []},
/* 0x52 */ {"sxi16", []},
/* 0x53 */ {"applytype", [OpcodeArgumentType.UIntLiteral]},
/* 0x54 */ {"0x54", [OpcodeArgumentType.Unknown]},
/* 0x55 */ {"newobject", [OpcodeArgumentType.UIntLiteral]},
@@ -992,13 +993,13 @@ private final class ABCReader
foreach (ref value; abc.bodies)
value = readMethodBody();
}
catch (Object o)
throw new Exception(format("Error at %d (0x%X): %s", pos, pos, o));
catch (Exception e)
throw new Exception(format("Error at %d (0x%X):", pos, pos), e);
}
ubyte readU8()
{
assert(pos < buf.length);
enforce(pos < buf.length, "End of file reached");
return buf[pos++];
}
@@ -1046,7 +1047,7 @@ private final class ABCReader
void readExact(void* ptr, size_t len)
{
assert(pos+len <= buf.length);
enforce(pos+len <= buf.length, "End of file reached");
(cast(ubyte*)ptr)[0..len] = buf[pos..pos+len];
pos += len;
}
@@ -1061,8 +1062,9 @@ private final class ABCReader
string readString()
{
string s = new char[readU30()];
readExact(s.ptr, s.length);
char[] buf = new char[readU30()];
readExact(buf.ptr, buf.length);
string s = assumeUnique(buf);
if (s.length == 0)
s = ""; // not null!
return s;
@@ -1471,7 +1473,7 @@ private final class ABCWriter
foreach (ref value; abc.instances)
writeInstance(value);
assert(abc.classes.length == abc.instances.length);
assert(abc.classes.length == abc.instances.length, "Number of classes and instances differs");
foreach (ref value; abc.classes)
writeClass(value);
@@ -1551,7 +1553,7 @@ private final class ABCWriter
writeU32(v);
}
void writeExact(void* ptr, size_t len)
void writeExact(const(void)* ptr, size_t len)
{
while (pos+len > buf.length)
buf.length = buf.length * 2;
@@ -1643,7 +1645,7 @@ private final class ABCWriter
}
if (v.flags & MethodFlags.HAS_PARAM_NAMES)
{
assert(v.paramNames.length == v.paramTypes.length);
assert(v.paramNames.length == v.paramTypes.length, "Mismatching number of parameter names and types");
foreach (value; v.paramNames)
writeU30(value);
}
@@ -1716,7 +1718,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);
}
}
@@ -1753,7 +1755,8 @@ private final class ABCWriter
// we don't know the length before writing all the instructions - swap buffer with a temporary one
auto globalBuf = buf;
auto globalPos = pos;
buf = new ubyte[1024];
static ubyte[1024*16] methodBuf;
buf = methodBuf[];
pos = 0;
struct Fixup { ABCFile.Label target; uint pos, base; }
+1 -1
View File
@@ -27,7 +27,7 @@ void main(string[] args)
if (args.length != 4)
throw new Exception("Bad arguments. Usage: abcreplace file.swf index code.abc");
auto swf = SWFFile.read(cast(ubyte[])read(args[1]));
auto index = toUint(args[2]);
auto index = to!uint(args[2]);
uint count;
foreach (ref tag; swf.tags)
if ((tag.type == TagType.DoABC || tag.type == TagType.DoABC2) && count++ == index)
+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"/>
+359 -172
View File
@@ -18,6 +18,8 @@
module asprogram;
import std.algorithm;
import core.stdc.string;
import abcfile;
import autodata;
@@ -41,16 +43,7 @@ final class ASProgram
uint privateIndex; // unique index for private namespaces
mixin AutoCompare;
//mixin ProcessAllData;
R processData(R, string prolog, string epilog, H)(ref H handler)
{
mixin(prolog);
mixin(addAutoField("kind"));
mixin(addAutoField("name"));
mixin(addAutoField("privateIndex"));
mixin(epilog);
}
mixin ProcessAllData;
}
static class Multiname
@@ -87,8 +80,9 @@ final class ASProgram
}
mixin AutoCompare;
mixin AutoToString;
R processData(R, string prolog, string epilog, H)(ref H handler)
R processData(R, string prolog, string epilog, H)(ref H handler) const
{
mixin(prolog);
mixin(addAutoField("kind"));
@@ -124,6 +118,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 +161,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 +236,11 @@ final class ASProgram
Trait[] traits;
Instance instance;
override string toString()
{
return instance.name.toString();
}
}
static class Script
@@ -371,7 +400,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 +428,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 +436,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 +516,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 +663,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)
@@ -685,20 +725,53 @@ private final class AStoABC
return value == T.init;
}
/// Maintain an unordered set of values; sort/index by usage count
struct Constant(T)
static void move(T)(T[] array, size_t from, size_t to)
{
static Constant!(T)[T] pool;
static T[] values;
assert(from<array.length && to<array.length);
if (from == to)
return;
T t = array[from];
if (from < to)
memmove(array.ptr+from, array.ptr+from+1, (to-from)*T.sizeof);
else
memmove(array.ptr+to+1, array.ptr+to, (from-to)*T.sizeof);
array[to] = t;
}
static bool add(T value) // return true if added
/// Maintain an unordered set of values; sort/index by usage count
struct ConstantPool(T, bool haveNull = true)
{
alias immutable(T) I;
struct Entry
{
if (isNull(value))
uint hits;
T value;
uint index;
mixin AutoCompare;
R processData(R, string prolog, string epilog, H)(ref H handler) const
{
mixin(prolog);
mixin(addAutoField("hits", true));
mixin(addAutoField("value"));
mixin(epilog);
}
}
Entry[immutable(T)] pool;
T[] values;
bool add(T value) // return true if added
{
enum ivalue = cast(I)value;
if (haveNull && isNull(value))
return false;
auto cp = value in pool;
auto cp = ivalue in pool;
if (cp is null)
{
pool[value] = Constant!(T)(1, value);
pool[ivalue] = Entry(1, value);
return true;
}
else
@@ -708,113 +781,165 @@ private final class AStoABC
}
}
static bool notAdded(T value)
bool notAdded(T value)
{
return !(isNull(value) || value in pool);
enum ivalue = cast(I)value;
auto ep = ivalue 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[cast(I)c.value].index = i + NullOffset;
values[i + NullOffset] = c.value;
}
}
static uint get(T value)
uint get(T value)
{
if (isNull(value))
enum ivalue = cast(I)value;
if (haveNull && isNull(value))
return 0;
return pool[value].index;
return pool[ivalue].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
mixin AutoToString;
mixin ProcessAllData;
}
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()
{
// create array
auto all = new Entry*[pool.length];
int i=0;
foreach (ref e; pool)
all[i++] = &e;
// sort
sort!q{a.hits > b.hits || (a.hits == b.hits && a.addIndex < b.addIndex)}(all);
// topographical sort
topSort:
// update indices
foreach (j, e; all)
e.index = j;
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)
{
move(all, pb.index, a.index);
goto topSort;
}
}
objects.length = i;
foreach (j, e; all)
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 +947,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,8 +973,8 @@ private final class AStoABC
default:
throw new .Exception("Unknown Multiname kind");
}
bool r = MultinameC.add(multiname);
assert(r);
bool r = multinames.add(multiname);
assert(r, "Recursive multiname reference");
}
}
@@ -885,6 +1010,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 +1033,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 +1065,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);
assert(r);
bool r = classes.add(vclass);
assert(r, "Recursive class reference");
}
}
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 +1111,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 +1150,7 @@ private final class AStoABC
visitMultiname(exception.varName);
}
visitMethod(vbody.method);
visitTraits(vbody.traits);
}
@@ -1029,13 +1170,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 +1184,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 +1195,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 +1232,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 +1276,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 +1339,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 +1374,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 +1382,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 +1397,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 +1409,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 +1417,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 +1468,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 +1555,11 @@ class ASTraitsVisitor
}
}
}
private bool contains(T)(T[] arr, T val)
{
foreach (v; arr)
if (v == val)
return true;
return false;
}
+55 -40
View File
@@ -22,6 +22,7 @@ import std.file;
import std.string;
import std.conv;
import std.path;
import std.exception;
import abcfile;
import asprogram;
@@ -44,8 +45,8 @@ final class Assembler
{
string filename;
string buf;
char* pos;
char* end;
immutable(char)* pos;
immutable(char)* end;
string[] arguments;
string basePath;
@@ -61,7 +62,7 @@ final class Assembler
static File fromData(string name, string data, string[] arguments = null, string basePath = null)
{
data ~= \0; data = data[0..$-1]; // hack to prevent readWord etc. from checking for end-of-file on every character
data ~= "\0"; data = data[0..$-1]; // hack to prevent readWord etc. from checking for end-of-file on every character
return File(name, data, data.ptr, data.ptr + data.length, arguments, basePath);
}
@@ -98,11 +99,11 @@ final class Assembler
{
if (filename.length == 0)
throw new Exception("Empty filename");
filename = filename.dup;
foreach (ref c; filename)
auto buf = filename.dup;
foreach (ref c; buf)
if (c == '\\')
c = '/';
return std.path.join(getBasePath, filename);
return std.path.join(getBasePath, assumeUnique(buf));
}
void skipWhitespace()
@@ -190,7 +191,7 @@ final class Assembler
foreach (ref file; files[0..fileCount])
if (file.arguments.length)
{
uint index = .toUint(name)-1;
uint index = .to!uint(name)-1;
if (index >= file.arguments.length)
throw new Exception("Argument index out-of-bounds");
string value = file.arguments[index];
@@ -211,7 +212,7 @@ final class Assembler
static bool isWordChar(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.'; // TODO: use lookup table?
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.';
}
string readWord()
@@ -254,7 +255,7 @@ final class Assembler
}
}
void pushFile(ref File file)
void pushFile(File file)
{
if (fileCount == files.length)
throw new Exception("Recursion limit exceeded");
@@ -264,7 +265,7 @@ final class Assembler
}
/// For restoring the position of an error
void setFile(ref File file)
void setFile(File file)
{
files[0] = file;
fileCount = -1;
@@ -357,7 +358,7 @@ final class Assembler
static const char[16] hexDigits = "0123456789ABCDEF";
// TODO: optimize
string s = \";
string s = "\"";
foreach (c; str)
if (c == 0x0A)
s ~= `\n`;
@@ -422,7 +423,7 @@ final class Assembler
return v;
}
ubyte readFlag(string[] names)
ubyte readFlag(const string[] names)
{
auto word = readWord();
ubyte f = 1;
@@ -490,7 +491,7 @@ final class Assembler
string w = readWord();
if (w == "null")
return ABCFile.NULL_INT;
return toInt(w);
return to!int(w);
}
ulong readUInt()
@@ -498,7 +499,7 @@ final class Assembler
string w = readWord();
if (w == "null")
return ABCFile.NULL_UINT;
return toUint(w);
return to!uint(w);
}
double readDouble()
@@ -506,7 +507,7 @@ final class Assembler
string w = readWord();
if (w == "null")
return ABCFile.NULL_DOUBLE;
return toDouble(w);
return to!double(w);
}
string readString()
@@ -656,6 +657,9 @@ final class Assembler
case "value":
t.vSlot.value = readValue();
break;
case "metadata":
t.metadata ~= readMetadata();
break;
case "end":
return t;
default:
@@ -678,6 +682,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 +707,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 +734,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 +748,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;
@@ -938,7 +968,7 @@ final class Assembler
if (c=='-' || c=='+')
{
name = label[0..i];
offset = .toInt(label[i..$]);
offset = to!int(label[i..$]);
break;
}
auto lp = name in labels;
@@ -1044,24 +1074,14 @@ final class Assembler
foreach (ref f; jumpFixups)
{
try
instructions[f.ii].arguments[f.ai].jumpTarget = parseLabel(f.name, labels);
catch (Object o)
{
setFile(f.where.load);
throw o;
}
scope(failure) setFile(f.where.load);
instructions[f.ii].arguments[f.ai].jumpTarget = parseLabel(f.name, labels);
}
foreach (ref f; switchFixups)
{
try
instructions[f.ii].arguments[f.ai].switchTargets[f.si] = parseLabel(f.name, labels);
catch (Object o)
{
setFile(f.where.load);
throw o;
}
scope(failure) setFile(f.where.load);
instructions[f.ii].arguments[f.ai].switchTargets[f.si] = parseLabel(f.name, labels);
}
foreach (ref f; localClassFixups)
@@ -1078,13 +1098,8 @@ final class Assembler
ABCFile.Label readLabel()
{
auto word = readWord();
try
return parseLabel(word, labels);
catch (Object o)
{
backpedal(word.length);
throw o;
}
scope(failure) backpedal(word.length);
return parseLabel(word, labels);
}
ASProgram.Exception e;
@@ -1184,15 +1199,15 @@ final class Assembler
*f.ptr = *mp;
}
}
catch (Object o)
catch (Exception e)
{
string s = files[0].positionStr ~ ": " ~ o.toString();
string s = files[0].positionStr ~ ": ";
if (fileCount == -1)
s ~= "\n\t(inclusion context unavailable)";
else
foreach (ref f; files[1..fileCount])
s ~= "\n\t(included from " ~ f.positionStr ~ ")";
throw new Exception(s);
throw new Exception(s, e);
}
classFixups = null;
+84 -79
View File
@@ -19,61 +19,67 @@
module autodata;
import murmurhash2a;
import std2.traits;
import std.traits;
string addAutoField(string name, bool reverseSort = false)
{
//return `mixin(handler.process!(typeof(` ~ name ~ `), "` ~ name ~ `")());`; // doesn't work due to DMD bug 3959
return `{ static const _AutoDataStr = handler.process!(typeof(this.` ~ name ~ `), "` ~ name ~ `", ` ~ (reverseSort ? "true" : "false") ~`)(); mixin(_AutoDataStr); }`;
return `mixin(typeof(handler).getMixin!(typeof(` ~ name ~ `), "` ~ name ~ `", ` ~ (reverseSort ? "true" : "false") ~`));`;
}
template AutoCompare()
{
hash_t toHash()
static if (is(typeof(this)==class))
{
alias typeof(this) _AutoDataTypeReference;
alias Object _AutoDataOtherTypeReference;
override hash_t toHash() const { return _AutoDataHash(); }
override bool opEquals(Object o) const { return _AutoDataEquals(o); }
override int opCmp(Object o) const { return _AutoDataCmp(o); }
}
else // struct
{
alias const(typeof(this)*) _AutoDataTypeReference;
alias const(typeof(this)*) _AutoDataOtherTypeReference;
hash_t toHash() const { return _AutoDataHash(); }
bool opEquals(ref const typeof(this) s) const { return _AutoDataEquals(&s); }
int opCmp(ref const typeof(this) s) const { return _AutoDataCmp(&s); }
}
private hash_t _AutoDataHash() const
{
HashDataHandler handler;
handler.hasher.Begin();
processData!(void, "", "")(handler);
processData!(void, q{}, q{})(handler);
return handler.hasher.End();
}
static if (is(typeof(this)==class))
alias Object _AutoDataOtherType;
else
alias typeof(this) _AutoDataOtherType;
int opEquals(_AutoDataOtherType other)
private bool _AutoDataEquals(_AutoDataOtherTypeReference other) const
{
EqualsDataHandler!(typeof(this)) handler;
static if (is(typeof(this)==class))
{
handler.other = cast(typeof(this)) other;
if (handler.other is null)
return false;
}
else
handler.other = other;
return processData!(bool, "auto _AutoDataOther = handler.other;", "return true;")(handler);
auto handler = EqualsDataHandler!_AutoDataTypeReference(cast(_AutoDataTypeReference) other);
if (handler.other is null)
return false;
return processData!(bool, q{auto _AutoDataOther = handler.other;}, q{return true;})(handler);
}
int opCmp(_AutoDataOtherType other)
private int _AutoDataCmp(_AutoDataOtherTypeReference other) const
{
CmpDataHandler!(typeof(this)) handler;
static if (is(typeof(this)==class))
{
handler.other = cast(typeof(this)) other;
if (handler.other is null)
return -1;
}
else
handler.other = other;
return processData!(int, "auto _AutoDataOther = handler.other;", "return 0;")(handler);
auto handler = CmpDataHandler!_AutoDataTypeReference(cast(_AutoDataTypeReference) other);
if (handler.other is null)
return false;
return processData!(int, q{auto _AutoDataOther = handler.other;}, "return 0;")(handler);
}
}
template AutoToString()
{
string toString()
static if (is(typeof(this)==class))
override string toString() { return _AutoDataToString(); }
else // struct
string toString() const { return _AutoDataToString(); }
string _AutoDataToString() const
{
ToStringDataHandler handler;
return processData!(string, "string _AutoDataResult;", "return _AutoDataResult;")(handler);
@@ -82,20 +88,11 @@ template AutoToString()
template ProcessAllData()
{
R processData(R, string prolog, string epilog, H)(ref H handler)
R processData(R, string prolog, string epilog, H)(ref H handler) const
{
mixin(prolog);
foreach (i, T; this.tupleof)
{
//mixin(addAutoField(T.stringof)); // doesn't work
static if (this.tupleof[i].stringof == typeof(this.tupleof[i]).stringof)
static assert(0, "DMD bug 2881 detected - can't use enums with ProcessAllData");
else
static if (is (typeof(this) == class))
mixin(addAutoField(this.tupleof[i].stringof[5..$])); // remove "this."
else
mixin(addAutoField(this.tupleof[i].stringof[8..$])); // remove "(*this)."
}
mixin(addAutoField(this.tupleof[i].stringof[5..$])); // remove "this."
mixin(epilog);
}
}
@@ -103,29 +100,27 @@ template ProcessAllData()
/// For data handlers that only need to look at the raw data (currently only HashDataHandler)
template RawDataHandlerWrapper()
{
static string process(T, string name, bool reverseSort)()
template getMixin(T, string name, bool reverseSort)
{
return processRecursive!(T, "this." ~ name, "");
enum getMixin = getMixinRecursive!(T, "this." ~ name, "");
}
static string processRecursive(T, string name, string loopDepth)()
template getMixinRecursive(T, string name, string loopDepth)
{
static if (!hasAliasing!(T))
return processRaw("&" ~ name, name ~ ".sizeof");
else
static if (is(T U : U[]))
{
string s = "{ bool _AutoDataNullTest = " ~ name ~ " is null; " ~ processRaw("&_AutoDataNullTest", "bool.sizeof") ~ "}";
static if (!hasAliasing!(U))
s ~= processRaw(name ~ ".ptr", name ~ ".length");
else
s ~= "foreach (ref _AutoDataArrayItem" ~ loopDepth ~ "; " ~ name ~ ") {" ~ processRecursive!(U, "_AutoDataArrayItem" ~ loopDepth, loopDepth~"Item")() ~ "}";
return s;
}
enum getMixinRecursive =
"{ bool _AutoDataNullTest = " ~ name ~ " is null; " ~ getRawMixin!("&_AutoDataNullTest", "bool.sizeof") ~ "}" ~
(!hasAliasing!(U) ?
getRawMixin!(name ~ ".ptr", name ~ ".length")
:
"foreach (ref _AutoDataArrayItem" ~ loopDepth ~ "; " ~ name ~ ") {" ~ getMixinRecursive!(U, "_AutoDataArrayItem" ~ loopDepth, loopDepth~"Item") ~ "}"
);
else
static if (!hasAliasing!(T))
enum getMixinRecursive = getRawMixin!("&" ~ name, name ~ ".sizeof");
else
static if (is(typeof((new T).toHash())))
//static assert(0, "aoeu: " ~ T.stringof);
return name ~ ".processData!(void, ``, ``)(handler);";
enum getMixinRecursive = name ~ ".processData!(void, ``, ``)(handler);";
else
static assert(0, "Don't know how to process type: " ~ T.stringof);
}
@@ -137,9 +132,9 @@ struct HashDataHandler
MurmurHash2A hasher;
static string processRaw(string ptr, string len)
template getRawMixin(string ptr, string len)
{
return "handler.hasher.Add(" ~ ptr ~ ", " ~ len ~ ");";
enum getRawMixin = "handler.hasher.Add(" ~ ptr ~ ", " ~ len ~ ");";
}
}
@@ -147,13 +142,17 @@ struct EqualsDataHandler(O)
{
O other;
static string process(T, string name, bool reverseSort)()
template nullCheck(T, string name)
{
string s;
static if (is(T U : U[]))
s ~= " if ((this." ~ name ~ " is null) != (_AutoDataOther." ~ name ~ " is null)) return false;";
s ~= "if (this." ~ name ~ " != _AutoDataOther." ~ name ~ ") return false;";
return s;
enum nullCheck = "if ((this." ~ name ~ " is null) != (_AutoDataOther." ~ name ~ " is null)) return false;";
else
enum nullCheck = "";
}
template getMixin(T, string name, bool reverseSort)
{
enum getMixin = nullCheck!(T, name) ~ "if (this." ~ name ~ " != _AutoDataOther." ~ name ~ ") return false;";
}
}
@@ -161,32 +160,38 @@ struct CmpDataHandler(O)
{
O other;
static string process(T, string name, bool reverseSort)()
template getMixin(T, string name, bool reverseSort)
{
string reverseStr = reverseSort ? "-" : "";
string s;
enum getMixin = getMixinComposite!(T, name, reverseSort).code;
}
template getMixinComposite(T, string name, bool reverseSort)
{
enum reverseStr = reverseSort ? "-" : "";
static if (is(T U : U[]))
s ~= "{ int _AutoDataCmp = cast(int)(this." ~ name ~ " !is null) - cast(int)(_AutoDataOther." ~ name ~ " !is null); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
enum arrCode = "{ int _AutoDataCmp = cast(int)(this." ~ name ~ " !is null) - cast(int)(_AutoDataOther." ~ name ~ " !is null); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
else
enum arrCode = "";
static if (is(T == string) && is(std.string.cmp))
s ~= "{ int _AutoDataCmp = std.string.cmp(this." ~ name ~ ", _AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
enum dataCode = "{ int _AutoDataCmp = std.string.cmp(this." ~ name ~ ", _AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
else
static if (is(T == int))
s ~= "{ int _AutoDataCmp = this." ~ name ~ " - _AutoDataOther." ~ name ~ "; if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }"; // TODO: use long?
enum dataCode = "{ int _AutoDataCmp = this." ~ name ~ " - _AutoDataOther." ~ name ~ "; if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }"; // TODO: use long?
else
static if (is(typeof(T.opCmp)))
s ~= "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
enum dataCode = "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
else
s ~= "if (this." ~ name ~ " < _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "(-1);" ~
"if (this." ~ name ~ " > _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "( 1);";
return s;
enum dataCode = "if (this." ~ name ~ " < _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "(-1);" ~
"if (this." ~ name ~ " > _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "( 1);";
enum code = arrCode ~ dataCode;
}
}
struct ToStringDataHandler
{
static string process(T, string name, bool reverseSort)()
template getMixin(T, string name, bool reverseSort)
{
return "_AutoDataResult ~= format(`%s = %s `, `" ~ name ~ "`, this." ~ name ~ ");";
enum getMixin = "_AutoDataResult ~= format(`%s = %s `, `" ~ name ~ "`, this." ~ name ~ ");";
}
}
+58 -27
View File
@@ -20,12 +20,14 @@ module disassembler;
import std.file;
import std.string;
import std.conv;
import std.exception;
import abcfile;
import asprogram;
final class StringBuilder
{
string buf;
char[] buf;
size_t pos;
string filename;
@@ -108,7 +110,7 @@ final class RefBuilder : ASTraitsVisitor
override void run()
{
foreach (i, ref v; as.scripts)
addMethod(v.sinit, "script" ~ .toString(i) ~ "_sinit");
addMethod(v.sinit, "script" ~ to!string(i) ~ "_sinit");
foreach (vclass; as.orphanClasses)
addClass(vclass, "orphan");
foreach (method; as.orphanMethods)
@@ -157,7 +159,7 @@ final class RefBuilder : ASTraitsVisitor
int n = 0;
uint* pindex;
while ((pindex = name in privateNamespaceByName) !is null && *pindex != index)
name = bname ~ .toString(++n);
name = bname ~ to!string(++n);
}
auto pname = index in privateNamespaceNames;
if (pname)
@@ -242,11 +244,11 @@ final class RefBuilder : ASTraitsVisitor
strings[i] = qNameToString(m);
if (field)
strings[$-1] = field;
string s = join(strings, "/");
char[] s = join(strings, "/").dup;
foreach (ref c; s)
if (c < 0x20 || c == '"')
c = '_';
return s;
return assumeUnique(s);
}
string addObject(T)(T obj, ref T[string] objByName, string field)
@@ -255,7 +257,7 @@ final class RefBuilder : ASTraitsVisitor
auto uniqueName = name;
int i = 1;
while (uniqueName in objByName)
uniqueName = name ~ "_" ~ .toString(++i);
uniqueName = name ~ "_" ~ to!string(++i);
objByName[uniqueName] = obj;
objName[cast(void*)obj] = uniqueName;
return uniqueName;
@@ -300,7 +302,7 @@ final class RefBuilder : ASTraitsVisitor
if (pname)
return *pname;
else
//throw new Exception("Nameless private namespace: " ~ .toString(index));
//throw new Exception("Nameless private namespace: " ~ to!string(index));
return addPrivateNamespace(index, "OrphanPrivateNamespace");
}
}
@@ -335,10 +337,10 @@ final class Disassembler
sb.indent++; sb.newLine();
sb ~= "minorversion ";
sb ~= .toString(as.minorVersion);
sb ~= to!string(as.minorVersion);
sb.newLine();
sb ~= "majorversion ";
sb ~= .toString(as.majorVersion);
sb ~= to!string(as.majorVersion);
sb.newLine();
sb.newLine();
@@ -388,7 +390,7 @@ final class Disassembler
uint[] indices = refs.privateNamespaceNames.keys.sort;
foreach (index; indices)
{
sb ~= "#privatens " ~ .toString(index) ~ " ";
sb ~= "#privatens " ~ to!string(index) ~ " ";
dumpString(sb, refs.privateNamespaceNames[index]);
sb.newLine();
}
@@ -400,7 +402,7 @@ final class Disassembler
if (v == ABCFile.NULL_INT)
sb ~= "null";
else
sb ~= .toString(v);
sb ~= to!string(v);
}
void dumpUInt(StringBuilder sb, ulong v)
@@ -408,7 +410,7 @@ final class Disassembler
if (v == ABCFile.NULL_UINT)
sb ~= "null";
else
sb ~= .toString(v);
sb ~= to!string(v);
}
void dumpDouble(StringBuilder sb, double v)
@@ -551,6 +553,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:
@@ -570,8 +573,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)
@@ -582,7 +584,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)
@@ -593,7 +594,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:
@@ -606,15 +606,45 @@ 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 dumpFlags(bool oneLine = false)(StringBuilder sb, ubyte flags, string[] names)
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, const string[] names)
{
for (int i=0; flags; i++, flags>>=1)
if (flags & 1)
@@ -719,13 +749,14 @@ final class Disassembler
string toFileName(string refid)
{
string filename = refid.dup;
foreach (ref c; filename)
char[] buf = refid.dup;
foreach (ref c; buf)
if (c == '.' || c == ':')
c = '/';
else
if (c == '\\' || c == '*' || c == '?' || c == '"' || c == '<' || c == '>' || c == '|')
c = '_';
string filename = assumeUnique(buf);
version (Windows)
{
@@ -807,7 +838,7 @@ final class Disassembler
void dumpScript(StringBuilder sb, ASProgram.Script script, uint index)
{
sb ~= "script ; ";
sb ~= .toString(index);
sb ~= to!string(index);
sb.indent++; sb.newLine();
sb ~= "sinit"; dumpMethod(sb, script.sinit);
dumpTraits(sb, script.traits);
@@ -825,12 +856,12 @@ final class Disassembler
void dumpLabel(StringBuilder sb, ref ABCFile.Label label)
{
sb ~= 'L';
sb ~= .toString(label.index);
sb ~= to!string(label.index);
if (label.offset != 0)
{
if (label.offset > 0)
sb ~= '+';
sb ~= .toString(label.offset);
sb ~= to!string(label.offset);
}
}
@@ -897,7 +928,7 @@ final class Disassembler
{
sb.noIndent();
sb ~= 'L';
sb ~= .toString(ii);
sb ~= to!string(ii);
sb ~= ':';
sb.newLine();
}
@@ -925,13 +956,13 @@ final class Disassembler
throw new Exception("Don't know how to disassemble OP_" ~ opcodeInfo[instruction.opcode].name);
case OpcodeArgumentType.UByteLiteral:
sb ~= .toString(instruction.arguments[i].ubytev);
sb ~= to!string(instruction.arguments[i].ubytev);
break;
case OpcodeArgumentType.IntLiteral:
sb ~= .toString(instruction.arguments[i].intv);
sb ~= to!string(instruction.arguments[i].intv);
break;
case OpcodeArgumentType.UIntLiteral:
sb ~= .toString(instruction.arguments[i].uintv);
sb ~= to!string(instruction.arguments[i].uintv);
break;
case OpcodeArgumentType.Int:
+1 -1
View File
@@ -31,7 +31,7 @@ public:
m_size = 0;
}
void Add ( void * vdata, int len )
void Add ( const(void) * vdata, int len )
{
ubyte * data = cast(ubyte*)vdata;
m_size += len;
+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