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

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

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

Powered by Google App Engine
This is Rietveld 408576698