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

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

Issue 125033003: Version 1.1.0-dev.5.1 (Closed) Base URL: http://dart.googlecode.com/svn/trunk/
Patch Set: Created 6 years, 11 months 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/foreign_helper.dart ('k') | dart/sdk/lib/_internal/lib/js_rti.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
===================================================================
--- dart/sdk/lib/_internal/lib/js_helper.dart (revision 31466)
+++ dart/sdk/lib/_internal/lib/js_helper.dart (working copy)
@@ -5,29 +5,33 @@
library _js_helper;
import 'dart:collection';
-import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS,
- JS,
- JS_CALL_IN_ISOLATE,
- JS_CONST,
- JS_CURRENT_ISOLATE,
- JS_CURRENT_ISOLATE_CONTEXT,
- JS_DART_OBJECT_CONSTRUCTOR,
- JS_FUNCTION_CLASS_NAME,
- JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG,
- JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG,
- JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG,
- JS_FUNCTION_TYPE_RETURN_TYPE_TAG,
- JS_FUNCTION_TYPE_TAG,
- JS_FUNCTION_TYPE_VOID_RETURN_TAG,
- JS_GET_NAME,
- JS_HAS_EQUALS,
- JS_IS_INDEXABLE_FIELD_NAME,
- JS_OBJECT_CLASS_NAME,
- JS_NULL_CLASS_NAME,
- JS_OPERATOR_AS_PREFIX,
- JS_OPERATOR_IS_PREFIX,
- JS_SIGNATURE_NAME,
- RAW_DART_FUNCTION_REF;
+
+import 'dart:_foreign_helper' show
+ DART_CLOSURE_TO_JS,
+ JS,
+ JS_CALL_IN_ISOLATE,
+ JS_CONST,
+ JS_CURRENT_ISOLATE,
+ JS_CURRENT_ISOLATE_CONTEXT,
+ JS_DART_OBJECT_CONSTRUCTOR,
+ JS_EFFECT,
+ JS_FUNCTION_CLASS_NAME,
+ JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG,
+ JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG,
+ JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG,
+ JS_FUNCTION_TYPE_RETURN_TYPE_TAG,
+ JS_FUNCTION_TYPE_TAG,
+ JS_FUNCTION_TYPE_VOID_RETURN_TAG,
+ JS_GET_NAME,
+ JS_HAS_EQUALS,
+ JS_IS_INDEXABLE_FIELD_NAME,
+ JS_NULL_CLASS_NAME,
+ JS_OBJECT_CLASS_NAME,
+ JS_OPERATOR_AS_PREFIX,
+ JS_OPERATOR_IS_PREFIX,
+ JS_SIGNATURE_NAME,
+ RAW_DART_FUNCTION_REF;
+
import 'dart:_interceptors';
import 'dart:_collection-dev' as _symbol_dev;
import 'dart:_collection-dev' show MappedIterable;
@@ -1741,10 +1745,21 @@
static const OPTIONAL_PARAMETER_INDEX = 4;
static const DEFAULT_ARGUMENTS_INDEX = 5;
+ /**
+ * Global counter to prevent reusing function code objects.
+ *
+ * V8 will share the underlying function code objects when the same string is
+ * passed to "new Function". Shared function code objects can lead to
+ * sub-optimal performance due to polymorhism, and can be prevented by
+ * ensuring the strings are different.
+ */
+ static int functionCounter = 0;
+
Closure();
/**
- * Creates a closure for use by implicit getters associated with a method.
+ * Creates a new closure class for use by implicit getters associated with a
+ * method.
*
* In other words, creates a tear-off closure.
*
@@ -1762,46 +1777,83 @@
* 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) {
+ static fromTearOff(receiver,
+ List functions,
+ List reflectionInfo,
+ bool isStatic,
+ jsArguments,
+ String propertyName) {
+ JS_EFFECT(() {
+ BoundClosure.receiverOf(JS('BoundClosure', 'void 0'));
+ BoundClosure.selfOf(JS('BoundClosure', 'void 0'));
+ });
// 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);
+ JS('', '#.\$reflectionInfo = #', function, reflectionInfo);
ReflectionInfo info = new ReflectionInfo(function);
var functionType = info.functionType;
+ // function tmp() {};
+ // tmp.prototype = BC.prototype;
+ // var proto = new tmp;
+ // for each computed prototype property:
+ // proto[property] = ...;
+ // proto._init = BC;
+ // var dynClosureConstructor =
+ // new Function('self', 'target', 'receiver', 'name',
+ // 'this._init(self, target, receiver, name)');
+ // proto.constructor = dynClosureConstructor; // Necessary?
+ // dynClosureConstructor.prototype = proto;
+ // return dynClosureConstructor;
+
+ // We need to create a new subclass of either TearOffClosure or
+ // BoundClosure. For this, we need to create an object whose prototype is
+ // the prototype is either TearOffClosure.prototype or
+ // BoundClosure.prototype, respectively in pseudo JavaScript code. The
+ // simplest way to access the JavaScript construction function of a Dart
+ // class is to create an instance and access its constructor property. The
+ // newly created instance could in theory be used directly as the
+ // prototype, but it might include additional fields that we don't need.
+ // So we only use the new instance to access the constructor property and
+ // use Object.create to create the desired prototype.
+ var prototype = isStatic
+ // TODO(ahe): Safe to use Object.create?
+ ? JS('TearOffClosure', 'Object.create(#.constructor.prototype)',
+ new TearOffClosure())
+ : JS('BoundClosure', 'Object.create(#.constructor.prototype)',
+ new BoundClosure(null, null, null, null));
+
+ JS('', '#.\$initialize = #', prototype, JS('', '#.constructor', prototype));
+ var constructor = isStatic
+ ? JS('', 'function(){this.\$initialize()}')
+ : isCsp
+ ? JS('', 'function(a,b,c,d) {this.\$initialize(a,b,c,d)}')
+ : JS('',
+ 'new Function("a","b","c","d",'
+ '"this.\$initialize(a,b,c,d);"+#)',
+ functionCounter++);
+
+ // TODO(ahe): Is it necessary to set the constructor property?
+ JS('', '#.constructor = #', prototype, constructor);
+
+ JS('', '#.prototype = #', constructor, prototype);
+
// Create a closure and "monkey" patch it with call stubs.
- Closure closure;
var trampoline = function;
+ var isIntercepted = false;
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);
+ isIntercepted = true;
}
+ trampoline = forwardCallTo(function, isIntercepted);
} else {
- closure = new TearOffClosure();
- JS('', '#.\$tearOff = #', function, closure);
- JS('', r'#.$name = #', closure, propertyName);
+ JS('', '#.\$name = #', prototype, propertyName);
}
var signatureFunction;
@@ -1811,54 +1863,241 @@
functionType);
} else if (!isStatic
&& JS('bool', 'typeof # == "function"', functionType)) {
- signatureFunction = functionType;
- JS('', r'#.$receiver = #', closure, receiver);
+ var getReceiver = isIntercepted
+ ? RAW_DART_FUNCTION_REF(BoundClosure.receiverOf)
+ : RAW_DART_FUNCTION_REF(BoundClosure.selfOf);
+ signatureFunction = JS(
+ '',
+ 'function(f,r){'
+ 'return function(){'
+ 'return f.apply({\$receiver:r(this)},arguments)'
+ '}'
+ '}(#,#)', functionType, getReceiver);
} else {
throw 'Error in reflectionInfo.';
}
- JS('', '#.\$signature = #', closure, signatureFunction);
+ JS('', '#.\$signature = #', prototype, signatureFunction);
- JS('', '#[#] = #', closure, callName, trampoline);
+ JS('', '#[#] = #', prototype, 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));
+ if (stubCallName != null) {
+ JS('', '#[#] = #', prototype, stubCallName,
+ isStatic ? stub : forwardCallTo(stub, isIntercepted));
+ }
}
- JS('', '#["call*"] = #', closure, function);
+ JS('', '#["call*"] = #', prototype, function);
- return closure;
+ return constructor;
}
- static forwardTo(receiver, function) {
- return JS(
- '',
- 'function(r,f){return function(){return f.apply(r,arguments)}}(#,#)',
- receiver, function);
+ static cspForwardCall(int arity, function) {
+ var getSelf = RAW_DART_FUNCTION_REF(BoundClosure.selfOf);
+ switch (arity) {
+ case 0:
+ return JS(
+ '',
+ 'function(F,S){'
+ 'return function(){'
+ 'return F.call(S(this))'
+ '}'
+ '}(#,#)', function, getSelf);
+ case 1:
+ return JS(
+ '',
+ 'function(F,S){'
+ 'return function(a){'
+ 'return F.call(S(this),a)'
+ '}'
+ '}(#,#)', function, getSelf);
+ case 2:
+ return JS(
+ '',
+ 'function(F,S){'
+ 'return function(a,b){'
+ 'return F.call(S(this),a,b)'
+ '}'
+ '}(#,#)', function, getSelf);
+ case 3:
+ return JS(
+ '',
+ 'function(F,S){'
+ 'return function(a,b,c){'
+ 'return F.call(S(this),a,b,c)'
+ '}'
+ '}(#,#)', function, getSelf);
+ case 4:
+ return JS(
+ '',
+ 'function(F,S){'
+ 'return function(a,b,c,d){'
+ 'return F.call(S(this),a,b,c,d)'
+ '}'
+ '}(#,#)', function, getSelf);
+ case 5:
+ return JS(
+ '',
+ 'function(F,S){'
+ 'return function(a,b,c,d,e){'
+ 'return F.call(S(this),a,b,c,d,e)'
+ '}'
+ '}(#,#)', function, getSelf);
+ default:
+ return JS(
+ '',
+ 'function(f,s){'
+ 'return function(){'
+ 'return f.apply(s(this),arguments)'
+ '}'
+ '}(#,#)', function, getSelf);
+ }
}
- 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);
+ static bool get isCsp => JS('bool', 'typeof dart_precompiled == "function"');
+
+ static forwardCallTo(function, bool isIntercepted) {
+ if (isIntercepted) return forwardInterceptedCallTo(function);
+ int arity = JS('int', '#.length', function);
+ if (isCsp) {
+ return cspForwardCall(arity, function);
+ } else if (arity == 0) {
+ return JS(
+ '',
+ '(new Function("F",#))(#)',
+ 'return function(){'
+ 'return F.call(this.${BoundClosure.selfFieldName()});${functionCounter++}'
+ '}',
+ function);
+ } else if (1 <= arity && arity < 27) {
+ String arguments = JS(
+ 'String',
+ '"abcdefghijklmnopqrstuvwxyz".split("").splice(0,#).join(",")',
+ arity);
+ return JS(
+ '',
+ '(new Function("F",#))(#)',
+ 'return function($arguments){'
+ 'return F.call(this.${BoundClosure.selfFieldName()},$arguments);'
+ '${functionCounter++}'
+ '}',
+ function);
+ } else {
+ return cspForwardCall(arity, function);
+ }
}
+ static cspForwardInterceptedCall(int arity, String name, function) {
+ var getSelf = RAW_DART_FUNCTION_REF(BoundClosure.selfOf);
+ var getReceiver = RAW_DART_FUNCTION_REF(BoundClosure.receiverOf);
+ switch (arity) {
+ case 0:
+ // Intercepted functions always takes at least one argument (the
+ // receiver).
+ throw new RuntimeError('Intercepted function with no arguments.');
+ case 1:
+ return JS(
+ '',
+ 'function(n,s,r){'
+ 'return function(){'
+ 'return s(this)[n](r(this))'
+ '}'
+ '}(#,#,#)', name, getSelf, getReceiver);
+ case 2:
+ return JS(
+ '',
+ 'function(n,s,r){'
+ 'return function(a){'
+ 'return s(this)[n](r(this),a)'
+ '}'
+ '}(#,#,#)', name, getSelf, getReceiver);
+ case 3:
+ return JS(
+ '',
+ 'function(n,s,r){'
+ 'return function(a,b){'
+ 'return s(this)[n](r(this),a,b)'
+ '}'
+ '}(#,#,#)', name, getSelf, getReceiver);
+ case 4:
+ return JS(
+ '',
+ 'function(n,s,r){'
+ 'return function(a,b,c){'
+ 'return s(this)[n](r(this),a,b,c)'
+ '}'
+ '}(#,#,#)', name, getSelf, getReceiver);
+ case 5:
+ return JS(
+ '',
+ 'function(n,s,r){'
+ 'return function(a,b,c,d){'
+ 'return s(this)[n](r(this),a,b,c,d)'
+ '}'
+ '}(#,#,#)', name, getSelf, getReceiver);
+ case 6:
+ return JS(
+ '',
+ 'function(n,s,r){'
+ 'return function(a,b,c,d,e){'
+ 'return s(this)[n](r(this),a,b,c,d,e)'
+ '}'
+ '}(#,#,#)', name, getSelf, getReceiver);
+ default:
+ return JS(
+ '',
+ 'function(f,s,r,a){'
+ 'return function(){'
+ 'a=[r(this)];'
+ 'Array.prototype.push.apply(a,arguments);'
+ 'return f.apply(s(this),a)'
+ '}'
+ '}(#,#,#)', function, getSelf, getReceiver);
+ }
+ }
+
+ static forwardInterceptedCallTo(function) {
+ String stubName = JS('String|Null', '#.\$stubName', function);
+ int arity = JS('int', '#.length', function);
+ bool isCsp = JS('bool', 'typeof dart_precompiled == "function"');
+ if (isCsp) {
+ return cspForwardInterceptedCall(arity, stubName, function);
+ } else if (arity == 1) {
+ return JS('', 'new Function(#)',
+ 'return this.${BoundClosure.selfFieldName()}.$stubName('
+ 'this.${BoundClosure.receiverFieldName()});'
+ '${functionCounter++}');
+ } else if (1 < arity && arity < 28) {
+ String arguments = JS(
+ 'String',
+ '"abcdefghijklmnopqrstuvwxyz".split("").splice(0,#).join(",")',
+ arity - 1);
+ return JS(
+ '',
+ '(new Function(#))()',
+ 'return function($arguments){'
+ 'return this.${BoundClosure.selfFieldName()}.$stubName('
+ 'this.${BoundClosure.receiverFieldName()},$arguments);'
+ '${functionCounter++}'
+ '}');
+ } else {
+ return cspForwardInterceptedCall(arity, stubName, function);
+ }
+ }
+
String toString() => "Closure";
}
/// Called from implicit method getter (aka tear-off).
-Closure closureFromTearOff(receiver,
- functions,
- reflectionInfo,
- isStatic,
- jsArguments,
- name) {
- return new Closure.fromTearOff(
+closureFromTearOff(receiver,
+ functions,
+ reflectionInfo,
+ isStatic,
+ jsArguments,
+ name) {
+ return Closure.fromTearOff(
receiver,
JSArray.markFixedList(functions),
JSArray.markFixedList(reflectionInfo),
@@ -1917,13 +2156,46 @@
return receiverHashCode ^ Primitives.objectHashCode(_target);
}
+ @NoInline
static selfOf(BoundClosure closure) => closure._self;
static targetOf(BoundClosure closure) => closure._target;
+ @NoInline
static receiverOf(BoundClosure closure) => closure._receiver;
static nameOf(BoundClosure closure) => closure._name;
+
+ static String selfFieldNameCache;
+
+ static String selfFieldName() {
+ if (selfFieldNameCache == null) {
+ selfFieldNameCache = computeFieldNamed('self');
+ }
+ return selfFieldNameCache;
+ }
+
+ static String receiverFieldNameCache;
+
+ static String receiverFieldName() {
+ if (receiverFieldNameCache == null) {
+ receiverFieldNameCache = computeFieldNamed('receiver');
+ }
+ return receiverFieldNameCache;
+ }
+
+ @NoInline() @NoSideEffects()
+ static String computeFieldNamed(String fieldName) {
+ var template = new BoundClosure('self', 'target', 'receiver', 'name');
+ var names = JSArray.markFixedList(
+ JS('', 'Object.getOwnPropertyNames(#)', template));
+ for (int i = 0; i < names.length; i++) {
+ var name = names[i];
+ if (JS('bool', '#[#] === #', template, name, fieldName)) {
+ return JS('String', '#', name);
+ }
+ }
+ }
}
bool jsHasOwnProperty(var jsObject, String property) {
« no previous file with comments | « dart/sdk/lib/_internal/lib/foreign_helper.dart ('k') | dart/sdk/lib/_internal/lib/js_rti.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698