Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48b221535f | ||
|
|
beb7229bf8 | ||
|
|
296196ff71 | ||
|
|
170e7d6a8b | ||
|
|
2c63eb592a | ||
|
|
16a5f649de | ||
|
|
111cd75eb4 | ||
|
|
e103048af6 | ||
|
|
61df70dd0e | ||
|
|
51536e4add | ||
|
|
fb5c03ada5 | ||
|
|
2226b0059f | ||
|
|
69cf64b85c | ||
|
|
2b49e77267 | ||
|
|
fab198a648 | ||
|
|
4e4fd2b56d | ||
|
|
7e4f7f5bac | ||
|
|
7a92e9fe81 | ||
|
|
41002f7085 | ||
|
|
3b131fa932 | ||
|
|
34316bd4c9 |
+95
-81
@@ -1,81 +1,95 @@
|
||||
RABCDAsm Changelog
|
||||
==================
|
||||
|
||||
RABCDAsm v1.6 (2011.05.13)
|
||||
--------------------------
|
||||
|
||||
* Overhaul of the structure of disassembler-generated files
|
||||
* Private namespace names and `refid`s for nested classes are now generated
|
||||
from their referenced contexts
|
||||
* This also fixes problems caused by private namespaces being referenced
|
||||
in multiple distinct contexts
|
||||
* One `#include`d file per script instead of per class
|
||||
* Nested classes, script initializers and other script traits are now in
|
||||
the same file as their main class
|
||||
* Inline methods are now in separate files, placed according to their usage
|
||||
context
|
||||
* Private namespaces are sorted by their autogenerated names in the
|
||||
`.privatens.asasm` file
|
||||
* No syntax changes, output is backwards-compatible
|
||||
* Improved escaping of file names
|
||||
* Output correct opcode error locations
|
||||
* Disassembler will now refuse to overwrite files
|
||||
* `rabcdasm` will now always generate output in the same directory as the
|
||||
input file
|
||||
* Failure to decode a method body is now not a fatal error
|
||||
* Aggressively try to shorten double literals to their shortest representation
|
||||
* Added the `swfbinexport` and `swfbinreplace` utilities, to aid in manipulating
|
||||
contents of `DefineBinaryData` tags
|
||||
* The package can now be built using a simple, dedicated build tool
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.5 (2011.04.14)
|
||||
--------------------------
|
||||
|
||||
* Fixed v1.4 constant pool regression
|
||||
* Added support for memory-access and sign-extend opcodes
|
||||
* Speed optimizations
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.4 (2011.03.07)
|
||||
--------------------------
|
||||
|
||||
* Source code ported to D2
|
||||
* Add support for forward-references for TypeName-kind Multinames
|
||||
* Correctly order classes by dependencies (extends/implements) and reference
|
||||
count
|
||||
* Finish Metadata support
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.3 (2010.11.11)
|
||||
--------------------------
|
||||
|
||||
* Fixed double precision problem
|
||||
* This also fixes problems with illegal default values for function
|
||||
parameters (default values for integer parameters are stored as doubles,
|
||||
which might become out-of-range due to inadequate double precision)
|
||||
* Added Changelog
|
||||
* Documentation markdown fixes
|
||||
|
||||
RABCDAsm v1.2 (2010.11.06)
|
||||
--------------------------
|
||||
|
||||
* Fixed ref generation for orphan objects which were only referenced
|
||||
by other orphans
|
||||
* Better error handling in `abcexport`; warn when no DoABC tags found
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.1 (2010.06.30)
|
||||
--------------------------
|
||||
|
||||
* Private namespaces are now referenced by auto-generated names
|
||||
* Use `:` to delimit namespace and name in QNames for consistency
|
||||
* Fixed relative include paths
|
||||
* Add optional byte offsets to labels, which allows lossless representation
|
||||
of jumps inside instructions and outside the function bounds
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.0 (2010.05.05)
|
||||
--------------------------
|
||||
|
||||
* Initial release.
|
||||
RABCDAsm Changelog
|
||||
==================
|
||||
|
||||
RABCDAsm v1.7 (2011.06.14)
|
||||
--------------------------
|
||||
|
||||
* Fixed handling of invalid UTF-8 in strings
|
||||
* Fixed handling of null namespaces and multinames
|
||||
* Fixed handling of exception info in corrupted method bodies
|
||||
* Fixed handling of orphan private namespaces
|
||||
* Fixed handling of out-of-bounds class/method indices
|
||||
* Disassembler now creates one file per class and script-level method
|
||||
* Disassembler now outputs blank lines after `si32` etc. instructions as well
|
||||
* `abcexport` now delimits the index with a dash in output filenames
|
||||
* Improved shortening of `double` literals
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.6 (2011.05.13)
|
||||
--------------------------
|
||||
|
||||
* Overhaul of the structure of disassembler-generated files
|
||||
* Private namespace names and `refid`s for nested classes are now generated
|
||||
from their referenced contexts
|
||||
* This also fixes problems caused by private namespaces being referenced
|
||||
in multiple distinct contexts
|
||||
* One `#include`d file per script instead of per class
|
||||
* Nested classes, script initializers and other script traits are now in
|
||||
the same file as their main class
|
||||
* Inline methods are now in separate files, placed according to their usage
|
||||
context
|
||||
* Private namespaces are sorted by their autogenerated names in the
|
||||
`.privatens.asasm` file
|
||||
* No syntax changes, output is backwards-compatible
|
||||
* Improved escaping of file names
|
||||
* Output correct opcode error locations
|
||||
* Disassembler will now refuse to overwrite files
|
||||
* `rabcdasm` will now always generate output in the same directory as the
|
||||
input file
|
||||
* Failure to decode a method body is now not a fatal error
|
||||
* Aggressively try to shorten double literals to their shortest representation
|
||||
* Added the `swfbinexport` and `swfbinreplace` utilities, to aid in manipulating
|
||||
contents of `DefineBinaryData` tags
|
||||
* The package can now be built using a simple, dedicated build tool
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.5 (2011.04.14)
|
||||
--------------------------
|
||||
|
||||
* Fixed v1.4 constant pool regression
|
||||
* Added support for memory-access and sign-extend opcodes
|
||||
* Speed optimizations
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.4 (2011.03.07)
|
||||
--------------------------
|
||||
|
||||
* Source code ported to D2
|
||||
* Add support for forward-references for TypeName-kind Multinames
|
||||
* Correctly order classes by dependencies (extends/implements) and reference
|
||||
count
|
||||
* Finish Metadata support
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.3 (2010.11.11)
|
||||
--------------------------
|
||||
|
||||
* Fixed double precision problem
|
||||
* This also fixes problems with illegal default values for function
|
||||
parameters (default values for integer parameters are stored as doubles,
|
||||
which might become out-of-range due to inadequate double precision)
|
||||
* Added Changelog
|
||||
* Documentation markdown fixes
|
||||
|
||||
RABCDAsm v1.2 (2010.11.06)
|
||||
--------------------------
|
||||
|
||||
* Fixed ref generation for orphan objects which were only referenced
|
||||
by other orphans
|
||||
* Better error handling in `abcexport`; warn when no DoABC tags found
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.1 (2010.06.30)
|
||||
--------------------------
|
||||
|
||||
* Private namespaces are now referenced by auto-generated names
|
||||
* Use `:` to delimit namespace and name in QNames for consistency
|
||||
* Fixed relative include paths
|
||||
* Add optional byte offsets to labels, which allows lossless representation
|
||||
of jumps inside instructions and outside the function bounds
|
||||
* Documentation updates
|
||||
|
||||
RABCDAsm v1.0 (2010.05.05)
|
||||
--------------------------
|
||||
|
||||
* Initial release.
|
||||
|
||||
@@ -17,7 +17,7 @@ These are:
|
||||
`abcexport` and `abcreplace` are reimplementations of similar utilities from
|
||||
my [swfutilsex][] Java package, however these work faster as they do not parse
|
||||
the SWF files as deeply.
|
||||
`swfdecompress` is ancilliary and is only useful for debugging and studying of
|
||||
`swfdecompress` is ancillary and is only useful for debugging and studying of
|
||||
the SWF file format, and not required for ABC manipulation. It is functionally
|
||||
equivalent to [flasm][]'s `-x` option.
|
||||
`swf7zcompress` is an utility to further reduce the size of SWF files. It uses
|
||||
@@ -87,25 +87,29 @@ To begin hacking on a SWF file:
|
||||
|
||||
abcexport file.swf
|
||||
|
||||
This will create `file0.abc` ... `fileN.abc` (often just `file0.abc`). Each
|
||||
This will create `file-0.abc` ... `file-N.abc` (often just `file-0.abc`). Each
|
||||
file corresponds to an ABC block inside the SWF file.
|
||||
|
||||
To disassemble one of the `.abc` files:
|
||||
|
||||
rabcdasm file0.abc
|
||||
rabcdasm file-0.abc
|
||||
|
||||
This will create a `file0` directory, which will contain `file0.main.asasm`
|
||||
(the main program file), `file0.privatens.asasm` (private namespace alias
|
||||
definitions) and a file per ActionScript class.
|
||||
This will create a `file-0` directory, which will contain `file-0.main.asasm`
|
||||
(the main program file), `file-0.privatens.asasm` (private namespace alias
|
||||
definitions), and files for ActionScript scripts, classes, and orphan and
|
||||
script-level methods.
|
||||
|
||||
To assemble the `.asasm` files back, and update the SWF file:
|
||||
|
||||
rabcasm file0/file0.main.asasm
|
||||
abcreplace file0.swf 0 file0/file0.main.abc
|
||||
rabcasm file-0/file-0.main.asasm
|
||||
abcreplace file-0.swf 0 file-0/file-0.main.abc
|
||||
|
||||
The second `abcreplace` argument represents the index of the ABC block in the
|
||||
SWF file, and corresponds to the number in the filename created by `abcexport`.
|
||||
|
||||
`swfbinexport` and `swfbinreplace` are used in the same manner as `abcexport`
|
||||
and `abcreplace`.
|
||||
|
||||
Syntax
|
||||
======
|
||||
|
||||
@@ -247,7 +251,7 @@ namespace. Internally (the ABC file format), private namespaces are
|
||||
distinguished by a numerical index - `rabcdasm` will attempt to give them
|
||||
descriptive names based on their context. Aliases can be defined using the
|
||||
`#privatens` directive. `rabcdasm` will create a separate file containing the
|
||||
aliases (`file0.privatens.asasm`).
|
||||
aliases (`file-0.privatens.asasm`).
|
||||
|
||||
Strings have a syntax similar to C string literals. Strings start and end with
|
||||
a `"`. Supported escape sequences (a backslash followed by a letter) are `\n`
|
||||
@@ -393,7 +397,7 @@ RABCDAsm users.
|
||||
|
||||
[debug Flash Player]: http://www.adobe.com/support/flashplayer/downloads.html
|
||||
|
||||
3. The [Fiddler][] Web Debugging Proxy can be very useful for analysing
|
||||
3. The [Fiddler][] Web Debugging Proxy can be very useful for analyzing
|
||||
websites with SWF content. The following script fragment (which is to be
|
||||
placed in the `OnBeforeResponse` function) will automatically save all SWF
|
||||
files while preserving the directory structure.
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ void main(string[] args)
|
||||
while (*p++) {} // skip name
|
||||
abc = tag.data[p-tag.data.ptr..$];
|
||||
}
|
||||
std.file.write(getName(arg) ~ to!string(count++) ~ ".abc", abc);
|
||||
std.file.write(getName(arg) ~ "-" ~ to!string(count++) ~ ".abc", abc);
|
||||
}
|
||||
if (count == 0)
|
||||
throw new Exception("No DoABC tags found");
|
||||
|
||||
@@ -1398,6 +1398,7 @@ private final class ABCReader
|
||||
{
|
||||
r.instructions = null;
|
||||
r.error = e.msg;
|
||||
instructionAtOffset[] = 0;
|
||||
}
|
||||
pos = end;
|
||||
|
||||
|
||||
+17
-8
@@ -449,6 +449,7 @@ private final class ABCtoAS
|
||||
n.vTypeName.params.length = multiname.TypeName.params.length;
|
||||
foreach (j, param; multiname.TypeName.params)
|
||||
n.vTypeName.params[j] = multinames[param];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -627,10 +628,10 @@ private final class ABCtoAS
|
||||
r.arguments[i].multinamev = multinames[instruction.arguments[i].index];
|
||||
break;
|
||||
case OpcodeArgumentType.Class:
|
||||
r.arguments[i].classv = classes[instruction.arguments[i].index];
|
||||
r.arguments[i].classv = classes.checkedGet(instruction.arguments[i].index);
|
||||
break;
|
||||
case OpcodeArgumentType.Method:
|
||||
r.arguments[i].methodv = methods[instruction.arguments[i].index];
|
||||
r.arguments[i].methodv = methods.checkedGet(instruction.arguments[i].index);
|
||||
break;
|
||||
|
||||
case OpcodeArgumentType.JumpTarget:
|
||||
@@ -1065,15 +1066,12 @@ private final class AStoABC
|
||||
|
||||
void visitClass(ASProgram.Class vclass)
|
||||
{
|
||||
if (classes.notAdded(vclass))
|
||||
if (classes.add(vclass))
|
||||
{
|
||||
visitMethod(vclass.cinit);
|
||||
visitTraits(vclass.traits);
|
||||
|
||||
visitInstance(vclass.instance);
|
||||
|
||||
bool r = classes.add(vclass);
|
||||
assert(r, "Recursive class reference");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1483,10 +1481,16 @@ private final class AStoABC
|
||||
r.arguments[i].index = multinames.get(instruction.arguments[i].multinamev);
|
||||
break;
|
||||
case OpcodeArgumentType.Class:
|
||||
r.arguments[i].index = classes.get(instruction.arguments[i].classv);
|
||||
if (instruction.arguments[i].classv is null)
|
||||
r.arguments[i].index = abc.classes.length;
|
||||
else
|
||||
r.arguments[i].index = classes.get(instruction.arguments[i].classv);
|
||||
break;
|
||||
case OpcodeArgumentType.Method:
|
||||
r.arguments[i].index = methods.get(instruction.arguments[i].methodv);
|
||||
if (instruction.arguments[i].methodv is null)
|
||||
r.arguments[i].index = abc.methods.length;
|
||||
else
|
||||
r.arguments[i].index = methods.get(instruction.arguments[i].methodv);
|
||||
break;
|
||||
|
||||
case OpcodeArgumentType.JumpTarget:
|
||||
@@ -1557,3 +1561,8 @@ private bool contains(T)(T[] arr, T val)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private T checkedGet(T)(T[] array, uint index, T def = null)
|
||||
{
|
||||
return index < array.length ? array[index] : def;
|
||||
}
|
||||
|
||||
+20
-14
@@ -1175,26 +1175,32 @@ final class Assembler
|
||||
readProgram();
|
||||
|
||||
foreach (ref f; classFixups)
|
||||
{
|
||||
auto cp = f.name in classesByID;
|
||||
if (cp is null)
|
||||
if (f.name is null)
|
||||
*f.ptr = null;
|
||||
else
|
||||
{
|
||||
setFile(f.where.load);
|
||||
throw new Exception("Unknown class refid: " ~ f.name);
|
||||
auto cp = f.name in classesByID;
|
||||
if (cp is null)
|
||||
{
|
||||
setFile(f.where.load);
|
||||
throw new Exception("Unknown class refid: " ~ f.name);
|
||||
}
|
||||
*f.ptr = *cp;
|
||||
}
|
||||
*f.ptr = *cp;
|
||||
}
|
||||
|
||||
foreach (ref f; methodFixups)
|
||||
{
|
||||
auto mp = f.name in methodsByID;
|
||||
if (mp is null)
|
||||
if (f.name is null)
|
||||
*f.ptr = null;
|
||||
else
|
||||
{
|
||||
setFile(f.where.load);
|
||||
throw new Exception("Unknown method refid: " ~ f.name);
|
||||
auto mp = f.name in methodsByID;
|
||||
if (mp is null)
|
||||
{
|
||||
setFile(f.where.load);
|
||||
throw new Exception("Unknown method refid: " ~ f.name);
|
||||
}
|
||||
*f.ptr = *mp;
|
||||
}
|
||||
*f.ptr = *mp;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
+27
-5
@@ -20,6 +20,7 @@ module autodata;
|
||||
|
||||
import murmurhash2a;
|
||||
import std.traits;
|
||||
public import std.conv;
|
||||
|
||||
string addAutoField(string name, bool reverseSort = false)
|
||||
{
|
||||
@@ -75,7 +76,7 @@ template AutoCompare()
|
||||
template AutoToString()
|
||||
{
|
||||
static if (is(typeof(this)==class))
|
||||
override string toString() { return _AutoDataToString(); }
|
||||
override string toString() const { return _AutoDataToString(); }
|
||||
else // struct
|
||||
string toString() { return _AutoDataToString(); }
|
||||
|
||||
@@ -119,8 +120,11 @@ template RawDataHandlerWrapper()
|
||||
static if (!hasAliasing!(T))
|
||||
enum getMixinRecursive = getRawMixin!("&" ~ name, name ~ ".sizeof");
|
||||
else
|
||||
static if (is(typeof(this)==struct) || is(typeof(this)==class))
|
||||
static if (is(T==struct))
|
||||
enum getMixinRecursive = name ~ ".processData!(void, ``, ``)(handler);";
|
||||
else
|
||||
static if (is(T==class))
|
||||
enum getMixinRecursive = "if ("~name~" !is null) " ~ name ~ ".processData!(void, ``, ``)(handler);";
|
||||
else
|
||||
static assert(0, "Don't know how to process type: " ~ T.stringof);
|
||||
}
|
||||
@@ -144,7 +148,7 @@ struct EqualsDataHandler(O)
|
||||
|
||||
template nullCheck(T, string name)
|
||||
{
|
||||
static if (is(T U : U[]))
|
||||
static if (is(typeof(T.init is null)))
|
||||
enum nullCheck = "if ((this." ~ name ~ " is null) != (_AutoDataOther." ~ name ~ " is null)) return false;";
|
||||
else
|
||||
enum nullCheck = "";
|
||||
@@ -165,6 +169,23 @@ struct CmpDataHandler(O)
|
||||
enum getMixin = getMixinComposite!(T, name, reverseSort).code;
|
||||
}
|
||||
|
||||
template nullCheck(T, string name, string reverseStr)
|
||||
{
|
||||
static if (is(typeof(T.init is null)))
|
||||
enum nullCheck = "
|
||||
if (this."~name~" is null && _AutoDataOther."~name~" is null)
|
||||
{ /* skip */ }
|
||||
else
|
||||
if (this."~name~" is null && _AutoDataOther."~name~" !is null)
|
||||
return " ~ reverseStr ~ "(-1);
|
||||
else
|
||||
if (this."~name~" !is null && _AutoDataOther."~name~" is null)
|
||||
return " ~ reverseStr ~ "( 1);
|
||||
else";
|
||||
else
|
||||
enum nullCheck = "";
|
||||
}
|
||||
|
||||
template getMixinComposite(T, string name, bool reverseSort)
|
||||
{
|
||||
enum reverseStr = reverseSort ? "-" : "";
|
||||
@@ -180,7 +201,8 @@ struct CmpDataHandler(O)
|
||||
enum dataCode = "{ int _AutoDataCmp = this." ~ name ~ " - _AutoDataOther." ~ name ~ "; if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }"; // TODO: use long?
|
||||
else
|
||||
static if (is(typeof(T.opCmp)))
|
||||
enum dataCode = "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
|
||||
enum dataCode = nullCheck!(T, name, reverseStr)
|
||||
~ "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
|
||||
else
|
||||
enum dataCode = "if (this." ~ name ~ " < _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "(-1);" ~
|
||||
"if (this." ~ name ~ " > _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "( 1);";
|
||||
@@ -192,6 +214,6 @@ struct ToStringDataHandler
|
||||
{
|
||||
template getMixin(T, string name, bool reverseSort)
|
||||
{
|
||||
enum getMixin = "_AutoDataResult ~= format(`%s = %s `, `" ~ name ~ "`, this." ~ name ~ ");";
|
||||
enum getMixin = "_AutoDataResult ~= `" ~ name ~ " = ` ~ to!string(this." ~ name ~ ") ~ ` `;";
|
||||
}
|
||||
}
|
||||
|
||||
+107
-36
@@ -23,10 +23,13 @@ import std.string;
|
||||
import std.conv;
|
||||
import std.exception;
|
||||
import std.algorithm;
|
||||
import std.path;
|
||||
import abcfile;
|
||||
import asprogram;
|
||||
import autodata;
|
||||
|
||||
alias std.array.join join;
|
||||
|
||||
final class StringBuilder
|
||||
{
|
||||
char[] buf;
|
||||
@@ -127,10 +130,9 @@ final class RefBuilder : ASTraitsVisitor
|
||||
{
|
||||
case Type.Multiname:
|
||||
assert(multiname.kind == ASType.QName);
|
||||
if (multiname.vQName.ns.kind == ASType.PrivateNamespace)
|
||||
throw new Exception("Stringifying unexpanded context");
|
||||
|
||||
auto nsName = multiname.vQName.ns.name;
|
||||
if (multiname.vQName.ns.kind == ASType.PrivateNamespace) // unexpanded private namespace
|
||||
nsName ~= '#' ~ to!string(multiname.vQName.ns.privateIndex);
|
||||
if (nsName.length)
|
||||
if (multiname.vQName.name.length)
|
||||
return [Segment('/', nsName), Segment(filename ? '/' : ':', multiname.vQName.name)];
|
||||
@@ -146,29 +148,29 @@ final class RefBuilder : ASTraitsVisitor
|
||||
}
|
||||
}
|
||||
|
||||
static ContextItem[] expand(RefBuilder refs, ContextItem[] context, bool noThrow) /// recursively expand all private namespaces
|
||||
static ContextItem[] expand(RefBuilder refs, ContextItem[] context, bool abortOnUnknown) /// recursively expand all private namespaces
|
||||
{
|
||||
ContextItem[] newContext;
|
||||
foreach (ref c; context)
|
||||
{
|
||||
auto cEx = c.expand(refs, noThrow);
|
||||
auto cEx = c.expand(refs, abortOnUnknown);
|
||||
if (cEx is null) return null;
|
||||
newContext ~= cEx;
|
||||
}
|
||||
return newContext;
|
||||
}
|
||||
|
||||
ContextItem[] expand(RefBuilder refs, bool noThrow)
|
||||
ContextItem[] expand(RefBuilder refs, bool abortOnUnknown)
|
||||
{
|
||||
if (type==Type.Multiname && multiname.vQName.ns.kind == ASType.PrivateNamespace)
|
||||
{
|
||||
auto pcontext = multiname.vQName.ns.privateIndex in refs.privateNamespaces.contexts;
|
||||
if (pcontext is null)
|
||||
if (noThrow)
|
||||
if (abortOnUnknown)
|
||||
return null;
|
||||
else
|
||||
throw new Exception("Expanding unknown private namespace " ~ to!string(multiname.vQName.ns.privateIndex));
|
||||
auto expanded = expand(refs, *pcontext, noThrow);
|
||||
return (&this)[0..1];
|
||||
auto expanded = expand(refs, *pcontext, abortOnUnknown);
|
||||
if (expanded is null) return null;
|
||||
return expanded ~ (multiname.vQName.name.length ? [ContextItem(multiname.vQName.name)] : null); // hack
|
||||
}
|
||||
@@ -229,7 +231,6 @@ final class RefBuilder : ASTraitsVisitor
|
||||
if (pexisting)
|
||||
{
|
||||
auto rootContext = contextRoot(*pexisting, context);
|
||||
enforce(rootContext.length, format("Can't find common private namespace root between ", *pexisting, " and ", context));
|
||||
contexts[p] = rootContext;
|
||||
}
|
||||
else
|
||||
@@ -285,7 +286,7 @@ final class RefBuilder : ASTraitsVisitor
|
||||
version (Windows)
|
||||
static string[string] filenameMappings;
|
||||
|
||||
string getFilename(U)(U obj)
|
||||
string getFilename(U)(U obj, string suffix)
|
||||
{
|
||||
auto pname = cast(T)obj in filenames;
|
||||
assert(pname, format("Unscanned object: ", obj));
|
||||
@@ -310,7 +311,7 @@ final class RefBuilder : ASTraitsVisitor
|
||||
filename = join(dirSegments, "/");
|
||||
}
|
||||
|
||||
return filename ~ ".asasm";
|
||||
return filename ~ "." ~ suffix ~ ".asasm";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,7 +344,10 @@ final class RefBuilder : ASTraitsVisitor
|
||||
if (c)
|
||||
classContexts ~= c;
|
||||
}
|
||||
context = reduce!contextRoot(new ContextItem[0], classContexts);
|
||||
if (classContexts.length)
|
||||
context = reduce!contextRoot(classContexts);
|
||||
else
|
||||
context = null;
|
||||
if (context.length==0)
|
||||
pushContext("script_" ~ to!string(i));
|
||||
scripts.add(v, context);
|
||||
@@ -366,6 +370,20 @@ final class RefBuilder : ASTraitsVisitor
|
||||
popContext();
|
||||
}
|
||||
|
||||
// add private namespaces referenced only at script level
|
||||
foreach (v; as.scripts)
|
||||
foreach (trait; v.traits)
|
||||
if (trait.name.kind == ASType.QName && trait.name.vQName.ns.kind == ASType.PrivateNamespace && !privateNamespaces.isAdded(trait.name.vQName.ns.privateIndex))
|
||||
privateNamespaces.add(trait.name.vQName.ns.privateIndex, scripts.getContext(v));
|
||||
|
||||
foreach (privateIndex, b; possibleOrphanPrivateNamespaces)
|
||||
if (!privateNamespaces.isAdded(privateIndex))
|
||||
{
|
||||
pushContext("private_namespace_" ~ to!string(privateIndex));
|
||||
privateNamespaces.add(privateIndex, context);
|
||||
popContext();
|
||||
}
|
||||
|
||||
privateNamespaces.coagulate(this);
|
||||
objects.coagulate(this);
|
||||
scripts.coagulate(this);
|
||||
@@ -413,7 +431,6 @@ final class RefBuilder : ASTraitsVisitor
|
||||
static bool uninteresting(ContextItem[] c)
|
||||
{
|
||||
return
|
||||
(c.length==0) ||
|
||||
(c.length==1 && c[0].type==ContextItem.Type.String && c[0].str.startsWith("script_") && c[0].str.endsWith("_sinit")) ||
|
||||
(c.length==1 && c[0].type==ContextItem.Type.String && c[0].str.startsWith("orphan_method_")) ||
|
||||
false;
|
||||
@@ -452,8 +469,11 @@ final class RefBuilder : ASTraitsVisitor
|
||||
return c;
|
||||
}
|
||||
|
||||
bool[uint] possibleOrphanPrivateNamespaces;
|
||||
|
||||
void visitNamespace(ASProgram.Namespace ns)
|
||||
{
|
||||
if (ns is null) return;
|
||||
if (ns.kind == ASType.PrivateNamespace)
|
||||
{
|
||||
assert(context.length > 0, "No context");
|
||||
@@ -467,7 +487,10 @@ final class RefBuilder : ASTraitsVisitor
|
||||
break;
|
||||
}
|
||||
if (myPos == 0)
|
||||
{
|
||||
possibleOrphanPrivateNamespaces[ns.privateIndex] = true;
|
||||
return;
|
||||
}
|
||||
auto myContext = context[0..myPos].dup;
|
||||
|
||||
privateNamespaces.add(ns.privateIndex, myContext);
|
||||
@@ -482,6 +505,7 @@ final class RefBuilder : ASTraitsVisitor
|
||||
|
||||
void visitMultiname(ASProgram.Multiname m)
|
||||
{
|
||||
if (m is null) return;
|
||||
with (m)
|
||||
switch (kind)
|
||||
{
|
||||
@@ -569,7 +593,7 @@ final class RefBuilder : ASTraitsVisitor
|
||||
foreach (i, ref s; segments)
|
||||
strings[i] = (i>0 ? cast(string)[s.delim] : null) ~ escape(s.str);
|
||||
|
||||
return join(strings);
|
||||
return arrayJoin(strings);
|
||||
}
|
||||
|
||||
void addObject(T)(T obj) { objects.add(obj, context); }
|
||||
@@ -599,17 +623,27 @@ final class Disassembler
|
||||
string name, dir;
|
||||
RefBuilder refs;
|
||||
|
||||
void newInclude(StringBuilder mainsb, string filename, void delegate(StringBuilder) callback)
|
||||
void newInclude(StringBuilder mainsb, string filename, void delegate(StringBuilder) callback, bool doInline = true)
|
||||
{
|
||||
if (mainsb.filename.split("/").length != 2)
|
||||
throw new Exception("TODO");
|
||||
StringBuilder sb = new StringBuilder(dir ~ "/" ~ filename);
|
||||
callback(sb);
|
||||
sb.save();
|
||||
if (doInline)
|
||||
{
|
||||
string base = dirname(mainsb.filename);
|
||||
string full = dir ~ "/" ~ filename;
|
||||
uint up = 0;
|
||||
while (!full.startsWith(base))
|
||||
base = dirname(base), up++;
|
||||
string rel = repeat("../", up) ~ full[base.length+1..$];
|
||||
|
||||
mainsb ~= "#include ";
|
||||
dumpString(mainsb, filename);
|
||||
mainsb.newLine();
|
||||
StringBuilder sb = new StringBuilder(full);
|
||||
callback(sb);
|
||||
sb.save();
|
||||
|
||||
mainsb ~= "#include ";
|
||||
dumpString(mainsb, rel);
|
||||
mainsb.newLine();
|
||||
}
|
||||
else
|
||||
callback(mainsb);
|
||||
}
|
||||
|
||||
this(ASProgram as, string dir, string name)
|
||||
@@ -643,7 +677,7 @@ final class Disassembler
|
||||
|
||||
foreach (i, script; as.scripts)
|
||||
{
|
||||
newInclude(sb, refs.scripts.getFilename(script), (StringBuilder sb) {
|
||||
newInclude(sb, refs.scripts.getFilename(script, "script"), (StringBuilder sb) {
|
||||
dumpScript(sb, script, i);
|
||||
});
|
||||
}
|
||||
@@ -656,7 +690,7 @@ final class Disassembler
|
||||
sb.newLine();
|
||||
|
||||
foreach (i, vclass; as.orphanClasses)
|
||||
newInclude(sb, refs.objects.getFilename(vclass), (StringBuilder sb) {
|
||||
newInclude(sb, refs.objects.getFilename(vclass, "class"), (StringBuilder sb) {
|
||||
dumpClass(sb, vclass);
|
||||
});
|
||||
|
||||
@@ -670,7 +704,7 @@ final class Disassembler
|
||||
sb.newLine();
|
||||
|
||||
foreach (i, method; as.orphanMethods)
|
||||
newInclude(sb, refs.objects.getFilename(method), (StringBuilder sb) {
|
||||
newInclude(sb, refs.objects.getFilename(method, "method"), (StringBuilder sb) {
|
||||
dumpMethod(sb, method, "method");
|
||||
});
|
||||
|
||||
@@ -742,8 +776,13 @@ final class Disassembler
|
||||
|
||||
static double forceDouble(double d) { static double n; n = d; return n; }
|
||||
if (s != "nan" && s != "inf" && s != "-inf")
|
||||
{
|
||||
for (int i=s.length-1; i>0; i--)
|
||||
if (s[i]>='0' && s[i]<='8' && forceDouble(to!double(s[0..i] ~ cast(char)(s[i]+1)))==v)
|
||||
s = s[0..i] ~ cast(char)(s[i]+1);
|
||||
while (s.length>2 && s[$-1]!='.' && forceDouble(to!double(s[0..$-1]))==v)
|
||||
s = s[0..$-1];
|
||||
}
|
||||
sb ~= s;
|
||||
}
|
||||
}
|
||||
@@ -870,9 +909,9 @@ final class Disassembler
|
||||
}
|
||||
}
|
||||
|
||||
void dumpTraits(StringBuilder sb, ASProgram.Trait[] traits)
|
||||
void dumpTraits(StringBuilder sb, ASProgram.Trait[] traits, bool inScript = false)
|
||||
{
|
||||
foreach (ref trait; traits)
|
||||
foreach (/*ref*/ trait; traits)
|
||||
{
|
||||
sb ~= "trait ";
|
||||
sb ~= TraitKindNames[trait.kind];
|
||||
@@ -909,7 +948,10 @@ final class Disassembler
|
||||
dumpUInt(sb, trait.vClass.slotId);
|
||||
}
|
||||
sb.indent++; sb.newLine();
|
||||
dumpClass(sb, trait.vClass.vclass);
|
||||
|
||||
newInclude(sb, refs.objects.getFilename(trait.vClass.vclass, "class"), (StringBuilder sb) {
|
||||
dumpClass(sb, trait.vClass.vclass);
|
||||
});
|
||||
break;
|
||||
case TraitKind.Function:
|
||||
if (trait.vFunction.slotId)
|
||||
@@ -918,7 +960,9 @@ final class Disassembler
|
||||
dumpUInt(sb, trait.vFunction.slotId);
|
||||
}
|
||||
sb.indent++; sb.newLine();
|
||||
dumpMethod(sb, trait.vFunction.vfunction, "method");
|
||||
newInclude(sb, refs.objects.getFilename(trait.vFunction.vfunction, "method"), (StringBuilder sb) {
|
||||
dumpMethod(sb, trait.vFunction.vfunction, "method");
|
||||
}, inScript);
|
||||
break;
|
||||
case TraitKind.Method:
|
||||
case TraitKind.Getter:
|
||||
@@ -929,7 +973,9 @@ final class Disassembler
|
||||
dumpUInt(sb, trait.vMethod.dispId);
|
||||
}
|
||||
sb.indent++; sb.newLine();
|
||||
dumpMethod(sb, trait.vMethod.vmethod, "method");
|
||||
newInclude(sb, refs.objects.getFilename(trait.vMethod.vmethod, "method"), (StringBuilder sb) {
|
||||
dumpMethod(sb, trait.vMethod.vmethod, "method");
|
||||
}, inScript);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown trait kind");
|
||||
@@ -1125,7 +1171,7 @@ final class Disassembler
|
||||
sb ~= to!string(index);
|
||||
sb.indent++; sb.newLine();
|
||||
dumpMethod(sb, script.sinit, "sinit");
|
||||
dumpTraits(sb, script.traits);
|
||||
dumpTraits(sb, script.traits, true);
|
||||
sb.indent--; sb ~= "end ; script"; sb.newLine();
|
||||
}
|
||||
|
||||
@@ -1288,10 +1334,16 @@ final class Disassembler
|
||||
dumpMultiname(sb, instruction.arguments[i].multinamev);
|
||||
break;
|
||||
case OpcodeArgumentType.Class:
|
||||
dumpString(sb, refs.objects.getName(instruction.arguments[i].classv));
|
||||
if (instruction.arguments[i].classv is null)
|
||||
sb ~= "null";
|
||||
else
|
||||
dumpString(sb, refs.objects.getName(instruction.arguments[i].classv));
|
||||
break;
|
||||
case OpcodeArgumentType.Method:
|
||||
dumpString(sb, refs.objects.getName(instruction.arguments[i].methodv));
|
||||
if (instruction.arguments[i].methodv is null)
|
||||
sb ~= "null";
|
||||
else
|
||||
dumpString(sb, refs.objects.getName(instruction.arguments[i].methodv));
|
||||
break;
|
||||
|
||||
case OpcodeArgumentType.JumpTarget:
|
||||
@@ -1321,6 +1373,8 @@ final class Disassembler
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool[256] newLineAfter;
|
||||
|
||||
static this()
|
||||
@@ -1357,7 +1411,24 @@ static this()
|
||||
Opcode.OP_setproperty,
|
||||
Opcode.OP_setpropertylate,
|
||||
Opcode.OP_setslot,
|
||||
Opcode.OP_setsuper
|
||||
Opcode.OP_setsuper,
|
||||
Opcode.OP_si8,
|
||||
Opcode.OP_si16,
|
||||
Opcode.OP_si32,
|
||||
Opcode.OP_sf32,
|
||||
Opcode.OP_sf64,
|
||||
])
|
||||
newLineAfter[o] = true;
|
||||
}
|
||||
|
||||
/// Force a raw data join (workaround for issue 6064)
|
||||
T[] arrayJoin(T)(T[][] arrays, T[] sep)
|
||||
{
|
||||
return cast(T[])join(cast(ubyte[][])arrays, cast(ubyte[])sep);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
T[] arrayJoin(T)(T[][] arrays)
|
||||
{
|
||||
return cast(T[])join(cast(ubyte[][])arrays);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user