Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3051)

Unified Diff: dart/sdk/lib/_internal/lib/js_helper.dart

Issue 103863004: Revert "Generate tear-off closures dynamically." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « dart/sdk/lib/_internal/lib/isolate_helper.dart ('k') | dart/sdk/lib/_internal/lib/js_mirrors.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: dart/sdk/lib/_internal/lib/js_helper.dart
diff --git a/dart/sdk/lib/_internal/lib/js_helper.dart b/dart/sdk/lib/_internal/lib/js_helper.dart
index ba5cf5bf68c5ba31092a8d792d645a91e63fd818..412572139b373c0a5bfd6d9906f704f6be8891b7 100644
--- a/dart/sdk/lib/_internal/lib/js_helper.dart
+++ b/dart/sdk/lib/_internal/lib/js_helper.dart
@@ -180,32 +180,12 @@ class JSInvocationMirror implements Invocation {
} else {
interceptor = null;
}
- bool isCatchAll = false;
var method = JS('var', '#[#]', receiver, name);
- if (JS('bool', 'typeof # != "function"', method) ) {
- String baseName = _symbol_dev.Symbol.getName(memberName);
- method = JS('', '#[# + "*"]', receiver, baseName);
- if (method == null) {
- interceptor = getInterceptor(object);
- method = JS('', '#[# + "*"]', interceptor, baseName);
- if (method != null) {
- isIntercepted = true;
- receiver = interceptor;
- } else {
- interceptor = null;
- }
- }
- isCatchAll = true;
- }
- if (JS('bool', 'typeof # == "function"', method)) {
+ if (JS('String', 'typeof #', method) == 'function') {
if (!hasReflectableProperty(method)) {
throwInvalidReflectionError(_symbol_dev.Symbol.getName(memberName));
}
- if (isCatchAll) {
- return new CachedCatchAllInvocation(method, isIntercepted, interceptor);
- } else {
- return new CachedInvocation(method, isIntercepted, interceptor);
- }
+ return new CachedInvocation(method, isIntercepted, interceptor);
} else {
// In this case, receiver doesn't implement name. So we should
// invoke noSuchMethod instead (which will often throw a
@@ -244,7 +224,7 @@ class CachedInvocation {
bool get isNoSuchMethod => false;
- /// Applies [jsFunction] to [victim] with [arguments].
+ /// Applies [jsFunction] to object with [arguments].
/// Users of this class must take care to check the arguments first.
invokeOn(Object victim, List arguments) {
var receiver = victim;
@@ -258,49 +238,6 @@ class CachedInvocation {
}
}
-class CachedCatchAllInvocation extends CachedInvocation {
- final ReflectionInfo info;
-
- CachedCatchAllInvocation(jsFunction,
- bool isIntercepted,
- Interceptor cachedInterceptor)
- : info = new ReflectionInfo(jsFunction),
- super(jsFunction, isIntercepted, cachedInterceptor);
-
- invokeOn(Object victim, List arguments) {
- var receiver = victim;
- int providedArgumentCount;
- if (!isIntercepted) {
- if (arguments is! JSArray) arguments = new List.from(arguments);
- providedArgumentCount = arguments.length;
- } else {
- arguments = [victim]..addAll(arguments);
- if (cachedInterceptor != null) receiver = cachedInterceptor;
- providedArgumentCount = arguments.length - 1;
- }
- int fullParameterCount =
- info.requiredParameterCount + info.optionalParameterCount;
- if (info.areOptionalParametersNamed &&
- (providedArgumentCount > info.requiredParameterCount)) {
- throw new UnimplementedNoSuchMethodError(
- "Invocation of unstubbed method '${info.reflectionName}'"
- " with ${arguments.length} arguments.");
- } else if (providedArgumentCount < info.requiredParameterCount) {
- throw new UnimplementedNoSuchMethodError(
- "Invocation of unstubbed method '${info.reflectionName}'"
- " with $providedArgumentCount arguments (too few).");
- } else if (providedArgumentCount > fullParameterCount) {
- throw new UnimplementedNoSuchMethodError(
- "Invocation of unstubbed method '${info.reflectionName}'"
- " with $providedArgumentCount arguments (too many).");
- }
- for (int i = providedArgumentCount; i < fullParameterCount; i++) {
- arguments.add(getMetadata(info.defaultValue(i)));
- }
- return JS("var", "#.apply(#, #)", jsFunction, receiver, arguments);
- }
-}
-
class CachedNoSuchMethodInvocation {
/// Non-null interceptor if this is an intercepted call through an
/// [Interceptor].
@@ -316,96 +253,6 @@ class CachedNoSuchMethodInvocation {
}
}
-class ReflectionInfo {
- static const int REQUIRED_PARAMETERS_INFO = 0;
- static const int OPTIONAL_PARAMETERS_INFO = 1;
- static const int FUNCTION_TYPE_INDEX = 2;
- static const int FIRST_DEFAULT_ARGUMENT = 3;
-
- /// A JavaScript function object.
- final jsFunction;
-
- /// Raw reflection information.
- final List data;
-
- /// Is this a getter or a setter.
- final bool isAccessor;
-
- /// Number of required parameters.
- final int requiredParameterCount;
-
- /// Number of optional parameters.
- final int optionalParameterCount;
-
- /// Are optional parameters named.
- final bool areOptionalParametersNamed;
-
- /// Either an index to the function type in [:init.metadata:] or a JavaScript
- /// function object which can compute such a type (presumably due to free
- /// type variables).
- final functionType;
-
- ReflectionInfo.internal(this.jsFunction,
- this.data,
- this.isAccessor,
- this.requiredParameterCount,
- this.optionalParameterCount,
- this.areOptionalParametersNamed,
- this.functionType);
-
- factory ReflectionInfo(jsFunction) {
- List data = JS('JSExtendableArray|Null', r'#.$reflectionInfo', jsFunction);
- if (data == null) return null;
- data = JSArray.markFixedList(data);
-
- int requiredParametersInfo =
- JS('int', '#[#]', data, REQUIRED_PARAMETERS_INFO);
- int requiredParameterCount = JS('int', '# >> 1', requiredParametersInfo);
- bool isAccessor = (requiredParametersInfo & 1) == 1;
-
- int optionalParametersInfo =
- JS('int', '#[#]', data, OPTIONAL_PARAMETERS_INFO);
- int optionalParameterCount = JS('int', '# >> 1', optionalParametersInfo);
- bool areOptionalParametersNamed = (optionalParametersInfo & 1) == 1;
-
- var functionType = JS('', '#[#]', data, FUNCTION_TYPE_INDEX);
- return new ReflectionInfo.internal(
- jsFunction, data, isAccessor, requiredParameterCount,
- optionalParameterCount, areOptionalParametersNamed, functionType);
- }
-
- String parameterName(int parameter) {
- int metadataIndex = JS('int', '#[# + # + #]', data, parameter,
- optionalParameterCount, FIRST_DEFAULT_ARGUMENT);
- return JS('String', 'init.metadata[#]', metadataIndex);
- }
-
- int defaultValue(int parameter) {
- if (parameter < requiredParameterCount) return null;
- return JS('int', '#[# + # - #]', data,
- FIRST_DEFAULT_ARGUMENT, parameter, requiredParameterCount);
- }
-
- @NoInline
- computeFunctionRti(jsConstructor) {
- if (JS('bool', 'typeof # == "number"', functionType)) {
- return getMetadata(functionType);
- } else if (JS('bool', 'typeof # == "function"', functionType)) {
- var fakeInstance = JS('', 'new #()', jsConstructor);
- setRuntimeTypeInfo(
- fakeInstance, JS('JSExtendableArray', '#["<>"]', fakeInstance));
- return JS('=Object|Null', r'#.apply({$receiver:#})',
- functionType, fakeInstance);
- } else {
- throw new RuntimeError('Unexpected function type');
- }
- }
-
- String get reflectionName => JS('String', r'#.$reflectionName', jsFunction);
-}
-
-getMetadata(int index) => JS('', 'init.metadata[#]', index);
-
class Primitives {
/// Isolate-unique ID for caching [JsClosureMirror.function].
/// Note the initial value is used by the first isolate (or if there are no
@@ -833,109 +680,62 @@ class Primitives {
JS('void', '#[#] = #', object, key, value);
}
- static functionNoSuchMethod(function,
- List positionalArguments,
- Map<String, dynamic> namedArguments) {
+ static applyFunction(Function function,
+ List positionalArguments,
+ Map<String, dynamic> namedArguments) {
int argumentCount = 0;
+ StringBuffer buffer = new StringBuffer();
List arguments = [];
- List namedArgumentList = [];
if (positionalArguments != null) {
argumentCount += positionalArguments.length;
arguments.addAll(positionalArguments);
}
- String names = '';
- if (namedArguments != null && !namedArguments.isEmpty) {
- namedArguments.forEach((String name, argument) {
- names = '$names\$$name';
- namedArgumentList.add(name);
- arguments.add(argument);
- argumentCount++;
- });
- }
-
- String selectorName = 'call\$$argumentCount$names';
-
- return function.noSuchMethod(
- createUnmangledInvocationMirror(
- #call,
- selectorName,
- JSInvocationMirror.METHOD,
- arguments,
- namedArgumentList));
- }
-
- static applyFunction(Function function,
- List positionalArguments,
- Map<String, dynamic> namedArguments) {
- if (namedArguments != null && !namedArguments.isEmpty) {
- // TODO(ahe): The following code can be shared with
- // JsInstanceMirror.invoke.
- var interceptor = getInterceptor(function);
- var jsFunction = JS('', '#["call*"]', interceptor);
-
- if (jsFunction == null) {
- return functionNoSuchMethod(
- function, positionalArguments, namedArguments);
- }
- ReflectionInfo info = new ReflectionInfo(jsFunction);
- if (info == null || !info.areOptionalParametersNamed) {
- return functionNoSuchMethod(
- function, positionalArguments, namedArguments);
- }
-
- if (positionalArguments != null) {
- positionalArguments = new List.from(positionalArguments);
- } else {
- positionalArguments = [];
- }
- // Check the number of positional arguments is valid.
- if (info.requiredParameterCount != positionalArguments.length) {
- return functionNoSuchMethod(
- function, positionalArguments, namedArguments);
+ if (JS('bool', r'# in #', JS_GET_NAME('CALL_CATCH_ALL'), function)) {
+ // We expect the closure to have a "call$catchAll" (the value of
+ // JS_GET_NAME('CALL_CATCH_ALL')) function that returns all the expected
+ // named parameters as a (new) JavaScript object literal. The keys in
+ // the object literal correspond to the argument names, and the values
+ // are the default values. The compiler emits the properties sorted by
+ // keys, and this order is preserved in JavaScript, so we don't need to
+ // sort the keys. Since a new object is returned each time we call
+ // call$catchAll, we can simply overwrite default entries with the
+ // provided named arguments. If there are incorrectly named arguments in
+ // [namedArguments], noSuchMethod will be called as expected.
+ var allNamedArguments =
+ JS('var', r'#[#]()', function, JS_GET_NAME('CALL_CATCH_ALL'));
+ if (namedArguments != null && !namedArguments.isEmpty) {
+ namedArguments.forEach((String key, argument) {
+ JS('void', '#[#] = #', allNamedArguments, key, argument);
+ });
}
- var defaultArguments = new Map();
- for (int i = 0; i < info.optionalParameterCount; i++) {
- var parameterName = info.parameterName(i + info.requiredParameterCount);
- var defaultValue =
- getMetadata(info.defaultValue(i + info.requiredParameterCount));
- defaultArguments[parameterName] = defaultValue;
- }
- bool bad = false;
- namedArguments.forEach((String parameter, value) {
- if (defaultArguments.containsKey(parameter)) {
- defaultArguments[parameter] = value;
- } else {
- // Extraneous named argument.
- bad = true;
- }
+ List<String> listOfNamedArguments =
+ JS('List', 'Object.getOwnPropertyNames(#)', allNamedArguments);
+ argumentCount += listOfNamedArguments.length;
+ listOfNamedArguments.forEach((String name) {
+ buffer.write('\$$name');
+ arguments.add(JS('', '#[#]', allNamedArguments, name));
});
- if (bad) {
- return functionNoSuchMethod(
- function, positionalArguments, namedArguments);
+ } else {
+ if (namedArguments != null && !namedArguments.isEmpty) {
+ namedArguments.forEach((String name, argument) {
+ buffer.write('\$$name');
+ arguments.add(argument);
+ argumentCount++;
+ });
}
- positionalArguments.addAll(defaultArguments.values);
- return JS('', '#.apply(#, #)', jsFunction, function, positionalArguments);
- }
-
- int argumentCount = 0;
- List arguments = [];
-
- if (positionalArguments != null) {
- argumentCount += positionalArguments.length;
- arguments.addAll(positionalArguments);
}
- String selectorName = 'call\$$argumentCount';
+ String selectorName = 'call\$$argumentCount$buffer';
var jsFunction = JS('var', '#[#]', function, selectorName);
if (jsFunction == null) {
-
- // TODO(ahe): This might occur for optional arguments if there is no call
- // selector with that many arguments.
-
- return
- functionNoSuchMethod(function, positionalArguments, namedArguments);
+ return function.noSuchMethod(createUnmangledInvocationMirror(
+ const Symbol('call'),
+ selectorName,
+ JSInvocationMirror.METHOD,
+ arguments,
+ namedArguments == null ? [] : namedArguments.keys.toList()));
}
// We bound 'this' to [function] because of how we compile
// closures: escaped local variables are stored and accessed through
@@ -1731,149 +1531,13 @@ convertDartClosureToJS(closure, int arity) {
/**
* Super class for Dart closures.
*/
-abstract class Closure implements Function {
- // TODO(ahe): These constants must be in sync with
- // reflection_data_parser.dart.
- static const FUNCTION_INDEX = 0;
- static const NAME_INDEX = 1;
- static const CALL_NAME_INDEX = 2;
- static const REQUIRED_PARAMETER_INDEX = 3;
- static const OPTIONAL_PARAMETER_INDEX = 4;
- static const DEFAULT_ARGUMENTS_INDEX = 5;
-
- Closure();
-
- /**
- * Creates a closure for use by implicit getters associated with a method.
- *
- * In other words, creates a tear-off closure.
- *
- * Called from [closureFromTearOff] as well as from reflection when tearing
- * of a method via [:getField:].
- *
- * This method assumes that [functions] was created by the JavaScript function
- * `addStubs` in `reflection_data_parser.dart`. That is, a list of JavaScript
- * function objects with properties `$stubName` and `$callName`.
- *
- * Further assumes that [reflectionInfo] is the end of the array created by
- * [dart2js.js_emitter.ContainerBuilder.addMemberMethod] starting with
- * required parameter count.
- *
- * Caution: this function may be called when building constants.
- * TODO(ahe): Don't call this function when building constants.
- */
- factory Closure.fromTearOff(receiver,
- List functions,
- List reflectionInfo,
- bool isStatic,
- jsArguments,
- String propertyName) {
- // TODO(ahe): All the place below using \$ should be rewritten to go
- // through the namer.
- var function = JS('', '#[#]', functions, 0);
- // TODO(ahe): Try fetching the property directly instead of using "in".
- if (isStatic && JS('bool', '"\$tearOff" in #', function)) {
- // The implicit closure of a static function is always the same.
- return JS('Closure', '#.\$tearOff', function);
- }
-
- String name = JS('String|Null', '#.\$stubName', function);
- String callName = JS('String|Null', '#.\$callName', function);
-
- JS('', r'#.$reflectionInfo = #', function, reflectionInfo);
- ReflectionInfo info = new ReflectionInfo(function);
-
- var functionType = info.functionType;
-
- // Create a closure and "monkey" patch it with call stubs.
- Closure closure;
- var trampoline = function;
- if (!isStatic) {
- if (JS('bool', '#.length == 1', jsArguments)) {
- // Intercepted call.
- var argument = JS('', '#[0]', jsArguments);
- trampoline = forwardInterceptedCallTo(argument, receiver, function);
- closure = new BoundClosure(receiver, function, argument, name);
- } else {
- trampoline = forwardTo(receiver, function);
- closure = new BoundClosure(receiver, function, null, name);
- }
- } else {
- closure = new TearOffClosure();
- JS('', '#.\$tearOff = #', function, closure);
- JS('', r'#.$name = #', closure, propertyName);
- }
-
- var signatureFunction;
- if (JS('bool', 'typeof # == "number"', functionType)) {
- signatureFunction =
- JS('', '(function(s){return function(){return init.metadata[s]}})(#)',
- functionType);
- } else if (!isStatic
- && JS('bool', 'typeof # == "function"', functionType)) {
- signatureFunction = functionType;
- JS('', r'#.$receiver = #', closure, receiver);
- } else {
- throw 'Error in reflectionInfo.';
- }
-
- JS('', '#.\$signature = #', closure, signatureFunction);
-
- JS('', '#[#] = #', closure, callName, trampoline);
- for (int i = 1; i < functions.length; i++) {
- var stub = functions[i];
- var stubCallName = JS('String|Null', '#.\$callName', stub);
- // TODO(ahe): Support interceptors here.
- JS('', '#[#] = #', closure, stubCallName,
- isStatic ? stub : forwardTo(receiver, stub));
- }
-
- JS('', '#["call*"] = #', closure, function);
-
- return closure;
- }
-
- static forwardTo(receiver, function) {
- return JS(
- '',
- 'function(r,f){return function(){return f.apply(r,arguments)}}(#,#)',
- receiver, function);
- }
-
- static forwardInterceptedCallTo(self, interceptor, function) {
- return JS(
- '',
- 'function(i,s,f){return function(){'
- 'return f.call.bind(f,i,s).apply(i,arguments)}}(#,#,#)',
- interceptor, self, function);
- }
-
+class Closure implements Function {
String toString() => "Closure";
}
-/// Called from implicit method getter (aka tear-off).
-Closure closureFromTearOff(receiver,
- functions,
- reflectionInfo,
- isStatic,
- jsArguments,
- name) {
- return new Closure.fromTearOff(
- receiver,
- JSArray.markFixedList(functions),
- JSArray.markFixedList(reflectionInfo),
- JS('bool', '!!#', isStatic),
- jsArguments,
- JS('String', '#', name));
-}
-
-/// Represents an implicit closure of a function.
-class TearOffClosure extends Closure {
-}
-
/// Represents a 'tear-off' closure, that is an instance method bound
/// to a specific receiver (instance).
-class BoundClosure extends TearOffClosure {
+class BoundClosure extends Closure {
/// The receiver or interceptor.
// TODO(ahe): This could just be the interceptor, we always know if
// we need the interceptor when generating the call method.
@@ -1888,8 +1552,6 @@ class BoundClosure extends TearOffClosure {
/// The name of the function. Only used by the mirror system.
final String _name;
- BoundClosure(this._self, this._target, this._receiver, this._name);
-
bool operator==(other) {
if (identical(this, other)) return true;
if (other is! BoundClosure) return false;
@@ -2739,13 +2401,3 @@ class FunctionTypeInfoDecoderRing {
return _cachedToString = "$s";
}
}
-
-// TODO(ahe): Remove this class and call noSuchMethod instead.
-class UnimplementedNoSuchMethodError extends Error
- implements NoSuchMethodError {
- final String _message;
-
- UnimplementedNoSuchMethodError(this._message);
-
- String toString() => "Unsupported operation: $_message";
-}
« no previous file with comments | « dart/sdk/lib/_internal/lib/isolate_helper.dart ('k') | dart/sdk/lib/_internal/lib/js_mirrors.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698