83 Commits
Author SHA1 Message Date
Vladimir Panteleev 8ed623a322 Merge pull request #58 from Aaron1011/dlang-update
Get RABCDAsm compiling with latest dlang
2023-03-11 11:10:34 -06:00
Vladimir Panteleev ed130fe1b0 zlibx: Free memory on error
This is closer to the original behavior.
2023-03-11 17:09:38 +00:00
Aaron Hill a2d18a1a72 Get RABCDAsm compiling with latest dlang
Both `opCatAssign` and `destroy` produce deprecation errors
as of the latest dlang (dmd 2.100.0). This commit changes them
both to use the suggested replacements, making RABCDAsm compile
with the latest dlang.
2022-06-15 20:14:47 -05:00
Vladimir Panteleev f455e29deb abcfile: Add missing argument to getouterscope 2021-10-18 08:05:48 +00:00
Vladimir Panteleev 03b13d0c87 Replace getpropertylate with getouterscope
Fixes #55.
2021-10-18 07:18:20 +00:00
Vladimir Panteleev cdbac56b21 abcfile: Change main name of ASType 0x00 from Void to Undefined
Fixes issue #54.
2021-02-08 19:11:39 +00:00
Vladimir Panteleev a5153c7d22 CHANGELOG: Update 2021-02-08 19:11:39 +00:00
Vladimir Panteleev 7818a79009 Merge pull request #52 from Graham-G/patch-1
Updating dead links
2019-11-16 07:24:01 -06:00
Graham-G 656c2e01f4 Updating dead links 2019-11-16 03:42:05 +00:00
Vladimir Panteleev 0d2c02af46 zlibx: Improve robustness of zlib decompression 2016-12-24 00:29:57 +00:00
Vladimir Panteleev 086ee07659 zlibx: Improve "Wrong uncompressed file length" error message 2016-12-24 00:13:25 +00:00
Vladimir Panteleev efdde9271c zlibx: Make over-estimation of destlen non-fatal 2016-11-26 22:01:57 +00:00
Vladimir Panteleev f7c28ac340 assembler: Fix deprecation warnings 2016-11-26 21:53:06 +00:00
Vladimir Panteleev 17a70c6c9b asprogram: Ignore duplicate class names during dependency registration 2016-11-26 21:53:06 +00:00
Vladimir Panteleev 8454abf95e .gitignore 2016-11-26 21:53:06 +00:00
Vladimir Panteleev d68e7a1f7f .gitignore: dos2unix 2016-11-26 21:53:06 +00:00
Vladimir Panteleev 62f13d4bab swf7zcompress: Fix deprecation warnings 2016-11-26 21:53:06 +00:00
Vladimir Panteleev c1adeddbea build_rabcdasm: Fix deprecation warnings 2016-11-26 21:53:06 +00:00
Vladimir Panteleev 37131650e5 build_rabcdasm: Detect DMD bug 15861 2016-04-01 16:33:49 +00:00
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
Vladimir Panteleev ef8e86388a Update Changelog 2014-01-11 14:18:24 +00:00
Vladimir Panteleev d4f7a070b6 assembler: Memory/performance optimizations 2013-12-29 09:01:59 +00:00
Vladimir Panteleev e5e01ea90f assembler: Memory/performance optimizations 2013-12-29 07:57:27 +00:00
Vladimir Panteleev fbebd86f47 assembler: Memory/performance optimizations 2013-12-29 07:45:18 +00:00
Vladimir Panteleev e9fbbef9b7 assembler: Replace some casts with std.conv.to 2013-12-29 01:51:54 +00:00
Vladimir Panteleev 9c28d20e99 disassembler: Further optimize an optimization 2013-12-21 03:48:46 +00:00
Vladimir Panteleev 3449822efd disassembler: Use one buffer for all files 2013-12-21 03:38:29 +00:00
Vladimir Panteleev 3ded602166 disassembler: Avoid more memory allocations by using formattedWrite 2013-12-21 03:07:51 +00:00
Vladimir Panteleev 08008d5643 disassembler: Remove memory allocation from dumpDouble 2013-12-21 03:00:08 +00:00
Vladimir Panteleev 98e9198c36 disassembler: Flush output periodically to reduce memory usage 2013-12-21 02:45:36 +00:00
Vladimir Panteleev f6dbbf08ee diasassembler: Remove a redundant .dup 2013-12-21 02:09:34 +00:00
Vladimir Panteleev 4251e409a2 asprogram: Track TypeName names as dependencies as well 2013-11-12 18:38:08 +00:00
Vladimir Panteleev 7ad6949b45 asprogram: 64-bit fix 2013-11-12 16:58:59 +00:00
Vladimir Panteleev 9bd472e4f8 asprogram: Remove dead code 2013-11-12 05:43:15 +00:00
Vladimir Panteleev efada3d5c1 asprogram: Don't emit forward typename references (sort multinames topographically) 2013-11-12 05:19:27 +00:00
Vladimir Panteleev 05d9624f68 asprogram: Unify constant pool logic; sort doubles bitwise 2013-11-12 05:18:30 +00:00
Vladimir Panteleev a2ea734202 build_rabcdasm: Bypass /tmp/ noexec problems by using -od. 2013-11-09 02:58:16 +00:00
Vladimir Panteleev 32909f3893 Update Changelog 2013-08-21 21:11:03 +00:00
Vladimir Panteleev 472a9cec57 Add finddef instruction 2013-08-18 08:16:11 +00:00
Vladimir Panteleev 6cbb185114 disassembler: Use std.digest.md
Silences compiler deprecation notice.
2013-08-18 07:58:46 +00:00
Vladimir Panteleev f32f6fb128 abcfile: Add unimplemented {get|set|delete}propertylate instructions
Needed to handle code obfuscated by DoSWF.
Parameter list (empty) guessed judging by {as|is}typelate instructions.

