| Index: lib/src/codegen/js_codegen.dart
|
| diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart
|
| index afbeb695f176f000612fd3b1258acf8bf09396d4..e9bb89b0ed0b4f75aaf7b4a9b497dc36cfd93184 100644
|
| --- a/lib/src/codegen/js_codegen.dart
|
| +++ b/lib/src/codegen/js_codegen.dart
|
| @@ -1029,14 +1029,22 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| if (element is ClassMemberElement && element is! ConstructorElement) {
|
| bool isStatic = element.isStatic;
|
| var type = element.enclosingElement.type;
|
| + var member = _emitMemberName(name, isStatic: isStatic, type: type);
|
|
|
| - // For instance methods, we add implicit-this.
|
| // For static methods, we add the raw type name, without generics or
|
| // library prefix. We don't need those because static calls can't use
|
| // the generic type.
|
| - var target = isStatic ? new JS.Identifier(type.name) : new JS.This();
|
| - var member = _emitMemberName(name, isStatic: isStatic, type: type);
|
| - return new JS.PropertyAccess(target, member);
|
| + if (isStatic) {
|
| + return js.call('#.#', [type.name, member]);
|
| + }
|
| +
|
| + // For instance members, we add implicit-this.
|
| + // For method tear-offs, we ensure it's a bound method.
|
| + var code = 'this.#';
|
| + if (element is MethodElement && !inInvocationContext(node)) {
|
| + code += '.bind(this)';
|
| + }
|
| + return js.call(code, member);
|
| }
|
|
|
| // initializing formal parameter, e.g. `Point(this.x)`
|
| @@ -1228,8 +1236,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| var result = _emitForeignJS(node);
|
| if (result != null) return result;
|
|
|
| - // TODO(jmesserly): if we try to call a getter returning a function with
|
| - // a call method, we don't generate the `.call` correctly.
|
| String code;
|
| if (target == null || isLibraryPrefix(target)) {
|
| if (rules.isDynamicCall(node.methodName)) {
|
| @@ -1241,11 +1247,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| code, [_visit(node.methodName), _visit(node.argumentList)]);
|
| }
|
|
|
| - // TODO(jmesserly): if the methodName resolves statically but the call is
|
| - // dynamic (e.g. `obj.method` is resolved to a field of type `Function`), we
|
| - // could generate call(#.#, #). Not sure if that's worth it.
|
| - if (rules.isDynamicCall(node.methodName)) {
|
| + if (rules.isDynamicTarget(target)) {
|
| code = 'dart.$DSEND(#, #, #)';
|
| + } else if (rules.isDynamicCall(node.methodName)) {
|
| + // This is a dynamic call to a statically know target. For example:
|
| + // class Foo { Function bar; }
|
| + // new Foo().bar(); // dynamic call
|
| + code = 'dart.$DCALL(#.#, #)';
|
| } else {
|
| code = '#.#(#)';
|
| }
|
| @@ -1796,24 +1804,41 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| if (isLibraryPrefix(node.prefix)) {
|
| return _visit(node.identifier);
|
| } else {
|
| - return _emitGet(node.prefix, node.identifier.name);
|
| + return _emitGet(node.prefix, node.identifier);
|
| }
|
| }
|
|
|
| @override
|
| visitPropertyAccess(PropertyAccess node) =>
|
| - _emitGet(_getTarget(node), node.propertyName.name);
|
| + _emitGet(_getTarget(node), node.propertyName);
|
|
|
| /// Shared code for [PrefixedIdentifier] and [PropertyAccess].
|
| - JS.Expression _emitGet(Expression target, String memberName) {
|
| - var name = _emitMemberName(memberName, type: getStaticType(target));
|
| + JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) {
|
| + var name = _emitMemberName(memberId.name, type: getStaticType(target));
|
| if (rules.isDynamicTarget(target)) {
|
| return js.call('dart.$DLOAD(#, #)', [_visit(target), name]);
|
| + }
|
| +
|
| + String code;
|
| + var member = memberId.staticElement;
|
| + if (member != null && member is MethodElement) {
|
| + // Tear-off methods: explicitly bind it.
|
| + if (isStateless(target, target)) {
|
| + return js.call('#.#.bind(#)', [_visit(target), name, _visit(target)]);
|
| + }
|
| + code = 'dart.bind(#, #)';
|
| } else {
|
| - return js.call('#.#', [_visit(target), name]);
|
| + code = '#.#';
|
| }
|
| +
|
| + return js.call(code, [_visit(target), name]);
|
| }
|
|
|
| + /// Emits a generic send, like an operator method.
|
| + ///
|
| + /// **Please note** this function does not support method invocation syntax
|
| + /// `obj.name(args)` because that could be a getter followed by a call.
|
| + /// See [visitMethodInvocation].
|
| JS.Expression _emitSend(
|
| Expression target, String name, List<Expression> args) {
|
| var type = getStaticType(target);
|
| @@ -1831,8 +1856,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| _visitList(args)
|
| ]);
|
| }
|
| +
|
| if (_isJSBuiltinType(type)) {
|
| - // static call pattern for bultins.
|
| + // static call pattern for builtins.
|
| return js.call('#.#(#, #)', [
|
| _emitTypeName(type),
|
| memberName,
|
|
|