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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_emitter/interceptor_stub_generator.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014, 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 part of dart2js.js_emitter;
6
7 class InterceptorStubGenerator {
8 final Compiler compiler;
9 final Namer namer;
10 final JavaScriptBackend backend;
11
12 InterceptorStubGenerator(this.compiler, this.namer, this.backend);
13
14 jsAst.Expression generateGetInterceptorMethod(Set<ClassElement> classes) {
15 jsAst.Expression interceptorFor(ClassElement cls) {
16 return js('#.prototype', namer.elementAccess(cls));
17 }
18
19 /**
20 * Build a JavaScrit AST node for doing a type check on
21 * [cls]. [cls] must be a non-native interceptor class.
22 */
23 jsAst.Statement buildInterceptorCheck(ClassElement cls) {
24 jsAst.Expression condition;
25 assert(backend.isInterceptorClass(cls));
26 if (cls == backend.jsBoolClass) {
27 condition = js('(typeof receiver) == "boolean"');
28 } else if (cls == backend.jsIntClass ||
29 cls == backend.jsDoubleClass ||
30 cls == backend.jsNumberClass) {
31 throw 'internal error';
32 } else if (cls == backend.jsArrayClass ||
33 cls == backend.jsMutableArrayClass ||
34 cls == backend.jsFixedArrayClass ||
35 cls == backend.jsExtendableArrayClass) {
36 condition = js('receiver.constructor == Array');
37 } else if (cls == backend.jsStringClass) {
38 condition = js('(typeof receiver) == "string"');
39 } else if (cls == backend.jsNullClass) {
40 condition = js('receiver == null');
41 } else {
42 throw 'internal error';
43 }
44 return js.statement('if (#) return #', [condition, interceptorFor(cls)]);
45 }
46
47 bool hasArray = false;
48 bool hasBool = false;
49 bool hasDouble = false;
50 bool hasInt = false;
51 bool hasNull = false;
52 bool hasNumber = false;
53 bool hasString = false;
54 bool hasNative = false;
55 bool anyNativeClasses = compiler.enqueuer.codegen.nativeEnqueuer
56 .hasInstantiatedNativeClasses();
57
58 for (ClassElement cls in classes) {
59 if (cls == backend.jsArrayClass ||
60 cls == backend.jsMutableArrayClass ||
61 cls == backend.jsFixedArrayClass ||
62 cls == backend.jsExtendableArrayClass) hasArray = true;
63 else if (cls == backend.jsBoolClass) hasBool = true;
64 else if (cls == backend.jsDoubleClass) hasDouble = true;
65 else if (cls == backend.jsIntClass) hasInt = true;
66 else if (cls == backend.jsNullClass) hasNull = true;
67 else if (cls == backend.jsNumberClass) hasNumber = true;
68 else if (cls == backend.jsStringClass) hasString = true;
69 else {
70 // The set of classes includes classes mixed-in to interceptor classes
71 // and user extensions of native classes.
72 //
73 // The set of classes also includes the 'primitive' interceptor
74 // PlainJavaScriptObject even when it has not been resolved, since it is
75 // only resolved through the reference in getNativeInterceptor when
76 // getNativeInterceptor is marked as used. Guard against probing
77 // unresolved PlainJavaScriptObject by testing for anyNativeClasses.
78
79 if (anyNativeClasses) {
80 if (Elements.isNativeOrExtendsNative(cls)) hasNative = true;
81 }
82 }
83 }
84 if (hasDouble) {
85 hasNumber = true;
86 }
87 if (hasInt) hasNumber = true;
88
89 if (classes.containsAll(backend.interceptedClasses)) {
90 // I.e. this is the general interceptor.
91 hasNative = anyNativeClasses;
92 }
93
94 List<jsAst.Statement> statements = <jsAst.Statement>[];
95
96 if (hasNumber) {
97 jsAst.Statement whenNumber;
98
99 /// Note: there are two number classes in play: Dart's [num],
100 /// and JavaScript's Number (typeof receiver == 'number'). This
101 /// is the fallback used when we have determined that receiver
102 /// is a JavaScript Number.
103 jsAst.Expression interceptorForNumber = interceptorFor(
104 hasDouble ? backend.jsDoubleClass : backend.jsNumberClass);
105
106 if (hasInt) {
107 whenNumber = js.statement('''{
108 if (Math.floor(receiver) == receiver) return #;
109 return #;
110 }''', [interceptorFor(backend.jsIntClass), interceptorForNumber]);
111 } else {
112 whenNumber = js.statement('return #', interceptorForNumber);
113 }
114 statements.add(
115 js.statement('if (typeof receiver == "number") #;', whenNumber));
116 }
117
118 if (hasString) {
119 statements.add(buildInterceptorCheck(backend.jsStringClass));
120 }
121 if (hasNull) {
122 statements.add(buildInterceptorCheck(backend.jsNullClass));
123 } else {
124 // Returning "undefined" or "null" here will provoke a JavaScript
125 // TypeError which is later identified as a null-error by
126 // [unwrapException] in js_helper.dart.
127 statements.add(
128 js.statement('if (receiver == null) return receiver'));
129 }
130 if (hasBool) {
131 statements.add(buildInterceptorCheck(backend.jsBoolClass));
132 }
133 // TODO(ahe): It might be faster to check for Array before
134 // function and bool.
135 if (hasArray) {
136 statements.add(buildInterceptorCheck(backend.jsArrayClass));
137 }
138
139 if (hasNative) {
140 statements.add(js.statement(r'''{
141 if (typeof receiver != "object") return receiver;
142 if (receiver instanceof #) return receiver;
143 return #(receiver);
144 }''', [
145 namer.elementAccess(compiler.objectClass),
146 namer.elementAccess(backend.getNativeInterceptorMethod)]));
147
148 } else {
149 ClassElement jsUnknown = backend.jsUnknownJavaScriptObjectClass;
150 if (compiler.codegenWorld
151 .directlyInstantiatedClasses.contains(jsUnknown)) {
152 statements.add(
153 js.statement('if (!(receiver instanceof #)) return #;',
154 [namer.elementAccess(compiler.objectClass),
155 interceptorFor(jsUnknown)]));
156 }
157
158 statements.add(js.statement('return receiver'));
159 }
160
161 return js('''function(receiver) { #; }''', new jsAst.Block(statements));
162 }
163
164 // Returns a statement that takes care of performance critical
165 // common case for a one-shot interceptor, or null if there is no
166 // fast path.
167 jsAst.Statement _fastPathForOneShotInterceptor(Selector selector,
168 Set<ClassElement> classes) {
169
170 if (selector.isOperator) {
171 String name = selector.name;
172 if (name == '==') {
173 return js.statement('''{
174 if (receiver == null) return a0 == null;
175 if (typeof receiver != "object")
176 return a0 != null && receiver === a0;
177 }''');
178 }
179 if (!classes.contains(backend.jsIntClass)
180 && !classes.contains(backend.jsNumberClass)
181 && !classes.contains(backend.jsDoubleClass)) {
182 return null;
183 }
184 if (selector.argumentCount == 1) {
185 // The following operators do not map to a JavaScript operator.
186 if (name == '~/' || name == '<<' || name == '%' || name == '>>') {
187 return null;
188 }
189 jsAst.Expression result = js('receiver $name a0');
190 if (name == '&' || name == '|' || name == '^') {
191 result = js('# >>> 0', result);
192 }
193 return js.statement(
194 'if (typeof receiver == "number" && typeof a0 == "number")'
195 ' return #;',
196 result);
197 } else if (name == 'unary-') {
198 return js.statement(
199 'if (typeof receiver == "number") return -receiver');
200 } else {
201 assert(name == '~');
202 return js.statement('''
203 if (typeof receiver == "number" && Math.floor(receiver) == receiver)
204 return (~receiver) >>> 0;
205 ''');
206 }
207 } else if (selector.isIndex || selector.isIndexSet) {
208 // For an index operation, this code generates:
209 //
210 // if (receiver.constructor == Array || typeof receiver == "string") {
211 // if (a0 >>> 0 === a0 && a0 < receiver.length) {
212 // return receiver[a0];
213 // }
214 // }
215 //
216 // For an index set operation, this code generates:
217 //
218 // if (receiver.constructor == Array && !receiver.immutable$list) {
219 // if (a0 >>> 0 === a0 && a0 < receiver.length) {
220 // return receiver[a0] = a1;
221 // }
222 // }
223 bool containsArray = classes.contains(backend.jsArrayClass);
224 bool containsString = classes.contains(backend.jsStringClass);
225 bool containsJsIndexable =
226 backend.jsIndexingBehaviorInterface.isResolved && classes.any((cls) {
227 return compiler.world.isSubtypeOf(cls,
228 backend.jsIndexingBehaviorInterface);
229 });
230 // The index set operator requires a check on its set value in
231 // checked mode, so we don't optimize the interceptor if the
232 // compiler has type assertions enabled.
233 if (selector.isIndexSet
234 && (compiler.enableTypeAssertions || !containsArray)) {
235 return null;
236 }
237 if (!containsArray && !containsString) {
238 return null;
239 }
240 jsAst.Expression arrayCheck = js('receiver.constructor == Array');
241 jsAst.Expression indexableCheck =
242 backend.generateIsJsIndexableCall(js('receiver'), js('receiver'));
243
244 jsAst.Expression orExp(left, right) {
245 return left == null ? right : js('# || #', [left, right]);
246 }
247
248 if (selector.isIndex) {
249 jsAst.Expression typeCheck;
250 if (containsArray) {
251 typeCheck = arrayCheck;
252 }
253
254 if (containsString) {
255 typeCheck = orExp(typeCheck, js('typeof receiver == "string"'));
256 }
257
258 if (containsJsIndexable) {
259 typeCheck = orExp(typeCheck, indexableCheck);
260 }
261
262 return js.statement('''
263 if (#)
264 if ((a0 >>> 0) === a0 && a0 < receiver.length)
265 return receiver[a0];
266 ''', typeCheck);
267 } else {
268 jsAst.Expression typeCheck;
269 if (containsArray) {
270 typeCheck = arrayCheck;
271 }
272
273 if (containsJsIndexable) {
274 typeCheck = orExp(typeCheck, indexableCheck);
275 }
276
277 return js.statement(r'''
278 if (# && !receiver.immutable$list &&
279 (a0 >>> 0) === a0 && a0 < receiver.length)
280 return receiver[a0] = a1;
281 ''', typeCheck);
282 }
283 }
284 return null;
285 }
286
287 jsAst.Expression generateOneShotInterceptor(String name) {
288 Selector selector = backend.oneShotInterceptors[name];
289 Set<ClassElement> classes =
290 backend.getInterceptedClassesOn(selector.name);
291 String getInterceptorName =
292 namer.getInterceptorName(backend.getInterceptorMethod, classes);
293
294 List<String> parameterNames = <String>[];
295 parameterNames.add('receiver');
296
297 if (selector.isSetter) {
298 parameterNames.add('value');
299 } else {
300 for (int i = 0; i < selector.argumentCount; i++) {
301 parameterNames.add('a$i');
302 }
303 }
304
305 String invocationName = backend.namer.invocationName(selector);
306 String globalObject = namer.globalObjectFor(backend.interceptorsLibrary);
307
308 jsAst.Statement optimizedPath =
309 _fastPathForOneShotInterceptor(selector, classes);
310 if (optimizedPath == null) optimizedPath = js.statement(';');
311
312 return js(
313 'function(#) { #; return #.#(receiver).#(#) }',
314 [parameterNames,
315 optimizedPath,
316 globalObject, getInterceptorName, invocationName, parameterNames]);
317 }
318 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698