Chromium Code Reviews| Index: lib/src/compiler/code_generator.dart |
| diff --git a/lib/src/compiler/code_generator.dart b/lib/src/compiler/code_generator.dart |
| index d0bdf2043190b9a91a642c7f84ea0751b1be688b..4c723483646594d53e5e30d8a04999bf77258898 100644 |
| --- a/lib/src/compiler/code_generator.dart |
| +++ b/lib/src/compiler/code_generator.dart |
| @@ -701,6 +701,10 @@ class CodeGenerator extends GeneralizingAstVisitor |
| var fields = <FieldDeclaration>[]; |
| var staticFields = <FieldDeclaration>[]; |
| var methods = <MethodDeclaration>[]; |
| + |
| + // The "call" method or getter, if one exists. This can also be |
| + // "noSuchMethod" method, because nSM could implement "call". |
| + ExecutableElement callMethod; |
| for (var member in node.members) { |
| if (member is ConstructorDeclaration) { |
| ctors.add(member); |
| @@ -708,6 +712,16 @@ class CodeGenerator extends GeneralizingAstVisitor |
| (member.isStatic ? staticFields : fields).add(member); |
| } else if (member is MethodDeclaration) { |
| methods.add(member); |
| + var name = member.name.name; |
| + if (name == 'call' && !member.isSetter) { |
| + callMethod = member.element; |
| + } else if (name == 'noSuchMethod' && |
| + !member.isGetter && |
| + !member.isSetter && |
| + // Exclude SDK because we know they don't use nSM to implement call. |
| + !classElem.library.source.isInSystemLibrary) { |
| + callMethod ??= member.element; |
| + } |
| } |
| } |
| @@ -738,7 +752,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
| _emitSuperHelperSymbols(_superHelperSymbols, body); |
| // Emit the class, e.g. `core.Object = class Object { ... }` |
| - _defineClass(classElem, className, classExpr, body); |
| + _defineClass(classElem, className, classExpr, callMethod, body); |
| // Emit things that come after the ES6 `class ... { ... }`. |
| var jsPeerName = _getJSPeerName(classElem); |
| @@ -746,7 +760,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
| _emitClassTypeTests(classElem, className, body); |
| - _defineNamedConstructors(ctors, body, className); |
| + _defineNamedConstructors(ctors, body, className, callMethod); |
| _emitVirtualFieldSymbols(virtualFieldSymbols, body); |
| _emitClassSignature(methods, classElem, ctors, extensions, className, body); |
| _defineExtensionMembers(extensions, className, body); |
| @@ -764,6 +778,45 @@ class CodeGenerator extends GeneralizingAstVisitor |
| return _statement(body); |
| } |
| + /// Emits code to support a class with a "call" method and an unnamed |
| + /// constructor. |
| + /// |
| + /// This ensures instances created by the unnamed constructor are functions. |
| + /// Named constructors are handled elsewhere, see [_defineNamedConstructors]. |
| + JS.Expression _emitCallableClass(JS.ClassExpression classExpr, |
| + ConstructorElement unnamedCtor, ExecutableElement callMethod) { |
| + var ctor = new JS.NamedFunction( |
| + classExpr.name, _emitCallableClassConstructor(unnamedCtor, callMethod)); |
| + |
| + // Name the constructor function the same as the class. |
| + return js.call('dart.callableClass(#, #)', [ctor, classExpr]); |
| + } |
| + |
| + JS.Fun _emitCallableClassConstructor( |
| + ConstructorElement ctor, ExecutableElement callMethod) { |
| + bool dynamicCall = false; |
| + if (callMethod is PropertyAccessorElement) { |
| + assert(callMethod.isGetter); |
| + dynamicCall = callMethod.returnType is! FunctionType; |
|
vsm
2016/06/15 19:52:26
Do we need a dcall here if one of the param types
Jennifer Messerly
2016/06/15 20:16:51
I don't think so. Those dcalls will be handled by
|
| + } else if (callMethod.name == 'noSuchMethod') { |
| + dynamicCall = true; |
| + } |
| + |
| + var callCall = |
| + dynamicCall ? 'dart.dcall(self, args)' : 'self.call.apply(self, args)'; |
| + return js.call( |
| + r'''function (...args) { |
| + const self = this; |
| + function call(...args) { |
| + return #; |
| + } |
| + call.__proto__ = this.__proto__; |
| + call.#.apply(call, args); |
| + return call; |
| + }''', |
| + [js.call(callCall), _constructorName(ctor)]); |
| + } |
| + |
| void _emitClassTypeTests(ClassElement classElem, JS.Expression className, |
| List<JS.Statement> body) { |
| if (classElem == objectClass) { |
| @@ -978,12 +1031,26 @@ class CodeGenerator extends GeneralizingAstVisitor |
| } |
| } |
| - void _defineClass(ClassElement classElem, JS.Expression className, |
| - JS.ClassExpression classExpr, List<JS.Statement> body) { |
| + void _defineClass( |
| + ClassElement classElem, |
| + JS.Expression className, |
| + JS.ClassExpression classExpr, |
| + ExecutableElement callMethod, |
| + List<JS.Statement> body) { |
| + JS.Expression callableClass; |
| + if (callMethod != null && classElem.unnamedConstructor != null) { |
| + callableClass = _emitCallableClass( |
| + classExpr, classElem.unnamedConstructor, callMethod); |
| + } |
| + |
| if (classElem.typeParameters.isNotEmpty) { |
| - body.add(new JS.ClassDeclaration(classExpr)); |
| + if (callableClass != null) { |
| + body.add(js.statement('const # = #;', [classExpr.name, callableClass])); |
| + } else { |
| + body.add(new JS.ClassDeclaration(classExpr)); |
| + } |
| } else { |
| - body.add(js.statement('# = #;', [className, classExpr])); |
| + body.add(js.statement('# = #;', [className, callableClass ?? classExpr])); |
| } |
| } |
| @@ -1400,12 +1467,23 @@ class CodeGenerator extends GeneralizingAstVisitor |
| } |
| } |
| - void _defineNamedConstructors(List<ConstructorDeclaration> ctors, |
| - List<JS.Statement> body, JS.Expression className) { |
| + void _defineNamedConstructors( |
| + List<ConstructorDeclaration> ctors, |
| + List<JS.Statement> body, |
| + JS.Expression className, |
| + ExecutableElement callMethod) { |
| + var code = callMethod != null |
| + ? 'dart.defineNamedConstructorCallable(#, #, #);' |
| + : 'dart.defineNamedConstructor(#, #)'; |
| + |
| for (ConstructorDeclaration member in ctors) { |
| if (member.name != null && member.factoryKeyword == null) { |
| - body.add(js.statement('dart.defineNamedConstructor(#, #);', |
| - [className, _constructorName(member.element)])); |
| + var args = [className, _constructorName(member.element)]; |
| + if (callMethod != null) { |
| + args.add(_emitCallableClassConstructor(member.element, callMethod)); |
| + } |
| + |
| + body.add(js.statement(code, args)); |
| } |
| } |
| } |