31 Commits
Author SHA1 Message Date
Vladimir Panteleev 68f6fa241c Update Changelog 2016-01-16 09:10:28 +00:00
Vladimir Panteleev 77b5366ada lzma: Decompress piecewise, ignore decompressed size from header 2016-01-13 11:09:57 +00:00
Vladimir Panteleev 59a0b2c167 swffile: Ignore mismatched decompressed file size 2016-01-13 11:09:44 +00:00
Vladimir Panteleev 9bd1c698b5 Improve size mismatch error messages 2016-01-13 10:52:00 +00:00
Vladimir Panteleev e01cff3f24 assembler: Close main file when done 2015-11-30 15:33:30 +00:00
Vladimir Panteleev 988b17a401 assembler: Rename File to SourceFile to remove std.stdio.File ambiguity 2015-11-30 15:24:29 +00:00
Vladimir Panteleev 3ed3fb1a27 diassembler: Make raw byte dumps optional 2015-11-24 19:18:42 +00:00
Vladimir Panteleev 1e7cbaca9b disassembler: Don't write script index comment
Complicates merging
2015-11-24 08:45:37 +00:00
Vladimir Panteleev 9b6dfe13b3 disassembler: Dump doubles in hex notation as necessary; verify precision 2015-01-18 02:59:56 +00:00
Vladimir Panteleev dc01be0604 asprogram: Work around DMD issue 13998 2015-01-04 18:58:44 +00:00
Vladimir Panteleev 5e7b2ea4e7 build_rabcdasm: Detect DMD 2.064 x64 isWordChar bug 2015-01-04 18:54:46 +00:00
Vladimir Panteleev 8ab69fc6b7 disassembler: Fix usage of deprecated .sort property 2014-12-20 21:04:56 +00:00
Vladimir Panteleev 576294f949 Add .gitignore 2014-12-20 21:04:35 +00:00
Vladimir Panteleev 21650d2056 lzma.index: Fix C-style array syntax 2014-12-20 21:03:13 +00:00
Vladimir Panteleev 0983145a0d Fix handling of pushbyte argument 2014-10-12 22:25:42 +00:00
Vladimir Panteleev 62c8fce63b assembler: Fix buffer reuse bugs with macros 2014-09-22 03:17:47 +00:00
Vladimir Panteleev 7097774834 assembler: Minor refactoring 2014-09-22 03:17:36 +00:00
Vladimir Panteleev fe93f5e764 Update Changelog 2014-09-10 11:41:27 +00:00
Vladimir Panteleev 0fbfba1186 README: Update 2.066 note 2014-09-10 11:41:27 +00:00
Vladimir Panteleev 6bdf6dae39 abcfile: 64-bit fix 2014-09-10 11:41:26 +00:00
Vladimir Panteleev 62ef183d95 Do not attempt to disassemble unreachable code
Also, improve handling of disassembly errors -
methods will be partially disassembled as far as possible.
2014-09-03 15:12:26 +00:00
Vladimir Panteleev f6eb080be7 disassembler: Print new lines after OP_throw 2014-09-03 15:12:26 +00:00
Vladimir Panteleev a17a03e6f6 asprogram: Work around issue 13420 in DMD 2.066 2014-09-03 15:11:26 +00:00
Vladimir Panteleev 0486c1a54a lzma: Provide 10% slack space for uncompressable data 2014-07-19 23:28:40 +00:00
Vladimir Panteleev a2d22e7606 README: Add link to liblzma 2014-06-10 03:05:07 +00:00
Vladimir Panteleev 6568793aaf Update Changelog 2014-04-21 07:52:43 +00:00
Vladimir Panteleev b0bdb250c5 README: Document build requirement for long path support 2014-04-21 07:51:27 +00:00
Vladimir Panteleev 99e49a9385 Fix handling very long paths on Windows again 2014-03-17 11:09:30 +00:00
Vladimir Panteleev 6313739b5e asprogram: Ignore null parameters in TypeName multinames 2014-02-16 21:03:11 +00:00
Vladimir Panteleev 8ed3295199 autodata: Fix stringizing null references 2014-02-16 20:52:23 +00:00
Vladimir Panteleev 31ad8dcef7 disassembler: Fix compilation with 2.063.2
This change may also improve performance.
2014-01-12 22:14:18 +00:00
13 changed files with 551 additions and 212 deletions
+17
View File
@@ -0,0 +1,17 @@
*.exe
*.dll
*.pdb
*.ilk
*.exp
*.lib
*.suo
/rabcasm
/rabcdasm
/abcexport
/abcreplace
/swfbinexport
/swfbinreplace
/swfdecompress
/swf7zcompress
/swflzmacompress
+31
View File
@@ -1,6 +1,37 @@
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)
---------------------------
+5 -1
View File
@@ -72,12 +72,16 @@ Substitute `dmd` with `gdmd` if you're using gdc. You can use the `DC` and
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
---------------------
+242 -99
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012, 2013 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", []},
@@ -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;
+22 -8
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012, 2013 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
@@ -261,8 +261,7 @@ final class ASProgram
Exception[] exceptions;
Trait[] traits;
string error;
ubyte[] rawBytes;
ABCFile.Error[] errors;
}
struct Instruction
@@ -270,6 +269,7 @@ final class ASProgram
Opcode opcode;
union Argument
{
byte bytev;
ubyte ubytev;
long intv;
@@ -583,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;
}
@@ -600,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;
@@ -747,12 +749,18 @@ private final class AStoABC : ASVisitor
{
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)
{
return cast(Key)value;
static if (is(T == double))
return *cast(ulong*)&value;
else
return cast(Key)value;
}
struct Entry
@@ -827,7 +835,8 @@ private final class AStoABC : ASVisitor
// sort
static if (is(T == double))
{
static ulong repr(double d) { static assert(double.sizeof == ulong.sizeof); return *cast(ulong*)(&d); }
// 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
@@ -994,7 +1003,8 @@ private final class AStoABC : ASVisitor
{
multinames.registerDependency(m, m.vTypeName.name);
foreach (t; m.vTypeName.params)
multinames.registerDependency(m, t);
if (t)
multinames.registerDependency(m, t);
}
}
@@ -1234,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;
@@ -1519,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:
+67 -48
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012, 2013 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
@@ -25,6 +25,7 @@ import std.path;
import std.range;
import std.stdio;
import std.string;
import std.typecons;
import abcfile;
import asprogram;
@@ -34,10 +35,10 @@ final class Assembler
{
static struct Position
{
File file;
SourceFile file;
ulong offset;
File load()
SourceFile load()
{
assert(file.ptr == file.end);
file.filePosition = offset;
@@ -45,14 +46,14 @@ final class Assembler
}
}
static final class File
static final class SourceFile
{
string name;
string[] arguments;
string data;
@property bool isVirtual() { return data ! is null; }
File parent;
SourceFile parent;
const(char)* ptr, end;
@@ -63,7 +64,7 @@ final class Assembler
ulong filePosition; // File position of buffer.ptr
sizediff_t shift; // Error location adjustment
std.stdio.File f;
File f;
this(string name, string data = null, string[] arguments = null)
{
@@ -73,17 +74,23 @@ final class Assembler
if (data)
{
createBuffer(data.length);
buffer[] = data[];
buffer[0..data.length] = data[];
ptr = buffer.ptr;
end = ptr + data.length;
}
else
{
createBuffer(BUF_SIZE);
f.open(name, "rb");
f = openFile(name, "rb");
}
}
~this()
{
if (f.isOpen)
f.close();
}
bool loadNextChunk()
{
if (!f.isOpen)
@@ -120,6 +127,7 @@ final class Assembler
{
buffer = usedBuffers[0];
usedBuffers = usedBuffers[1..$];
buffer[size..bufferSize] = 0;
}
else
buffer = new char[bufferSize];
@@ -144,7 +152,7 @@ final class Assembler
dataSource = (cast(ubyte[])data).only.inputRangeObject;
else
{
f.open(name, "rb");
f = openFile(name, "rb");
dataSource = f.byChunk(BUF_SIZE).inputRangeObject;
}
scope(exit) if (!isVirtual) f.close();
@@ -176,7 +184,7 @@ final class Assembler
}
}
File currentFile;
SourceFile currentFile;
string getBasePath()
{
@@ -233,17 +241,17 @@ final class Assembler
switch (word)
{
case "mixin":
pushFile(new File("#mixin", readImmString()));
pushFile(new SourceFile("#mixin", readImmString()));
break;
case "call": // #mixin with arguments
pushFile(new File("#call", readImmString(), readList!('(', ')', readImmString, false)()));
pushFile(new SourceFile("#call", readImmString(), readList!('(', ')', readImmString, false)()));
break;
case "include":
pushFile(new File(convertFilename(readString())));
pushFile(new SourceFile(convertFilename(readString())));
break;
case "get":
auto filename = convertFilename(readString());
pushFile(new File(filename, toStringLiteral(cast(string)read(longPath(filename)))));
pushFile(new SourceFile(filename, toStringLiteral(cast(string)read(longPath(filename)))));
break;
case "set":
vars[readWord()] = readImmString();
@@ -258,7 +266,7 @@ final class Assembler
break;
case "version":
sourceVersion = readUInt().to!uint();
enforce(sourceVersion >= 1 && sourceVersion <= 3, "Invalid/unknown #version");
enforce(sourceVersion >= 1 && sourceVersion <= 4, "Invalid/unknown #version");
break;
default:
backpedal(word.length);
@@ -292,7 +300,7 @@ final class Assembler
if (index >= f.arguments.length)
throw new Exception("Argument index out-of-bounds");
string value = f.arguments[index];
pushFile(new File('$' ~ name.assumeUnique(), asStringLiteral ? toStringLiteral(value) : value));
pushFile(new SourceFile('$' ~ name.assumeUnique(), asStringLiteral ? toStringLiteral(value) : value));
return;
}
throw new Exception("No arguments in context");
@@ -303,7 +311,7 @@ final class Assembler
if (pvalue is null)
throw new Exception("variable %s is not defined".format(name));
string value = *pvalue;
pushFile(new File('$' ~ name.assumeUnique(), asStringLiteral ? toStringLiteral(value) : value));
pushFile(new SourceFile('$' ~ name.assumeUnique(), asStringLiteral ? toStringLiteral(value) : value));
}
}
@@ -356,14 +364,14 @@ final class Assembler
}
}
void pushFile(File file)
void pushFile(SourceFile file)
{
file.parent = currentFile;
currentFile = file;
}
/// For restoring the position of an error
void setFile(File file)
void setFile(SourceFile file)
{
currentFile = file;
}
@@ -1163,6 +1171,12 @@ final class Assembler
case OpcodeArgumentType.Unknown:
throw new Exception("Don't know how to assemble OP_" ~ opcodeInfo[instruction.opcode].name);
case OpcodeArgumentType.ByteLiteral:
if (sourceVersion < 4)
instruction.arguments[i].bytev = to!ubyte(readInt());
else
instruction.arguments[i].bytev = to!byte(readInt());
break;
case OpcodeArgumentType.UByteLiteral:
instruction.arguments[i].ubytev = to!ubyte(readUInt());
break;
@@ -1324,45 +1338,50 @@ final class Assembler
void assemble(string mainFilename)
{
pushFile(new File(mainFilename));
auto mainFile = scoped!SourceFile(mainFilename);
pushFile(mainFile);
try
{
readProgram();
foreach (ref f; classFixups)
if (f.name is null)
*f.ptr = null;
else
{
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;
}
foreach (ref f; methodFixups)
if (f.name is null)
*f.ptr = null;
else
{
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;
}
applyFixups();
}
catch (Exception e)
{
e.msg = "\n%s\n%s".format(context(), e.msg);
throw e;
}
}
void applyFixups()
{
foreach (ref f; classFixups)
if (f.name is null)
*f.ptr = null;
else
{
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;
}
foreach (ref f; methodFixups)
if (f.name is null)
*f.ptr = null;
else
{
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;
}
classFixups = null;
methodFixups = null;
+3 -3
View File
@@ -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 ~ ");";
}
+15
View File
@@ -61,6 +61,16 @@ void test(string code, string extraFlags=null)
stderr.writeln(" >>> OK");
}
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
@@ -89,6 +99,11 @@ int main(string[] args)
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...");
+57 -3
View File
@@ -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;
}
+1 -1
View File
@@ -223,7 +223,7 @@ struct lzma_index_iter
size_t s;
lzma_vli v;
}
InternalData internal[6];
InternalData[6] internal;
}
+59 -36
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012, 2013 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
@@ -58,7 +58,7 @@ final class StringBuilder
if (subdir.length && !exists(longPath(subdir)))
mkdir(longPath(subdir));
}
file.open(longPath(filename), "wb");
file = openFile(filename, "wb");
assert(!pos, "Opening new file with unflushed buffer");
}
@@ -535,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);
@@ -1012,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)
{
@@ -1051,7 +1052,7 @@ final class Disassembler
StringBuilder sb = new StringBuilder(dir ~ "/" ~ name ~ ".main.asasm");
sb ~= "#version 3";
sb ~= "#version 4";
sb.newLine();
sb ~= "program";
@@ -1129,6 +1130,7 @@ final class Disassembler
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]; }
}
@@ -1145,17 +1147,33 @@ 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')
{
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];
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;
}
@@ -1544,8 +1562,7 @@ final class Disassembler
void dumpScript(StringBuilder sb, ASProgram.Script script, uint index)
{
sb ~= "script ; ";
sb.write(index);
sb ~= "script";
sb.indent++; sb.newLine();
dumpMethod(sb, script.sinit, "sinit");
dumpTraits(sb, script.traits, true);
@@ -1574,13 +1591,6 @@ final class Disassembler
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);
@@ -1596,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";
@@ -1631,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)
@@ -1661,6 +1661,10 @@ final class Disassembler
}
}
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)
{
@@ -1669,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)
@@ -1682,6 +1701,9 @@ 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.write(instruction.arguments[i].ubytev);
break;
@@ -1802,6 +1824,7 @@ static this()
Opcode.OP_si32,
Opcode.OP_sf32,
Opcode.OP_sf64,
Opcode.OP_throw,
])
newLineAfter[o] = true;
}
+27 -11
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2012, 2013 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,21 +41,30 @@ 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");
}
@@ -65,7 +75,13 @@ ubyte[] lzmaDecompress(LZMAHeader header, in ubyte[] 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;
}
@@ -79,7 +95,7 @@ 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;
+5 -2
View File
@@ -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();