Fixes issue #18.
2013-06-23 06:38:25 +00:00
Vladimir Panteleev d6d7ecf47e swflzmacompress: Add an option to update the SWF version number 2013-06-02 15:27:55 +00:00
Vladimir Panteleev 0cefc587e6 Fix DMD 2.063 slice warnings 2013-03-23 18:53:22 +00:00
Vladimir Panteleev a45b4e2bdc lzma: Work around liblzma dictionary size limitation 2013-03-18 11:25:04 +00:00
Vladimir Panteleev e383172765 lzma: Remove debugging code 2013-03-18 09:35:00 +00:00
Vladimir Panteleev c62b5438d7 Documentation: Update default DCFLAGS 2013-03-18 09:28:27 +00:00
Vladimir Panteleev a1a214ae72 build_rabcdasm: Allow overriding program list / build flags on command line 2013-03-18 09:28:09 +00:00
Vladimir Panteleev 222fa1109c Documentation: Update download link 2012-12-26 13:48:53 +02:00
Vladimir Panteleev 9fa2d122fa disassembler: Further improve refname generation by adding context priority 2012-12-02 12:06:06 +02:00
Vladimir Panteleev 93adc953c2 disassembler: Separate class and instance ref context 2012-12-02 12:05:34 +02:00
Vladimir Panteleev dc222f9e43 disassembler: Relax trait multiname kind restrictions 2012-12-01 14:05:33 +02:00
Vladimir Panteleev fb2f96b81a disassembler: Visit instance protected namespace 2012-12-01 14:01:19 +02:00
Vladimir Panteleev 14f5e669ee disassembler: Visit slot types 2012-11-27 15:23:24 +02:00
17 changed files with 1412 additions and 638 deletions
+18
View File
@@ -0,0 +1,18 @@
*.exe
*.dll
*.pdb
*.ilk
*.exp
*.lib
*.suo
/build_rabcdasm
/rabcasm
/rabcdasm
/abcexport
/abcreplace
/swfbinexport
/swfbinreplace
/swfdecompress
/swf7zcompress
/swflzmacompress
+60
View File
@@ -1,6 +1,66 @@
RABCDAsm Changelog
==================
RABCDAsm v1.19 (????.??.??)
---------------------------
* Ignore duplicate class names during dependency registration
* Make over-estimation of uncompressed SWF size non-fatal
* Improve robustness of zlib decompression
* Disassemble type `0x00` as `Undefined` instead of `Void`
- This matches the terminology used in the AVM specification.
- `Void` continues to be recognized as a synonym for `Undefined`.
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)
---------------------------
+12 -8
View File
@@ -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
-----
@@ -137,8 +141,8 @@ use [this handy list][avm2i] as well). You will find it difficult to
understand the disassembly without good understanding of concepts such as
namespaces and multinames.
[avm2]: http://www.adobe.com/devnet-archive/actionscript/articles/avm2overview.pdf
[avm2i]: http://www.anotherbigidea.com/javaswf/avm2/AVM2Instructions.html
[avm2]: https://www.adobe.com/content/dam/acom/en/devnet/pdf/avm2overview.pdf
[avm2i]: https://web.archive.org/web/20160215185222/www.anotherbigidea.com/javaswf/avm2/AVM2Instructions.html
Overview
--------
+251 -107
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2013, 2014, 2021 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;
@@ -262,8 +269,8 @@ class ABCFile
enum ASType : ubyte
{
Void = 0x00, // not actually interned
Undefined = Void,
Undefined = 0x00,
Void = Undefined, // not actually interned
Utf8 = 0x01,
Decimal = 0x02,
Integer = 0x03,
@@ -295,7 +302,7 @@ enum ASType : ubyte
}
string[ASType.Max] ASTypeNames = [
"Void",
"Undefined",
"Utf8",
"Decimal",
"Integer",
@@ -333,6 +340,7 @@ static this()
{
foreach (t, n; ASTypeNames)
ASTypeByName[n] = cast(ASType)t;
ASTypeByName["Void"] = ASType.Void;
ASTypeByName = ASTypeByName.rehash;
}
@@ -439,6 +447,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,
@@ -536,7 +545,7 @@ enum Opcode : ubyte
OP_getglobalscope = 0x64,
OP_getscopeobject = 0x65,
OP_getproperty = 0x66,
OP_getpropertylate = 0x67,
OP_getouterscope = 0x67,
OP_initproperty = 0x68,
OP_setpropertylate = 0x69,
OP_deleteproperty = 0x6A,
@@ -634,6 +643,7 @@ enum OpcodeArgumentType
{
Unknown,
ByteLiteral,
UByteLiteral,
IntLiteral,
UIntLiteral,
@@ -659,7 +669,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 +705,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 +764,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 +772,11 @@ const OpcodeInfo[256] opcodeInfo = [
/* 0x64 */ {"getglobalscope", []},
/* 0x65 */ {"getscopeobject", [OpcodeArgumentType.UByteLiteral]},
/* 0x66 */ {"getproperty", [OpcodeArgumentType.Multiname]},
/* 0x67 */ {"getpropertylate", [OpcodeArgumentType.Unknown]},
/* 0x67 */ {"getouterscope", [OpcodeArgumentType.UIntLiteral]},
/* 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 +936,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 +1311,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 +1531,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 +1939,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;
+2 -1
View File
@@ -110,6 +110,7 @@
</keywords>
<keywords ignorecase="no" region="ASType">
<word name="Undefined"/>
<word name="Void"/>
<word name="Utf8"/>
<word name="Decimal"/>
@@ -240,7 +241,7 @@
<word name="getglobalscope"/>
<word name="getscopeobject"/>
<word name="getproperty"/>
<word name="getpropertylate"/>
<word name="getouterscope"/>
<word name="initproperty"/>
<word name="setpropertylate"/>
<word name="deleteproperty"/>
+105 -108
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 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
@@ -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,10 +978,10 @@ 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());
//assert(!(c.instance.name in classByName), "Duplicate class name " ~ c.instance.name.toString());
classByName[c.instance.name] = c;
}
@@ -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
View File
File diff suppressed because it is too large Load Diff
+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 ~ ");";
}
+44 -17
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2013, 2016 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;
@@ -34,7 +35,7 @@ else
const DEFAULT_COMPILER = "gdmd";
const DEFAULT_FLAGS = "-O -inline";
const LZMA_FLAGS = "-version=HAVE_LZMA";
const LZMA_FLAGS = ["-version=HAVE_LZMA"];
import std.exception;
import std.file;
@@ -42,40 +43,66 @@ import std.process;
import std.stdio;
import std.string;
string compiler, flags;
string compiler;
string[] flags;
void compile(string program)
{
stderr.writeln("* Building ", program);
enforce(system(format("rdmd --build-only --compiler=%s %s %s", compiler, flags, program)) == 0, "Compilation of " ~ program ~ " failed");
enforce(spawnProcess(["rdmd", "--build-only", "--compiler=" ~ compiler] ~ flags ~ program).wait() == 0, "Compilation of " ~ program ~ " failed");
}
void test(string code, string extraFlags=null)
void test(string code, in 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(spawnProcess(["rdmd", "--force", "--compiler=" ~ compiler, "-od."] ~ flags ~ extraFlags ~ FN).wait() == 0, "Test failed");
stderr.writeln(" >>> OK");
}
int main()
void testBug(string description, int bugId, string code)
{
stderr.writefln("* Checking for compiler bug %d...", bugId);
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
{
compiler = getenv("DC");
if (compiler is null)
compiler = DEFAULT_COMPILER;
auto programs = ["rabcasm", "rabcdasm", "abcexport", "abcreplace", "swfbinexport", "swfbinreplace", "swfdecompress", "swf7zcompress"];
flags = getenv("DCFLAGS");
if (flags is null)
flags = DEFAULT_FLAGS;
compiler = environment.get("DC", DEFAULT_COMPILER);
flags = environment.get("DCFLAGS", DEFAULT_FLAGS).split(" ");
string[] optionArgs, programArgs;
foreach (arg; args[1..$])
(arg.startsWith("-") ? optionArgs : programArgs) ~= arg;
if (optionArgs.length)
flags = optionArgs;
if (programArgs.length)
programs = programArgs;
stderr.writeln("* Checking for working compiler...");
test(`
void main() {}
`);
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; }
`);
testBug("[REG 2.069] Wrong double-to-string conversion with -O", 15861, `
import std.format; int main() { return format("%.18g", 4286853117.0) == "4286853117" ? 0 : 1; }
`);
bool haveLZMA;
stderr.writeln("* Checking for LZMA...");
@@ -101,9 +128,9 @@ int main()
stderr.writeln(" >>> LZMA not found, building without LZMA support.");
if (haveLZMA)
flags ~= " " ~ LZMA_FLAGS;
flags ~= LZMA_FLAGS;
foreach (program; ["rabcasm", "rabcdasm", "abcexport", "abcreplace", "swfbinexport", "swfbinreplace", "swfdecompress", "swf7zcompress"])
foreach (program; programs)
compile(program);
if (haveLZMA)
+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;
}
+274 -139
View File
@@ -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,61 @@ 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;
}
void opOpAssign(string op : "~", V)(V s) {
put(s);
}
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 +241,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 +296,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 +385,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 +479,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 +506,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 +538,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 +577,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 +664,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 +700,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 +720,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 +808,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 +822,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 +833,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 +851,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 +878,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 +959,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 +972,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 +1015,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 +1028,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 +1055,7 @@ final class Disassembler
StringBuilder sb = new StringBuilder(dir ~ "/" ~ name ~ ".main.asasm");
sb ~= "#version 3";
sb ~= "#version 4";
sb.newLine();
sb ~= "program";
@@ -1020,7 +1117,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 +1125,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 +1143,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 +1565,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 +1583,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 +1609,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 +1634,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 +1658,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 +1676,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 +1704,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 +1827,7 @@ static this()
Opcode.OP_si32,
Opcode.OP_sf32,
Opcode.OP_sf64,
Opcode.OP_throw,
])
newLineAfter[o] = true;
}
+43 -12
View File
@@ -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;
}
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2016 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -71,7 +71,7 @@ void main(string[] args)
if (header.fileLength != data.length + 8)
throw new Exception("Incorrect file length in file header");
write(arg ~ ".tempdata", data);
if (system(`7z a -tgzip -mx=9 -mfb=258 "` ~ arg ~ `.tempdata.gz" "` ~ arg ~ `.tempdata"`) || !exists(arg ~ ".tempdata.gz"))
if (spawnProcess(["7z", "a", "-tgzip", "-mx=9", "-mfb=258", arg ~ ".tempdata.gz", arg ~ ".tempdata"]).wait() || !exists(arg ~ ".tempdata.gz"))
throw new Exception("7-Zip failed");
remove(arg ~ ".tempdata");
auto gzipdata = cast(ubyte[])read(arg ~ ".tempdata.gz");
+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();
+35 -3
View File
@@ -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());
}
+48 -10
View File
@@ -2,9 +2,12 @@
module zlibx;
import core.memory;
import std.string : format;
import std.zlib, etc.c.zlib, std.conv;
static import etc.c.zlib;
alias std.zlib.Z_SYNC_FLUSH Z_SYNC_FLUSH;
debug import std.stdio : stderr;
/// Avoid bug(?) in D zlib implementation with 7zip-generated zlib streams
ubyte[] exactUncompress(ubyte[] srcbuf, size_t destlen)
@@ -23,24 +26,59 @@ ubyte[] exactUncompress(ubyte[] srcbuf, size_t destlen)
err = etc.c.zlib.inflateInit2(&zs, 15);
if (err)
{
delete destbuf;
GC.free(destbuf.ptr);
throw new ZlibException(err);
}
err = etc.c.zlib.inflate(&zs, Z_SYNC_FLUSH);
if (err != Z_OK && err != Z_STREAM_END)
while (true)
{
Lerr:
delete destbuf;
etc.c.zlib.inflateEnd(&zs);
throw new ZlibException(err);
err = etc.c.zlib.inflate(&zs, Z_SYNC_FLUSH);
if (err != Z_OK && err != Z_STREAM_END)
{
Lerr:
GC.free(destbuf.ptr);
etc.c.zlib.inflateEnd(&zs);
throw new ZlibException(err);
}
if (err == Z_STREAM_END)
break;
else
if (zs.avail_out == 0)
{
debug stderr.writefln("Wrong uncompressed file length (read %d/%d bytes and wrote %d/%d bytes)",
srcbuf .length - zs.avail_in , srcbuf .length,
destlen - zs.avail_out, destlen);
auto out_pos = zs.next_out - destbuf.ptr;
destbuf.length = 1024 + destbuf.length * 2;
zs.next_out = destbuf.ptr + out_pos;
zs.avail_out = to!uint(destbuf.length - out_pos);
continue;
}
else
if (zs.avail_in == 0)
{
debug stderr.writefln("Unterminated Zlib stream (read %d/%d bytes and wrote %d/%d bytes)",
srcbuf .length - zs.avail_in , srcbuf .length,
destlen - zs.avail_out, destlen);
break;
}
else
throw new Exception(format("Unexpected zlib state (err=%d, avail_in == %d, avail_out = %d)",
err, zs.avail_in , zs.avail_out));
}
if (zs.avail_out != 0)
throw new Exception("Too little data in stream");
if (zs.avail_in != 0 || zs.avail_out != 0)
debug stderr.writefln("Zlib stream incongruity (read %d/%d bytes and wrote %d/%d bytes)",
srcbuf .length - zs.avail_in , srcbuf .length,
destlen - zs.avail_out, destlen);
err = etc.c.zlib.inflateEnd(&zs);
if (err != Z_OK)
goto Lerr;
return destbuf;
if (zs.avail_out != 0)
debug stderr.writefln("Too little data in zlib stream: expected %d, got %d", destlen, destlen - zs.avail_out);
return destbuf[0..$-zs.avail_out];
}