Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68f6fa241c | ||
|
|
77b5366ada | ||
|
|
59a0b2c167 | ||
|
|
9bd1c698b5 | ||
|
|
e01cff3f24 | ||
|
|
988b17a401 | ||
|
|
3ed3fb1a27 | ||
|
|
1e7cbaca9b | ||
|
|
9b6dfe13b3 | ||
|
|
dc01be0604 | ||
|
|
5e7b2ea4e7 | ||
|
|
8ab69fc6b7 | ||
|
|
576294f949 | ||
|
|
21650d2056 | ||
|
|
0983145a0d | ||
|
|
62c8fce63b | ||
|
|
7097774834 | ||
|
|
fe93f5e764 | ||
|
|
0fbfba1186 | ||
|
|
6bdf6dae39 | ||
|
|
62ef183d95 | ||
|
|
f6eb080be7 | ||
|
|
a17a03e6f6 | ||
|
|
0486c1a54a | ||
|
|
a2d22e7606 | ||
|
|
6568793aaf | ||
|
|
b0bdb250c5 | ||
|
|
99e49a9385 | ||
|
|
6313739b5e | ||
|
|
8ed3295199 | ||
|
|
31ad8dcef7 | ||
|
|
ef8e86388a | ||
|
|
d4f7a070b6 | ||
|
|
e5e01ea90f | ||
|
|
fbebd86f47 | ||
|
|
e9fbbef9b7 | ||
|
|
9c28d20e99 | ||
|
|
3449822efd | ||
|
|
3ded602166 | ||
|
|
08008d5643 | ||
|
|
98e9198c36 | ||
|
|
f6dbbf08ee | ||
|
|
4251e409a2 | ||
|
|
7ad6949b45 | ||
|
|
9bd472e4f8 | ||
|
|
efada3d5c1 | ||
|
|
05d9624f68 | ||
|
|
a2ea734202 | ||
|
|
32909f3893 | ||
|
|
472a9cec57 | ||
|
|
6cbb185114 | ||
|
|
f32f6fb128 | ||
|
|
d6d7ecf47e | ||
|
|
0cefc587e6 | ||
|
|
a45b4e2bdc | ||
|
|
e383172765 | ||
|
|
c62b5438d7 | ||
|
|
a1a214ae72 | ||
|
|
222fa1109c | ||
|
|
9fa2d122fa | ||
|
|
93adc953c2 | ||
|
|
dc222f9e43 | ||
|
|
fb2f96b81a | ||
|
|
14f5e669ee |
+17
@@ -0,0 +1,17 @@
|
||||
*.exe
|
||||
*.dll
|
||||
*.pdb
|
||||
*.ilk
|
||||
*.exp
|
||||
*.lib
|
||||
*.suo
|
||||
|
||||
/rabcasm
|
||||
/rabcdasm
|
||||
/abcexport
|
||||
/abcreplace
|
||||
/swfbinexport
|
||||
/swfbinreplace
|
||||
/swfdecompress
|
||||
/swf7zcompress
|
||||
/swflzmacompress
|
||||
@@ -1,6 +1,56 @@
|
||||
RABCDAsm Changelog
|
||||
==================
|
||||
|
||||
RABCDAsm v1.18 (2016.01.16)
|
||||
---------------------------
|
||||
|
||||
* Fix disassembly of `pushbyte` instructions (the AVM specification
|
||||
incorrectly lists the argument as unsigned).
|
||||
* Bump `#version` directive of new disassemblies to 4:
|
||||
- Versions below 4 treat `pushbyte` as unsigned, and throw an exception if
|
||||
the argument is outside the range `0` ... `255`.
|
||||
- Versions 4 and above treat `pushbyte` as signed, and throw an exception
|
||||
if the argument is outside the range `-128` ... `127`.
|
||||
* Fix buffer reuse bugs when using macros
|
||||
* Dump floating-point numbers in hex notation if necessary to ensure precision
|
||||
* Cease emitting a number in a comment after opening `script` tags
|
||||
* Detect a known DMD bug in `build_rabcdasm`
|
||||
* Ignore invalid file size in header
|
||||
|
||||
RABCDAsm v1.17 (2014.09.10)
|
||||
---------------------------
|
||||
|
||||
* Do not attempt to disassemble unreachable code
|
||||
* Improve handling of disassembly errors:
|
||||
methods will be partially disassembled as far as possible.
|
||||
* Fix LZMA errors with uncompressable data.
|
||||
|
||||
RABCDAsm v1.16 (2014.04.21)
|
||||
---------------------------
|
||||
|
||||
* Fix handling of TypeName-kind Multinames with null parameters
|
||||
* Fix v1.15 regression in handling very long paths on Windows
|
||||
(DMD 2.066 is required when building from source for this to work)
|
||||
|
||||
RABCDAsm v1.15 (2014.01.11)
|
||||
---------------------------
|
||||
|
||||
* Fix building on systems with a noexec `/tmp/`
|
||||
* Improve compatibility with 3rd-party players
|
||||
* Don't emit forward references in TypeName-kind Multinames
|
||||
* Improve performance and memory usage
|
||||
|
||||
RABCDAsm v1.14 (2013.08.21)
|
||||
---------------------------
|
||||
|
||||
* Improved refid generation
|
||||
* Worked around liblzma dictionary size limitation, which prevented
|
||||
decompression of some LZMA-compressed files
|
||||
* Added an option to `swflzmacompress` to update the SWF version number
|
||||
* Added `finddef` instruction
|
||||
* Added unimplemented `{get|set|delete}propertylate` instructions
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.13 (2012.09.29)
|
||||
---------------------------
|
||||
|
||||
|
||||
@@ -69,24 +69,28 @@ installed, compiling should be as straight-forward as:
|
||||
|
||||
Substitute `dmd` with `gdmd` if you're using gdc. You can use the `DC` and
|
||||
`DCFLAGS` environment variables to override the detected compiler and default
|
||||
compilation flags (`-w -O -inline`).
|
||||
compilation flags (`-O -inline`).
|
||||
|
||||
To be able to manipulate SWF files packed with LZMA compression, you'll need
|
||||
to have the liblzma library and development files installed on your system.
|
||||
to have the [liblzma library][] and development files installed on your system.
|
||||
|
||||
Note: DMD 2.066 is required for long path support on Windows since RABCDAsm
|
||||
1.16.
|
||||
|
||||
[d2]: http://dlang.org/
|
||||
[dmd]: http://www.digitalmars.com/d/download.html
|
||||
[gdc]: http://bitbucket.org/goshawk/gdc/
|
||||
[git]: http://git-scm.com/
|
||||
[liblzma library]: http://tukaani.org/xz/
|
||||
|
||||
Pre-compiled binaries
|
||||
---------------------
|
||||
|
||||
You can find pre-compiled Windows binaries in the [Downloads section on
|
||||
GitHub][downloads]. However, please don't expect them to be up-to-date with
|
||||
the latest source versions.
|
||||
You can find pre-compiled Windows binaries on [my website][downloads].
|
||||
However, please don't expect them to be up-to-date with the latest source
|
||||
versions.
|
||||
|
||||
[downloads]: http://github.com/CyberShadow/RABCDAsm/downloads
|
||||
[downloads]: http://files.thecybershadow.net/RABCDAsm/
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011, 2012, 2013, 2014 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -208,8 +208,7 @@ class ABCFile
|
||||
ExceptionInfo[] exceptions;
|
||||
TraitsInfo[] traits;
|
||||
|
||||
string error;
|
||||
ubyte[] rawBytes;
|
||||
Error[] errors;
|
||||
}
|
||||
|
||||
/// Destination for a jump or exception block boundary
|
||||
@@ -219,18 +218,26 @@ class ABCFile
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint index; /// instruction index
|
||||
int offset; /// signed offset relative to said instruction
|
||||
uint index = uint.max; /// instruction index
|
||||
int offset = int.max; /// signed offset relative to said instruction
|
||||
}
|
||||
private ptrdiff_t absoluteOffset; /// internal temporary value used during reading and writing
|
||||
}
|
||||
}
|
||||
|
||||
/// Disassembly/decoding error
|
||||
struct Error
|
||||
{
|
||||
Label loc;
|
||||
string msg;
|
||||
}
|
||||
|
||||
struct Instruction
|
||||
{
|
||||
Opcode opcode;
|
||||
union Argument
|
||||
{
|
||||
byte bytev;
|
||||
ubyte ubytev;
|
||||
long intv;
|
||||
ulong uintv;
|
||||
@@ -439,6 +446,7 @@ const string[4] TraitAttributeNames = ["FINAL", "OVERRIDE", "METADATA", "0x08"];
|
||||
|
||||
enum Opcode : ubyte
|
||||
{
|
||||
OP_raw = 0x00, /// Used internally by RABCDAsm
|
||||
OP_bkpt = 0x01,
|
||||
OP_nop = 0x02,
|
||||
OP_throw = 0x03,
|
||||
@@ -634,6 +642,7 @@ enum OpcodeArgumentType
|
||||
{
|
||||
Unknown,
|
||||
|
||||
ByteLiteral,
|
||||
UByteLiteral,
|
||||
IntLiteral,
|
||||
UIntLiteral,
|
||||
@@ -659,7 +668,7 @@ struct OpcodeInfo
|
||||
}
|
||||
|
||||
const OpcodeInfo[256] opcodeInfo = [
|
||||
/* 0x00 */ {"0x00", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x00 */ {"db", [OpcodeArgumentType.UByteLiteral]},
|
||||
/* 0x01 */ {"bkpt", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x02 */ {"nop", []},
|
||||
/* 0x03 */ {"throw", []},
|
||||
@@ -695,7 +704,7 @@ const OpcodeInfo[256] opcodeInfo = [
|
||||
/* 0x21 */ {"pushundefined", []},
|
||||
/* 0x22 */ {"pushuninitialized", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x23 */ {"nextvalue", []},
|
||||
/* 0x24 */ {"pushbyte", [OpcodeArgumentType.UByteLiteral]},
|
||||
/* 0x24 */ {"pushbyte", [OpcodeArgumentType.ByteLiteral]},
|
||||
/* 0x25 */ {"pushshort", [OpcodeArgumentType.IntLiteral]},
|
||||
/* 0x26 */ {"pushtrue", []},
|
||||
/* 0x27 */ {"pushfalse", []},
|
||||
@@ -754,7 +763,7 @@ const OpcodeInfo[256] opcodeInfo = [
|
||||
/* 0x5C */ {"0x5C", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x5D */ {"findpropstrict", [OpcodeArgumentType.Multiname]},
|
||||
/* 0x5E */ {"findproperty", [OpcodeArgumentType.Multiname]},
|
||||
/* 0x5F */ {"finddef", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x5F */ {"finddef", [OpcodeArgumentType.Multiname]},
|
||||
/* 0x60 */ {"getlex", [OpcodeArgumentType.Multiname]},
|
||||
/* 0x61 */ {"setproperty", [OpcodeArgumentType.Multiname]},
|
||||
/* 0x62 */ {"getlocal", [OpcodeArgumentType.UIntLiteral]},
|
||||
@@ -762,11 +771,11 @@ const OpcodeInfo[256] opcodeInfo = [
|
||||
/* 0x64 */ {"getglobalscope", []},
|
||||
/* 0x65 */ {"getscopeobject", [OpcodeArgumentType.UByteLiteral]},
|
||||
/* 0x66 */ {"getproperty", [OpcodeArgumentType.Multiname]},
|
||||
/* 0x67 */ {"getpropertylate", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x67 */ {"getpropertylate", []},
|
||||
/* 0x68 */ {"initproperty", [OpcodeArgumentType.Multiname]},
|
||||
/* 0x69 */ {"setpropertylate", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x69 */ {"setpropertylate", []},
|
||||
/* 0x6A */ {"deleteproperty", [OpcodeArgumentType.Multiname]},
|
||||
/* 0x6B */ {"deletepropertylate", [OpcodeArgumentType.Unknown]},
|
||||
/* 0x6B */ {"deletepropertylate", []},
|
||||
/* 0x6C */ {"getslot", [OpcodeArgumentType.UIntLiteral]},
|
||||
/* 0x6D */ {"setslot", [OpcodeArgumentType.UIntLiteral]},
|
||||
/* 0x6E */ {"getglobalslot", [OpcodeArgumentType.UIntLiteral]},
|
||||
@@ -926,6 +935,16 @@ static this()
|
||||
OpcodeByName = OpcodeByName.rehash;
|
||||
}
|
||||
|
||||
bool[256] genLookup(Opcode[] opcodes)
|
||||
{
|
||||
bool[256] result;
|
||||
foreach (op; opcodes)
|
||||
result[op] = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
const bool[256] stopsExecution = genLookup([Opcode.OP_returnvalue, Opcode.OP_returnvoid, Opcode.OP_throw, Opcode.OP_jump, Opcode.OP_lookupswitch]);
|
||||
|
||||
private final class ABCReader
|
||||
{
|
||||
ubyte[] buf;
|
||||
@@ -1291,8 +1310,197 @@ private final class ABCReader
|
||||
r.instructions = null;
|
||||
|
||||
size_t len = readU30();
|
||||
size_t start = pos;
|
||||
size_t end = pos + len;
|
||||
|
||||
pos = end;
|
||||
r.exceptions.length = readU30();
|
||||
foreach (ref value; r.exceptions)
|
||||
value = readExceptionInfo();
|
||||
|
||||
size_t postExceptions = pos;
|
||||
|
||||
enum TraceState : ubyte
|
||||
{
|
||||
unexplored,
|
||||
pending,
|
||||
instruction,
|
||||
instructionBody,
|
||||
error,
|
||||
}
|
||||
|
||||
auto traceState = new TraceState[len];
|
||||
auto instructions = new ABCFile.Instruction[len];
|
||||
|
||||
@property size_t offset() { return pos - start; }
|
||||
|
||||
bool havePending;
|
||||
|
||||
void queue(size_t traceOffset)
|
||||
{
|
||||
if (traceOffset < len && traceState[traceOffset] == TraceState.unexplored)
|
||||
{
|
||||
traceState[traceOffset] = TraceState.pending;
|
||||
havePending = true;
|
||||
}
|
||||
}
|
||||
|
||||
queue(0);
|
||||
|
||||
foreach (ref value; r.exceptions)
|
||||
queue(value.target.absoluteOffset);
|
||||
|
||||
while (havePending)
|
||||
{
|
||||
havePending = false;
|
||||
pos = start;
|
||||
while (pos < end)
|
||||
{
|
||||
if (traceState[offset] == TraceState.pending)
|
||||
{
|
||||
size_t instructionOffset;
|
||||
|
||||
try
|
||||
{
|
||||
while (pos < end)
|
||||
{
|
||||
instructionOffset = offset;
|
||||
|
||||
enforce(traceState[instructionOffset] != TraceState.instructionBody, "Overlapping instruction");
|
||||
if (traceState[instructionOffset] == TraceState.instruction)
|
||||
break; // already decoded
|
||||
|
||||
ABCFile.Instruction instruction;
|
||||
instruction.opcode = cast(Opcode)readU8();
|
||||
enforce(instruction.opcode != Opcode.OP_raw, "Null opcode");
|
||||
instruction.arguments.length = opcodeInfo[instruction.opcode].argumentTypes.length;
|
||||
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
|
||||
final switch (type)
|
||||
{
|
||||
case OpcodeArgumentType.Unknown:
|
||||
throw new Exception("Don't know how to decode OP_" ~ opcodeInfo[instruction.opcode].name);
|
||||
|
||||
case OpcodeArgumentType.ByteLiteral:
|
||||
instruction.arguments[i].bytev = readU8();
|
||||
break;
|
||||
case OpcodeArgumentType.UByteLiteral:
|
||||
instruction.arguments[i].ubytev = readU8();
|
||||
break;
|
||||
case OpcodeArgumentType.IntLiteral:
|
||||
instruction.arguments[i].intv = readS32();
|
||||
break;
|
||||
case OpcodeArgumentType.UIntLiteral:
|
||||
instruction.arguments[i].uintv = readU32();
|
||||
break;
|
||||
|
||||
case OpcodeArgumentType.Int:
|
||||
case OpcodeArgumentType.UInt:
|
||||
case OpcodeArgumentType.Double:
|
||||
case OpcodeArgumentType.String:
|
||||
case OpcodeArgumentType.Namespace:
|
||||
case OpcodeArgumentType.Multiname:
|
||||
case OpcodeArgumentType.Class:
|
||||
case OpcodeArgumentType.Method:
|
||||
{
|
||||
auto index = readU30();
|
||||
size_t length;
|
||||
switch (type)
|
||||
{
|
||||
case OpcodeArgumentType.Int: length = abc.ints .length; break;
|
||||
case OpcodeArgumentType.UInt: length = abc.uints .length; break;
|
||||
case OpcodeArgumentType.Double: length = abc.doubles .length; break;
|
||||
case OpcodeArgumentType.String: length = abc.strings .length; break;
|
||||
case OpcodeArgumentType.Namespace: length = abc.namespaces.length; break;
|
||||
case OpcodeArgumentType.Multiname: length = abc.multinames.length; break;
|
||||
case OpcodeArgumentType.Class: length = abc.classes .length; break;
|
||||
case OpcodeArgumentType.Method: length = abc.methods .length; break;
|
||||
default: assert(false);
|
||||
}
|
||||
enforce(index < length, "Out-of-bounds constant index");
|
||||
instruction.arguments[i].index = index;
|
||||
break;
|
||||
}
|
||||
|
||||
case OpcodeArgumentType.JumpTarget:
|
||||
{
|
||||
auto delta = readS24();
|
||||
auto target = offset + delta;
|
||||
instruction.arguments[i].jumpTarget.absoluteOffset = target;
|
||||
queue(target);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpcodeArgumentType.SwitchDefaultTarget:
|
||||
{
|
||||
auto target = instructionOffset + readS24();
|
||||
instruction.arguments[i].jumpTarget.absoluteOffset = target;
|
||||
queue(target);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpcodeArgumentType.SwitchTargets:
|
||||
instruction.arguments[i].switchTargets.length = readU30()+1;
|
||||
foreach (ref label; instruction.arguments[i].switchTargets)
|
||||
{
|
||||
label.absoluteOffset = instructionOffset + readS24();
|
||||
queue(label.absoluteOffset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
enforce(offset <= len, "Out-of-bounds code read error");
|
||||
|
||||
instructions[instructionOffset] = instruction;
|
||||
traceState[instructionOffset] = TraceState.instruction;
|
||||
traceState[instructionOffset+1..offset] = TraceState.instructionBody;
|
||||
|
||||
if (stopsExecution[instruction.opcode])
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
traceState[instructionOffset] = TraceState.error;
|
||||
ABCFile.Label loc;
|
||||
loc.absoluteOffset = instructionOffset;
|
||||
r.errors ~= ABCFile.Error(loc, e.msg);
|
||||
|
||||
pos = start + instructionOffset + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
size_t[] instructionOffsets;
|
||||
auto instructionAtOffset = new uint[len];
|
||||
r.rawBytes = buf[pos..pos+len];
|
||||
instructionAtOffset[] = uint.max;
|
||||
|
||||
void addInstruction(ref ABCFile.Instruction i, size_t offset)
|
||||
{
|
||||
instructionAtOffset[offset] = to!uint(r.instructions.length);
|
||||
r.instructions ~= i;
|
||||
instructionOffsets ~= offset;
|
||||
}
|
||||
|
||||
foreach (o, state; traceState)
|
||||
{
|
||||
assert(state != TraceState.pending);
|
||||
if (state == TraceState.instruction)
|
||||
addInstruction(instructions[o], o);
|
||||
else
|
||||
if (state == TraceState.unexplored || state == TraceState.error)
|
||||
{
|
||||
ABCFile.Instruction instruction;
|
||||
instruction.opcode = Opcode.OP_raw;
|
||||
instruction.arguments.length = 1;
|
||||
instruction.arguments[0].ubytev = buf[start + o];
|
||||
addInstruction(instruction, o);
|
||||
}
|
||||
else
|
||||
assert(state == TraceState.instructionBody);
|
||||
}
|
||||
|
||||
void translateLabel(ref ABCFile.Label label)
|
||||
{
|
||||
@@ -1322,105 +1530,37 @@ private final class ABCReader
|
||||
label.offset = to!int(absoluteOffset-instructionOffset);
|
||||
}
|
||||
|
||||
size_t start = pos;
|
||||
size_t end = pos + len;
|
||||
// convert jump target offsets to instruction indices
|
||||
foreach (ii, ref instruction; r.instructions)
|
||||
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
|
||||
switch (type)
|
||||
{
|
||||
case OpcodeArgumentType.JumpTarget:
|
||||
case OpcodeArgumentType.SwitchDefaultTarget:
|
||||
translateLabel(instruction.arguments[i].jumpTarget);
|
||||
break;
|
||||
case OpcodeArgumentType.SwitchTargets:
|
||||
foreach (ref x; instruction.arguments[i].switchTargets)
|
||||
translateLabel(x);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@property size_t offset() { return pos - start; }
|
||||
// convert error offsets to instruction indices
|
||||
foreach (ref e; r.errors)
|
||||
translateLabel(e.loc);
|
||||
|
||||
try
|
||||
{
|
||||
instructionAtOffset[] = uint.max;
|
||||
size_t[] instructionOffsets;
|
||||
while (pos < end)
|
||||
{
|
||||
auto instructionOffset = offset;
|
||||
scope(failure) pos = start + instructionOffset;
|
||||
instructionAtOffset[instructionOffset] = to!uint(r.instructions.length);
|
||||
ABCFile.Instruction instruction;
|
||||
instruction.opcode = cast(Opcode)readU8();
|
||||
instruction.arguments.length = opcodeInfo[instruction.opcode].argumentTypes.length;
|
||||
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
|
||||
final switch (type)
|
||||
{
|
||||
case OpcodeArgumentType.Unknown:
|
||||
throw new Exception("Don't know how to decode OP_" ~ opcodeInfo[instruction.opcode].name);
|
||||
|
||||
case OpcodeArgumentType.UByteLiteral:
|
||||
instruction.arguments[i].ubytev = readU8();
|
||||
break;
|
||||
case OpcodeArgumentType.IntLiteral:
|
||||
instruction.arguments[i].intv = readS32();
|
||||
break;
|
||||
case OpcodeArgumentType.UIntLiteral:
|
||||
instruction.arguments[i].uintv = readU32();
|
||||
break;
|
||||
|
||||
case OpcodeArgumentType.Int:
|
||||
case OpcodeArgumentType.UInt:
|
||||
case OpcodeArgumentType.Double:
|
||||
case OpcodeArgumentType.String:
|
||||
case OpcodeArgumentType.Namespace:
|
||||
case OpcodeArgumentType.Multiname:
|
||||
case OpcodeArgumentType.Class:
|
||||
case OpcodeArgumentType.Method:
|
||||
instruction.arguments[i].index = readU30();
|
||||
break;
|
||||
|
||||
case OpcodeArgumentType.JumpTarget:
|
||||
int delta = readS24();
|
||||
instruction.arguments[i].jumpTarget.absoluteOffset = offset + delta;
|
||||
break;
|
||||
|
||||
case OpcodeArgumentType.SwitchDefaultTarget:
|
||||
instruction.arguments[i].jumpTarget.absoluteOffset = instructionOffset + readS24();
|
||||
break;
|
||||
|
||||
case OpcodeArgumentType.SwitchTargets:
|
||||
instruction.arguments[i].switchTargets.length = readU30()+1;
|
||||
foreach (ref label; instruction.arguments[i].switchTargets)
|
||||
label.absoluteOffset = instructionOffset + readS24();
|
||||
break;
|
||||
}
|
||||
r.instructions ~= instruction;
|
||||
instructionOffsets ~= instructionOffset;
|
||||
}
|
||||
|
||||
if (pos > end)
|
||||
throw new Exception("Out-of-bounds code read error");
|
||||
|
||||
// convert jump target offsets to instruction indices
|
||||
foreach (ii, ref instruction; r.instructions)
|
||||
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
|
||||
switch (type)
|
||||
{
|
||||
case OpcodeArgumentType.JumpTarget:
|
||||
case OpcodeArgumentType.SwitchDefaultTarget:
|
||||
translateLabel(instruction.arguments[i].jumpTarget);
|
||||
break;
|
||||
case OpcodeArgumentType.SwitchTargets:
|
||||
foreach (ref x; instruction.arguments[i].switchTargets)
|
||||
translateLabel(x);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
r.instructions = null;
|
||||
r.error = e.msg;
|
||||
instructionAtOffset[] = 0;
|
||||
}
|
||||
pos = end;
|
||||
|
||||
r.exceptions.length = readU30();
|
||||
// convert exception offsets to instruction indices
|
||||
foreach (ref value; r.exceptions)
|
||||
{
|
||||
value = readExceptionInfo();
|
||||
translateLabel(value.from);
|
||||
translateLabel(value.to);
|
||||
translateLabel(value.target);
|
||||
}
|
||||
|
||||
pos = postExceptions;
|
||||
|
||||
r.traits.length = readU30();
|
||||
foreach (ref value; r.traits)
|
||||
value = readTrait();
|
||||
@@ -1798,6 +1938,9 @@ private final class ABCWriter
|
||||
case OpcodeArgumentType.Unknown:
|
||||
throw new Exception("Don't know how to encode OP_" ~ opcodeInfo[instruction.opcode].name);
|
||||
|
||||
case OpcodeArgumentType.ByteLiteral:
|
||||
writeU8(instruction.arguments[i].bytev);
|
||||
break;
|
||||
case OpcodeArgumentType.UByteLiteral:
|
||||
writeU8(instruction.arguments[i].ubytev);
|
||||
break;
|
||||
|
||||
+104
-107
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011, 2012, 2013, 2014 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -157,6 +157,11 @@ final class ASProgram
|
||||
uint id; // file index
|
||||
|
||||
MethodBody vbody;
|
||||
|
||||
override string toString() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
static class Metadata
|
||||
@@ -165,6 +170,7 @@ final class ASProgram
|
||||
string[] keys, values;
|
||||
|
||||
mixin AutoCompare;
|
||||
mixin AutoToString;
|
||||
mixin ProcessAllData;
|
||||
}
|
||||
|
||||
@@ -232,7 +238,7 @@ final class ASProgram
|
||||
|
||||
Instance instance;
|
||||
|
||||
override string toString()
|
||||
override string toString() const
|
||||
{
|
||||
return instance.name.toString();
|
||||
}
|
||||
@@ -255,8 +261,7 @@ final class ASProgram
|
||||
Exception[] exceptions;
|
||||
Trait[] traits;
|
||||
|
||||
string error;
|
||||
ubyte[] rawBytes;
|
||||
ABCFile.Error[] errors;
|
||||
}
|
||||
|
||||
struct Instruction
|
||||
@@ -264,6 +269,7 @@ final class ASProgram
|
||||
Opcode opcode;
|
||||
union Argument
|
||||
{
|
||||
byte bytev;
|
||||
ubyte ubytev;
|
||||
|
||||
long intv;
|
||||
@@ -577,8 +583,7 @@ private final class ABCtoAS
|
||||
e.varName = multinames[exc.varName];
|
||||
}
|
||||
n.traits = convertTraits(vbody.traits);
|
||||
n.error = vbody.error;
|
||||
n.rawBytes = vbody.rawBytes;
|
||||
n.errors = vbody.errors;
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -594,6 +599,9 @@ private final class ABCtoAS
|
||||
case OpcodeArgumentType.Unknown:
|
||||
throw new Exception("Don't know how to convert OP_" ~ opcodeInfo[instruction.opcode].name);
|
||||
|
||||
case OpcodeArgumentType.ByteLiteral:
|
||||
r.arguments[i].bytev = instruction.arguments[i].bytev;
|
||||
break;
|
||||
case OpcodeArgumentType.UByteLiteral:
|
||||
r.arguments[i].ubytev = instruction.arguments[i].ubytev;
|
||||
break;
|
||||
@@ -737,135 +745,85 @@ private final class AStoABC : ASVisitor
|
||||
}
|
||||
|
||||
/// Maintain an unordered set of values; sort/index by usage count
|
||||
struct ConstantPool(T, bool haveNull = true)
|
||||
struct Pool(T, bool byRef, bool haveNull = true)
|
||||
{
|
||||
alias immutable(T) I;
|
||||
static if (byRef)
|
||||
alias void* Key;
|
||||
else
|
||||
static if (is(T == double))
|
||||
alias ulong Key; // https://issues.dlang.org/show_bug.cgi?id=13420
|
||||
else
|
||||
alias immutable(T) Key;
|
||||
|
||||
Key toKey(T value)
|
||||
{
|
||||
static if (is(T == double))
|
||||
return *cast(ulong*)&value;
|
||||
else
|
||||
return cast(Key)value;
|
||||
}
|
||||
|
||||
struct Entry
|
||||
{
|
||||
uint hits;
|
||||
T value;
|
||||
uint index;
|
||||
size_t addIndex, index;
|
||||
Key[] parents;
|
||||
|
||||
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);
|
||||
}
|
||||
mixin AutoToString;
|
||||
mixin ProcessAllData;
|
||||
}
|
||||
|
||||
Entry[immutable(T)] pool;
|
||||
Entry[Key] pool;
|
||||
T[] values;
|
||||
|
||||
bool add(T value) // return true if added
|
||||
{
|
||||
if (haveNull && isNull(value))
|
||||
if ((haveNull || byRef) && isNull(value))
|
||||
return false;
|
||||
auto cp = cast(I)value in pool;
|
||||
if (cp is null)
|
||||
auto p = toKey(value) in pool;
|
||||
if (p is null)
|
||||
{
|
||||
pool[cast(I)value] = Entry(1, value);
|
||||
pool[toKey(value)] = Entry(1, value, pool.length);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cp.hits++;
|
||||
p.hits++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool notAdded(T value)
|
||||
{
|
||||
auto ep = cast(I)value in pool;
|
||||
static if (haveNull || byRef)
|
||||
if (isNull(value))
|
||||
return false;
|
||||
auto ep = toKey(value) in pool;
|
||||
if (ep)
|
||||
ep.hits++;
|
||||
return !((haveNull && isNull(value)) || ep);
|
||||
}
|
||||
|
||||
void finalize()
|
||||
{
|
||||
auto all = pool.values;
|
||||
all.sort;
|
||||
enum { NullOffset = haveNull ? 1 : 0 }
|
||||
values.length = all.length + NullOffset;
|
||||
foreach (uint i, ref c; all)
|
||||
{
|
||||
pool[cast(I)c.value].index = i + NullOffset;
|
||||
values[i + NullOffset] = c.value;
|
||||
}
|
||||
}
|
||||
|
||||
uint get(T value)
|
||||
{
|
||||
if (haveNull && isNull(value))
|
||||
return 0;
|
||||
return pool[cast(I)value].index;
|
||||
}
|
||||
}
|
||||
|
||||
/// Pair an index with class instances
|
||||
struct ReferencePool(T : Object)
|
||||
{
|
||||
struct Entry
|
||||
{
|
||||
uint hits;
|
||||
void* object;
|
||||
uint addIndex, index;
|
||||
void*[] parents;
|
||||
|
||||
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 pool;
|
||||
if (rp is null)
|
||||
{
|
||||
pool[p] = Entry(1, p, to!uint(pool.length));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
rp.hits++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool notAdded(T obj)
|
||||
{
|
||||
auto ep = (cast(void*)obj) in pool;
|
||||
if (ep)
|
||||
ep.hits++;
|
||||
return !(obj is null || ep);
|
||||
return !ep;
|
||||
}
|
||||
|
||||
/// "from" is child of "to", thus "from" must come after "to"
|
||||
void registerDependency(T from, T to)
|
||||
{
|
||||
auto pfrom = (cast(void*)from) in pool;
|
||||
auto pfrom = toKey(from) in pool;
|
||||
assert(pfrom, "Unknown dependency source");
|
||||
auto vto = cast(void*)to;
|
||||
auto vto = toKey(to);
|
||||
auto pto = vto in pool;
|
||||
assert(pto, "Unknown dependency target");
|
||||
assert(!pfrom.parents.contains(vto), "Dependency already set");
|
||||
assert(!pfrom.parents.contains!Key(vto), "Dependency already set");
|
||||
pfrom.parents ~= vto;
|
||||
}
|
||||
|
||||
T[] getPreliminaryObjects()
|
||||
T[] getPreliminaryValues()
|
||||
{
|
||||
return cast(T[])pool.keys;
|
||||
}
|
||||
|
||||
enum { NullOffset = haveNull ? 1 : 0 }
|
||||
|
||||
void finalize()
|
||||
{
|
||||
// create array
|
||||
@@ -875,7 +833,18 @@ private final class AStoABC : ASVisitor
|
||||
all[i++] = &e;
|
||||
|
||||
// sort
|
||||
sort!q{a.hits > b.hits || (a.hits == b.hits && a.addIndex < b.addIndex)}(all);
|
||||
static if (is(T == double))
|
||||
{
|
||||
// Need ref to work around https://issues.dlang.org/show_bug.cgi?id=13998
|
||||
static ulong repr(ref double d) { static assert(double.sizeof == ulong.sizeof); return *cast(ulong*)(&d); }
|
||||
static bool sortPred(Entry* a, Entry* b) { return a.hits > b.hits || (a.hits == b.hits && repr(a.value) < repr(b.value)); }
|
||||
}
|
||||
else
|
||||
static if (byRef)
|
||||
enum sortPred = q{a.hits > b.hits || (a.hits == b.hits && a.addIndex < b.addIndex)};
|
||||
else
|
||||
enum sortPred = q{a.hits > b.hits || (a.hits == b.hits && a.value < b.value)};
|
||||
all.sort!sortPred();
|
||||
|
||||
// topographical sort
|
||||
topSort:
|
||||
@@ -896,18 +865,29 @@ private final class AStoABC : ASVisitor
|
||||
}
|
||||
}
|
||||
|
||||
objects.length = i;
|
||||
values.length = NullOffset + i;
|
||||
foreach (j, e; all)
|
||||
objects[j] = cast(T)e.object;
|
||||
values[NullOffset + j] = e.value;
|
||||
}
|
||||
|
||||
uint get(T obj)
|
||||
uint get(T value)
|
||||
{
|
||||
assert(obj !is null, "Trying to get index of null object");
|
||||
return pool[cast(void*)obj].index;
|
||||
if (haveNull && isNull(value))
|
||||
return 0;
|
||||
return NullOffset + to!uint(pool[toKey(value)].index);
|
||||
}
|
||||
}
|
||||
|
||||
template ConstantPool(T, bool haveNull=true)
|
||||
{
|
||||
alias Pool!(T, false, haveNull) ConstantPool;
|
||||
}
|
||||
|
||||
template ReferencePool(T : Object)
|
||||
{
|
||||
alias Pool!(T, true, false) ReferencePool;
|
||||
}
|
||||
|
||||
ConstantPool!(long) ints;
|
||||
ConstantPool!(ulong) uints;
|
||||
ConstantPool!(double) doubles;
|
||||
@@ -998,7 +978,7 @@ private final class AStoABC : ASVisitor
|
||||
{
|
||||
ASProgram.Class[ASProgram.Multiname] classByName;
|
||||
|
||||
ASProgram.Class[] classObjects = classes.getPreliminaryObjects();
|
||||
ASProgram.Class[] classObjects = classes.getPreliminaryValues();
|
||||
foreach (c; classObjects)
|
||||
{
|
||||
assert(!(c.instance.name in classByName), "Duplicate class name " ~ c.instance.name.toString());
|
||||
@@ -1016,6 +996,18 @@ private final class AStoABC : ASVisitor
|
||||
}
|
||||
}
|
||||
|
||||
void registerMultinameDependencies()
|
||||
{
|
||||
foreach (m; multinames.getPreliminaryValues())
|
||||
if (m.kind == ASType.TypeName)
|
||||
{
|
||||
multinames.registerDependency(m, m.vTypeName.name);
|
||||
foreach (t; m.vTypeName.params)
|
||||
if (t)
|
||||
multinames.registerDependency(m, t);
|
||||
}
|
||||
}
|
||||
|
||||
this(ASProgram as)
|
||||
{
|
||||
super(as);
|
||||
@@ -1027,6 +1019,7 @@ private final class AStoABC : ASVisitor
|
||||
super.run();
|
||||
|
||||
registerClassDependencies();
|
||||
registerMultinameDependencies();
|
||||
|
||||
ints.finalize();
|
||||
uints.finalize();
|
||||
@@ -1115,8 +1108,8 @@ private final class AStoABC : ASVisitor
|
||||
|
||||
ASProgram.MethodBody[] bodies;
|
||||
|
||||
abc.methods.length = methods.objects.length;
|
||||
foreach (i, o; methods.objects)
|
||||
abc.methods.length = methods.values.length;
|
||||
foreach (i, o; methods.values)
|
||||
{
|
||||
auto n = &abc.methods[i];
|
||||
n.paramTypes.length = o.paramTypes.length;
|
||||
@@ -1139,8 +1132,8 @@ private final class AStoABC : ASVisitor
|
||||
bodies ~= o.vbody;
|
||||
}
|
||||
|
||||
abc.instances.length = classes.objects.length;
|
||||
foreach (i, c; classes.objects)
|
||||
abc.instances.length = classes.values.length;
|
||||
foreach (i, c; classes.values)
|
||||
{
|
||||
auto o = c.instance;
|
||||
auto n = &abc.instances[i];
|
||||
@@ -1156,8 +1149,8 @@ private final class AStoABC : ASVisitor
|
||||
n.traits = convertTraits(o.traits);
|
||||
}
|
||||
|
||||
abc.classes.length = classes.objects.length;
|
||||
foreach (i, o; classes.objects)
|
||||
abc.classes.length = classes.values.length;
|
||||
foreach (i, o; classes.values)
|
||||
{
|
||||
auto n = &abc.classes[i];
|
||||
n.cinit = methods.get(o.cinit);
|
||||
@@ -1251,6 +1244,9 @@ private final class AStoABC : ASVisitor
|
||||
case OpcodeArgumentType.Unknown:
|
||||
throw new Exception("Don't know how to convert OP_" ~ opcodeInfo[instruction.opcode].name);
|
||||
|
||||
case OpcodeArgumentType.ByteLiteral:
|
||||
r.arguments[i].bytev = instruction.arguments[i].bytev;
|
||||
break;
|
||||
case OpcodeArgumentType.UByteLiteral:
|
||||
r.arguments[i].ubytev = instruction.arguments[i].ubytev;
|
||||
break;
|
||||
@@ -1536,6 +1532,7 @@ class ASVisitor : ASTraitsVisitor
|
||||
case OpcodeArgumentType.Unknown:
|
||||
throw new Exception("Don't know how to visit OP_" ~ opcodeInfo[instruction.opcode].name);
|
||||
|
||||
case OpcodeArgumentType.ByteLiteral:
|
||||
case OpcodeArgumentType.UByteLiteral:
|
||||
case OpcodeArgumentType.IntLiteral:
|
||||
case OpcodeArgumentType.UIntLiteral:
|
||||
|
||||
+452
-222
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011, 2012, 2014 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -222,8 +222,8 @@ struct ToStringDataHandler
|
||||
_AutoDataResult ~= to!string(" ~ name ~ ");
|
||||
";
|
||||
*/
|
||||
static if (is(typeof(T.init.toString())))
|
||||
enum getMixinSingle = "_AutoDataResult ~= " ~ name ~ ".toString();";
|
||||
static if (is(typeof(T.init is null)))
|
||||
enum getMixinSingle = "_AutoDataResult ~= " ~ name ~ " ? to!string(" ~ name ~ ") : `null`;";
|
||||
else
|
||||
enum getMixinSingle = "_AutoDataResult ~= to!string(" ~ name ~ ");";
|
||||
}
|
||||
|
||||
+34
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011, 2012, 2013 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
/// A simple tool to build RABCDAsm in one command.
|
||||
/// You can use the DC and DCFLAGS environment variables to override the detected compiler and compilation flags.
|
||||
/// You can also pass program names or compilation options on the command-line to override the default ones.
|
||||
|
||||
module build_rabcdasm;
|
||||
|
||||
@@ -52,17 +53,30 @@ void compile(string program)
|
||||
|
||||
void test(string code, string extraFlags=null)
|
||||
{
|
||||
const FN = "test.d";
|
||||
const BASE = "build_rabcdasm_buildtest";
|
||||
const FN = BASE ~ ".d";
|
||||
std.file.write(FN, code);
|
||||
scope(exit) remove(FN);
|
||||
enforce(system(format("rdmd --force --compiler=%s %s %s %s", compiler, flags, extraFlags, FN)) == 0, "Test failed");
|
||||
scope(exit) foreach (de; dirEntries(".", BASE ~ "*", SpanMode.shallow)) remove(de.name);
|
||||
enforce(system(format("rdmd --force --compiler=%s -od. %s %s %s", compiler, flags, extraFlags, FN)) == 0, "Test failed");
|
||||
stderr.writeln(" >>> OK");
|
||||
}
|
||||
|
||||
int main()
|
||||
void testBug(string description, int bugId, string code)
|
||||
{
|
||||
scope(failure)
|
||||
{
|
||||
stderr.writefln("Compiler bug detected: %s ( https://issues.dlang.org/show_bug.cgi?id=%d ).", description, bugId);
|
||||
stderr.writeln("Try again with a different D compiler, compiler version, or build flags (DCFLAGS environment variable)");
|
||||
}
|
||||
test(code);
|
||||
}
|
||||
|
||||
int main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto programs = ["rabcasm", "rabcdasm", "abcexport", "abcreplace", "swfbinexport", "swfbinreplace", "swfdecompress", "swf7zcompress"];
|
||||
|
||||
compiler = getenv("DC");
|
||||
if (compiler is null)
|
||||
compiler = DEFAULT_COMPILER;
|
||||
@@ -71,11 +85,25 @@ int main()
|
||||
if (flags is null)
|
||||
flags = DEFAULT_FLAGS;
|
||||
|
||||
string[] optionArgs, programArgs;
|
||||
foreach (arg; args[1..$])
|
||||
(arg.startsWith("-") ? optionArgs : programArgs) ~= arg;
|
||||
|
||||
if (optionArgs.length)
|
||||
flags = optionArgs.join(" ");
|
||||
if (programArgs.length)
|
||||
programs = programArgs;
|
||||
|
||||
stderr.writeln("* Checking for working compiler...");
|
||||
test(`
|
||||
void main() {}
|
||||
`);
|
||||
|
||||
stderr.writeln("* Checking for known compiler bugs...");
|
||||
testBug("[REG 2.064] Wrong code with -O on x86_64 for char comparisons", 11508, `
|
||||
import assembler; int main() { foreach (c; "_") if (!Assembler.isWordChar(c)) return 1; return 0; }
|
||||
`);
|
||||
|
||||
bool haveLZMA;
|
||||
|
||||
stderr.writeln("* Checking for LZMA...");
|
||||
@@ -103,7 +131,7 @@ int main()
|
||||
if (haveLZMA)
|
||||
flags ~= " " ~ LZMA_FLAGS;
|
||||
|
||||
foreach (program; ["rabcasm", "rabcdasm", "abcexport", "abcreplace", "swfbinexport", "swfbinreplace", "swfdecompress", "swf7zcompress"])
|
||||
foreach (program; programs)
|
||||
compile(program);
|
||||
|
||||
if (haveLZMA)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2012, 2014 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -18,9 +18,10 @@
|
||||
|
||||
module common;
|
||||
|
||||
import std.path;
|
||||
import std.string;
|
||||
import std.array;
|
||||
import std.path;
|
||||
import std.stdio;
|
||||
import std.string;
|
||||
|
||||
string longPath(string s)
|
||||
{
|
||||
@@ -34,3 +35,56 @@ string longPath(string s)
|
||||
else
|
||||
return s;
|
||||
}
|
||||
|
||||
File openFile(string fn, string mode)
|
||||
{
|
||||
File f;
|
||||
static if (is(typeof(&f.windowsHandleOpen)))
|
||||
{
|
||||
import core.sys.windows.windows;
|
||||
|
||||
import std.exception;
|
||||
import std.utf;
|
||||
import std.windows.syserror;
|
||||
|
||||
string winMode;
|
||||
foreach (c; mode)
|
||||
switch (c)
|
||||
{
|
||||
case 'r':
|
||||
case 'w':
|
||||
case 'a':
|
||||
case '+':
|
||||
winMode ~= c;
|
||||
break;
|
||||
case 'b':
|
||||
case 't':
|
||||
break;
|
||||
default:
|
||||
assert(false, "Unknown character in mode");
|
||||
}
|
||||
DWORD access, creation;
|
||||
bool append;
|
||||
switch (winMode)
|
||||
{
|
||||
case "r" : access = GENERIC_READ ; creation = OPEN_EXISTING; break;
|
||||
case "r+": access = GENERIC_READ | GENERIC_WRITE; creation = OPEN_EXISTING; break;
|
||||
case "w" : access = GENERIC_WRITE; creation = OPEN_ALWAYS ; break;
|
||||
case "w+": access = GENERIC_READ | GENERIC_WRITE; creation = OPEN_ALWAYS ; break;
|
||||
case "a" : access = GENERIC_WRITE; creation = OPEN_ALWAYS ; append = true; break;
|
||||
case "a+": assert(false, "Not implemented"); // requires two file pointers
|
||||
default: assert(false, "Bad file mode: " ~ mode);
|
||||
}
|
||||
|
||||
auto pathW = toUTF16z(longPath(fn));
|
||||
auto h = CreateFileW(pathW, access, FILE_SHARE_READ, null, creation, 0, HANDLE.init);
|
||||
enforce(h != INVALID_HANDLE_VALUE, "Failed to open file \"" ~ fn ~ "\": " ~ sysErrorString(GetLastError()));
|
||||
|
||||
assert(!append, "'a' mode not implemented");
|
||||
|
||||
f.windowsHandleOpen(h, mode);
|
||||
}
|
||||
else
|
||||
f.open(fn, mode);
|
||||
return f;
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ struct lzma_index_iter
|
||||
size_t s;
|
||||
lzma_vli v;
|
||||
}
|
||||
InternalData internal[6];
|
||||
InternalData[6] internal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+271
-139
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011, 2012, 2013, 2014, 2015 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -18,14 +18,16 @@
|
||||
|
||||
module disassembler;
|
||||
|
||||
import std.file;
|
||||
import std.string;
|
||||
import std.algorithm;
|
||||
import std.array;
|
||||
import std.conv;
|
||||
import std.digest.md;
|
||||
import std.exception;
|
||||
import std.algorithm;
|
||||
import std.file;
|
||||
import std.format;
|
||||
import std.path;
|
||||
import std.md5;
|
||||
import std.stdio;
|
||||
import std.string;
|
||||
import abcfile;
|
||||
import asprogram;
|
||||
import autodata;
|
||||
@@ -35,37 +37,20 @@ alias std.array.join join;
|
||||
|
||||
final class StringBuilder
|
||||
{
|
||||
char[] buf;
|
||||
size_t pos;
|
||||
enum BUF_SIZE = 256*1024;
|
||||
|
||||
static char[] buf;
|
||||
static size_t pos;
|
||||
|
||||
string filename;
|
||||
File file;
|
||||
|
||||
this(string filename)
|
||||
{
|
||||
this.filename = filename;
|
||||
if (exists(longPath(filename)))
|
||||
throw new Exception(filename ~ " exists");
|
||||
this.filename = filename;
|
||||
buf.length = 1024;
|
||||
}
|
||||
|
||||
void opCatAssign(string s)
|
||||
{
|
||||
checkIndent();
|
||||
auto end = pos + s.length;
|
||||
while (buf.length < end)
|
||||
buf.length = buf.length*2;
|
||||
buf[pos..end] = s;
|
||||
pos = end;
|
||||
}
|
||||
|
||||
void opCatAssign(char c)
|
||||
{
|
||||
if (buf.length < pos+1) // speed hack: no loop, no indent check
|
||||
buf.length = buf.length*2;
|
||||
buf[pos++] = c;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
string[] dirSegments = split(filename, "/");
|
||||
for (int l=0; l<dirSegments.length-1; l++)
|
||||
{
|
||||
@@ -73,7 +58,58 @@ final class StringBuilder
|
||||
if (subdir.length && !exists(longPath(subdir)))
|
||||
mkdir(longPath(subdir));
|
||||
}
|
||||
write(longPath(filename), buf[0..pos]);
|
||||
file = openFile(filename, "wb");
|
||||
assert(!pos, "Opening new file with unflushed buffer");
|
||||
}
|
||||
|
||||
static this()
|
||||
{
|
||||
buf = new char[BUF_SIZE];
|
||||
}
|
||||
|
||||
void put(in char[] s)
|
||||
{
|
||||
checkIndent();
|
||||
auto end = pos + s.length;
|
||||
if (end > buf.length)
|
||||
{
|
||||
flush();
|
||||
end = s.length;
|
||||
while (end > buf.length)
|
||||
buf.length = buf.length*2;
|
||||
}
|
||||
buf[pos..end] = s[];
|
||||
pos = end;
|
||||
}
|
||||
|
||||
void put(char c)
|
||||
{
|
||||
if (pos == buf.length) // speed hack: no indent check
|
||||
flush();
|
||||
buf[pos++] = c;
|
||||
}
|
||||
|
||||
alias put opCatAssign;
|
||||
|
||||
void write(T)(T v)
|
||||
{
|
||||
checkIndent();
|
||||
formattedWrite(this, "%s", v);
|
||||
}
|
||||
|
||||
void flush()
|
||||
{
|
||||
if (pos)
|
||||
{
|
||||
file.rawWrite(buf[0..pos]);
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
flush();
|
||||
file.close();
|
||||
}
|
||||
|
||||
int indent;
|
||||
@@ -202,7 +238,7 @@ final class RefBuilder : ASTraitsVisitor
|
||||
assert(0);
|
||||
}
|
||||
case Type.String:
|
||||
return [Segment(filenameSuffix ? '.' : '/', str)];
|
||||
return [Segment((filename && filenameSuffix) ? '.' : '/', str)];
|
||||
case Type.Group:
|
||||
{
|
||||
Segment[] segments;
|
||||
@@ -257,18 +293,32 @@ final class RefBuilder : ASTraitsVisitor
|
||||
final switch (type)
|
||||
{
|
||||
case Type.Multiname:
|
||||
auto ns = multiname.vQName.ns;
|
||||
if (ns.kind == ASType.PrivateNamespace)
|
||||
switch (multiname.kind)
|
||||
{
|
||||
// auto pcontext = ns.id in refs.namespaces[ns.kind].contexts;
|
||||
// if (pcontext is null)
|
||||
// return (&this)[0..1];
|
||||
// assert(pcontext);
|
||||
auto context = refs.namespaces[ns.kind].getContext(refs, ns.id);
|
||||
debug(CONTEXTS) std.stdio.writefln("Context of namespace %s is:\n\t%s\n", ns, context);
|
||||
// auto expanded = expand(refs, context);
|
||||
// if (expanded is null) return null;
|
||||
return /*expanded*/context ~ (multiname.vQName.name.length ? [ContextItem(multiname.vQName.name)] : null); // hack
|
||||
case ASType.QName:
|
||||
{
|
||||
auto ns = multiname.vQName.ns;
|
||||
if (ns.kind == ASType.PrivateNamespace)
|
||||
{
|
||||
// auto pcontext = ns.id in refs.namespaces[ns.kind].contexts;
|
||||
// if (pcontext is null)
|
||||
// return (&this)[0..1];
|
||||
// assert(pcontext);
|
||||
auto context = refs.namespaces[ns.kind].getContext(refs, ns.id);
|
||||
debug(CONTEXTS) std.stdio.writefln("Context of namespace %s is:\n\t%s\n", ns, context);
|
||||
// auto expanded = expand(refs, context);
|
||||
// if (expanded is null) return null;
|
||||
return /*expanded*/context ~ (multiname.vQName.name.length ? [ContextItem(multiname.vQName.name)] : null); // hack
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ASType.Multiname:
|
||||
return multiname.vMultiname.name.length ? [ContextItem(multiname.vMultiname.name)] : null;
|
||||
default:
|
||||
debug
|
||||
assert(false, text(multiname.kind));
|
||||
else
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Type.String:
|
||||
@@ -332,6 +382,7 @@ final class RefBuilder : ASTraitsVisitor
|
||||
case ContextItem.Type.String:
|
||||
return i1.str == i2.str;
|
||||
case ContextItem.Type.Multiname:
|
||||
assert(i1.multiname.kind == ASType.QName && i2.multiname.kind == ASType.QName);
|
||||
if (i1.multiname.vQName.name != i2.multiname.vQName.name) return false;
|
||||
return nsSimilar(i1.multiname.vQName.ns, i2.multiname.vQName.ns);
|
||||
case ContextItem.Type.Group:
|
||||
@@ -425,16 +476,24 @@ final class RefBuilder : ASTraitsVisitor
|
||||
void pushContext(T...)(T v) { context ~= ContextItem(v); }
|
||||
void popContext() { context = context[0..$-1]; }
|
||||
|
||||
enum ContextPriority
|
||||
{
|
||||
declaration,
|
||||
usage,
|
||||
orphan,
|
||||
max
|
||||
}
|
||||
|
||||
struct ContextSet(T, bool ALLOW_DUPLICATES)
|
||||
{
|
||||
ContextItem[][T] contexts;
|
||||
ContextItem[][][T] contextSets;
|
||||
ContextItem[][][ContextPriority.max][T] contextSets;
|
||||
debug bool contextsSealed;
|
||||
|
||||
string[T] names, filenames;
|
||||
debug bool coagulated;
|
||||
|
||||
bool add(U)(U obj, ContextItem[] context)
|
||||
bool add(U)(U obj, ContextItem[] context, ContextPriority priority)
|
||||
{
|
||||
debug assert(!coagulated);
|
||||
debug assert(!contextsSealed);
|
||||
@@ -444,23 +503,25 @@ final class RefBuilder : ASTraitsVisitor
|
||||
|
||||
if (!pset)
|
||||
{
|
||||
contextSets[p] ~= context.dup;
|
||||
contextSets[p] = contextSets[p].init;
|
||||
contextSets[p][priority] ~= context.dup;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((*pset)[$-1] != context)
|
||||
*pset ~= context.dup;
|
||||
static bool rawEqual(T)(T[] arr1, T[] arr2) { return cast(ubyte[])arr1 == cast(ubyte[])arr2; }
|
||||
if ((*pset)[priority].length==0 || !rawEqual((*pset)[priority][$-1], context)) // Optimization: don't add contexts identical to the last added
|
||||
(*pset)[priority] ~= context.dup;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool addIfNew(U)(U obj, ContextItem[] context)
|
||||
bool addIfNew(U)(U obj, ContextItem[] context, ContextPriority priority)
|
||||
{
|
||||
if (isAdded(obj))
|
||||
return false;
|
||||
else
|
||||
return add(obj, context);
|
||||
return add(obj, context, priority);
|
||||
}
|
||||
|
||||
void coagulate(RefBuilder refs)
|
||||
@@ -474,7 +535,7 @@ final class RefBuilder : ASTraitsVisitor
|
||||
if (obj !in contexts)
|
||||
getContext(refs, obj);
|
||||
|
||||
foreach (obj; contexts.keys.sort)
|
||||
foreach (obj; contexts.keys.sort())
|
||||
{
|
||||
auto context = contexts[obj];
|
||||
auto bname = refs.contextToString(context, false);
|
||||
@@ -513,7 +574,10 @@ final class RefBuilder : ASTraitsVisitor
|
||||
if (pcontext)
|
||||
return *pcontext;
|
||||
|
||||
auto set = contextSets[p];
|
||||
ContextItem[][] set;
|
||||
foreach (prioritySet; contextSets[p])
|
||||
if (prioritySet)
|
||||
set = prioritySet;
|
||||
|
||||
static if (ALLOW_DUPLICATES)
|
||||
{
|
||||
@@ -597,31 +661,33 @@ final class RefBuilder : ASTraitsVisitor
|
||||
ContextItem[] classContexts;
|
||||
|
||||
foreach (trait; v.traits)
|
||||
if (trait.name.vQName.ns.kind != ASType.PrivateNamespace)
|
||||
{
|
||||
if (trait.name.kind == ASType.QName && trait.name.vQName.ns.kind != ASType.PrivateNamespace)
|
||||
classContexts ~= ContextItem(trait.name);
|
||||
}
|
||||
|
||||
if (!classContexts.length)
|
||||
foreach (trait; v.traits)
|
||||
classContexts ~= ContextItem(trait.name);
|
||||
|
||||
context = [ContextItem(classContexts, "script_" ~ to!string(i))];
|
||||
scripts.add(v, context);
|
||||
pushContext("sinit", true);
|
||||
addMethod(v.sinit);
|
||||
scripts.add(v, context, ContextPriority.declaration);
|
||||
pushContext("init", true);
|
||||
addMethod(v.sinit, ContextPriority.declaration);
|
||||
context = null;
|
||||
}
|
||||
foreach (i, vclass; as.orphanClasses)
|
||||
if (!objects.isAdded(vclass))
|
||||
{
|
||||
pushContext("orphan_class_" ~ to!string(i));
|
||||
addClass(vclass);
|
||||
addClass(vclass, ContextPriority.orphan);
|
||||
popContext();
|
||||
}
|
||||
foreach (i, method; as.orphanMethods)
|
||||
if (!objects.isAdded(method))
|
||||
{
|
||||
pushContext("orphan_method_" ~ to!string(i));
|
||||
addMethod(method);
|
||||
addMethod(method, ContextPriority.orphan);
|
||||
popContext();
|
||||
}
|
||||
|
||||
@@ -631,13 +697,13 @@ final class RefBuilder : ASTraitsVisitor
|
||||
foreach (v; as.scripts)
|
||||
foreach (trait; v.traits)
|
||||
if (trait.name.kind == ASType.QName)
|
||||
namespaces[trait.name.vQName.ns.kind].addIfNew(trait.name.vQName.ns.id, scripts.getContext(this, v));
|
||||
namespaces[trait.name.vQName.ns.kind].addIfNew(trait.name.vQName.ns.id, scripts.getContext(this, v), ContextPriority.declaration);
|
||||
|
||||
foreach (id, b; possibleOrphanPrivateNamespaces)
|
||||
if (!namespaces[ASType.PrivateNamespace].isAdded(id))
|
||||
{
|
||||
pushContext("orphan_namespace_" ~ to!string(id));
|
||||
namespaces[ASType.PrivateNamespace].add(id, context);
|
||||
namespaces[ASType.PrivateNamespace].add(id, context, ContextPriority.orphan);
|
||||
popContext();
|
||||
}
|
||||
|
||||
@@ -651,36 +717,59 @@ final class RefBuilder : ASTraitsVisitor
|
||||
{
|
||||
auto m = trait.name;
|
||||
|
||||
if (m.kind != ASType.QName)
|
||||
throw new Exception("Trait name is not a QName");
|
||||
// if (m.kind != ASType.QName)
|
||||
// throw new Exception("Trait name is not a QName");
|
||||
|
||||
pushContext(m);
|
||||
visitMultiname(m);
|
||||
visitMultiname(m, ContextPriority.declaration);
|
||||
switch (trait.kind)
|
||||
{
|
||||
case TraitKind.Slot:
|
||||
case TraitKind.Const:
|
||||
visitMultiname(trait.vSlot.typeName, ContextPriority.usage);
|
||||
|
||||
super.visitTrait(trait);
|
||||
break;
|
||||
case TraitKind.Class:
|
||||
addClass(trait.vClass.vclass);
|
||||
addClass(trait.vClass.vclass, ContextPriority.declaration);
|
||||
|
||||
pushContext("class", true);
|
||||
visitTraits(trait.vClass.vclass.traits);
|
||||
popContext();
|
||||
|
||||
pushContext("instance", true);
|
||||
visitTraits(trait.vClass.vclass.instance.traits);
|
||||
popContext();
|
||||
|
||||
break;
|
||||
case TraitKind.Function:
|
||||
addMethod(trait.vFunction.vfunction);
|
||||
addMethod(trait.vFunction.vfunction, ContextPriority.declaration);
|
||||
|
||||
super.visitTrait(trait);
|
||||
break;
|
||||
case TraitKind.Method:
|
||||
addMethod(trait.vMethod.vmethod);
|
||||
addMethod(trait.vMethod.vmethod, ContextPriority.declaration);
|
||||
|
||||
super.visitTrait(trait);
|
||||
break;
|
||||
case TraitKind.Getter:
|
||||
pushContext("getter");
|
||||
addMethod(trait.vMethod.vmethod);
|
||||
addMethod(trait.vMethod.vmethod, ContextPriority.declaration);
|
||||
popContext();
|
||||
|
||||
super.visitTrait(trait);
|
||||
break;
|
||||
case TraitKind.Setter:
|
||||
pushContext("setter");
|
||||
addMethod(trait.vMethod.vmethod);
|
||||
addMethod(trait.vMethod.vmethod, ContextPriority.declaration);
|
||||
popContext();
|
||||
|
||||
super.visitTrait(trait);
|
||||
break;
|
||||
default:
|
||||
super.visitTrait(trait);
|
||||
break;
|
||||
}
|
||||
super.visitTrait(trait);
|
||||
popContext();
|
||||
}
|
||||
|
||||
@@ -716,7 +805,7 @@ final class RefBuilder : ASTraitsVisitor
|
||||
|
||||
bool[uint] possibleOrphanPrivateNamespaces;
|
||||
|
||||
void visitNamespace(ASProgram.Namespace ns)
|
||||
void visitNamespace(ASProgram.Namespace ns, ContextPriority priority)
|
||||
{
|
||||
if (ns is null) return;
|
||||
|
||||
@@ -730,7 +819,7 @@ final class RefBuilder : ASTraitsVisitor
|
||||
|
||||
auto myPos = context.length;
|
||||
foreach (i, ref item; context)
|
||||
if (item.type == ContextItem.Type.Multiname && item.multiname.vQName.ns == ns)
|
||||
if (item.type == ContextItem.Type.Multiname && item.multiname.kind == ASType.QName && item.multiname.vQName.ns == ns)
|
||||
{
|
||||
myPos = i;
|
||||
break;
|
||||
@@ -741,17 +830,17 @@ final class RefBuilder : ASTraitsVisitor
|
||||
return;
|
||||
}
|
||||
|
||||
auto myContext = context[0..myPos].dup;
|
||||
namespaces[ns.kind].add(ns.id, myContext);
|
||||
auto myContext = context[0..myPos];
|
||||
namespaces[ns.kind].add(ns.id, myContext, priority);
|
||||
}
|
||||
|
||||
void visitNamespaceSet(ASProgram.Namespace[] nsSet)
|
||||
void visitNamespaceSet(ASProgram.Namespace[] nsSet, ContextPriority priority)
|
||||
{
|
||||
foreach (ns; nsSet)
|
||||
visitNamespace(ns);
|
||||
visitNamespace(ns, priority);
|
||||
}
|
||||
|
||||
void visitMultiname(ASProgram.Multiname m)
|
||||
void visitMultiname(ASProgram.Multiname m, ContextPriority priority)
|
||||
{
|
||||
if (m is null) return;
|
||||
with (m)
|
||||
@@ -759,20 +848,20 @@ final class RefBuilder : ASTraitsVisitor
|
||||
{
|
||||
case ASType.QName:
|
||||
case ASType.QNameA:
|
||||
visitNamespace(vQName.ns);
|
||||
visitNamespace(vQName.ns, priority);
|
||||
break;
|
||||
case ASType.Multiname:
|
||||
case ASType.MultinameA:
|
||||
visitNamespaceSet(vMultiname.nsSet);
|
||||
visitNamespaceSet(vMultiname.nsSet, priority);
|
||||
break;
|
||||
case ASType.MultinameL:
|
||||
case ASType.MultinameLA:
|
||||
visitNamespaceSet(vMultinameL.nsSet);
|
||||
visitNamespaceSet(vMultinameL.nsSet, priority);
|
||||
break;
|
||||
case ASType.TypeName:
|
||||
visitMultiname(vTypeName.name);
|
||||
visitMultiname(vTypeName.name, priority);
|
||||
foreach (param; vTypeName.params)
|
||||
visitMultiname(param);
|
||||
visitMultiname(param, priority);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -786,21 +875,21 @@ final class RefBuilder : ASTraitsVisitor
|
||||
switch (type)
|
||||
{
|
||||
case OpcodeArgumentType.Namespace:
|
||||
visitNamespace(instruction.arguments[i].namespacev);
|
||||
visitNamespace(instruction.arguments[i].namespacev, ContextPriority.usage);
|
||||
break;
|
||||
case OpcodeArgumentType.Multiname:
|
||||
visitMultiname(instruction.arguments[i].multinamev);
|
||||
visitMultiname(instruction.arguments[i].multinamev, ContextPriority.usage);
|
||||
break;
|
||||
case OpcodeArgumentType.Class:
|
||||
pushContext("inline_class");
|
||||
if (isOrphan(instruction.arguments[i].classv))
|
||||
addClass(instruction.arguments[i].classv);
|
||||
addClass(instruction.arguments[i].classv, ContextPriority.usage);
|
||||
popContext();
|
||||
break;
|
||||
case OpcodeArgumentType.Method:
|
||||
pushContext("inline_method");
|
||||
if (isOrphan(instruction.arguments[i].methodv))
|
||||
addMethod(instruction.arguments[i].methodv);
|
||||
addMethod(instruction.arguments[i].methodv, ContextPriority.usage);
|
||||
popContext();
|
||||
break;
|
||||
default:
|
||||
@@ -867,7 +956,7 @@ final class RefBuilder : ASTraitsVisitor
|
||||
}
|
||||
|
||||
if (pathSegment.length > 240)
|
||||
pathSegment = pathSegment[0..200] ~ '-' ~ getDigestString(pathSegment);
|
||||
pathSegment = assumeUnique(pathSegment[0..200] ~ '-' ~ toHexString(md5Of(pathSegment)));
|
||||
}
|
||||
|
||||
return arrayJoin(pathSegments, "/");
|
||||
@@ -880,35 +969,38 @@ final class RefBuilder : ASTraitsVisitor
|
||||
return arrayJoin(strings);
|
||||
}
|
||||
|
||||
bool addObject(T)(T obj) { return objects.add(obj, context); }
|
||||
bool addObject(T)(T obj, ContextPriority priority) { return objects.add(obj, context, priority); }
|
||||
|
||||
void addClass(ASProgram.Class vclass)
|
||||
void addClass(ASProgram.Class vclass, ContextPriority priority)
|
||||
{
|
||||
addObject(vclass);
|
||||
addObject(vclass, priority);
|
||||
|
||||
pushContext("cinit");
|
||||
addMethod(vclass.cinit);
|
||||
popContext();
|
||||
pushContext("class", true);
|
||||
pushContext("init", true);
|
||||
addMethod(vclass.cinit, ContextPriority.declaration);
|
||||
popContext(); // init
|
||||
popContext(); // class
|
||||
|
||||
pushContext("iinit");
|
||||
addMethod(vclass.instance.iinit);
|
||||
popContext();
|
||||
pushContext("instance", true);
|
||||
pushContext("init", true);
|
||||
addMethod(vclass.instance.iinit, ContextPriority.declaration);
|
||||
popContext(); // init
|
||||
|
||||
pushContext("instance");
|
||||
visitMultiname(vclass.instance.name);
|
||||
visitMultiname(vclass.instance.superName);
|
||||
visitMultiname(vclass.instance.name, ContextPriority.declaration);
|
||||
visitMultiname(vclass.instance.superName, ContextPriority.usage);
|
||||
visitNamespace(vclass.instance.protectedNs, ContextPriority.declaration);
|
||||
foreach (iface; vclass.instance.interfaces)
|
||||
visitMultiname(iface);
|
||||
popContext();
|
||||
visitMultiname(iface, ContextPriority.usage);
|
||||
popContext(); // instance
|
||||
}
|
||||
|
||||
void addMethod(ASProgram.Method method)
|
||||
void addMethod(ASProgram.Method method, ContextPriority priority)
|
||||
{
|
||||
if (addObject(method))
|
||||
if (addObject(method, priority))
|
||||
{
|
||||
foreach (paramType; method.paramTypes)
|
||||
visitMultiname(paramType);
|
||||
visitMultiname(method.returnType);
|
||||
visitMultiname(paramType, ContextPriority.usage);
|
||||
visitMultiname(method.returnType, ContextPriority.usage);
|
||||
if (method.vbody)
|
||||
visitMethodBody(method.vbody);
|
||||
}
|
||||
@@ -920,6 +1012,7 @@ final class Disassembler
|
||||
ASProgram as;
|
||||
string name, dir;
|
||||
RefBuilder refs;
|
||||
bool dumpRaw = true;
|
||||
|
||||
void newInclude(StringBuilder mainsb, string filename, void delegate(StringBuilder) callback, bool doInline = true)
|
||||
{
|
||||
@@ -932,6 +1025,7 @@ final class Disassembler
|
||||
base = dirName(base), up++;
|
||||
string rel = replicate("../", up) ~ full[base.length+1..$];
|
||||
|
||||
mainsb.flush();
|
||||
StringBuilder sb = new StringBuilder(full);
|
||||
callback(sb);
|
||||
sb.save();
|
||||
@@ -958,7 +1052,7 @@ final class Disassembler
|
||||
|
||||
StringBuilder sb = new StringBuilder(dir ~ "/" ~ name ~ ".main.asasm");
|
||||
|
||||
sb ~= "#version 3";
|
||||
sb ~= "#version 4";
|
||||
sb.newLine();
|
||||
|
||||
sb ~= "program";
|
||||
@@ -1020,7 +1114,7 @@ final class Disassembler
|
||||
if (v == ABCFile.NULL_INT)
|
||||
sb ~= "null";
|
||||
else
|
||||
sb ~= to!string(v);
|
||||
sb.write(v);
|
||||
}
|
||||
|
||||
void dumpUInt(StringBuilder sb, ulong v)
|
||||
@@ -1028,7 +1122,16 @@ final class Disassembler
|
||||
if (v == ABCFile.NULL_UINT)
|
||||
sb ~= "null";
|
||||
else
|
||||
sb ~= to!string(v);
|
||||
sb.write(v);
|
||||
}
|
||||
|
||||
static struct StaticBuf(T, size_t size)
|
||||
{
|
||||
T[size] buf;
|
||||
size_t pos;
|
||||
void put(T v) { buf[pos++] = v; }
|
||||
void put(in T[] v) { buf[pos..pos+v.length] = v[]; pos+=v.length; }
|
||||
T[] data() { return buf[0..pos]; }
|
||||
}
|
||||
|
||||
void dumpDouble(StringBuilder sb, double v)
|
||||
@@ -1037,16 +1140,40 @@ final class Disassembler
|
||||
sb ~= "null";
|
||||
else
|
||||
{
|
||||
string s = format("%.18g", v);
|
||||
StaticBuf!(char, 64) buf;
|
||||
formattedWrite(&buf, "%.18g", v);
|
||||
char[] s = buf.data();
|
||||
|
||||
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];
|
||||
if (forceDouble(to!double(s)) == v)
|
||||
{
|
||||
foreach_reverse (i; 1..s.length)
|
||||
if (s[i]>='0' && s[i]<='8')
|
||||
{
|
||||
s[i]++;
|
||||
if (forceDouble(to!double(s[0..i+1]))==v)
|
||||
s = s[0..i+1];
|
||||
else
|
||||
s[i]--;
|
||||
}
|
||||
while (s.length>2 && s[$-1]!='.' && forceDouble(to!double(s[0..$-1]))==v)
|
||||
s = s[0..$-1];
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.pos = 0;
|
||||
formattedWrite(&buf, "%.13a", v);
|
||||
s = buf.data();
|
||||
auto n = forceDouble(to!double(s));
|
||||
assert(n == v,
|
||||
"Can't print precise representation of double: %(%02X %) (%.18g) => %s => %(%02X %) (%.18g)".format(
|
||||
cast(ubyte[])cast(void[])[v], v,
|
||||
s,
|
||||
cast(ubyte[])cast(void[])[n], n,
|
||||
));
|
||||
}
|
||||
}
|
||||
sb ~= s;
|
||||
}
|
||||
@@ -1435,8 +1562,7 @@ final class Disassembler
|
||||
|
||||
void dumpScript(StringBuilder sb, ASProgram.Script script, uint index)
|
||||
{
|
||||
sb ~= "script ; ";
|
||||
sb ~= to!string(index);
|
||||
sb ~= "script";
|
||||
sb.indent++; sb.newLine();
|
||||
dumpMethod(sb, script.sinit, "sinit");
|
||||
dumpTraits(sb, script.traits, true);
|
||||
@@ -1454,24 +1580,17 @@ final class Disassembler
|
||||
void dumpLabel(StringBuilder sb, ref ABCFile.Label label)
|
||||
{
|
||||
sb ~= 'L';
|
||||
sb ~= to!string(label.index);
|
||||
sb.write(label.index);
|
||||
if (label.offset != 0)
|
||||
{
|
||||
if (label.offset > 0)
|
||||
sb ~= '+';
|
||||
sb ~= to!string(label.offset);
|
||||
sb.write(label.offset);
|
||||
}
|
||||
}
|
||||
|
||||
void dumpMethodBody(StringBuilder sb, ASProgram.MethodBody mbody)
|
||||
{
|
||||
if (mbody.error)
|
||||
{
|
||||
sb ~= "; Error while disassembling method: " ~ mbody.error;
|
||||
sb.newLine();
|
||||
sb.linePrefix = "; ";
|
||||
}
|
||||
|
||||
sb ~= "body";
|
||||
sb.indent++; sb.newLine();
|
||||
dumpUIntField(sb, "maxstack", mbody.maxStack);
|
||||
@@ -1487,17 +1606,7 @@ final class Disassembler
|
||||
labels[e.from.index] = labels[e.to.index] = labels[e.target.index] = true;
|
||||
|
||||
sb.indent++;
|
||||
if (mbody.error)
|
||||
foreach (i, b; mbody.rawBytes)
|
||||
{
|
||||
sb ~= format("0x%02X", b);
|
||||
if (i%16==15 || i==mbody.rawBytes.length-1)
|
||||
sb.newLine();
|
||||
else
|
||||
sb ~= " ";
|
||||
}
|
||||
else
|
||||
dumpInstructions(sb, mbody.instructions, labels);
|
||||
dumpInstructions(sb, mbody.instructions, labels, mbody.errors);
|
||||
sb.indent--;
|
||||
|
||||
sb ~= "end ; code";
|
||||
@@ -1522,7 +1631,7 @@ final class Disassembler
|
||||
sb.linePrefix = null;
|
||||
}
|
||||
|
||||
void dumpInstructions(StringBuilder sb, ASProgram.Instruction[] instructions, bool[] labels)
|
||||
void dumpInstructions(StringBuilder sb, ASProgram.Instruction[] instructions, bool[] labels, ABCFile.Error[] errors)
|
||||
{
|
||||
foreach (ref instruction; instructions)
|
||||
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
|
||||
@@ -1546,12 +1655,16 @@ final class Disassembler
|
||||
{
|
||||
sb.noIndent();
|
||||
sb ~= 'L';
|
||||
sb ~= to!string(ii);
|
||||
sb.write(ii);
|
||||
sb ~= ':';
|
||||
sb.newLine();
|
||||
}
|
||||
}
|
||||
|
||||
string[] iErrors = new string[instructions.length + 1];
|
||||
foreach (ref error; errors)
|
||||
iErrors[error.loc.index] = error.msg;
|
||||
|
||||
bool extraNewLine = false;
|
||||
foreach (uint ii, ref instruction; instructions)
|
||||
{
|
||||
@@ -1560,6 +1673,21 @@ final class Disassembler
|
||||
extraNewLine = newLineAfter[instruction.opcode];
|
||||
checkLabel(ii);
|
||||
|
||||
if (iErrors[ii])
|
||||
{
|
||||
sb ~= "; Error: ";
|
||||
sb ~= iErrors[ii];
|
||||
sb.newLine();
|
||||
}
|
||||
|
||||
if (instruction.opcode == Opcode.OP_raw)
|
||||
{
|
||||
if (dumpRaw)
|
||||
sb ~= "; 0x%02X".format(instruction.arguments[0].ubytev);
|
||||
sb.newLine();
|
||||
continue;
|
||||
}
|
||||
|
||||
sb ~= opcodeInfo[instruction.opcode].name;
|
||||
auto argTypes = opcodeInfo[instruction.opcode].argumentTypes;
|
||||
if (argTypes.length)
|
||||
@@ -1573,14 +1701,17 @@ final class Disassembler
|
||||
case OpcodeArgumentType.Unknown:
|
||||
throw new Exception("Don't know how to disassemble OP_" ~ opcodeInfo[instruction.opcode].name);
|
||||
|
||||
case OpcodeArgumentType.ByteLiteral:
|
||||
sb.write(instruction.arguments[i].bytev);
|
||||
break;
|
||||
case OpcodeArgumentType.UByteLiteral:
|
||||
sb ~= to!string(instruction.arguments[i].ubytev);
|
||||
sb.write(instruction.arguments[i].ubytev);
|
||||
break;
|
||||
case OpcodeArgumentType.IntLiteral:
|
||||
sb ~= to!string(instruction.arguments[i].intv);
|
||||
sb.write(instruction.arguments[i].intv);
|
||||
break;
|
||||
case OpcodeArgumentType.UIntLiteral:
|
||||
sb ~= to!string(instruction.arguments[i].uintv);
|
||||
sb.write(instruction.arguments[i].uintv);
|
||||
break;
|
||||
|
||||
case OpcodeArgumentType.Int:
|
||||
@@ -1693,6 +1824,7 @@ static this()
|
||||
Opcode.OP_si32,
|
||||
Opcode.OP_sf32,
|
||||
Opcode.OP_sf64,
|
||||
Opcode.OP_throw,
|
||||
])
|
||||
newLineAfter[o] = true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2012, 2013, 2014, 2016 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -23,6 +23,7 @@ version(HAVE_LZMA) {} else static assert(0, "LZMA is not available (HAVE_LZMA ve
|
||||
import deimos.lzma;
|
||||
import std.conv;
|
||||
import std.exception;
|
||||
import std.string : format;
|
||||
|
||||
version (Windows)
|
||||
{ pragma(lib, "liblzma"); }
|
||||
@@ -40,31 +41,47 @@ static assert(LZMAHeader.sizeof == 13);
|
||||
|
||||
ubyte[] lzmaDecompress(LZMAHeader header, in ubyte[] compressedData)
|
||||
{
|
||||
enforce(header.decompressedSize > 0, "Decompression with unknown size is unsupported");
|
||||
|
||||
lzma_stream strm;
|
||||
lzmaEnforce(lzma_alone_decoder(&strm, ulong.max), "lzma_alone_decoder");
|
||||
scope(exit) lzma_end(&strm);
|
||||
|
||||
auto outBuf = new ubyte[to!size_t(header.decompressedSize)];
|
||||
strm.next_out = outBuf.ptr;
|
||||
strm.avail_out = outBuf.length;
|
||||
auto outBuf = new ubyte[1024];
|
||||
size_t pos = 0;
|
||||
|
||||
void decompress(in ubyte[] chunk)
|
||||
{
|
||||
strm.next_in = chunk.ptr;
|
||||
strm.avail_in = chunk.length;
|
||||
lzmaEnforce!true(lzma_code(&strm, lzma_action.LZMA_RUN), "lzma_code (LZMA_RUN)");
|
||||
strm.next_in = chunk.ptr;
|
||||
strm.avail_in = chunk.length;
|
||||
|
||||
again:
|
||||
strm.next_out = outBuf.ptr + pos;
|
||||
strm.avail_out = outBuf.length - pos;
|
||||
auto ret = lzma_code(&strm, lzma_action.LZMA_RUN);
|
||||
pos = strm.next_out - outBuf.ptr;
|
||||
|
||||
if (ret == lzma_ret.LZMA_OK && strm.avail_in && !strm.avail_out)
|
||||
{
|
||||
outBuf.length = outBuf.length * 2;
|
||||
goto again;
|
||||
}
|
||||
lzmaEnforce!true(ret, "lzma_code (LZMA_RUN)");
|
||||
enforce(strm.avail_in == 0, "Not all data was read");
|
||||
}
|
||||
|
||||
header.decompressedSize = -1; // Required as Flash uses End-of-Stream marker
|
||||
fixDictSize(header.dictionarySize);
|
||||
decompress(cast(ubyte[])(&header)[0..1]);
|
||||
decompress(compressedData);
|
||||
|
||||
lzmaEnforce!true(lzma_code(&strm, lzma_action.LZMA_FINISH), "lzma_code (LZMA_FINISH)");
|
||||
|
||||
enforce(strm.avail_out == 0, "Decompressed size mismatch");
|
||||
// enforce(strm.avail_out == 0,
|
||||
// "Decompressed size mismatch (expected %d/0x%X, got %d/0x%X)".format(
|
||||
// outBuf.length, outBuf.length,
|
||||
// outBuf.length - strm.avail_out, outBuf.length - strm.avail_out,
|
||||
// ));
|
||||
|
||||
outBuf = outBuf[0..pos];
|
||||
|
||||
return outBuf;
|
||||
}
|
||||
@@ -78,13 +95,12 @@ ubyte[] lzmaCompress(in ubyte[] decompressedData, LZMAHeader* header)
|
||||
lzmaEnforce(lzma_alone_encoder(&strm, &opts), "lzma_alone_encoder");
|
||||
scope(exit) lzma_end(&strm);
|
||||
|
||||
auto outBuf = new ubyte[decompressedData.length + 1024];
|
||||
auto outBuf = new ubyte[decompressedData.length * 11 / 10 + 1024];
|
||||
strm.next_out = outBuf.ptr;
|
||||
strm.avail_out = outBuf.length;
|
||||
strm.next_in = decompressedData.ptr;
|
||||
strm.avail_in = decompressedData.length;
|
||||
lzmaEnforce(lzma_code(&strm, lzma_action.LZMA_RUN), "lzma_code (LZMA_RUN)");
|
||||
scope(failure) { import std.stdio; writeln("avail_in=", strm.avail_in); }
|
||||
enforce(strm.avail_in == 0, "Not all data was read");
|
||||
enforce(strm.avail_out != 0, "Ran out of compression space");
|
||||
|
||||
@@ -99,3 +115,18 @@ private void lzmaEnforce(bool STREAM_END_OK=false)(lzma_ret v, string f)
|
||||
if (v != lzma_ret.LZMA_OK && (!STREAM_END_OK || v != lzma_ret.LZMA_STREAM_END))
|
||||
throw new Exception(text(f, " error: ", v));
|
||||
}
|
||||
|
||||
/// Work around an artificial lzma_alone_decoder limitation in liblzma
|
||||
/// which prevents it from accepting any streams with a dictionary size
|
||||
/// that is not 2^n or 2^n + 2^(n-1).
|
||||
/// See xz\src\liblzma\common\alone_decoder.c (git commit e7b424d267), line 87
|
||||
private void fixDictSize(ref uint d)
|
||||
{
|
||||
--d;
|
||||
d |= d >> 2;
|
||||
d |= d >> 3;
|
||||
d |= d >> 4;
|
||||
d |= d >> 8;
|
||||
d |= d >> 16;
|
||||
++d;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011, 2012, 2016 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -20,6 +20,7 @@ module swffile;
|
||||
|
||||
import std.conv;
|
||||
import std.exception;
|
||||
import std.string : format;
|
||||
import std.zlib;
|
||||
import zlibx;
|
||||
version (HAVE_LZMA) import lzma;
|
||||
@@ -115,7 +116,9 @@ private final class SWFReader
|
||||
else
|
||||
enforce(false, "This version was built without LZMA support");
|
||||
}
|
||||
enforce(swf.header.fileLength == buf.length, "Incorrect file length in file header");
|
||||
//enforce(swf.header.fileLength == buf.length,
|
||||
// "Incorrect file length in file header (expected %d, got %d)"
|
||||
// .format(swf.header.fileLength , buf.length));
|
||||
swf.frameSize = readRect();
|
||||
swf.frameRate = readU16();
|
||||
swf.frameCount = readU16();
|
||||
|
||||
+35
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* Copyright 2010, 2011, 2012, 2013 Vladimir Panteleev <vladimir@thecybershadow.net>
|
||||
* This file is part of RABCDAsm.
|
||||
*
|
||||
* RABCDAsm is free software: you can redistribute it and/or modify
|
||||
@@ -18,18 +18,50 @@
|
||||
|
||||
module swflzmacompress;
|
||||
|
||||
import std.exception;
|
||||
import std.file;
|
||||
import std.getopt;
|
||||
import std.string;
|
||||
import swffile;
|
||||
|
||||
void main(string[] args)
|
||||
{
|
||||
bool force, updateVersion;
|
||||
getopt(args,
|
||||
"--force", &force,
|
||||
"--update-version", &updateVersion,
|
||||
);
|
||||
|
||||
if (args.length == 1)
|
||||
throw new Exception("No file specified");
|
||||
enum MIN_LZMA_VER = 13;
|
||||
foreach (arg; args[1..$])
|
||||
{
|
||||
auto swf = SWFFile.read(cast(ubyte[])read(arg));
|
||||
if (swf.header.signature[0] == cast(ubyte)'Z')
|
||||
throw new Exception("Already LZMA-compressed");
|
||||
enforce(swf.header.signature[0] != cast(ubyte)'Z', "Already LZMA-compressed");
|
||||
if (swf.header.ver < MIN_LZMA_VER)
|
||||
{
|
||||
if (updateVersion)
|
||||
{
|
||||
if (swf.header.ver < 8 && !force)
|
||||
throw new Exception(format(
|
||||
"SWF version %d has different file format than version %d, " ~
|
||||
"required for LZMA. Resulting file may not work. " ~
|
||||
"Use --force to override and update version anyway.",
|
||||
swf.header.ver, MIN_LZMA_VER
|
||||
));
|
||||
swf.header.ver = MIN_LZMA_VER;
|
||||
}
|
||||
else
|
||||
if (!force)
|
||||
throw new Exception(format(
|
||||
"SWF version %d is too old to support SWF LZMA compression, " ~
|
||||
"which requires version %d. " ~
|
||||
"Use --update-version to update the version number, " ~
|
||||
"or --force to compress anyway without updating it.",
|
||||
swf.header.ver, MIN_LZMA_VER
|
||||
));
|
||||
}
|
||||
swf.header.signature[0] = cast(ubyte)'Z'; // LZMA
|
||||
write(arg, swf.write());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user