29 Commits
1.6 ... 64
Author SHA1 Message Date
Vladimir Panteleev 1f0ef64b83 64-bit fixes 2011-07-25 02:12:22 +03:00
Vladimir Panteleev 701e5b6f80 build_rabcdasm: build with warnings 2011-07-25 02:11:15 +03:00
Vladimir Panteleev fc46ad02e9 Documentation: fix abcreplace example 2011-07-13 19:22:45 +03:00
Vladimir Panteleev e7a5dd92c6 Fix DMD 2.054 deprecation warnings 2011-07-11 19:47:32 +03:00
Vladimir Panteleev 352456d257 Update Changelog 2011-07-06 14:25:14 +03:00
Vladimir Panteleev d6544ae7cb Documentation: add link to FiddlerAutoCapture 2011-06-23 01:31:17 +03:00
Vladimir Panteleev 37cd4b415b disassembler: fixed handling of orphan methods with multiple references 2011-06-16 13:02:59 +03:00
Vladimir Panteleev 62d0756dbc asprogram: treat out-of-bounds namespace/multiname indices as null 2011-06-16 01:00:36 +03:00
Vladimir Panteleev 48b221535f disassembler: apply "blank line" change for si32-like instructions as well 2011-06-14 15:23:32 +03:00
Vladimir Panteleev beb7229bf8 Update Changelog 2011-06-14 15:15:21 +03:00
Vladimir Panteleev 296196ff71 Add support for out-of-bounds class/method indices 2011-06-12 18:57:01 +03:00
Vladimir Panteleev 170e7d6a8b Revert "abcfile: validate constant indices when reading bytecode"
This reverts commit 51536e4add.
2011-06-12 18:14:37 +03:00
Vladimir Panteleev 2c63eb592a disassembler: track private namespaces referenced only at script level 2011-06-12 18:09:19 +03:00
Vladimir Panteleev 16a5f649de disassembler: add support for orphan private namespaces 2011-06-12 18:08:45 +03:00
Vladimir Panteleev 111cd75eb4 disassembler: fix handling of null multinames 2011-06-12 16:35:45 +03:00
Vladimir Panteleev e103048af6 abcfile: Fix handling of exception info in corrupted method bodies 2011-06-12 16:21:06 +03:00
Vladimir Panteleev 61df70dd0e autodata: use to!string instead of format in AutoToString, for enum names 2011-06-12 14:43:52 +03:00
Vladimir Panteleev 51536e4add abcfile: validate constant indices when reading bytecode 2011-06-12 10:57:09 +03:00
Vladimir Panteleev fb5c03ada5 Update documentation 2011-06-12 10:40:15 +03:00
Vladimir Panteleev 2226b0059f disassembler: improve double shortest representation code 2011-06-12 10:32:05 +03:00
Vladimir Panteleev 69cf64b85c abcexport: delimit base file name and index with a dash 2011-06-10 18:51:33 +03:00
Vladimir Panteleev 2b49e77267 Fix handling of null namespaces 2011-06-10 18:44:10 +03:00
Vladimir Panteleev fab198a648 disassembler: add newlines after si32 as well 2011-06-10 14:21:32 +03:00
Vladimir Panteleev 4e4fd2b56d disassembler: externalize script-level methods, too 2011-06-10 13:24:57 +03:00
Vladimir Panteleev 7e4f7f5bac disassembler: dump classes to own file, use second-level extensions 2011-06-10 13:15:20 +03:00
Vladimir Panteleev 7a92e9fe81 asprogram: fix compiler warning 2011-06-10 12:36:25 +03:00
Vladimir Panteleev 41002f7085 Disassembler: fix handling of malformed UTF-8 2011-06-09 17:32:24 +03:00
Vladimir Panteleev 3b131fa932 Documentation: add a note about swfbinexport and swfbinreplace usage 2011-06-09 14:55:44 +03:00
Vladimir Panteleev 34316bd4c9 Documentation: fix typos 2011-05-26 15:39:01 +03:00
12 changed files with 350 additions and 205 deletions
+102 -81
View File
@@ -1,81 +1,102 @@
RABCDAsm Changelog
==================
RABCDAsm v1.6 (2011.05.13)
--------------------------
* Overhaul of the structure of disassembler-generated files
* Private namespace names and `refid`s for nested classes are now generated
from their referenced contexts
* This also fixes problems caused by private namespaces being referenced
in multiple distinct contexts
* One `#include`d file per script instead of per class
* Nested classes, script initializers and other script traits are now in
the same file as their main class
* Inline methods are now in separate files, placed according to their usage
context
* Private namespaces are sorted by their autogenerated names in the
`.privatens.asasm` file
* No syntax changes, output is backwards-compatible
* Improved escaping of file names
* Output correct opcode error locations
* Disassembler will now refuse to overwrite files
* `rabcdasm` will now always generate output in the same directory as the
input file
* Failure to decode a method body is now not a fatal error
* Aggressively try to shorten double literals to their shortest representation
* Added the `swfbinexport` and `swfbinreplace` utilities, to aid in manipulating
contents of `DefineBinaryData` tags
* The package can now be built using a simple, dedicated build tool
* Documentation updates
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)
--------------------------
* 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
* 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.
RABCDAsm Changelog
==================
RABCDAsm v1.8 (2011.07.06)
--------------------------
* Fixed handling of orphan methods with multiple references
* Treat out-of-bounds namespace/multiname indices as null
* Documentation updates
RABCDAsm v1.7 (2011.06.14)
--------------------------
* Fixed handling of invalid UTF-8 in strings
* Fixed handling of null namespaces and multinames
* Fixed handling of exception info in corrupted method bodies
* Fixed handling of orphan private namespaces
* Fixed handling of out-of-bounds class/method indices
* Disassembler now creates one file per class and script-level method
* Disassembler now outputs blank lines after `si32` etc. instructions as well
* `abcexport` now delimits the index with a dash in output filenames
* Improved shortening of `double` literals
* Documentation updates
RABCDAsm v1.6 (2011.05.13)
--------------------------
* Overhaul of the structure of disassembler-generated files
* Private namespace names and `refid`s for nested classes are now generated
from their referenced contexts
* This also fixes problems caused by private namespaces being referenced
in multiple distinct contexts
* One `#include`d file per script instead of per class
* Nested classes, script initializers and other script traits are now in
the same file as their main class
* Inline methods are now in separate files, placed according to their usage
context
* Private namespaces are sorted by their autogenerated names in the
`.privatens.asasm` file
* No syntax changes, output is backwards-compatible
* Improved escaping of file names
* Output correct opcode error locations
* Disassembler will now refuse to overwrite files
* `rabcdasm` will now always generate output in the same directory as the
input file
* Failure to decode a method body is now not a fatal error
* Aggressively try to shorten double literals to their shortest representation
* Added the `swfbinexport` and `swfbinreplace` utilities, to aid in manipulating
contents of `DefineBinaryData` tags
* The package can now be built using a simple, dedicated build tool
* Documentation updates
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)
--------------------------
* 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
* 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.
+18 -10
View File
@@ -17,7 +17,7 @@ These are:
`abcexport` and `abcreplace` are reimplementations of similar utilities from
my [swfutilsex][] Java package, however these work faster as they do not parse
the SWF files as deeply.
`swfdecompress` is ancilliary and is only useful for debugging and studying of
`swfdecompress` is ancillary and is only useful for debugging and studying of
the SWF file format, and not required for ABC manipulation. It is functionally
equivalent to [flasm][]'s `-x` option.
`swf7zcompress` is an utility to further reduce the size of SWF files. It uses
@@ -87,25 +87,29 @@ To begin hacking on a SWF file:
abcexport file.swf
This will create `file0.abc` ... `fileN.abc` (often just `file0.abc`). Each
This will create `file-0.abc` ... `file-N.abc` (often just `file-0.abc`). Each
file corresponds to an ABC block inside the SWF file.
To disassemble one of the `.abc` files:
rabcdasm file0.abc
rabcdasm file-0.abc
This will create a `file0` directory, which will contain `file0.main.asasm`
(the main program file), `file0.privatens.asasm` (private namespace alias
definitions) and a file per ActionScript class.
This will create a `file-0` directory, which will contain `file-0.main.asasm`
(the main program file), `file-0.privatens.asasm` (private namespace alias
definitions), and files for ActionScript scripts, classes, and orphan and
script-level methods.
To assemble the `.asasm` files back, and update the SWF file:
rabcasm file0/file0.main.asasm
abcreplace file0.swf 0 file0/file0.main.abc
rabcasm file-0/file-0.main.asasm
abcreplace file.swf 0 file-0/file-0.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`.
`swfbinexport` and `swfbinreplace` are used in the same manner as `abcexport`
and `abcreplace`.
Syntax
======
@@ -247,7 +251,7 @@ namespace. Internally (the ABC file format), private namespaces are
distinguished by a numerical index - `rabcdasm` will attempt to give them
descriptive names based on their context. Aliases can be defined using the
`#privatens` directive. `rabcdasm` will create a separate file containing the
aliases (`file0.privatens.asasm`).
aliases (`file-0.privatens.asasm`).
Strings have a syntax similar to C string literals. Strings start and end with
a `"`. Supported escape sequences (a backslash followed by a letter) are `\n`
@@ -393,7 +397,7 @@ RABCDAsm users.
[debug Flash Player]: http://www.adobe.com/support/flashplayer/downloads.html
3. The [Fiddler][] Web Debugging Proxy can be very useful for analysing
3. The [Fiddler][] Web Debugging Proxy can be very useful for analyzing
websites with SWF content. The following script fragment (which is to be
placed in the `OnBeforeResponse` function) will automatically save all SWF
files while preserving the directory structure.
@@ -412,11 +416,15 @@ RABCDAsm users.
oSession.SaveResponseBody(path);
}
A more robust version of the above snippet is available as a Fiddler plugin
[here][FiddlerAutoCapture].
Once you have edited a SWF file, you can use Fiddler's [AutoResponder][] to
replace the original file with your modified version.
[Fiddler]: http://www.fiddler2.com/fiddler2/
[AutoResponder]: http://www.fiddler2.com/fiddler2/help/AutoResponder.asp
[FiddlerAutoCapture]: https://github.com/CyberShadow/FiddlerAutoCapture
Limitations
===========
+1 -1
View File
@@ -45,7 +45,7 @@ void main(string[] args)
while (*p++) {} // skip name
abc = tag.data[p-tag.data.ptr..$];
}
std.file.write(getName(arg) ~ to!string(count++) ~ ".abc", abc);
std.file.write(getName(arg) ~ "-" ~ to!string(count++) ~ ".abc", abc);
}
if (count == 0)
throw new Exception("No DoABC tags found");
+20 -18
View File
@@ -19,6 +19,7 @@
module abcfile;
import std.string : format; // exception formatting
import std.conv;
import std.exception;
/**
@@ -222,7 +223,7 @@ class ABCFile
uint index; /// instruction index
int offset; /// signed offset relative to said instruction
}
private int absoluteOffset; /// internal temporary value used during reading and writing
private ptrdiff_t absoluteOffset; /// internal temporary value used during reading and writing
}
}
@@ -1280,18 +1281,18 @@ private final class ABCReader
r.instructions = null;
size_t len = readU30();
uint[] instructionAtOffset = new uint[len];
auto instructionAtOffset = new uint[len];
r.rawBytes = buf[pos..pos+len];
void translateLabel(ref ABCFile.Label label)
{
int absoluteOffset = label.absoluteOffset;
int instructionOffset = absoluteOffset;
auto absoluteOffset = label.absoluteOffset;
auto instructionOffset = absoluteOffset;
while (true)
{
if (instructionOffset >= cast(int)len)
if (instructionOffset >= len)
{
label.index = r.instructions.length;
label.index = to!uint(r.instructions.length);
instructionOffset = len;
break;
}
@@ -1308,23 +1309,23 @@ private final class ABCReader
}
instructionOffset--;
}
label.offset = absoluteOffset-instructionOffset;
label.offset = to!int(absoluteOffset-instructionOffset);
}
size_t start = pos;
size_t end = pos + len;
uint offset() { return pos - start; }
size_t offset() { return pos - start; }
try
{
instructionAtOffset[] = uint.max;
uint[] instructionOffsets;
size_t[] instructionOffsets;
while (pos < end)
{
uint instructionOffset = offset;
auto instructionOffset = offset;
scope(failure) pos = start + instructionOffset;
instructionAtOffset[instructionOffset] = r.instructions.length;
instructionAtOffset[instructionOffset] = to!uint(r.instructions.length);
ABCFile.Instruction instruction;
instruction.opcode = cast(Opcode)readU8();
instruction.arguments.length = opcodeInfo[instruction.opcode].argumentTypes.length;
@@ -1398,6 +1399,7 @@ private final class ABCReader
{
r.instructions = null;
r.error = e.msg;
instructionAtOffset[] = 0;
}
pos = end;
@@ -1516,7 +1518,6 @@ private final class ABCWriter
writeU8(cast(ubyte)(v>>16));
}
/// Note: may return values larger than 0xFFFFFFFF.
void writeU32(ulong v)
{
if ( v < 128)
@@ -1556,8 +1557,9 @@ private final class ABCWriter
writeU32(cast(ulong)v);
}
void writeU30(uint v)
void writeU30(ulong v)
{
enforce(v < (1<<30));
writeU32(v);
}
@@ -1755,9 +1757,9 @@ private final class ABCWriter
writeU30(v.initScopeDepth);
writeU30(v.maxScopeDepth);
uint[] instructionOffsets = new uint[v.instructions.length+1];
auto instructionOffsets = new size_t[v.instructions.length+1];
uint resolveLabel(ref ABCFile.Label label) { return instructionOffsets[label.index]+label.offset; }
ptrdiff_t resolveLabel(ref ABCFile.Label label) { return instructionOffsets[label.index]+label.offset; }
{
// we don't know the length before writing all the instructions - swap buffer with a temporary one
@@ -1767,12 +1769,12 @@ private final class ABCWriter
buf = methodBuf[];
pos = 0;
struct Fixup { ABCFile.Label target; uint pos, base; }
struct Fixup { ABCFile.Label target; size_t pos, base; }
Fixup[] fixups;
foreach (ii, ref instruction; v.instructions)
{
uint instructionOffset = pos;
auto instructionOffset = pos;
instructionOffsets[ii] = instructionOffset;
writeU8(instruction.opcode);
@@ -1836,7 +1838,7 @@ private final class ABCWriter
foreach (ref fixup; fixups)
{
pos = fixup.pos;
writeS24(resolveLabel(fixup.target)-fixup.base);
writeS24(to!int(cast(ptrdiff_t)(resolveLabel(fixup.target)-fixup.base)));
}
auto code = buf;
+24 -15
View File
@@ -449,6 +449,7 @@ private final class ABCtoAS
n.vTypeName.params.length = multiname.TypeName.params.length;
foreach (j, param; multiname.TypeName.params)
n.vTypeName.params[j] = multinames[param];
break;
default:
break;
}
@@ -621,16 +622,16 @@ private final class ABCtoAS
r.arguments[i].stringv = abc.strings[instruction.arguments[i].index];
break;
case OpcodeArgumentType.Namespace:
r.arguments[i].namespacev = namespaces[instruction.arguments[i].index];
r.arguments[i].namespacev = namespaces.checkedGet(instruction.arguments[i].index);
break;
case OpcodeArgumentType.Multiname:
r.arguments[i].multinamev = multinames[instruction.arguments[i].index];
r.arguments[i].multinamev = multinames.checkedGet(instruction.arguments[i].index);
break;
case OpcodeArgumentType.Class:
r.arguments[i].classv = classes[instruction.arguments[i].index];
r.arguments[i].classv = classes.checkedGet(instruction.arguments[i].index);
break;
case OpcodeArgumentType.Method:
r.arguments[i].methodv = methods[instruction.arguments[i].index];
r.arguments[i].methodv = methods.checkedGet(instruction.arguments[i].index);
break;
case OpcodeArgumentType.JumpTarget:
@@ -654,7 +655,7 @@ private final class ABCtoAS
as.majorVersion = abc.majorVersion;
namespaces.length = abc.namespaces.length;
foreach (i, ref namespace; abc.namespaces)
foreach (uint i, ref namespace; abc.namespaces)
if (i)
namespaces[i] = convertNamespace(namespace, i);
@@ -684,7 +685,7 @@ private final class ABCtoAS
instances[i] = convertInstance(instance);
classes.length = abc.classes.length;
foreach (i, ref vclass; abc.classes)
foreach (uint i, ref vclass; abc.classes)
classes[i] = convertClass(vclass, i);
as.scripts.length = abc.scripts.length;
@@ -797,7 +798,7 @@ private final class AStoABC
all.sort;
enum { NullOffset = haveNull ? 1 : 0 }
values.length = all.length + NullOffset;
foreach (i, ref c; all)
foreach (uint i, ref c; all)
{
pool[cast(I)c.value].index = i + NullOffset;
values[i + NullOffset] = c.value;
@@ -837,7 +838,7 @@ private final class AStoABC
auto rp = p in pool;
if (rp is null)
{
pool[p] = Entry(1, p, pool.length);
pool[p] = Entry(1, p, to!uint(pool.length));
return true;
}
else
@@ -886,7 +887,7 @@ private final class AStoABC
topSort:
// update indices
foreach (j, e; all)
foreach (uint j, e; all)
e.index = j;
foreach (ref a; pool)
@@ -1065,15 +1066,12 @@ private final class AStoABC
void visitClass(ASProgram.Class vclass)
{
if (classes.notAdded(vclass))
if (classes.add(vclass))
{
visitMethod(vclass.cinit);
visitTraits(vclass.traits);
visitInstance(vclass.instance);
bool r = classes.add(vclass);
assert(r, "Recursive class reference");
}
}
@@ -1483,10 +1481,16 @@ private final class AStoABC
r.arguments[i].index = multinames.get(instruction.arguments[i].multinamev);
break;
case OpcodeArgumentType.Class:
r.arguments[i].index = classes.get(instruction.arguments[i].classv);
if (instruction.arguments[i].classv is null)
r.arguments[i].index = to!uint(abc.classes.length);
else
r.arguments[i].index = classes.get(instruction.arguments[i].classv);
break;
case OpcodeArgumentType.Method:
r.arguments[i].index = methods.get(instruction.arguments[i].methodv);
if (instruction.arguments[i].methodv is null)
r.arguments[i].index = to!uint(abc.methods.length);
else
r.arguments[i].index = methods.get(instruction.arguments[i].methodv);
break;
case OpcodeArgumentType.JumpTarget:
@@ -1557,3 +1561,8 @@ private bool contains(T)(T[] arr, T val)
return true;
return false;
}
private T checkedGet(T)(T[] array, uint index, T def = T.init)
{
return index < array.length ? array[index] : def;
}
+28 -22
View File
@@ -76,7 +76,7 @@ final class Assembler
string positionStr()
{
auto lines = splitlines(buf);
auto lines = splitLines(buf);
foreach (i, line; lines)
if (pos <= line.ptr + line.length)
return format("%s(%d,%d)", filename, i+1, pos-line.ptr+1);
@@ -992,7 +992,7 @@ final class Assembler
break;
if (peekChar() == ':')
{
addUnique!("label")(labels, word, instructions.length);
addUnique!("label")(labels, word, to!uint(instructions.length));
skipChar(); // :
continue;
}
@@ -1008,7 +1008,7 @@ final class Assembler
instruction.opcode = *popcode;
auto argTypes = opcodeInfo[instruction.opcode].argumentTypes;
instruction.arguments.length = argTypes.length;
foreach (i, type; argTypes)
foreach (uint i, type; argTypes)
{
final switch (type)
{
@@ -1044,22 +1044,22 @@ final class Assembler
instruction.arguments[i].multinamev = readMultiname();
break;
case OpcodeArgumentType.Class:
localClassFixups ~= LocalFixup(files[0].position, instructions.length, i, readString());
localClassFixups ~= LocalFixup(files[0].position, to!uint(instructions.length), i, readString());
break;
case OpcodeArgumentType.Method:
localMethodFixups ~= LocalFixup(files[0].position, instructions.length, i, readString());
localMethodFixups ~= LocalFixup(files[0].position, to!uint(instructions.length), i, readString());
break;
case OpcodeArgumentType.JumpTarget:
case OpcodeArgumentType.SwitchDefaultTarget:
jumpFixups ~= LocalFixup(files[0].position, instructions.length, i, readWord());
jumpFixups ~= LocalFixup(files[0].position, to!uint(instructions.length), i, readWord());
break;
case OpcodeArgumentType.SwitchTargets:
string[] switchTargetLabels = readList!('[', ']', readWord, false)();
instruction.arguments[i].switchTargets.length = switchTargetLabels.length;
foreach (li, s; switchTargetLabels)
switchFixups ~= LocalFixup(files[0].position, instructions.length, i, s, li);
foreach (uint li, s; switchTargetLabels)
switchFixups ~= LocalFixup(files[0].position, to!uint(instructions.length), i, s, li);
break;
}
if (i < argTypes.length-1)
@@ -1175,26 +1175,32 @@ final class Assembler
readProgram();
foreach (ref f; classFixups)
{
auto cp = f.name in classesByID;
if (cp is null)
if (f.name is null)
*f.ptr = null;
else
{
setFile(f.where.load);
throw new Exception("Unknown class refid: " ~ f.name);
auto cp = f.name in classesByID;
if (cp is null)
{
setFile(f.where.load);
throw new Exception("Unknown class refid: " ~ f.name);
}
*f.ptr = *cp;
}
*f.ptr = *cp;
}
foreach (ref f; methodFixups)
{
auto mp = f.name in methodsByID;
if (mp is null)
if (f.name is null)
*f.ptr = null;
else
{
setFile(f.where.load);
throw new Exception("Unknown method refid: " ~ f.name);
auto mp = f.name in methodsByID;
if (mp is null)
{
setFile(f.where.load);
throw new Exception("Unknown method refid: " ~ f.name);
}
*f.ptr = *mp;
}
*f.ptr = *mp;
}
}
catch (Exception e)
{
+28 -6
View File
@@ -20,6 +20,7 @@ module autodata;
import murmurhash2a;
import std.traits;
public import std.conv;
string addAutoField(string name, bool reverseSort = false)
{
@@ -75,7 +76,7 @@ template AutoCompare()
template AutoToString()
{
static if (is(typeof(this)==class))
override string toString() { return _AutoDataToString(); }
override string toString() const { return _AutoDataToString(); }
else // struct
string toString() { return _AutoDataToString(); }
@@ -119,8 +120,11 @@ template RawDataHandlerWrapper()
static if (!hasAliasing!(T))
enum getMixinRecursive = getRawMixin!("&" ~ name, name ~ ".sizeof");
else
static if (is(typeof(this)==struct) || is(typeof(this)==class))
static if (is(T==struct))
enum getMixinRecursive = name ~ ".processData!(void, ``, ``)(handler);";
else
static if (is(T==class))
enum getMixinRecursive = "if ("~name~" !is null) " ~ name ~ ".processData!(void, ``, ``)(handler);";
else
static assert(0, "Don't know how to process type: " ~ T.stringof);
}
@@ -134,7 +138,7 @@ struct HashDataHandler
template getRawMixin(string ptr, string len)
{
enum getRawMixin = "handler.hasher.Add(" ~ ptr ~ ", " ~ len ~ ");";
enum getRawMixin = "handler.hasher.Add(" ~ ptr ~ ", to!int(" ~ len ~ "));";
}
}
@@ -144,7 +148,7 @@ struct EqualsDataHandler(O)
template nullCheck(T, string name)
{
static if (is(T U : U[]))
static if (is(typeof(T.init is null)))
enum nullCheck = "if ((this." ~ name ~ " is null) != (_AutoDataOther." ~ name ~ " is null)) return false;";
else
enum nullCheck = "";
@@ -165,6 +169,23 @@ struct CmpDataHandler(O)
enum getMixin = getMixinComposite!(T, name, reverseSort).code;
}
template nullCheck(T, string name, string reverseStr)
{
static if (is(typeof(T.init is null)))
enum nullCheck = "
if (this."~name~" is null && _AutoDataOther."~name~" is null)
{ /* skip */ }
else
if (this."~name~" is null && _AutoDataOther."~name~" !is null)
return " ~ reverseStr ~ "(-1);
else
if (this."~name~" !is null && _AutoDataOther."~name~" is null)
return " ~ reverseStr ~ "( 1);
else";
else
enum nullCheck = "";
}
template getMixinComposite(T, string name, bool reverseSort)
{
enum reverseStr = reverseSort ? "-" : "";
@@ -180,7 +201,8 @@ struct CmpDataHandler(O)
enum dataCode = "{ int _AutoDataCmp = this." ~ name ~ " - _AutoDataOther." ~ name ~ "; if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }"; // TODO: use long?
else
static if (is(typeof(T.opCmp)))
enum dataCode = "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
enum dataCode = nullCheck!(T, name, reverseStr)
~ "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
else
enum dataCode = "if (this." ~ name ~ " < _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "(-1);" ~
"if (this." ~ name ~ " > _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "( 1);";
@@ -192,6 +214,6 @@ struct ToStringDataHandler
{
template getMixin(T, string name, bool reverseSort)
{
enum getMixin = "_AutoDataResult ~= format(`%s = %s `, `" ~ name ~ "`, this." ~ name ~ ");";
enum getMixin = "_AutoDataResult ~= `" ~ name ~ " = ` ~ to!string(this." ~ name ~ ") ~ ` `;";
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ version(DigitalMars)
else
const DEFAULT_COMPILER = "gdmd";
const DEFAULT_FLAGS = "-O -inline";
const DEFAULT_FLAGS = "-w -O -inline";
import std.process;
import std.string;
+118 -44
View File
@@ -20,13 +20,17 @@ module disassembler;
import std.file;
import std.string;
import std.array;
import std.conv;
import std.exception;
import std.algorithm;
import std.path;
import abcfile;
import asprogram;
import autodata;
alias std.array.join join;
final class StringBuilder
{
char[] buf;
@@ -127,10 +131,9 @@ final class RefBuilder : ASTraitsVisitor
{
case Type.Multiname:
assert(multiname.kind == ASType.QName);
if (multiname.vQName.ns.kind == ASType.PrivateNamespace)
throw new Exception("Stringifying unexpanded context");
auto nsName = multiname.vQName.ns.name;
if (multiname.vQName.ns.kind == ASType.PrivateNamespace) // unexpanded private namespace
nsName ~= '#' ~ to!string(multiname.vQName.ns.privateIndex);
if (nsName.length)
if (multiname.vQName.name.length)
return [Segment('/', nsName), Segment(filename ? '/' : ':', multiname.vQName.name)];
@@ -146,29 +149,29 @@ final class RefBuilder : ASTraitsVisitor
}
}
static ContextItem[] expand(RefBuilder refs, ContextItem[] context, bool noThrow) /// recursively expand all private namespaces
static ContextItem[] expand(RefBuilder refs, ContextItem[] context, bool abortOnUnknown) /// recursively expand all private namespaces
{
ContextItem[] newContext;
foreach (ref c; context)
{
auto cEx = c.expand(refs, noThrow);
auto cEx = c.expand(refs, abortOnUnknown);
if (cEx is null) return null;
newContext ~= cEx;
}
return newContext;
}
ContextItem[] expand(RefBuilder refs, bool noThrow)
ContextItem[] expand(RefBuilder refs, bool abortOnUnknown)
{
if (type==Type.Multiname && multiname.vQName.ns.kind == ASType.PrivateNamespace)
{
auto pcontext = multiname.vQName.ns.privateIndex in refs.privateNamespaces.contexts;
if (pcontext is null)
if (noThrow)
if (abortOnUnknown)
return null;
else
throw new Exception("Expanding unknown private namespace " ~ to!string(multiname.vQName.ns.privateIndex));
auto expanded = expand(refs, *pcontext, noThrow);
return (&this)[0..1];
auto expanded = expand(refs, *pcontext, abortOnUnknown);
if (expanded is null) return null;
return expanded ~ (multiname.vQName.name.length ? [ContextItem(multiname.vQName.name)] : null); // hack
}
@@ -229,7 +232,6 @@ final class RefBuilder : ASTraitsVisitor
if (pexisting)
{
auto rootContext = contextRoot(*pexisting, context);
enforce(rootContext.length, format("Can't find common private namespace root between ", *pexisting, " and ", context));
contexts[p] = rootContext;
}
else
@@ -237,8 +239,10 @@ final class RefBuilder : ASTraitsVisitor
}
else
{
enforce(p !in contexts, format("Duplicate object reference: ", contexts[p], " and ", context));
contexts[p] = context.dup;
if (p in contexts)
contexts[p] = [ContextItem("multireferenced")];
else
contexts[p] = context.dup;
}
}
@@ -285,7 +289,7 @@ final class RefBuilder : ASTraitsVisitor
version (Windows)
static string[string] filenameMappings;
string getFilename(U)(U obj)
string getFilename(U)(U obj, string suffix)
{
auto pname = cast(T)obj in filenames;
assert(pname, format("Unscanned object: ", obj));
@@ -298,7 +302,7 @@ final class RefBuilder : ASTraitsVisitor
{
again:
string subpath = join(dirSegments[0..l+1], "/");
string subpathl = tolower(subpath);
string subpathl = toLower(subpath);
string* canonicalp = subpathl in filenameMappings;
if (canonicalp && *canonicalp != subpath)
{
@@ -310,7 +314,7 @@ final class RefBuilder : ASTraitsVisitor
filename = join(dirSegments, "/");
}
return filename ~ ".asasm";
return filename ~ "." ~ suffix ~ ".asasm";
}
}
@@ -343,7 +347,10 @@ final class RefBuilder : ASTraitsVisitor
if (c)
classContexts ~= c;
}
context = reduce!contextRoot(new ContextItem[0], classContexts);
if (classContexts.length)
context = reduce!contextRoot(classContexts);
else
context = null;
if (context.length==0)
pushContext("script_" ~ to!string(i));
scripts.add(v, context);
@@ -366,6 +373,20 @@ final class RefBuilder : ASTraitsVisitor
popContext();
}
// add private namespaces referenced only at script level
foreach (v; as.scripts)
foreach (trait; v.traits)
if (trait.name.kind == ASType.QName && trait.name.vQName.ns.kind == ASType.PrivateNamespace && !privateNamespaces.isAdded(trait.name.vQName.ns.privateIndex))
privateNamespaces.add(trait.name.vQName.ns.privateIndex, scripts.getContext(v));
foreach (privateIndex, b; possibleOrphanPrivateNamespaces)
if (!privateNamespaces.isAdded(privateIndex))
{
pushContext("private_namespace_" ~ to!string(privateIndex));
privateNamespaces.add(privateIndex, context);
popContext();
}
privateNamespaces.coagulate(this);
objects.coagulate(this);
scripts.coagulate(this);
@@ -413,7 +434,6 @@ final class RefBuilder : ASTraitsVisitor
static bool uninteresting(ContextItem[] c)
{
return
(c.length==0) ||
(c.length==1 && c[0].type==ContextItem.Type.String && c[0].str.startsWith("script_") && c[0].str.endsWith("_sinit")) ||
(c.length==1 && c[0].type==ContextItem.Type.String && c[0].str.startsWith("orphan_method_")) ||
false;
@@ -452,14 +472,17 @@ final class RefBuilder : ASTraitsVisitor
return c;
}
bool[uint] possibleOrphanPrivateNamespaces;
void visitNamespace(ASProgram.Namespace ns)
{
if (ns is null) return;
if (ns.kind == ASType.PrivateNamespace)
{
assert(context.length > 0, "No context");
//assert(ns.name is null, "Named private namespace");
int myPos = context.length;
auto myPos = context.length;
foreach (i, ref item; context)
if (item.type == ContextItem.Type.Multiname && item.multiname.vQName.ns == ns)
{
@@ -467,7 +490,10 @@ final class RefBuilder : ASTraitsVisitor
break;
}
if (myPos == 0)
{
possibleOrphanPrivateNamespaces[ns.privateIndex] = true;
return;
}
auto myContext = context[0..myPos].dup;
privateNamespaces.add(ns.privateIndex, myContext);
@@ -482,6 +508,7 @@ final class RefBuilder : ASTraitsVisitor
void visitMultiname(ASProgram.Multiname m)
{
if (m is null) return;
with (m)
switch (kind)
{
@@ -569,7 +596,7 @@ final class RefBuilder : ASTraitsVisitor
foreach (i, ref s; segments)
strings[i] = (i>0 ? cast(string)[s.delim] : null) ~ escape(s.str);
return join(strings);
return arrayJoin(strings);
}
void addObject(T)(T obj) { objects.add(obj, context); }
@@ -599,17 +626,27 @@ final class Disassembler
string name, dir;
RefBuilder refs;
void newInclude(StringBuilder mainsb, string filename, void delegate(StringBuilder) callback)
void newInclude(StringBuilder mainsb, string filename, void delegate(StringBuilder) callback, bool doInline = true)
{
if (mainsb.filename.split("/").length != 2)
throw new Exception("TODO");
StringBuilder sb = new StringBuilder(dir ~ "/" ~ filename);
callback(sb);
sb.save();
if (doInline)
{
string base = dirname(mainsb.filename);
string full = dir ~ "/" ~ filename;
uint up = 0;
while (!full.startsWith(base))
base = dirname(base), up++;
string rel = replicate("../", up) ~ full[base.length+1..$];
mainsb ~= "#include ";
dumpString(mainsb, filename);
mainsb.newLine();
StringBuilder sb = new StringBuilder(full);
callback(sb);
sb.save();
mainsb ~= "#include ";
dumpString(mainsb, rel);
mainsb.newLine();
}
else
callback(mainsb);
}
this(ASProgram as, string dir, string name)
@@ -641,9 +678,9 @@ final class Disassembler
sb.newLine();
sb.newLine();
foreach (i, script; as.scripts)
foreach (uint i, script; as.scripts)
{
newInclude(sb, refs.scripts.getFilename(script), (StringBuilder sb) {
newInclude(sb, refs.scripts.getFilename(script, "script"), (StringBuilder sb) {
dumpScript(sb, script, i);
});
}
@@ -656,7 +693,7 @@ final class Disassembler
sb.newLine();
foreach (i, vclass; as.orphanClasses)
newInclude(sb, refs.objects.getFilename(vclass), (StringBuilder sb) {
newInclude(sb, refs.objects.getFilename(vclass, "class"), (StringBuilder sb) {
dumpClass(sb, vclass);
});
@@ -670,7 +707,7 @@ final class Disassembler
sb.newLine();
foreach (i, method; as.orphanMethods)
newInclude(sb, refs.objects.getFilename(method), (StringBuilder sb) {
newInclude(sb, refs.objects.getFilename(method, "method"), (StringBuilder sb) {
dumpMethod(sb, method, "method");
});
@@ -742,8 +779,13 @@ final class Disassembler
static double forceDouble(double d) { static double n; n = d; return n; }
if (s != "nan" && s != "inf" && s != "-inf")
{
foreach_reverse (i; 1..s.length)
if (s[i]>='0' && s[i]<='8' && forceDouble(to!double(s[0..i] ~ cast(char)(s[i]+1)))==v)
s = s[0..i] ~ cast(char)(s[i]+1);
while (s.length>2 && s[$-1]!='.' && forceDouble(to!double(s[0..$-1]))==v)
s = s[0..$-1];
}
sb ~= s;
}
}
@@ -870,9 +912,9 @@ final class Disassembler
}
}
void dumpTraits(StringBuilder sb, ASProgram.Trait[] traits)
void dumpTraits(StringBuilder sb, ASProgram.Trait[] traits, bool inScript = false)
{
foreach (ref trait; traits)
foreach (/*ref*/ trait; traits)
{
sb ~= "trait ";
sb ~= TraitKindNames[trait.kind];
@@ -909,7 +951,10 @@ final class Disassembler
dumpUInt(sb, trait.vClass.slotId);
}
sb.indent++; sb.newLine();
dumpClass(sb, trait.vClass.vclass);
newInclude(sb, refs.objects.getFilename(trait.vClass.vclass, "class"), (StringBuilder sb) {
dumpClass(sb, trait.vClass.vclass);
});
break;
case TraitKind.Function:
if (trait.vFunction.slotId)
@@ -918,7 +963,9 @@ final class Disassembler
dumpUInt(sb, trait.vFunction.slotId);
}
sb.indent++; sb.newLine();
dumpMethod(sb, trait.vFunction.vfunction, "method");
newInclude(sb, refs.objects.getFilename(trait.vFunction.vfunction, "method"), (StringBuilder sb) {
dumpMethod(sb, trait.vFunction.vfunction, "method");
}, inScript);
break;
case TraitKind.Method:
case TraitKind.Getter:
@@ -929,7 +976,9 @@ final class Disassembler
dumpUInt(sb, trait.vMethod.dispId);
}
sb.indent++; sb.newLine();
dumpMethod(sb, trait.vMethod.vmethod, "method");
newInclude(sb, refs.objects.getFilename(trait.vMethod.vmethod, "method"), (StringBuilder sb) {
dumpMethod(sb, trait.vMethod.vmethod, "method");
}, inScript);
break;
default:
throw new Exception("Unknown trait kind");
@@ -1125,7 +1174,7 @@ final class Disassembler
sb ~= to!string(index);
sb.indent++; sb.newLine();
dumpMethod(sb, script.sinit, "sinit");
dumpTraits(sb, script.traits);
dumpTraits(sb, script.traits, true);
sb.indent--; sb ~= "end ; script"; sb.newLine();
}
@@ -1239,7 +1288,7 @@ final class Disassembler
}
bool extraNewLine = false;
foreach (ii, ref instruction; instructions)
foreach (uint ii, ref instruction; instructions)
{
if (extraNewLine)
sb.newLine();
@@ -1250,7 +1299,7 @@ final class Disassembler
auto argTypes = opcodeInfo[instruction.opcode].argumentTypes;
if (argTypes.length)
{
for (int i=opcodeInfo[instruction.opcode].name.length; i<20; i++)
foreach (i; opcodeInfo[instruction.opcode].name.length..20)
sb ~= ' ';
foreach (i, type; argTypes)
{
@@ -1288,10 +1337,16 @@ final class Disassembler
dumpMultiname(sb, instruction.arguments[i].multinamev);
break;
case OpcodeArgumentType.Class:
dumpString(sb, refs.objects.getName(instruction.arguments[i].classv));
if (instruction.arguments[i].classv is null)
sb ~= "null";
else
dumpString(sb, refs.objects.getName(instruction.arguments[i].classv));
break;
case OpcodeArgumentType.Method:
dumpString(sb, refs.objects.getName(instruction.arguments[i].methodv));
if (instruction.arguments[i].methodv is null)
sb ~= "null";
else
dumpString(sb, refs.objects.getName(instruction.arguments[i].methodv));
break;
case OpcodeArgumentType.JumpTarget:
@@ -1317,10 +1372,12 @@ final class Disassembler
}
sb.newLine();
}
checkLabel(instructions.length);
checkLabel(to!uint(instructions.length));
}
}
private:
bool[256] newLineAfter;
static this()
@@ -1357,7 +1414,24 @@ static this()
Opcode.OP_setproperty,
Opcode.OP_setpropertylate,
Opcode.OP_setslot,
Opcode.OP_setsuper
Opcode.OP_setsuper,
Opcode.OP_si8,
Opcode.OP_si16,
Opcode.OP_si32,
Opcode.OP_sf32,
Opcode.OP_sf64,
])
newLineAfter[o] = true;
}
/// Force a raw data join (workaround for issue 6064)
T[] arrayJoin(T)(T[][] arrays, T[] sep)
{
return cast(T[])join(cast(ubyte[][])arrays, cast(ubyte[])sep);
}
/// ditto
T[] arrayJoin(T)(T[][] arrays)
{
return cast(T[])join(cast(ubyte[][])arrays);
}
+4 -2
View File
@@ -16,6 +16,8 @@
module murmurhash2a;
import std.conv;
struct MurmurHash2A
{
private static string mmix(string h, string k) { return "{ "~k~" *= m; "~k~" ^= "~k~" >> r; "~k~" *= m; "~h~" *= m; "~h~" ^= "~k~"; }"; }
@@ -67,8 +69,8 @@ public:
void Add(ref ubyte v) { Add(&v, v.sizeof); }
void Add(ref int v) { Add(&v, v.sizeof); }
void Add(ref uint v) { Add(&v, v.sizeof); }
void Add(string s) { Add(s.ptr, s.length); }
void Add(ubyte[] s) { Add(s.ptr, s.length); }
void Add(string s) { Add(s.ptr, to!uint(s.length)); }
void Add(ubyte[] s) { Add(s.ptr, to!uint(s.length)); }
private:
+3 -2
View File
@@ -19,6 +19,7 @@
module swffile;
import std.zlib;
import std.conv;
import zlibx;
/**
@@ -243,13 +244,13 @@ private final class SWFWriter
{
u |= 0x3F;
buf ~= toArray(u);
uint l = tag.data.length;
uint l = to!uint(tag.data.length);
buf ~= toArray(l);
}
buf ~= tag.data;
}
swf.header.fileLength = 8 + buf.length;
swf.header.fileLength = to!uint(8 + buf.length);
if (swf.header.signature[0] == 'C')
buf = cast(ubyte[])compress(buf, 9);
buf = toArray(swf.header) ~ buf;
+3 -3
View File
@@ -2,7 +2,7 @@
module zlibx;
import std.zlib, etc.c.zlib;
import std.zlib, etc.c.zlib, std.conv;
static import etc.c.zlib;
alias std.zlib.Z_SYNC_FLUSH Z_SYNC_FLUSH;
@@ -15,10 +15,10 @@ ubyte[] exactUncompress(ubyte[] srcbuf, size_t destlen)
uint err;
zs.next_in = srcbuf.ptr;
zs.avail_in = srcbuf.length;
zs.avail_in = to!uint(srcbuf.length);
zs.next_out = destbuf.ptr;
zs.avail_out = destbuf.length;
zs.avail_out = to!uint(destbuf.length);
err = etc.c.zlib.inflateInit2(&zs, 15);
if (err)