| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library _interceptors; | 5 library _interceptors; |
| 6 | 6 |
| 7 import 'dart:_js_embedded_names' show | |
| 8 DISPATCH_PROPERTY_NAME, | |
| 9 MAP_TYPE_TO_INTERCEPTOR; | |
| 10 | |
| 11 import 'dart:collection'; | 7 import 'dart:collection'; |
| 12 import 'dart:_internal' hide Symbol; | 8 import 'dart:_internal' hide Symbol; |
| 13 import "dart:_internal" as _symbol_dev show Symbol; | 9 import 'dart:_js_helper'; |
| 14 import 'dart:_js_helper' show allMatchesInStringUnchecked, | 10 import 'dart:_foreign_helper' show JS; |
| 15 Null, | |
| 16 JSSyntaxRegExp, | |
| 17 Primitives, | |
| 18 checkInt, | |
| 19 checkNull, | |
| 20 checkNum, | |
| 21 checkString, | |
| 22 defineProperty, | |
| 23 getRuntimeType, | |
| 24 initNativeDispatch, | |
| 25 initNativeDispatchFlag, | |
| 26 regExpGetNative, | |
| 27 regExpCaptureCount, | |
| 28 stringContainsUnchecked, | |
| 29 stringLastIndexOfUnchecked, | |
| 30 stringReplaceAllFuncUnchecked, | |
| 31 stringReplaceAllUnchecked, | |
| 32 stringReplaceFirstUnchecked, | |
| 33 lookupAndCacheInterceptor, | |
| 34 lookupDispatchRecord, | |
| 35 StringMatch, | |
| 36 firstMatchAfter, | |
| 37 NoInline; | |
| 38 import 'dart:_foreign_helper' show | |
| 39 JS, | |
| 40 JS_EFFECT, | |
| 41 JS_EMBEDDED_GLOBAL, | |
| 42 JS_INTERCEPTOR_CONSTANT, | |
| 43 JS_STRING_CONCAT; | |
| 44 import 'dart:math' show Random; | 11 import 'dart:math' show Random; |
| 45 | 12 |
| 46 part 'js_array.dart'; | 13 part 'js_array.dart'; |
| 47 part 'js_number.dart'; | 14 part 'js_number.dart'; |
| 48 part 'js_string.dart'; | 15 part 'js_string.dart'; |
| 49 | 16 |
| 50 String _symbolToString(Symbol symbol) => _symbol_dev.Symbol.getName(symbol); | 17 // TODO(jmesserly): remove, this doesn't do anything for us. |
| 51 | 18 abstract class Interceptor { |
| 52 _symbolMapToStringMap(Map<Symbol, dynamic> map) { | 19 const Interceptor(); |
| 53 if (map == null) return null; | |
| 54 var result = new Map<String, dynamic>(); | |
| 55 map.forEach((Symbol key, value) { | |
| 56 result[_symbolToString(key)] = value; | |
| 57 }); | |
| 58 return result; | |
| 59 } | 20 } |
| 60 | 21 |
| 61 /** | 22 // TODO(jmesserly): remove |
| 62 * Get the interceptor for [object]. Called by the compiler when it needs | 23 getInterceptor(obj) => obj; |
| 63 * to emit a call to an intercepted method, that is a method that is | |
| 64 * defined in an interceptor class. | |
| 65 */ | |
| 66 getInterceptor(object) { | |
| 67 // This is a magic method: the compiler does specialization of it | |
| 68 // depending on the uses of intercepted methods and instantiated | |
| 69 // primitive types. | |
| 70 | |
| 71 // The [JS] call prevents the type analyzer from making assumptions about the | |
| 72 // return type. | |
| 73 return JS('', 'void 0'); | |
| 74 } | |
| 75 | |
| 76 getDispatchProperty(object) { | |
| 77 return JS('', '#[#]', | |
| 78 object, JS_EMBEDDED_GLOBAL('String', DISPATCH_PROPERTY_NAME)); | |
| 79 } | |
| 80 | |
| 81 setDispatchProperty(object, value) { | |
| 82 defineProperty(object, | |
| 83 JS_EMBEDDED_GLOBAL('String', DISPATCH_PROPERTY_NAME), | |
| 84 value); | |
| 85 } | |
| 86 | |
| 87 // Avoid inlining this method because inlining gives us multiple allocation | |
| 88 // points for records which is bad because it leads to polymorphic access. | |
| 89 @NoInline() | |
| 90 makeDispatchRecord(interceptor, proto, extension, indexability) { | |
| 91 // Dispatch records are stored in the prototype chain, and in some cases, on | |
| 92 // instances. | |
| 93 // | |
| 94 // The record layout and field usage is designed to minimize the number of | |
| 95 // operations on the common paths. | |
| 96 // | |
| 97 // [interceptor] is the interceptor - a holder of methods for the object, | |
| 98 // i.e. the prototype of the interceptor class. | |
| 99 // | |
| 100 // [proto] is usually the prototype, used to check that the dispatch record | |
| 101 // matches the object and is not the dispatch record of a superclass. Other | |
| 102 // values: | |
| 103 // - `false` for leaf classes that need no check. | |
| 104 // - `true` for Dart classes where the object is its own interceptor (unused) | |
| 105 // - a function used to continue matching. | |
| 106 // | |
| 107 // [extension] is used for irregular cases. | |
| 108 // | |
| 109 // [indexability] is used to cache whether or not the object | |
| 110 // implements JavaScriptIndexingBehavior. | |
| 111 // | |
| 112 // proto interceptor extension action | |
| 113 // ----- ----------- --------- ------ | |
| 114 // false I use interceptor I | |
| 115 // true - use object | |
| 116 // P I if object's prototype is P, use I | |
| 117 // F - P if object's prototype is P, call F | |
| 118 | |
| 119 return JS('', '{i: #, p: #, e: #, x: #}', | |
| 120 interceptor, proto, extension, indexability); | |
| 121 } | |
| 122 | |
| 123 dispatchRecordInterceptor(record) => JS('', '#.i', record); | |
| 124 dispatchRecordProto(record) => JS('', '#.p', record); | |
| 125 dispatchRecordExtension(record) => JS('', '#.e', record); | |
| 126 dispatchRecordIndexability(record) => JS('bool|Null', '#.x', record); | |
| 127 | |
| 128 /** | |
| 129 * Returns the interceptor for a native class instance. Used by | |
| 130 * [getInterceptor]. | |
| 131 */ | |
| 132 getNativeInterceptor(object) { | |
| 133 var record = getDispatchProperty(object); | |
| 134 | |
| 135 if (record == null) { | |
| 136 if (initNativeDispatchFlag == null) { | |
| 137 initNativeDispatch(); | |
| 138 record = getDispatchProperty(object); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 if (record != null) { | |
| 143 var proto = dispatchRecordProto(record); | |
| 144 if (false == proto) return dispatchRecordInterceptor(record); | |
| 145 if (true == proto) return object; | |
| 146 var objectProto = JS('', 'Object.getPrototypeOf(#)', object); | |
| 147 if (JS('bool', '# === #', proto, objectProto)) { | |
| 148 return dispatchRecordInterceptor(record); | |
| 149 } | |
| 150 | |
| 151 var extension = dispatchRecordExtension(record); | |
| 152 if (JS('bool', '# === #', extension, objectProto)) { | |
| 153 // TODO(sra): The discriminator returns a tag. The tag is an uncached or | |
| 154 // instance-cached tag, defaulting to instance-cached if caching | |
| 155 // unspecified. | |
| 156 var discriminatedTag = JS('', '(#)(#, #)', proto, object, record); | |
| 157 throw new UnimplementedError('Return interceptor for $discriminatedTag'); | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 var interceptor = lookupAndCacheInterceptor(object); | |
| 162 if (interceptor == null) { | |
| 163 // JavaScript Objects created via object literals and `Object.create(null)` | |
| 164 // are 'plain' Objects. This test could be simplified and the dispatch path | |
| 165 // be faster if Object.prototype was pre-patched with a non-leaf dispatch | |
| 166 // record. | |
| 167 var proto = JS('', 'Object.getPrototypeOf(#)', object); | |
| 168 if (JS('bool', '# == null || # === Object.prototype', proto, proto)) { | |
| 169 return JS_INTERCEPTOR_CONSTANT(PlainJavaScriptObject); | |
| 170 } else { | |
| 171 return JS_INTERCEPTOR_CONSTANT(UnknownJavaScriptObject); | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 return interceptor; | |
| 176 } | |
| 177 | |
| 178 /** | |
| 179 * Data structure used to map a [Type] to the [Interceptor] and constructors for | |
| 180 * that type. It is JavaScript array of 3N entries of adjacent slots containing | |
| 181 * a [Type], followed by an [Interceptor] class for the type, followed by a | |
| 182 * JavaScript object map for the constructors. | |
| 183 * | |
| 184 * The value of this variable is set by the compiler and contains only types | |
| 185 * that are user extensions of native classes where the type occurs as a | |
| 186 * constant in the program. | |
| 187 * | |
| 188 * The compiler, in CustomElementsAnalysis, assumes that [mapTypeToInterceptor] | |
| 189 * is accessed only by code that also calls [findIndexForWebComponentType]. If | |
| 190 * this assumption is invalidated, the compiler will have to be updated. | |
| 191 */ | |
| 192 get mapTypeToInterceptor { | |
| 193 return JS_EMBEDDED_GLOBAL('', MAP_TYPE_TO_INTERCEPTOR); | |
| 194 } | |
| 195 | |
| 196 int findIndexForNativeSubclassType(Type type) { | |
| 197 if (JS('bool', '# == null', mapTypeToInterceptor)) return null; | |
| 198 List map = JS('JSFixedArray', '#', mapTypeToInterceptor); | |
| 199 for (int i = 0; i + 1 < map.length; i += 3) { | |
| 200 if (type == map[i]) { | |
| 201 return i; | |
| 202 } | |
| 203 } | |
| 204 return null; | |
| 205 } | |
| 206 | |
| 207 findInterceptorConstructorForType(Type type) { | |
| 208 var index = findIndexForNativeSubclassType(type); | |
| 209 if (index == null) return null; | |
| 210 List map = JS('JSFixedArray', '#', mapTypeToInterceptor); | |
| 211 return map[index + 1]; | |
| 212 } | |
| 213 | |
| 214 /** | |
| 215 * Returns a JavaScript function that runs the constructor on its argument, or | |
| 216 * `null` if there is no such constructor. | |
| 217 * | |
| 218 * The returned function takes one argument, the web component object. | |
| 219 */ | |
| 220 findConstructorForNativeSubclassType(Type type, String name) { | |
| 221 var index = findIndexForNativeSubclassType(type); | |
| 222 if (index == null) return null; | |
| 223 List map = JS('JSFixedArray', '#', mapTypeToInterceptor); | |
| 224 var constructorMap = map[index + 2]; | |
| 225 var constructorFn = JS('', '#[#]', constructorMap, name); | |
| 226 return constructorFn; | |
| 227 } | |
| 228 | |
| 229 findInterceptorForType(Type type) { | |
| 230 var constructor = findInterceptorConstructorForType(type); | |
| 231 if (constructor == null) return null; | |
| 232 return JS('', '#.prototype', constructor); | |
| 233 } | |
| 234 | |
| 235 /** | |
| 236 * The base interceptor class. | |
| 237 * | |
| 238 * The code `r.foo(a)` is compiled to `getInterceptor(r).foo$1(r, a)`. The | |
| 239 * value returned by [getInterceptor] holds the methods separately from the | |
| 240 * state of the instance. The compiler converts the methods on an interceptor | |
| 241 * to take the Dart `this` argument as an explicit `receiver` argument. The | |
| 242 * JavaScript `this` parameter is bound to the interceptor. | |
| 243 * | |
| 244 * In order to have uniform call sites, if a method is defined on an | |
| 245 * interceptor, methods of that name on plain unintercepted classes also use the | |
| 246 * interceptor calling convention. The plain classes are _self-interceptors_, | |
| 247 * and for them, `getInterceptor(r)` returns `r`. Methods on plain | |
| 248 * unintercepted classes have a redundant `receiver` argument and should ignore | |
| 249 * it in favour of `this`. | |
| 250 * | |
| 251 * In the case of mixins, a method may be placed on both an intercepted class | |
| 252 * and an unintercepted class. In this case, the method must use the `receiver` | |
| 253 * parameter. | |
| 254 * | |
| 255 * | |
| 256 * There are various optimizations of the general call pattern. | |
| 257 * | |
| 258 * When the interceptor can be statically determined, it can be used directly: | |
| 259 * | |
| 260 * CONSTANT_INTERCEPTOR.foo$1(r, a) | |
| 261 * | |
| 262 * If there are only a few classes, [getInterceptor] can be specialized with a | |
| 263 * more efficient dispatch: | |
| 264 * | |
| 265 * getInterceptor$specialized(r).foo$1(r, a) | |
| 266 * | |
| 267 * If it can be determined that the receiver is an unintercepted class, it can | |
| 268 * be called directly: | |
| 269 * | |
| 270 * r.foo$1(r, a) | |
| 271 * | |
| 272 * If, further, it is known that the call site cannot call a foo that is | |
| 273 * mixed-in to a native class, then it is known that the explicit receiver is | |
| 274 * ignored, and space-saving dummy value can be passed instead: | |
| 275 * | |
| 276 * r.foo$1(0, a) | |
| 277 * | |
| 278 * This class defines implementations of *all* methods on [Object] so no | |
| 279 * interceptor inherits an implementation from [Object]. This enables the | |
| 280 * implementations on Object to ignore the explicit receiver argument, which | |
| 281 * allows dummy receiver optimization. | |
| 282 */ | |
| 283 abstract class Interceptor { | |
| 284 const Interceptor(); | |
| 285 | |
| 286 bool operator ==(Object other) => identical(this, other); | |
| 287 | |
| 288 int get hashCode => Primitives.objectHashCode(this); | |
| 289 | |
| 290 String toString() => Primitives.objectToString(this); | |
| 291 | |
| 292 dynamic noSuchMethod(Invocation invocation) { | |
| 293 throw new NoSuchMethodError( | |
| 294 this, | |
| 295 invocation.memberName, | |
| 296 invocation.positionalArguments, | |
| 297 invocation.namedArguments); | |
| 298 } | |
| 299 | |
| 300 Type get runtimeType => getRuntimeType(this); | |
| 301 } | |
| 302 | 24 |
| 303 /** | 25 /** |
| 304 * The interceptor class for [bool]. | 26 * The interceptor class for [bool]. |
| 305 */ | 27 */ |
| 28 @JsPeerInterface(name: 'Boolean') |
| 306 class JSBool extends Interceptor implements bool { | 29 class JSBool extends Interceptor implements bool { |
| 307 const JSBool(); | 30 const JSBool(); |
| 308 | 31 |
| 309 // Note: if you change this, also change the function [S]. | 32 // Note: if you change this, also change the function [S]. |
| 310 String toString() => JS('String', r'String(#)', this); | 33 String toString() => JS('String', r'String(#)', this); |
| 311 | 34 |
| 312 // The values here are SMIs, co-prime and differ about half of the bit | 35 // The values here are SMIs, co-prime and differ about half of the bit |
| 313 // positions, including the low bit, so they are different mod 2^k. | 36 // positions, including the low bit, so they are different mod 2^k. |
| 314 int get hashCode => this ? (2 * 3 * 23 * 3761) : (269 * 811); | 37 int get hashCode => this ? (2 * 3 * 23 * 3761) : (269 * 811); |
| 315 | 38 |
| 316 Type get runtimeType => bool; | 39 Type get runtimeType => bool; |
| 317 } | 40 } |
| 318 | 41 |
| 319 /** | 42 /** |
| 320 * The interceptor class for [Null]. | |
| 321 * | |
| 322 * This class defines implementations for *all* methods on [Object] since | |
| 323 * the methods on Object assume the receiver is non-null. This means that | |
| 324 * JSNull will always be in the interceptor set for methods defined on Object. | |
| 325 */ | |
| 326 class JSNull extends Interceptor implements Null { | |
| 327 const JSNull(); | |
| 328 | |
| 329 bool operator ==(other) => identical(null, other); | |
| 330 | |
| 331 // Note: if you change this, also change the function [S]. | |
| 332 String toString() => 'null'; | |
| 333 | |
| 334 int get hashCode => 0; | |
| 335 | |
| 336 // The spec guarantees that `null` is the singleton instance of the `Null` | |
| 337 // class. In the mirrors library we also have to patch the `type` getter to | |
| 338 // special case `null`. | |
| 339 Type get runtimeType => Null; | |
| 340 | |
| 341 dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); | |
| 342 } | |
| 343 | |
| 344 /** | |
| 345 * The supertype for JSString and JSArray. Used by the backend as to | 43 * The supertype for JSString and JSArray. Used by the backend as to |
| 346 * have a type mask that contains the objects that we can use the | 44 * have a type mask that contains the objects that we can use the |
| 347 * native JS [] operator and length on. | 45 * native JS [] operator and length on. |
| 348 */ | 46 */ |
| 349 abstract class JSIndexable { | 47 abstract class JSIndexable { |
| 350 int get length; | 48 int get length; |
| 351 operator[](int index); | 49 operator[](int index); |
| 352 } | 50 } |
| 353 | 51 |
| 354 /** | 52 /** |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 397 * Interceptor for unclassified JavaScript objects, typically objects with a | 95 * Interceptor for unclassified JavaScript objects, typically objects with a |
| 398 * non-trivial prototype chain. | 96 * non-trivial prototype chain. |
| 399 * | 97 * |
| 400 * This class also serves as a fallback for unknown JavaScript exceptions. | 98 * This class also serves as a fallback for unknown JavaScript exceptions. |
| 401 */ | 99 */ |
| 402 class UnknownJavaScriptObject extends JavaScriptObject { | 100 class UnknownJavaScriptObject extends JavaScriptObject { |
| 403 const UnknownJavaScriptObject(); | 101 const UnknownJavaScriptObject(); |
| 404 | 102 |
| 405 String toString() => JS('String', 'String(#)', this); | 103 String toString() => JS('String', 'String(#)', this); |
| 406 } | 104 } |
| OLD | NEW |