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

Side by Side Diff: test/generated_sdk/lib/_internal/compiler/js_lib/interceptors.dart

Issue 1153003003: fixes #40, extension methods for primitive types (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 6 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 unified diff | Download patch
OLDNEW
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698