Start of compiler, can compile skeletons of classes

This commit is contained in:
Shu-yu Guo 2012-01-24 21:01:05 -08:00
Родитель 81e30f5e1b
Коммит 596dad608a
5 изменённых файлов: 395 добавлений и 27 удалений

Просмотреть файл

@ -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:])