Chromium Code Reviews| Index: sdk/lib/_internal/js_runtime/lib/js_helper.dart |
| diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
| index 39c897f066532fa068985a811fd99e44ba34a554..e93d2c72630ef8d18971958f3a7941bb5fb2bafb 100644 |
| --- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
| +++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
| @@ -1156,64 +1156,177 @@ class Primitives { |
| namedArgumentList)); |
| } |
| + /** |
| + * Implements [Function.apply] for the "new" emitters. |
|
Siggi Cherem (dart-lang)
2015/07/23 19:43:42
"new" emitters => start_up and lazy emitters?
floitsch
2015/07/29 18:26:46
Done.
|
| + * |
| + * There are two types of closures that can reach this function: |
| + * |
| + * 1. tear-offs (including tear-offs of static functions). |
| + * 2. anonymous closures. |
| + * |
| + * They are treated differently (although there are lots of similarities). |
| + * Both have in common that they have |
| + * a [JsGetName.CALL_CATCH_ALL] and |
| + * a [JsGetName.REQUIRED_PARAMETER_PROPERTY] property. |
| + * |
| + * If the closure supports optional parameters, then they also feature |
| + * a [JsGetName.DEFAULT_VALUES_PROPERTY] property. |
| + * |
| + * The catch-all property is a method that takes all arguments (including |
| + * all optional positional or named arguments). If the function accepts |
| + * optional arguments, then the default-values property stores (potentially |
| + * wrapped in a function) the default values for the optional arguments. If |
| + * the function accepts optional positional arguments, then the value is a |
| + * JavaScript array with the default values. Otherwise it is a |
|
Siggi Cherem (dart-lang)
2015/07/23 19:43:42
nit: Otherwise => Otherwise, when the function acc
floitsch
2015/07/29 18:26:46
Done.
|
| + * JavaScript object. |
| + * |
| + * The default-values property may either contain the value directly, or |
| + * it can be a function that returns the default-values when invoked. |
| + * |
| + * If the function is an anonymous closure, then the catch-all property |
| + * only contains a string pointing to the property that should be used |
| + * instead. For example, if the catch-all property contains the string |
| + * "call$4", then the object's "call$4" property should be used as if it was |
| + * the value of the catch-all property. |
| + */ |
| static applyFunctionNewEmitter(Function function, |
|
Siggi Cherem (dart-lang)
2015/07/23 19:43:42
not sure what to name this now that there is no "n
floitsch
2015/07/29 18:26:46
I ended up picking applyFunction2.
In the end it i
|
| List positionalArguments, |
| Map<String, dynamic> namedArguments) { |
| - if (namedArguments == null) { |
| - int requiredParameterCount = JS('int', r'#[#]', function, |
| - JS_GET_NAME(JsGetName.REQUIRED_PARAMETER_PROPERTY)); |
| - int argumentCount = positionalArguments.length; |
| - if (argumentCount < requiredParameterCount) { |
| - return functionNoSuchMethod(function, positionalArguments, null); |
| + // Fast shortcut for the common case. |
| + if (JS('bool', '# instanceof Array', positionalArguments) && |
| + (namedArguments == null || namedArguments.isEmpty)) { |
| + // Let the compiler know that we did a type-test. |
| + List arguments = (JS('JSArray', '#', positionalArguments)); |
| + int argumentCount = arguments.length; |
| + if (argumentCount == 0) { |
| + String selectorName = JS_GET_NAME(JsGetName.CALL_PREFIX0); |
| + if (JS('bool', '!!#[#]', function, selectorName)) { |
| + return JS('', '#[#]()', function, selectorName); |
| + } |
| + } else if (argumentCount == 1) { |
| + String selectorName = JS_GET_NAME(JsGetName.CALL_PREFIX1); |
| + if (JS('bool', '!!#[#]', function, selectorName)) { |
| + return JS('', '#[#](#[0])', function, selectorName, arguments); |
| + } |
| + } else if (argumentCount == 2) { |
| + String selectorName = JS_GET_NAME(JsGetName.CALL_PREFIX2); |
| + if (JS('bool', '!!#[#]', function, selectorName)) { |
| + return JS('', '#[#](#[0],#[1])', function, selectorName, |
| + arguments, arguments); |
| + } |
| + } else if (argumentCount == 3) { |
| + String selectorName = JS_GET_NAME(JsGetName.CALL_PREFIX3); |
| + if (JS('bool', '!!#[#]', function, selectorName)) { |
| + return JS('', '#[#](#[0],#[1],#[2])', function, selectorName, |
| + arguments, arguments, arguments); |
| + } |
| } |
| String selectorName = |
| '${JS_GET_NAME(JsGetName.CALL_PREFIX)}\$$argumentCount'; |
| var jsStub = JS('var', r'#[#]', function, selectorName); |
| - if (jsStub == null) { |
| - // Do a dynamic call. |
| - var interceptor = getInterceptor(function); |
| - var jsFunction = JS('', '#[#]', interceptor, |
| - JS_GET_NAME(JsGetName.CALL_CATCH_ALL)); |
| - var defaultValues = JS('var', r'#[#]', function, |
| - JS_GET_NAME(JsGetName.DEFAULT_VALUES_PROPERTY)); |
| - if (!JS('bool', '# instanceof Array', defaultValues)) { |
| - // The function expects named arguments! |
| - return functionNoSuchMethod(function, positionalArguments, null); |
| - } |
| - int defaultsLength = JS('int', "#.length", defaultValues); |
| - int maxArguments = requiredParameterCount + defaultsLength; |
| - if (argumentCount > maxArguments) { |
| - // The function expects less arguments! |
| - return functionNoSuchMethod(function, positionalArguments, null); |
| - } |
| - List arguments = new List.from(positionalArguments); |
| - List missingDefaults = JS('JSArray', '#.slice(#)', defaultValues, |
| - argumentCount - requiredParameterCount); |
| - arguments.addAll(missingDefaults); |
| - return JS('var', '#.apply(#, #)', jsFunction, function, arguments); |
| + if (jsStub != null) { |
| + return JS('var', '#.apply(#, #)', jsStub, function, arguments); |
| + } |
| + } |
| + |
| + return _genericApplyFunctionNewEmitter( |
| + function, positionalArguments, namedArguments); |
| + } |
| + |
| + static _genericApplyFunctionNewEmitter(Function function, |
| + List positionalArguments, |
| + Map<String, dynamic> namedArguments) { |
| + List arguments; |
| + if (positionalArguments != null) { |
| + if (JS('bool', '# instanceof Array', positionalArguments)) { |
| + arguments = JS('JSArray', '#', positionalArguments); |
| + } else { |
| + arguments = new List.from(positionalArguments); |
| } |
| - return JS('var', '#.apply(#, #)', jsStub, function, positionalArguments); |
| } else { |
| - var interceptor = getInterceptor(function); |
| - var jsFunction = JS('', '#[#]', interceptor, |
| + arguments = []; |
| + } |
| + |
| + int argumentCount = arguments.length; |
| + |
| + var interceptor = getInterceptor(function); |
|
Siggi Cherem (dart-lang)
2015/07/23 19:43:42
move this further down, closer to where we need it
floitsch
2015/07/29 18:26:46
Done.
|
| + var jsFunction = JS('', '#[#]', interceptor, |
| JS_GET_NAME(JsGetName.CALL_CATCH_ALL)); |
| - var defaultValues = JS('JSArray', r'#[#]', function, |
| + if (jsFunction is String) { |
| + // Anonymous closures redirect to the catch-all property instead of |
| + // storing the catch-all method directly in the catch-all property. |
| + jsFunction = JS('', '#[#]', interceptor, jsFunction); |
| + } |
| + int requiredParameterCount = JS('int', r'#[#]', function, |
| + JS_GET_NAME(JsGetName.REQUIRED_PARAMETER_PROPERTY)); |
| + |
| + if (argumentCount < requiredParameterCount) { |
| + return functionNoSuchMethod(function, arguments, namedArguments); |
| + } |
| + |
| + var defaultValues = JS('var', r'#[#]', function, |
| JS_GET_NAME(JsGetName.DEFAULT_VALUES_PROPERTY)); |
| + if (JS('bool', 'typeof # == "function"', defaultValues)) { |
| + // Anonymous closures return a function that returns the default values |
| + // instead of providing the default values directly. |
| + defaultValues = JS('', '#()', defaultValues); |
| + } |
| + bool acceptsOptionalArguments = defaultValues != null; |
| + |
| + if (!acceptsOptionalArguments) { |
| + if (argumentCount == requiredParameterCount) { |
| + return JS('var', r'#.apply(#, #)', jsFunction, function, arguments); |
| + } |
| + return functionNoSuchMethod(function, arguments, namedArguments); |
| + } |
| + |
| + bool acceptsPositionalArguments = |
| + JS('bool', '# instanceof Array', defaultValues); |
| + |
| + if (acceptsPositionalArguments) { |
| + if (namedArguments != null && namedArguments.isNotEmpty) { |
| + // Tried to invoke a function that takes optional positional arguments |
| + // with named arguments. |
| + return functionNoSuchMethod(function, arguments, namedArguments); |
| + } |
| + |
| + int defaultsLength = JS('int', "#.length", defaultValues); |
| + int maxArguments = requiredParameterCount + defaultsLength; |
| + if (argumentCount > maxArguments) { |
| + // The function expects fewer arguments. |
| + return functionNoSuchMethod(function, arguments, null); |
| + } |
| + List missingDefaults = JS('JSArray', '#.slice(#)', defaultValues, |
| + argumentCount - requiredParameterCount); |
| + arguments.addAll(missingDefaults); |
| + return JS('var', '#.apply(#, #)', jsFunction, function, arguments); |
| + } else { |
| + // Handle named arguments. |
| + |
| + if (argumentCount > requiredParameterCount) { |
| + // Tried to invoke a function that takes named parameters with |
| + // too many positional arguments. |
| + return functionNoSuchMethod(function, arguments, namedArguments); |
| + } |
| + |
| List keys = JS('JSArray', r'Object.keys(#)', defaultValues); |
| - List arguments = new List.from(positionalArguments); |
| - int used = 0; |
| - for (String key in keys) { |
| - var value = namedArguments[key]; |
| - if (value != null) { |
| - used++; |
| - arguments.add(value); |
| - } else { |
| - arguments.add(JS('var', r'#[#]', defaultValues, key)); |
| + if (namedArguments == null) { |
| + for (String key in keys) { |
| + arguments.add(JS('var', '#[#]', defaultValues, key)); |
| + } |
| + } else { |
| + int used = 0; |
| + for (String key in keys) { |
| + if (namedArguments.containsKey(key)) { |
| + used++; |
| + arguments.add(namedArguments[key]); |
| + } else { |
| + arguments.add(JS('var', r'#[#]', defaultValues, key)); |
| + } |
| + } |
| + if (used != namedArguments.length) { |
| + return functionNoSuchMethod(function, arguments, namedArguments); |
| } |
| - } |
| - if (used != namedArguments.length) { |
| - return functionNoSuchMethod(function, positionalArguments, |
| - namedArguments); |
| } |
| return JS('var', r'#.apply(#, #)', jsFunction, function, arguments); |
| } |