Chromium Code Reviews| Index: pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart |
| diff --git a/pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart |
| index 21da5da3008012dbe958e6c30bfc598c4621fd02..e94bcd3ba30b562b1d6562186540c0177ecb4601 100644 |
| --- a/pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart |
| +++ b/pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart |
| @@ -5,6 +5,7 @@ |
| library dart2js.new_js_emitter.model_emitter; |
| import '../../dart2jslib.dart' show Compiler; |
| +import '../../dart_types.dart' show DartType; |
| import '../../js/js.dart' as js; |
| import '../../js_backend/js_backend.dart' show |
| JavaScriptBackend, |
| @@ -22,9 +23,10 @@ import 'package:_internal/compiler/js_lib/shared/embedded_names.dart' show |
| METADATA, |
| TYPE_TO_INTERCEPTOR_MAP; |
| -import '../js_emitter.dart' show NativeGenerator; |
| +import '../js_emitter.dart' show NativeGenerator, buildTearOffCode; |
| import '../model.dart'; |
| + |
| class ModelEmitter { |
| final Compiler compiler; |
| final Namer namer; |
| @@ -108,6 +110,9 @@ class ModelEmitter { |
| boilerplate, |
| {'deferredInitializer': emitDeferredInitializerGlobal(program.loadMap), |
| 'holders': emitHolders(fragment.holders), |
| + 'tearOff': buildTearOffCode(backend), |
| + 'parseFunctionDescriptor': |
| + js.js.statement(parseFunctionDescriptorBoilerplate), |
| 'cyclicThrow': |
| backend.emitter.staticFunctionAccess(backend.getCyclicThrowHelper()), |
| 'outputContainsConstantList': program.outputContainsConstantList, |
| @@ -395,14 +400,7 @@ class ModelEmitter { |
| } else { |
| elements.add(_generateConstructor(cls)); |
| } |
| - Iterable<Method> methods = cls.methods.expand((Method method) { |
| - // TODO(floitsch): can there be anything else than a DartMethod? |
| - if (method is DartMethod) { |
| - return [method]..addAll(method.parameterStubs); |
| - } else { |
| - return [method]; |
| - } |
| - }); |
| + Iterable<Method> methods = cls.methods; |
| Iterable<Method> isChecks = cls.isChecks; |
| Iterable<Method> callStubs = cls.callStubs; |
| Iterable<Method> noSuchMethodStubs = cls.noSuchMethodStubs; |
| @@ -410,7 +408,8 @@ class ModelEmitter { |
| Iterable<Method> allMethods = |
| [methods, isChecks, callStubs, noSuchMethodStubs, gettersSetters] |
| .expand((x) => x); |
| - elements.addAll(allMethods.expand((e) => [js.string(e.name), e.code])); |
| + elements.addAll(allMethods.expand(emitInstanceMethod)); |
| + |
| return unparse(compiler, new js.ArrayInitializer(elements)); |
| } |
| @@ -419,6 +418,95 @@ class ModelEmitter { |
| return unparse(compiler, field.code); |
| } |
| + /// JavaScript code template that implements parsing of a function descriptor. |
| + /// Descriptors are used in place of the actual JavaScript function |
| + /// definition in the output if additional information needs to be passed to |
| + /// facilitate the generation of tearOffs at runtime. The format is an array |
| + /// with the following fields: |
| + /// |
| + /// [Method.code] |
| + /// [DartMethod.callName] |
| + /// [DartMethod.tearOffName] |
| + /// [JavaScriptBackend.isInterceptedMethod] |
| + /// functionType |
| + /// |
| + /// followed by |
| + /// |
| + /// [ParameterStubMethod.name] |
| + /// [ParameterStubMethod.code] |
| + /// |
| + /// for each stub in [DartMethod.parameterStubs]. |
| + |
| + static final String parseFunctionDescriptorBoilerplate = r""" |
| +function parseFunctionDescriptor(proto, name, descriptor) { |
| + if (descriptor instanceof Array) { |
| + proto[name] = descriptor[0]; |
| + var funs = [descriptor[0]]; |
| + funs[0].$callName = descriptor[1]; |
| + for (var pos = 5; pos < descriptor.length; pos += 3) { |
| + var stub = descriptor[pos+2]; |
|
floitsch
2015/02/03 19:40:02
nit: "pos + 2"
(and "pos + 1" in the next line).
herhut
2015/02/03 21:18:58
Done.
|
| + stub.$callName = descriptor[pos+1]; |
| + proto[descriptor[pos]] = stub; |
| + funs.push(stub); |
| + } |
| + if (descriptor[2] != null) { |
| + var isIntercepted = descriptor[3]; |
| + var reflectionInfo = descriptor[4]; |
| + proto[descriptor[2]] = |
| + tearOff(funs, reflectionInfo, false, name, isIntercepted); |
| + } |
| + } else { |
| + proto[name] = descriptor; |
| + } |
| +} |
| +"""; |
| + |
| + js.Expression _generateFunctionType(DartType memberType) { |
| + if (memberType.containsTypeVariables) { |
| + js.Expression thisAccess = js.js(r'this.$receiver'); |
| + return backend.rti.getSignatureEncoding(memberType, thisAccess); |
| + } else { |
| + return js.number(backend.emitter.metadataCollector.reifyType(memberType)); |
| + } |
| + } |
| + |
| + Iterable<js.Expression> emitInstanceMethod(Method method) { |
| + |
| + List<js.Expression> makeNameCodePair(Method method) { |
| + return [js.string(method.name), method.code]; |
| + } |
| + |
| + List<js.Expression> makeNameCallNameCodeTriplet(ParameterStubMethod stub) { |
| + js.Expression callName = stub.callName == null |
| + ? new js.LiteralNull() |
| + : js.string(stub.callName); |
| + return [js.string(stub.name), callName, stub.code]; |
| + } |
| + |
| + if (method is DartMethod) { |
| + if (method.needsTearOff) { |
| + /// See [parseFunctionDescriptorBoilerplate] for a full description of |
| + /// the format. |
| + // [name, [function, callName, tearOffName, isIntercepted, functionType, |
| + // stub1_name, stub1_callName, stub1_code, ...] |
| + bool isIntercepted = backend.isInterceptedMethod(method.element); |
| + var data = [method.code]; |
| + data.add(js.string(method.callName)); |
| + data.add(js.string(method.tearOffName)); |
| + data.add(new js.LiteralBool(isIntercepted)); |
| + data.add(_generateFunctionType(method.type)); |
| + data.addAll(method.parameterStubs.expand(makeNameCallNameCodeTriplet)); |
| + return [js.string(method.name), new js.ArrayInitializer(data)]; |
| + } else { |
| + // TODO(floitsch): not the most efficient way... |
| + return ([method]..addAll(method.parameterStubs)) |
| + .expand(makeNameCodePair); |
| + } |
| + } else { |
| + return makeNameCodePair(method); |
| + } |
| + } |
| + |
| Iterable<js.Expression> emitStaticMethod(StaticMethod method) { |
| js.Expression holderIndex = js.number(method.holder.index); |
| List<js.Expression> output = <js.Expression>[]; |
| @@ -430,10 +518,31 @@ class ModelEmitter { |
| output.add(unparsed); |
| } |
| + List<js.Expression> makeNameCallNameCodeTriplet(ParameterStubMethod stub) { |
| + js.Expression callName = stub.callName == null |
| + ? new js.LiteralNull() |
| + : js.string(stub.callName); |
| + return [js.string(stub.name), callName, unparse(compiler, stub.code)]; |
| + } |
| + |
| _addMethod(method); |
| // TODO(floitsch): can there be anything else than a StaticDartMethod? |
|
floitsch
2015/02/03 19:40:02
Well now there can be, since you run it over every
herhut
2015/02/03 21:18:58
Doesn't this still only run over static methods?
floitsch
2015/02/03 21:44:17
Never mind...
|
| if (method is StaticDartMethod) { |
| - method.parameterStubs.forEach(_addMethod); |
| + if (method.needsTearOff) { |
| + /// The format emitted is the same as for the parser specified at |
| + /// [parseFunctionDescriptorBoilerplate] except for the missing |
| + /// field whether the method is intercepted. |
| + // [name, [function, callName, tearOffName, functionType, |
| + // stub1_name, stub1_callName, stub1_code, ...] |
| + var data = [unparse(compiler, method.code)]; |
| + data.add(js.string(method.callName)); |
| + data.add(js.string(method.tearOffName)); |
| + data.add(_generateFunctionType(method.type)); |
| + data.addAll(method.parameterStubs.expand(makeNameCallNameCodeTriplet)); |
| + return [js.string(method.name), holderIndex, new js.ArrayInitializer(data)]; |
|
floitsch
2015/02/03 19:40:02
long line.
herhut
2015/02/03 21:18:58
Done.
|
| + } else { |
| + method.parameterStubs.forEach(_addMethod); |
| + } |
| } |
| return output; |
| } |
| @@ -444,10 +553,12 @@ class ModelEmitter { |
| #deferredInitializer; |
| !function(start, program) { |
| - |
| // Initialize holder objects. |
| #holders; |
| + // Counter to generate unique names for tear offs. |
| + var functionCounter = 0; |
|
floitsch
2015/02/03 19:40:02
:/
Can this be emitted with the tear-off function?
herhut
2015/02/03 21:18:58
In a separate CL to do it for both emitters.
floitsch
2015/02/03 21:44:17
Acknowledged.
|
| + |
| function setupProgram() { |
| for (var i = 0; i < program.length - 1; i++) { |
| setupLibrary(program[i]); |
| @@ -481,11 +592,50 @@ class ModelEmitter { |
| } |
| function setupStatic(name, holder, descriptor) { |
| - holder[name] = function() { |
| - var method = compile(name, descriptor); |
| - holder[name] = method; |
| - return method.apply(this, arguments); |
| - }; |
| + if (typeof descriptor == 'string') { |
| + holder[name] = function() { |
| + var method = compile(name, descriptor); |
| + holder[name] = method; |
| + return method.apply(this, arguments); |
| + }; |
| + } else { |
| + // Parse the tear off information and generate compile handlers. |
| + // TODO(herhut): Share parser with instance methods. |
|
floitsch
2015/02/03 19:40:02
I'm not sure that's worth the effort, but I agree
herhut
2015/02/03 21:18:58
Acknowledged.
|
| + function compileAllStubs() { |
| + var funs; |
| + var fun = compile(name, descriptor[0]); |
| + fun.\$callName = descriptor[1]; |
| + holder[name] = fun; |
| + funs = [fun]; |
| + for (var pos = 4; pos < descriptor.length; pos += 3) { |
| + var stubName = descriptor[pos]; |
| + fun = compile(stubName, descriptor[pos + 2]); |
| + fun.\$callName = descriptor[pos + 1]; |
| + holder[stubName] = fun; |
| + funs.push(fun); |
| + } |
| + if (descriptor[2] != null) { // tear-off name. |
| + // functions, reflectionInfo, isStatic, name, isIntercepted. |
| + holder[descriptor[2]] = |
| + tearOff(funs, descriptor[3], true, name, false); |
| + } |
| + } |
| + |
| + function setupCompileAllAndDelegateStub(name) { |
| + holder[name] = function() { |
| + compileAllStubs(); |
| + return holder[name].apply(this, arguments); |
| + }; |
| + } |
| + |
| + setupCompileAllAndDelegateStub(name); |
| + for (var pos = 4; pos < descriptor.length; pos += 3) { |
| + setupCompileAllAndDelegateStub(descriptor[pos]); |
| + } |
| + if (descriptor[2] != null) { // tear-off name. |
| + setupCompileAllAndDelegateStub(descriptor[2]) |
| + } |
| + } |
| } |
| function setupLazyStatic(name, getterName, holder, descriptor) { |
| @@ -533,6 +683,10 @@ class ModelEmitter { |
| holder[name] = patch; |
| } |
| + #tearOff; |
| + |
| + #parseFunctionDescriptor; |
| + |
| function compileConstructor(name, descriptor) { |
| descriptor = compile(name, descriptor); |
| var prototype = determinePrototype(descriptor); |
| @@ -541,12 +695,12 @@ class ModelEmitter { |
| if (typeof descriptor[2] !== 'function') { |
| constructor = compileMixinConstructor(name, prototype, descriptor); |
| for (var i = 4; i < descriptor.length; i += 2) { |
| - prototype[descriptor[i]] = descriptor[i + 1]; |
| + parseFunctionDescriptor(prototype, descriptor, descriptor[i + 1]); |
| } |
| } else { |
| constructor = descriptor[2]; |
| for (var i = 3; i < descriptor.length; i += 2) { |
| - prototype[descriptor[i]] = descriptor[i + 1]; |
| + parseFunctionDescriptor(prototype, descriptor[i], descriptor[i + 1]); |
| } |
| } |
| constructor.builtin\$cls = name; // Needed for RTI. |