зеркало из https://github.com/mozilla/shumway.git
Start of compiler, can compile skeletons of classes
This commit is contained in:
Родитель
81e30f5e1b
Коммит
596dad608a
|
@ -13,3 +13,33 @@ function nullcheck(v) {
|
|||
nullcheckfailed(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
function findBinding(index) {
|
||||
for (var base = this.traits; !!base; base = base.super.prototype.traits) {
|
||||
var val = base.findLocalBinding(index);
|
||||
if (val)
|
||||
return val;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function findLocalBinding(index) {
|
||||
var multiname = abc.constantPool.multinames[index];
|
||||
var traits = this.traits
|
||||
assert(!!multiname);
|
||||
|
||||
/* :FIXME: Quadratic probe, super slow! */
|
||||
traits.forEach(function(t1) {
|
||||
if (t1.matches(multiname)) {
|
||||
traits.forEach(function(t2) {
|
||||
if (t1 !== t2 && t2.matches(multiname)) {
|
||||
throw new TypeError("ambiguous binding");
|
||||
}
|
||||
});
|
||||
|
||||
return this[t1];
|
||||
}
|
||||
});
|
||||
|
||||
return undefined;
|
||||
}
|
|
@ -1,9 +1,325 @@
|
|||
/* -*- mode: javascript; tab-width: 4; insert-tabs-mode: nil; indent-tabs-mode: nil -*- */
|
||||
|
||||
function Emitter() {
|
||||
this.stmts = [];
|
||||
this.exprs = [];
|
||||
}
|
||||
|
||||
Emitter.prototype = {
|
||||
stmt: function stmt(s) {
|
||||
if (!s) {
|
||||
s = this.exprs.pop();
|
||||
}
|
||||
this.stmts.push(s + ";");
|
||||
},
|
||||
|
||||
decl: function decl() {
|
||||
this.stmts.push(this.exprs.pop());
|
||||
},
|
||||
|
||||
expr: function expr(e) {
|
||||
this.exprs.push(e);
|
||||
},
|
||||
|
||||
assign: function assign() {
|
||||
var lhs = this.exprs.pop();
|
||||
var rhs = this.exprs.pop();
|
||||
this.stmt(lhs + "=" + rhs);
|
||||
},
|
||||
|
||||
call: function assign(nargs) {
|
||||
var lhs = this.exprs.pop();
|
||||
var args = this.exprs.slice(-nargs).reverse()
|
||||
this.expr("(" + lhs + ")(" + args.join(",") + ")");
|
||||
},
|
||||
|
||||
ret: function ret() {
|
||||
this.stmt("return " + this.exprs.pop());
|
||||
},
|
||||
|
||||
func: function func(name, nparams, body) {
|
||||
var tmpEmit = new Emitter;
|
||||
body(tmpEmit);
|
||||
var params = [];
|
||||
for (var i = 0; i < nparams; i++) {
|
||||
params.push("param" + i);
|
||||
}
|
||||
this.expr("function " + name + "(" + params.join(",") + ") {" +
|
||||
tmpEmit.finish() + "}");
|
||||
},
|
||||
|
||||
varDecl: function varDecl(name) {
|
||||
this.stmt("var " + name);
|
||||
},
|
||||
|
||||
thunk: function thunk(body) {
|
||||
this.func("", 0, body);
|
||||
},
|
||||
|
||||
setProp: function setProp(scope, multiname) {
|
||||
if (multiname.isQName()) {
|
||||
this.expr(scope + "." + mangleQName(multiname));
|
||||
this.assign();
|
||||
return;
|
||||
}
|
||||
this.expr(multiname.index);
|
||||
this.expr(scope + ".setBinding");
|
||||
this.call(2);
|
||||
this.stmt();
|
||||
},
|
||||
|
||||
findProp: function findProp(scope, multiname) {
|
||||
if (multiname.isQName()) {
|
||||
this.expr(scope + "." + mangleQName(multiname));
|
||||
}
|
||||
this.expr(multiname.index);
|
||||
this.expr(scope + ".findBinding");
|
||||
this.call(1);
|
||||
},
|
||||
|
||||
finish: function finish() {
|
||||
var out = this.stmts.join("\n");
|
||||
this.stmts = [];
|
||||
this.exprs = [];
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
function mangleQName(multiname) {
|
||||
assert(multiname.isQName());
|
||||
return multiname.getNamespace(0) + "$$" + multiname.getName();
|
||||
}
|
||||
|
||||
function compiler(abc) {
|
||||
const args = "args";
|
||||
const localScope = "scope";
|
||||
const parentScope = "parentScope";
|
||||
const proto = "prototype";
|
||||
|
||||
function defaultValue(typeName) {
|
||||
/* :XXX: Is this right? */
|
||||
switch (typeName.name) {
|
||||
case undefined:
|
||||
return "undefined";
|
||||
case "Boolean":
|
||||
return "false";
|
||||
case "int":
|
||||
return "0";
|
||||
case "Number":
|
||||
return "NaN";
|
||||
case "uint":
|
||||
return "0";
|
||||
default:
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Classes in AVM are split into two parts:
|
||||
* - instance_info, which describe layout of class instances.
|
||||
* - class_info, which describe layout of classes themselves, i.e. static
|
||||
* properties.
|
||||
*
|
||||
* Some AVM terms:
|
||||
* iinit - instance initializer, i.e. constructor
|
||||
* cinit - class initializer, i.e. static constructor
|
||||
* traits - descriptions of properties
|
||||
*
|
||||
* AVM classes are compiled to JS functions using the folowing mappings:
|
||||
*
|
||||
* Classes are functions with their iinit folded in,
|
||||
* class C { public function C(args) { ... } }
|
||||
* =>
|
||||
* function C(args) { ... }
|
||||
*
|
||||
* Class traits (static properties) are set on C itself,
|
||||
* class C { ... public static const CPROP: int = 0 ... }
|
||||
* =>
|
||||
* ... C.public$$CPROP = 0 ...
|
||||
*
|
||||
* Instance traits of type TRAIT_Slot, TRAIT_Const are set in the beginning
|
||||
* of the JS function,
|
||||
* class C { public var x }
|
||||
* =>
|
||||
* function C() { this.public$$x = undefined }
|
||||
*
|
||||
* Instance traits of type TRAIT_Method, TRAIT_Getter, TRAIT_Setter are set
|
||||
* on C.prototype,
|
||||
* class C { public function m() { ... } }
|
||||
* =>
|
||||
* C.prototype.public$$m = function () { ... }
|
||||
*
|
||||
* Classes' cinit functions are compiled as a special function init and are
|
||||
* called at the toplevel.
|
||||
* class C { ... }
|
||||
* =>
|
||||
* C.init = function C$init() { }; ...; C.init();
|
||||
*
|
||||
* Inheritance is dealt with by copying instance properties and chaining
|
||||
* prototypes,
|
||||
* class C extends B { ... }
|
||||
* =>
|
||||
* function C() { B(); ... }
|
||||
* C.prototype = B.prototype;
|
||||
*/
|
||||
|
||||
function isSlot(t) {
|
||||
return (t.kind === TRAIT_Slot ||
|
||||
t.kind === TRAIT_Const);
|
||||
}
|
||||
|
||||
function isMethod(t) {
|
||||
return (t.kind === TRAIT_Method ||
|
||||
t.kind === TRAIT_Setter ||
|
||||
t.kind === TRAIT_Setter);
|
||||
}
|
||||
|
||||
function compileClass(emit, classInfo, i) {
|
||||
var instanceInfo = classInfo.instance;
|
||||
var itraits = instanceInfo.traits;
|
||||
var className = instanceInfo.name.getName();
|
||||
|
||||
emit.thunk(function (emit) {
|
||||
emit.func(className, 0, function (emit) {
|
||||
itraits.filter(isSlot).forEach(compileSlot.bind(undefined, emit));
|
||||
});
|
||||
emit.decl();
|
||||
|
||||
emit.expr("abc.classes[" + i + "]");
|
||||
emit.expr(className + ".class");
|
||||
emit.assign();
|
||||
|
||||
if (instanceInfo.superName) {
|
||||
emit.findProp(localScope, instanceInfo.superName);
|
||||
emit.expr(className + ".super");
|
||||
emit.assign();
|
||||
}
|
||||
|
||||
emit.varDecl(proto);
|
||||
|
||||
emit.expr("new " + className + ".super");
|
||||
emit.expr(proto);
|
||||
emit.assign();
|
||||
|
||||
emit.expr(className);
|
||||
emit.expr(proto + ".constructor");
|
||||
emit.assign();
|
||||
|
||||
emit.expr(className + ".class.instance.traits");
|
||||
emit.expr(proto + ".traits");
|
||||
emit.assign();
|
||||
|
||||
/* The instance initializer. */
|
||||
var tmpEmit = new Emitter;
|
||||
compileMethod(tmpEmit, instanceInfo.init);
|
||||
emit.expr(tmpEmit.finish());
|
||||
emit.expr(proto + ".init");
|
||||
emit.assign();
|
||||
|
||||
itraits.filter(isMethod).forEach(function (trait) {
|
||||
compileMethod(tmpEmit, trait.method);
|
||||
emit.expr(tmpEmit.finish());
|
||||
emit.setProp(proto, trait.name);
|
||||
});
|
||||
|
||||
emit.expr(proto);
|
||||
emit.expr(className + "." + proto);
|
||||
emit.assign();
|
||||
|
||||
/* The static initializer. */
|
||||
// compileCode(emit, classInfo.init.code);
|
||||
|
||||
emit.expr(className);
|
||||
emit.ret();
|
||||
});
|
||||
}
|
||||
|
||||
function compileSlot(emit, trait) {
|
||||
assert(trait.kind === TRAIT_Slot || trait.kind === TRAIT_Const);
|
||||
|
||||
if (trait.kind === TRAIT_Slot) {
|
||||
emit.expr(defaultValue(trait.typeName));
|
||||
emit.setProp("this", trait.name);
|
||||
} else {
|
||||
/* Where's my quasiquote? */
|
||||
propDesc = { value: defaultValue(trait.typeName),
|
||||
writable: false,
|
||||
configurable: false };
|
||||
emit.expr(JSON.stringify(propDesc));
|
||||
emit.expr(mangleQName(traits.name));
|
||||
emit.expr("this");
|
||||
emit.expr("Object.defineProperty");
|
||||
emit.call(3);
|
||||
emit.stmt();
|
||||
}
|
||||
}
|
||||
|
||||
function compileMethod(emit, method, options) {
|
||||
emit.func(method.name ? method.name.getName() : "", 0, function (emit) {
|
||||
emit.ret();
|
||||
});
|
||||
emit.decl();
|
||||
}
|
||||
|
||||
function compileTraits(emit, traits) {
|
||||
traits.forEach(function (trait, i) {
|
||||
switch (trait.kind) {
|
||||
case TRAIT_Slot:
|
||||
case TRAIT_Const:
|
||||
compileSlot(emit, trait);
|
||||
break;
|
||||
case TRAIT_Method:
|
||||
case TRAIT_Setter:
|
||||
case TRAIT_Getter:
|
||||
compileMethod(emit, trait.method);
|
||||
break;
|
||||
case TRAIT_Class:
|
||||
compileClass(emit, trait.class, i);
|
||||
break;
|
||||
default:
|
||||
unexpected();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function compileScript(emit, script) {
|
||||
compileTraits(emit, script.traits);
|
||||
}
|
||||
|
||||
return {
|
||||
/*
|
||||
* We expose compileClass because the interpreter uses it for name
|
||||
* resolution.
|
||||
*/
|
||||
compileClass: compileClass,
|
||||
compileScript: compileScript
|
||||
};
|
||||
}
|
||||
|
||||
function compileAbc(abc) {
|
||||
var emit = new Emitter;
|
||||
var cc = compiler(abc);
|
||||
cc.compileScript(emit, abc.lastScript);
|
||||
return emit.finish();
|
||||
}
|
||||
|
||||
function runScript(scope, abc, code) {
|
||||
/*
|
||||
* The compiled code requires a scope object named 'scope' and an abcFile
|
||||
* named 'abc' in scope!
|
||||
*/
|
||||
return eval(code);
|
||||
}
|
||||
|
||||
/*
|
||||
function compileAbc(abc) {
|
||||
var constants = abc.constants;
|
||||
var strings = constants.strings;
|
||||
var names = constants.names;
|
||||
var scripts = abc.scripts;
|
||||
|
||||
for (var i = 0, j = scripts.length; i < j; i++)
|
||||
compileScript(scripts[i]);
|
||||
|
||||
function compileBody(body) {
|
||||
var maxStack = body.maxStack;
|
||||
|
@ -602,8 +918,8 @@ function compileAbc(abc) {
|
|||
/*
|
||||
for (var n = 0; n < length; ++n)
|
||||
methodBodies[n].compiled = Function(compileBody(methodBodies[n]));
|
||||
*/
|
||||
|
||||
//compile(methods[scripts[0].init]);
|
||||
return abc;
|
||||
}
|
||||
*/
|
|
@ -338,12 +338,12 @@ var Multiname = (function () {
|
|||
const NAMESPACE_SET = 0x10;
|
||||
const TYPE_PARAMETER = 0x20;
|
||||
|
||||
function multiname() {
|
||||
|
||||
function multiname(index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
multiname.prototype.clone = function clone() {
|
||||
var clone = new multiname();
|
||||
var clone = new multiname(this.index);
|
||||
clone.flags = this.flags;
|
||||
clone.name = this.name;
|
||||
clone.namespace = this.namespace;
|
||||
|
@ -531,7 +531,7 @@ var Multiname = (function () {
|
|||
return false;
|
||||
}
|
||||
if (multiname.namespace) {
|
||||
return this.namespace == multiname.namespace;
|
||||
return this.namespace === multiname.namespace;
|
||||
} else {
|
||||
return multiname.namespaceSet.indexOf(this.namespace) >= 0;
|
||||
}
|
||||
|
@ -566,7 +566,7 @@ var Multiname = (function () {
|
|||
str += "}::" + this.nameToString();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
return multiname;
|
||||
})();
|
||||
|
@ -635,7 +635,7 @@ var ConstantPool = (function constantPool() {
|
|||
var multinames = [undefined];
|
||||
n = stream.readU30();
|
||||
for (i = 1; i < n; ++i) {
|
||||
var multiname = new Multiname(this, stream, multinames);
|
||||
var multiname = new Multiname(i);
|
||||
multiname.parse(this, stream, multinames);
|
||||
multinames.push(multiname);
|
||||
}
|
||||
|
|
|
@ -14,8 +14,9 @@ if (arguments.length == 0) {
|
|||
}
|
||||
|
||||
function printUsage() {
|
||||
print("avm: [-d | -x] file");
|
||||
print("avm: [-d | -c | -x] file");
|
||||
print(" -d = Disassemble .abc file.");
|
||||
print(" -c = Compile .abc file to .js.");
|
||||
print(" -x = Execute .abc file.");
|
||||
print(" -q = Quiet.");
|
||||
}
|
||||
|
@ -24,6 +25,7 @@ var file = arguments[arguments.length - 1];
|
|||
var options = arguments.slice(0, arguments.length - 1);
|
||||
|
||||
var disassemble = options.indexOf("-d") >= 0;
|
||||
var compile = options.indexOf("-c") >= 0;
|
||||
var execute = options.indexOf("-x") >= 0;
|
||||
var quiet = options.indexOf("-q") >= 0;
|
||||
|
||||
|
@ -38,6 +40,10 @@ if (disassemble) {
|
|||
abc.trace(new IndentingWriter(false));
|
||||
}
|
||||
|
||||
if (compile) {
|
||||
print(compileAbc(abc));
|
||||
}
|
||||
|
||||
if (execute) {
|
||||
interpretAbc(abc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,22 +6,22 @@ class Base:
|
|||
asc = None
|
||||
avm = None
|
||||
builtin_abc = None
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.setEnvironmentVariables();
|
||||
pass
|
||||
|
||||
|
||||
def setEnvironmentVariables(self):
|
||||
if 'ASC' in os.environ:
|
||||
self.asc = os.environ['ASC'].strip();
|
||||
else:
|
||||
print "Environment variable ASC is not defined, set it to asc.jar"
|
||||
|
||||
|
||||
if 'BUILTIN_ABC' in os.environ:
|
||||
self.builtin_abc = os.environ['BUILTIN_ABC'].strip();
|
||||
else:
|
||||
print "Environment variable BUILTIN_ABC is not defined, set it to builtin.abc"
|
||||
|
||||
|
||||
if not self.asc:
|
||||
sys.exit();
|
||||
|
||||
|
@ -35,32 +35,34 @@ class Base:
|
|||
args = ["java", "-jar", self.asc, "-swf", "cls,1,1", "-d", file]
|
||||
subprocess.call(args)
|
||||
|
||||
def runAvm(self, file, execute = True, trace = False, disassemble = False):
|
||||
def runAvm(self, file, execute = True, trace = False, disassemble = False, comp = False):
|
||||
args = ["js", "-m", "-n", "avm.js"];
|
||||
if disassemble:
|
||||
args.append("-d")
|
||||
if not trace:
|
||||
args.append("-q")
|
||||
if comp:
|
||||
args.append("-c")
|
||||
if execute:
|
||||
args.append("-x")
|
||||
args.append(file)
|
||||
subprocess.call(args)
|
||||
|
||||
|
||||
class Command(Base):
|
||||
name = ""
|
||||
|
||||
|
||||
def __init__(self, name):
|
||||
Base.__init__(self)
|
||||
self.name = name
|
||||
|
||||
|
||||
|
||||
|
||||
class Asc(Command):
|
||||
def __init__(self):
|
||||
Command.__init__(self, "asc")
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
def execute(self, args):
|
||||
parser = argparse.ArgumentParser(description='Compiles an ActionScript source file to .abc or .swf using the asc.jar compiler.')
|
||||
parser.add_argument('src', help="source .as file")
|
||||
|
@ -73,10 +75,10 @@ class Asc(Command):
|
|||
class Avm(Command):
|
||||
def __init__(self):
|
||||
Command.__init__(self, "avm")
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
def execute(self, args):
|
||||
parser = argparse.ArgumentParser(description='Runs an .abc file using Shumway AVM')
|
||||
parser.add_argument('src', help="source .abc file")
|
||||
|
@ -88,19 +90,33 @@ class Avm(Command):
|
|||
class Dis(Command):
|
||||
def __init__(self):
|
||||
Command.__init__(self, "dis")
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
def execute(self, args):
|
||||
parser = argparse.ArgumentParser(description='Disassembles an .abc file ')
|
||||
parser.add_argument('src', help="source .abc file")
|
||||
args = parser.parse_args(args)
|
||||
print "Disassembling %s" % args.src
|
||||
self.runAvm(args.src, execute = False, disassemble = True)
|
||||
|
||||
|
||||
class Compile(Command):
|
||||
def __init__(self):
|
||||
Command.__init__(self, "compile")
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
def execute(self, args):
|
||||
parser = argparse.ArgumentParser(description='Compiles an .abc file to .js ')
|
||||
parser.add_argument('src', help="source .abc file")
|
||||
args = parser.parse_args(args)
|
||||
print "Compiling %s" % args.src
|
||||
self.runAvm(args.src, execute = False, comp = True)
|
||||
|
||||
commands = {}
|
||||
for command in [Asc(), Avm(), Dis()]:
|
||||
for command in [Asc(), Avm(), Dis(), Compile()]:
|
||||
commands[str(command)] = command;
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
@ -112,4 +128,4 @@ if (not args.command in commands):
|
|||
parser.print_help()
|
||||
|
||||
command = commands[args.command];
|
||||
command.execute(sys.argv[2:])
|
||||
command.execute(sys.argv[2:])
|
||||
|
|
Загрузка…
Ссылка в новой задаче