| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library dart2js.new_js_emitter.model; |
| |
| import '../dart2jslib.dart' show Compiler; |
| import '../js/js.dart' as js; |
| |
| js.LiteralString unparse(Compiler compiler, js.Expression value) { |
| String text = js.prettyPrint(value, compiler).getText(); |
| if (value is js.Fun) text = '($text)'; |
| return js.js.escapedString(text); |
| } |
| |
| class Program { |
| final List<Output> outputs; |
| Program(this.outputs); |
| |
| void emit(Compiler compiler) { |
| MainOutput mainUnit = outputs.first; |
| String mainCode = js.prettyPrint(mainUnit.emit(compiler), compiler) |
| .getText(); |
| compiler.outputProvider('', 'js') |
| ..add(_buildGeneratedBy(compiler)) |
| ..add(mainCode) |
| ..close(); |
| compiler.assembledCode = mainCode; |
| |
| outputs.skip(1).forEach((DeferredOutput deferredUnit) { |
| String code = js.prettyPrint(deferredUnit.emit(compiler), compiler) |
| .getText(); |
| compiler.outputProvider('', 'js') |
| ..add(code) |
| ..close(); |
| }); |
| } |
| |
| String _buildGeneratedBy(compiler) { |
| var suffix = ''; |
| if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}'; |
| return '// Generated by dart2js, the Dart to JavaScript compiler$suffix.\n'; |
| } |
| } |
| |
| class Holder { |
| final String name; |
| final int index; |
| Holder(this.name, this.index); |
| } |
| |
| abstract class Output { |
| bool get isMainOutput => mainOutput == this; |
| MainOutput get mainOutput; |
| final List<Library> libraries; |
| |
| Output(this.libraries); |
| } |
| |
| class MainOutput extends Output { |
| final js.Expression main; |
| final List<Holder> holders; |
| |
| MainOutput(this.main, List<Library> libraries, this.holders) |
| : super(libraries); |
| |
| MainOutput get mainOutput => this; |
| |
| js.Expression emit(Compiler compiler) { |
| js.Expression program = new js.ArrayInitializer.from( |
| libraries.map((e) => e.emit(compiler))); |
| return js.js(boilerplate, [emitHolders(), main, program]); |
| } |
| |
| js.Block emitHolders() { |
| // The top-level variables for holders must *not* be renamed by the |
| // JavaScript pretty printer because a lot of code already uses the |
| // non-renamed names. The generated code looks like this: |
| // |
| // var H = {}, ..., G = {}; |
| // var holders = [ H, ..., G ]; |
| // |
| // and it is inserted at the top of the top-level function expression |
| // that covers the entire program. |
| |
| List<js.Statement> statements = [ |
| new js.ExpressionStatement( |
| new js.VariableDeclarationList(holders.map((e) => |
| new js.VariableInitialization( |
| new js.VariableDeclaration(e.name, allowRename: false), |
| new js.ObjectInitializer(const []))).toList())), |
| js.js.statement('var holders = #', new js.ArrayInitializer.from( |
| holders.map((e) => new js.VariableUse(e.name)))) |
| ]; |
| return new js.Block(statements); |
| } |
| } |
| |
| class DeferredOutput extends Output { |
| final MainOutput mainOutput; |
| |
| List<Holder> get holders => mainOutput.holders; |
| |
| DeferredOutput(this.mainOutput, List<Library> libraries) |
| : super(libraries); |
| |
| js.Expression emit(Compiler compiler) { |
| throw "Unimplemented"; |
| } |
| } |
| |
| class Library { |
| final String uri; |
| final List<StaticMethod> statics; |
| final List<Class> classes; |
| Library(this.uri, this.statics, this.classes); |
| |
| js.Expression emit(Compiler compiler) { |
| Iterable staticDescriptors = statics.expand((e) => |
| [ js.string(e.name), js.js.number(e.holder.index), e.emit(compiler) ]); |
| Iterable classDescriptors = classes.expand((e) => |
| [ js.string(e.name), js.js.number(e.holder.index), e.emit(compiler) ]); |
| |
| js.Expression staticArray = new js.ArrayInitializer.from(staticDescriptors); |
| js.Expression classArray = new js.ArrayInitializer.from(classDescriptors); |
| |
| return new js.ArrayInitializer.from([staticArray, classArray]); |
| } |
| } |
| |
| class Class { |
| final String name; |
| final Holder holder; |
| Class superclass; |
| final List<Method> methods; |
| Class(this.name, this.holder, this.methods); |
| |
| void setSuperclass(Class superclass) { |
| this.superclass = superclass; |
| } |
| |
| String get superclassName |
| => (superclass == null) ? "" : superclass.name; |
| int get superclassHolderIndex |
| => (superclass == null) ? 0 : superclass.holder.index; |
| |
| js.Expression emit(Compiler compiler) { |
| List elements = [ js.string(superclassName), |
| js.js.number(superclassHolderIndex) ]; |
| elements.addAll(methods.expand((e) => [ js.string(e.name), e.code ])); |
| return unparse(compiler, new js.ArrayInitializer.from(elements)); |
| } |
| } |
| |
| class Method { |
| final String name; |
| final js.Expression code; |
| Method(this.name, this.code); |
| } |
| |
| class StaticMethod extends Method { |
| final Holder holder; |
| StaticMethod(String name, this.holder, js.Expression code) |
| : super(name, code); |
| |
| js.Expression emit(Compiler compiler) { |
| return unparse(compiler, code); |
| } |
| } |
| |
| final String boilerplate = r""" |
| !function(start, program) { |
| |
| // Initialize holder objects. |
| #; |
| |
| function setupProgram() { |
| for (var i = 0; i < program.length; i++) { |
| setupLibrary(program[i]); |
| } |
| } |
| |
| function setupLibrary(library) { |
| var statics = library[0]; |
| for (var i = 0; i < statics.length; i += 3) { |
| var holderIndex = statics[i + 1]; |
| setupStatic(statics[i], holders[holderIndex], statics[i + 2]); |
| } |
| |
| var classes = library[1]; |
| for (var i = 0; i < classes.length; i += 3) { |
| var holderIndex = classes[i + 1]; |
| setupClass(classes[i], holders[holderIndex], classes[i + 2]); |
| } |
| } |
| |
| function setupStatic(name, holder, descriptor) { |
| holder[name] = function() { |
| var method = compile(descriptor); |
| holder[name] = method; |
| return method.apply(this, arguments); |
| }; |
| } |
| |
| function setupClass(name, holder, descriptor) { |
| var resolve = function() { |
| var constructor = compileConstructor(name, descriptor); |
| holder[name] = constructor; |
| return constructor; |
| }; |
| |
| var patch = function() { |
| var constructor = resolve(); |
| var object = new constructor(); |
| constructor.apply(object, arguments); |
| return object; |
| }; |
| |
| // We store the resolve function on the patch function to make it possible |
| // to resolve superclass references without constructing instances. The |
| // resolve property also serves as a marker that indicates whether or not |
| // a class has been resolved yet. |
| patch.resolve = resolve; |
| holder[name] = patch; |
| } |
| |
| function compileConstructor(name, descriptor) { |
| descriptor = compile(descriptor); |
| var prototype = determinePrototype(descriptor); |
| for (var i = 2; i < descriptor.length; i += 2) { |
| prototype[descriptor[i]] = descriptor[i + 1]; |
| } |
| var result = function() { }; // TODO(kasperl): Compile. |
| result.prototype = prototype; |
| return result; |
| } |
| |
| function determinePrototype(descriptor) { |
| var superclassName = descriptor[0]; |
| if (!superclassName) return { }; |
| |
| // Look up the superclass constructor function in the right holder. |
| var holderIndex = descriptor[1]; |
| var superclass = holders[holderIndex][superclassName]; |
| if (superclass.resolve) superclass = superclass.resolve(); |
| |
| // Create a new prototype object chained to the superclass prototype. |
| var intermediate = function() { }; |
| intermediate.prototype = superclass.prototype; |
| return new intermediate(); |
| } |
| |
| function compile(s) { |
| 'use strict'; |
| return eval(s); |
| } |
| |
| setupProgram(); |
| var end = Date.now(); |
| print('Setup: ' + (end - start) + ' ms.'); |
| |
| if (true) #(); |
| |
| }(Date.now(), #) |
| """; |