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

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

Issue 150973003: Deal with intercepted fields in the InstanceMirror.getField optimizations. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: More comments. 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 | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/_internal/lib/js_mirrors.dart
diff --git a/sdk/lib/_internal/lib/js_mirrors.dart b/sdk/lib/_internal/lib/js_mirrors.dart
index 24e784023e4da9998711c40b2ffd8b4ccdce986e..067b61633620b80888548e8e3af49fdd3203870b 100644
--- a/sdk/lib/_internal/lib/js_mirrors.dart
+++ b/sdk/lib/_internal/lib/js_mirrors.dart
@@ -934,21 +934,26 @@ class JsInstanceMirror extends JsObjectMirror implements InstanceMirror {
// JS helpers for getField optimizations.
static bool isUndefined(x)
=> JS('bool', 'typeof # == "undefined"', x);
+ static bool isMissingCache(x)
+ => JS('bool', 'typeof # == "number"', x);
static bool isMissingProbe(Symbol symbol)
=> JS('bool', 'typeof #.\$p == "undefined"', symbol);
- static bool isNewFunctionAllowed()
+ static bool isEvalAllowed()
=> JS('bool', 'typeof dart_precompiled != "function"');
- /// The getter cache is specific to this [InstanceMirror]
- /// and maps reflective names to functions that will invoke
- /// the corresponding getter on the reflectee. The reflectee
- /// is passed to the function as the first argument to avoid
- /// the overhead of fetching it from this mirror repeatedly.
+ /// The getter cache is lazily allocated after a couple
+ /// of invocations of [InstanceMirror.getField]. The delay is
+ /// used to avoid too aggressive caching and dynamic function
+ /// generation for rarely used mirrors. The cache is specific to
+ /// this [InstanceMirror] and maps reflective names to functions
+ /// that will invoke the corresponding getter on the reflectee.
+ /// The reflectee is passed to the function as the first argument
+ /// to avoid the overhead of fetching it from this mirror repeatedly.
/// The cache is lazily initialized to a JS object so we can
/// benefit from "map transitions" in the underlying JavaScript
/// engine to speed up cache probing.
- var _getterCache;
+ var _getterCache = 4;
InstanceMirror getField(Symbol fieldName) {
// BUG(16400): This should be a labelled block, but that makes
@@ -956,7 +961,7 @@ class JsInstanceMirror extends JsObjectMirror implements InstanceMirror {
// inferencing implementation.
do {
var cache = _getterCache;
- if (cache == null || isMissingProbe(fieldName)) break;
+ if (isMissingCache(cache) || isMissingProbe(fieldName)) break;
// If the [fieldName] has an associated probe function, we can use
// it to read from the getter cache specific to this [InstanceMirror].
var getter = JS('', '#.\$p(#)', fieldName, cache);
@@ -985,29 +990,29 @@ class JsInstanceMirror extends JsObjectMirror implements InstanceMirror {
String name = n(fieldName);
var result = _invoke(fieldName, JSInvocationMirror.GETTER, name, const []);
var cacheEntry = JsCache.fetch(_classInvocationCache, name);
- if (cacheEntry.isNoSuchMethod || cacheEntry.isIntercepted) {
+ if (cacheEntry.isNoSuchMethod) {
return result;
}
// Make sure we have a getter cache in this [InstanceMirror].
var cache = _getterCache;
- if (cache == null) {
- cache = _getterCache = JS('=Object', 'Object.create(null)');
+ if (isMissingCache(cache)) {
+ if ((_getterCache = --cache) != 0) return result;
+ cache = _getterCache = JS('=Object', '({})');
}
// Make sure that symbol [fieldName] has a cache probing function ($p).
+ bool useEval = isEvalAllowed();
if (isMissingProbe(fieldName)) {
- var probe = isNewFunctionAllowed()
- ? JS('', 'new Function("c", "return c." + # + ";")', name)
- : JS('', 'function (c) { return c[#]; }', name);
+ var probe = _newProbeFn(name, useEval);
JS('void', '#.\$p = #', fieldName, probe);
}
// Create a new getter function and install it in the cache.
var mangledName = cacheEntry.mangledName;
- var getter = isNewFunctionAllowed()
- ? JS('', 'new Function("o", "return o." + # + "();")', mangledName)
- : JS('', 'function(o) { return o[#](); }', mangledName);
+ var getter = (cacheEntry.isIntercepted)
+ ? _newInterceptedGetterFn(mangledName, useEval)
+ : _newGetterFn(mangledName, useEval);
JS('void', '#[#] = #', cache, name, getter);
// Initialize the last value (v) and last mirror (m) on the
@@ -1019,6 +1024,53 @@ class JsInstanceMirror extends JsObjectMirror implements InstanceMirror {
return result;
}
+ _newProbeFn(String id, bool useEval) {
+ if (useEval) {
+ // We give the probe function a name to make it appear nicely in
+ // profiles and when debugging. The name also makes the source code
+ // for the function more "unique" so the underlying JavaScript
+ // engine is less likely to re-use an existing piece of generated
+ // code as the result of calling eval. In return, this leads to
+ // less polymorphic access in the probe function.
+ var body = "(function probe\$$id(c){return c.$id})";
+ return JS('', '(function(b){return eval(b)})(#)', body);
+ } else {
+ return JS('', '(function(n){return(function(c){return c[n]})})(#)', id);
+ }
+ }
+
+ _newGetterFn(String name, bool useEval) {
+ if (!useEval) return _newGetterNoEvalFn(name);
+ // We give the getter function a name that associates it with the
+ // class of the reflectee. This makes it easier to spot in profiles
+ // and when debugging, but it also means that the underlying JavaScript
+ // engine will only share the generated code for accessors on the
+ // same class (through caching of eval'ed code). This makes the
+ // generated call to the getter - e.g. o.get$foo() - much more likely
+ // to be monomorphic and inlineable.
+ String className = JS('String', '#.constructor.name', reflectee);
+ var body = "(function $className\$$name(o){return o.$name()})";
+ return JS('', '(function(b){return eval(b)})(#)', body);
+ }
+
+ _newGetterNoEvalFn(n) => JS('',
+ '(function(n){return(function(o){return o[n]()})})(#)', n);
+
+ _newInterceptedGetterFn(String name, bool useEval) {
+ var object = reflectee;
+ // It is possible that the interceptor for a given object is the object
+ // itself, so it is important not to share the code that captures the
+ // interceptor between multiple different instances of [InstanceMirror].
+ var interceptor = getInterceptor(object);
+ if (!useEval) return _newInterceptGetterNoEvalFn(name, interceptor);
+ String className = JS('String', '#.constructor.name', object);
+ var body = "(function $className\$$name(o){return i.$name(o)})";
+ return JS('', '(function(b,i){return eval(b)})(#,#)', body, interceptor);
+ }
+
+ _newInterceptGetterNoEvalFn(n, i) => JS('',
+ '(function(n,i){return(function(o){return i[n](o)})})(#,#)', n, i);
+
delegate(Invocation invocation) {
return JSInvocationMirror.invokeFromMirror(invocation, reflectee);
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698