Index: pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart |
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart |
index 5f5543926f33ac58d5bc4687c708fa304cfcba9e..9471cac584dc5366edb071db14adff70c8a67242 100644 |
--- a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart |
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart |
@@ -30,7 +30,9 @@ import '../../constants/values.dart' show |
import '../../core_types.dart' show |
CoreClasses; |
import '../../dart_types.dart' show |
- DartType; |
+ DartType, |
+ FunctionType, |
+ TypedefType; |
import '../../elements/elements.dart' show |
ClassElement, |
Element, |
@@ -38,6 +40,7 @@ import '../../elements/elements.dart' show |
FieldElement, |
FunctionElement, |
FunctionSignature, |
+ GetterElement, |
LibraryElement, |
MethodElement, |
Name, |
@@ -407,10 +410,50 @@ class ProgramBuilder { |
} |
} |
+ // Generating stubs for direct calls and stubs for call-through |
+ // of getters that happen to be functions. |
+ bool isFunctionLike = false; |
+ FunctionType functionType = null; |
+ |
if (member.isFunction) { |
+ FunctionElement fn = member; |
+ functionType = fn.type; |
+ } else if (member.isGetter) { |
+ if (_compiler.trustTypeAnnotations) { |
+ GetterElement getter = member; |
+ DartType returnType = getter.type.returnType; |
+ if (returnType.isFunctionType) { |
+ functionType = returnType; |
+ } else if (returnType.treatAsDynamic || |
+ _compiler.types.isSubtype(returnType, |
+ backend.coreTypes.functionType)) { |
+ if (returnType.isTypedef) { |
+ TypedefType typedef = returnType; |
+ // TODO(jacobr): can we just use typdef.unaliased instead? |
+ functionType = typedef.element.functionSignature.type; |
+ } else { |
+ // Other misc function type such as coreTypes.Function. |
+ // Allow any number of arguments. |
+ isFunctionLike = true; |
+ } |
+ } |
+ } else { |
+ isFunctionLike = true; |
+ } |
+ } // TODO(jacobr): handle field elements. |
+ |
+ if (isFunctionLike || functionType != null) { |
+ int minArgs; |
+ int maxArgs; |
+ if (functionType != null) { |
+ minArgs = functionType.parameterTypes.length; |
+ maxArgs = minArgs + functionType.optionalParameterTypes.length; |
+ } else { |
+ minArgs = 0; |
+ maxArgs = 32767; |
+ } |
var selectors = |
_compiler.codegenWorld.invocationsByName(member.name); |
- FunctionElement fn = member; |
// Named arguments are not yet supported. In the future we |
// may want to map named arguments to an object literal containing |
// all named arguments. |
@@ -418,16 +461,23 @@ class ProgramBuilder { |
for (var selector in selectors.keys) { |
// Check whether the arity matches this member. |
var argumentCount = selector.argumentCount; |
- if (argumentCount > fn.parameters.length) break; |
- if (argumentCount < fn.parameters.length && |
- !fn.parameters[argumentCount].isOptional) break; |
+ // JS interop does not support named arguments. |
+ if (selector.namedArgumentCount > 0) break; |
+ if (argumentCount < minArgs) break; |
+ if (argumentCount > maxArgs) break; |
var stubName = namer.invocationName(selector); |
if (!stubNames.add(stubName.key)) break; |
- var candidateParameterNames = |
- 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMOPQRSTUVWXYZ'; |
var parameters = new List<String>.generate(argumentCount, |
- (i) => candidateParameterNames[i]); |
- |
+ (i) => 'p$i'); |
+ |
+ // We intentionally generate the same stub method for direct |
+ // calls and call-throughs of getters so that calling a |
+ // getter that returns a function behaves the same as calling |
+ // a method. This is helpful as many typed JavaScript APIs |
+ // specify member functions with getters that return |
+ // functions. The behavior of this solution matches JavaScript |
+ // behavior implicitly binding this only when JavaScript |
+ // would. |
interceptorClass.callStubs.add(_buildStubMethod( |
stubName, |
js.js('function(receiver, #) { return receiver.#(#) }', |