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

Side by Side Diff: pkg/compiler/lib/src/js_emitter/interceptor_stub_generator.dart

Issue 2814453005: Merge CommonElements and BackendHelpers! (Closed)
Patch Set: comments and re-merge, take two Created 3 years, 8 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) 2014, the Dart project authors. Please see the AUTHORS file 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 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 dart2js.js_emitter.interceptor_stub_generator; 5 library dart2js.js_emitter.interceptor_stub_generator;
6 6
7 import '../compiler.dart' show Compiler; 7 import '../compiler.dart' show Compiler;
8 import '../constants/values.dart'; 8 import '../constants/values.dart';
9 import '../elements/entities.dart'; 9 import '../elements/entities.dart';
10 import '../elements/types.dart' show InterfaceType; 10 import '../elements/types.dart' show InterfaceType;
11 import '../js/js.dart' as jsAst; 11 import '../js/js.dart' as jsAst;
12 import '../js/js.dart' show js; 12 import '../js/js.dart' show js;
13 import '../js_backend/backend_helpers.dart' show BackendHelpers;
14 import '../js_backend/js_backend.dart' 13 import '../js_backend/js_backend.dart'
15 show 14 show
16 CustomElementsCodegenAnalysis, 15 CustomElementsCodegenAnalysis,
17 JavaScriptBackend, 16 JavaScriptBackend,
18 JavaScriptConstantCompiler, 17 JavaScriptConstantCompiler,
19 Namer; 18 Namer;
20 import '../universe/selector.dart' show Selector; 19 import '../universe/selector.dart' show Selector;
21 import '../world.dart' show ClosedWorld; 20 import '../world.dart' show ClosedWorld;
22 21
23 import 'code_emitter_task.dart' show Emitter; 22 import 'code_emitter_task.dart' show Emitter;
24 23
25 class InterceptorStubGenerator { 24 class InterceptorStubGenerator {
26 final Compiler compiler; 25 final Compiler compiler;
27 final Namer namer; 26 final Namer namer;
28 final JavaScriptBackend backend; 27 final JavaScriptBackend backend;
29 final ClosedWorld closedWorld; 28 final ClosedWorld closedWorld;
30 29
31 InterceptorStubGenerator( 30 InterceptorStubGenerator(
32 this.compiler, this.namer, this.backend, this.closedWorld); 31 this.compiler, this.namer, this.backend, this.closedWorld);
33 32
34 Emitter get emitter => backend.emitter.emitter; 33 Emitter get emitter => backend.emitter.emitter;
35 34
36 BackendHelpers get helpers => backend.helpers;
37
38 jsAst.Expression generateGetInterceptorMethod(Set<ClassEntity> classes) { 35 jsAst.Expression generateGetInterceptorMethod(Set<ClassEntity> classes) {
39 jsAst.Expression interceptorFor(ClassEntity cls) { 36 jsAst.Expression interceptorFor(ClassEntity cls) {
40 return backend.emitter.interceptorPrototypeAccess(cls); 37 return backend.emitter.interceptorPrototypeAccess(cls);
41 } 38 }
42 39
43 /** 40 /**
44 * Build a JavaScrit AST node for doing a type check on 41 * Build a JavaScrit AST node for doing a type check on
45 * [cls]. [cls] must be a non-native interceptor class. 42 * [cls]. [cls] must be a non-native interceptor class.
46 */ 43 */
47 jsAst.Statement buildInterceptorCheck(ClassEntity cls) { 44 jsAst.Statement buildInterceptorCheck(ClassEntity cls) {
48 jsAst.Expression condition; 45 jsAst.Expression condition;
49 assert(backend.interceptorData.isInterceptedClass(cls)); 46 assert(backend.interceptorData.isInterceptedClass(cls));
50 if (cls == helpers.jsBoolClass) { 47 if (cls == compiler.commonElements.jsBoolClass) {
51 condition = js('(typeof receiver) == "boolean"'); 48 condition = js('(typeof receiver) == "boolean"');
52 } else if (cls == helpers.jsIntClass || 49 } else if (cls == compiler.commonElements.jsIntClass ||
53 cls == helpers.jsDoubleClass || 50 cls == compiler.commonElements.jsDoubleClass ||
54 cls == helpers.jsNumberClass) { 51 cls == compiler.commonElements.jsNumberClass) {
55 throw 'internal error'; 52 throw 'internal error';
56 } else if (cls == helpers.jsArrayClass || 53 } else if (cls == compiler.commonElements.jsArrayClass ||
57 cls == helpers.jsMutableArrayClass || 54 cls == compiler.commonElements.jsMutableArrayClass ||
58 cls == helpers.jsFixedArrayClass || 55 cls == compiler.commonElements.jsFixedArrayClass ||
59 cls == helpers.jsExtendableArrayClass) { 56 cls == compiler.commonElements.jsExtendableArrayClass) {
60 condition = js('receiver.constructor == Array'); 57 condition = js('receiver.constructor == Array');
61 } else if (cls == helpers.jsStringClass) { 58 } else if (cls == compiler.commonElements.jsStringClass) {
62 condition = js('(typeof receiver) == "string"'); 59 condition = js('(typeof receiver) == "string"');
63 } else if (cls == helpers.jsNullClass) { 60 } else if (cls == compiler.commonElements.jsNullClass) {
64 condition = js('receiver == null'); 61 condition = js('receiver == null');
65 } else { 62 } else {
66 throw 'internal error'; 63 throw 'internal error';
67 } 64 }
68 return js.statement('if (#) return #', [condition, interceptorFor(cls)]); 65 return js.statement('if (#) return #', [condition, interceptorFor(cls)]);
69 } 66 }
70 67
71 bool hasArray = false; 68 bool hasArray = false;
72 bool hasBool = false; 69 bool hasBool = false;
73 bool hasDouble = false; 70 bool hasDouble = false;
74 bool hasInt = false; 71 bool hasInt = false;
75 bool hasNull = false; 72 bool hasNull = false;
76 bool hasNumber = false; 73 bool hasNumber = false;
77 bool hasString = false; 74 bool hasString = false;
78 bool hasNative = false; 75 bool hasNative = false;
79 bool anyNativeClasses = 76 bool anyNativeClasses =
80 backend.nativeCodegenEnqueuer.hasInstantiatedNativeClasses; 77 backend.nativeCodegenEnqueuer.hasInstantiatedNativeClasses;
81 78
82 for (ClassEntity cls in classes) { 79 for (ClassEntity cls in classes) {
83 if (cls == helpers.jsArrayClass || 80 if (cls == compiler.commonElements.jsArrayClass ||
84 cls == helpers.jsMutableArrayClass || 81 cls == compiler.commonElements.jsMutableArrayClass ||
85 cls == helpers.jsFixedArrayClass || 82 cls == compiler.commonElements.jsFixedArrayClass ||
86 cls == helpers.jsExtendableArrayClass) 83 cls == compiler.commonElements.jsExtendableArrayClass)
87 hasArray = true; 84 hasArray = true;
88 else if (cls == helpers.jsBoolClass) 85 else if (cls == compiler.commonElements.jsBoolClass)
89 hasBool = true; 86 hasBool = true;
90 else if (cls == helpers.jsDoubleClass) 87 else if (cls == compiler.commonElements.jsDoubleClass)
91 hasDouble = true; 88 hasDouble = true;
92 else if (cls == helpers.jsIntClass) 89 else if (cls == compiler.commonElements.jsIntClass)
93 hasInt = true; 90 hasInt = true;
94 else if (cls == helpers.jsNullClass) 91 else if (cls == compiler.commonElements.jsNullClass)
95 hasNull = true; 92 hasNull = true;
96 else if (cls == helpers.jsNumberClass) 93 else if (cls == compiler.commonElements.jsNumberClass)
97 hasNumber = true; 94 hasNumber = true;
98 else if (cls == helpers.jsStringClass) 95 else if (cls == compiler.commonElements.jsStringClass)
99 hasString = true; 96 hasString = true;
100 else { 97 else {
101 // The set of classes includes classes mixed-in to interceptor classes 98 // The set of classes includes classes mixed-in to interceptor classes
102 // and user extensions of native classes. 99 // and user extensions of native classes.
103 // 100 //
104 // The set of classes also includes the 'primitive' interceptor 101 // The set of classes also includes the 'primitive' interceptor
105 // PlainJavaScriptObject even when it has not been resolved, since it is 102 // PlainJavaScriptObject even when it has not been resolved, since it is
106 // only resolved through the reference in getNativeInterceptor when 103 // only resolved through the reference in getNativeInterceptor when
107 // getNativeInterceptor is marked as used. Guard against probing 104 // getNativeInterceptor is marked as used. Guard against probing
108 // unresolved PlainJavaScriptObject by testing for anyNativeClasses. 105 // unresolved PlainJavaScriptObject by testing for anyNativeClasses.
(...skipping 15 matching lines...) Expand all
124 121
125 List<jsAst.Statement> statements = <jsAst.Statement>[]; 122 List<jsAst.Statement> statements = <jsAst.Statement>[];
126 123
127 if (hasNumber) { 124 if (hasNumber) {
128 jsAst.Statement whenNumber; 125 jsAst.Statement whenNumber;
129 126
130 /// Note: there are two number classes in play: Dart's [num], 127 /// Note: there are two number classes in play: Dart's [num],
131 /// and JavaScript's Number (typeof receiver == 'number'). This 128 /// and JavaScript's Number (typeof receiver == 'number'). This
132 /// is the fallback used when we have determined that receiver 129 /// is the fallback used when we have determined that receiver
133 /// is a JavaScript Number. 130 /// is a JavaScript Number.
134 jsAst.Expression interceptorForNumber = interceptorFor( 131 jsAst.Expression interceptorForNumber = interceptorFor(hasDouble
135 hasDouble ? helpers.jsDoubleClass : helpers.jsNumberClass); 132 ? compiler.commonElements.jsDoubleClass
133 : compiler.commonElements.jsNumberClass);
136 134
137 if (hasInt) { 135 if (hasInt) {
138 whenNumber = js.statement( 136 whenNumber = js.statement(
139 '''{ 137 '''{
140 if (Math.floor(receiver) == receiver) return #; 138 if (Math.floor(receiver) == receiver) return #;
141 return #; 139 return #;
142 }''', 140 }''',
143 [interceptorFor(helpers.jsIntClass), interceptorForNumber]); 141 [
142 interceptorFor(compiler.commonElements.jsIntClass),
143 interceptorForNumber
144 ]);
144 } else { 145 } else {
145 whenNumber = js.statement('return #', interceptorForNumber); 146 whenNumber = js.statement('return #', interceptorForNumber);
146 } 147 }
147 statements 148 statements
148 .add(js.statement('if (typeof receiver == "number") #;', whenNumber)); 149 .add(js.statement('if (typeof receiver == "number") #;', whenNumber));
149 } 150 }
150 151
151 if (hasString) { 152 if (hasString) {
152 statements.add(buildInterceptorCheck(helpers.jsStringClass)); 153 statements
154 .add(buildInterceptorCheck(compiler.commonElements.jsStringClass));
153 } 155 }
154 if (hasNull) { 156 if (hasNull) {
155 statements.add(buildInterceptorCheck(helpers.jsNullClass)); 157 statements
158 .add(buildInterceptorCheck(compiler.commonElements.jsNullClass));
156 } else { 159 } else {
157 // Returning "undefined" or "null" here will provoke a JavaScript 160 // Returning "undefined" or "null" here will provoke a JavaScript
158 // TypeError which is later identified as a null-error by 161 // TypeError which is later identified as a null-error by
159 // [unwrapException] in js_helper.dart. 162 // [unwrapException] in js_helper.dart.
160 statements.add(js.statement('if (receiver == null) return receiver')); 163 statements.add(js.statement('if (receiver == null) return receiver'));
161 } 164 }
162 if (hasBool) { 165 if (hasBool) {
163 statements.add(buildInterceptorCheck(helpers.jsBoolClass)); 166 statements
167 .add(buildInterceptorCheck(compiler.commonElements.jsBoolClass));
164 } 168 }
165 // TODO(ahe): It might be faster to check for Array before 169 // TODO(ahe): It might be faster to check for Array before
166 // function and bool. 170 // function and bool.
167 if (hasArray) { 171 if (hasArray) {
168 statements.add(buildInterceptorCheck(helpers.jsArrayClass)); 172 statements
173 .add(buildInterceptorCheck(compiler.commonElements.jsArrayClass));
169 } 174 }
170 175
171 if (hasNative) { 176 if (hasNative) {
172 statements.add(js.statement( 177 statements.add(js.statement(
173 r'''{ 178 r'''{
174 if (typeof receiver != "object") { 179 if (typeof receiver != "object") {
175 if (typeof receiver == "function" ) return #; 180 if (typeof receiver == "function" ) return #;
176 return receiver; 181 return receiver;
177 } 182 }
178 if (receiver instanceof #) return receiver; 183 if (receiver instanceof #) return receiver;
179 return #(receiver); 184 return #(receiver);
180 }''', 185 }''',
181 [ 186 [
182 interceptorFor(helpers.jsJavaScriptFunctionClass), 187 interceptorFor(compiler.commonElements.jsJavaScriptFunctionClass),
183 backend.emitter 188 backend.emitter
184 .constructorAccess(compiler.commonElements.objectClass), 189 .constructorAccess(compiler.commonElements.objectClass),
185 backend.emitter 190 backend.emitter.staticFunctionAccess(
186 .staticFunctionAccess(helpers.getNativeInterceptorMethod) 191 compiler.commonElements.getNativeInterceptorMethod)
187 ])); 192 ]));
188 } else { 193 } else {
189 ClassEntity jsUnknown = helpers.jsUnknownJavaScriptObjectClass; 194 ClassEntity jsUnknown =
195 compiler.commonElements.jsUnknownJavaScriptObjectClass;
190 if (compiler.codegenWorldBuilder.directlyInstantiatedClasses 196 if (compiler.codegenWorldBuilder.directlyInstantiatedClasses
191 .contains(jsUnknown)) { 197 .contains(jsUnknown)) {
192 statements.add(js.statement('if (!(receiver instanceof #)) return #;', [ 198 statements.add(js.statement('if (!(receiver instanceof #)) return #;', [
193 backend.emitter 199 backend.emitter
194 .constructorAccess(compiler.commonElements.objectClass), 200 .constructorAccess(compiler.commonElements.objectClass),
195 interceptorFor(jsUnknown) 201 interceptorFor(jsUnknown)
196 ])); 202 ]));
197 } 203 }
198 204
199 statements.add(js.statement('return receiver')); 205 statements.add(js.statement('return receiver'));
200 } 206 }
201 207
202 return js('''function(receiver) { #; }''', new jsAst.Block(statements)); 208 return js('''function(receiver) { #; }''', new jsAst.Block(statements));
203 } 209 }
204 210
205 // Returns a statement that takes care of performance critical 211 // Returns a statement that takes care of performance critical
206 // common case for a one-shot interceptor, or null if there is no 212 // common case for a one-shot interceptor, or null if there is no
207 // fast path. 213 // fast path.
208 jsAst.Statement _fastPathForOneShotInterceptor( 214 jsAst.Statement _fastPathForOneShotInterceptor(
209 Selector selector, Set<ClassEntity> classes) { 215 Selector selector, Set<ClassEntity> classes) {
210 if (selector.isOperator) { 216 if (selector.isOperator) {
211 String name = selector.name; 217 String name = selector.name;
212 if (name == '==') { 218 if (name == '==') {
213 return js.statement('''{ 219 return js.statement('''{
214 if (receiver == null) return a0 == null; 220 if (receiver == null) return a0 == null;
215 if (typeof receiver != "object") 221 if (typeof receiver != "object")
216 return a0 != null && receiver === a0; 222 return a0 != null && receiver === a0;
217 }'''); 223 }''');
218 } 224 }
219 if (!classes.contains(helpers.jsIntClass) && 225 if (!classes.contains(compiler.commonElements.jsIntClass) &&
220 !classes.contains(helpers.jsNumberClass) && 226 !classes.contains(compiler.commonElements.jsNumberClass) &&
221 !classes.contains(helpers.jsDoubleClass)) { 227 !classes.contains(compiler.commonElements.jsDoubleClass)) {
222 return null; 228 return null;
223 } 229 }
224 if (selector.argumentCount == 1) { 230 if (selector.argumentCount == 1) {
225 // The following operators do not map to a JavaScript operator. 231 // The following operators do not map to a JavaScript operator.
226 if (name == '~/' || name == '<<' || name == '%' || name == '>>') { 232 if (name == '~/' || name == '<<' || name == '%' || name == '>>') {
227 return null; 233 return null;
228 } 234 }
229 jsAst.Expression result = js('receiver $name a0'); 235 jsAst.Expression result = js('receiver $name a0');
230 if (name == '&' || name == '|' || name == '^') { 236 if (name == '&' || name == '|' || name == '^') {
231 result = js('# >>> 0', result); 237 result = js('# >>> 0', result);
(...skipping 26 matching lines...) Expand all
258 // 264 //
259 // For an index set operation, this code generates: 265 // For an index set operation, this code generates:
260 // 266 //
261 // if (typeof a0 === "number") { 267 // if (typeof a0 === "number") {
262 // if (receiver.constructor == Array && !receiver.immutable$list) { 268 // if (receiver.constructor == Array && !receiver.immutable$list) {
263 // if (a0 >>> 0 === a0 && a0 < receiver.length) { 269 // if (a0 >>> 0 === a0 && a0 < receiver.length) {
264 // return receiver[a0] = a1; 270 // return receiver[a0] = a1;
265 // } 271 // }
266 // } 272 // }
267 // } 273 // }
268 bool containsArray = classes.contains(helpers.jsArrayClass); 274 bool containsArray =
269 bool containsString = classes.contains(helpers.jsStringClass); 275 classes.contains(compiler.commonElements.jsArrayClass);
270 bool containsJsIndexable = 276 bool containsString =
271 closedWorld.isImplemented(helpers.jsIndexingBehaviorInterface) && 277 classes.contains(compiler.commonElements.jsStringClass);
272 classes.any((cls) { 278 bool containsJsIndexable = closedWorld.isImplemented(
273 return closedWorld.isSubtypeOf( 279 compiler.commonElements.jsIndexingBehaviorInterface) &&
274 cls, helpers.jsIndexingBehaviorInterface); 280 classes.any((cls) {
275 }); 281 return closedWorld.isSubtypeOf(
282 cls, compiler.commonElements.jsIndexingBehaviorInterface);
283 });
276 // The index set operator requires a check on its set value in 284 // The index set operator requires a check on its set value in
277 // checked mode, so we don't optimize the interceptor if the 285 // checked mode, so we don't optimize the interceptor if the
278 // compiler has type assertions enabled. 286 // compiler has type assertions enabled.
279 if (selector.isIndexSet && 287 if (selector.isIndexSet &&
280 (compiler.options.enableTypeAssertions || !containsArray)) { 288 (compiler.options.enableTypeAssertions || !containsArray)) {
281 return null; 289 return null;
282 } 290 }
283 if (!containsArray && !containsString) { 291 if (!containsArray && !containsString) {
284 return null; 292 return null;
285 } 293 }
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
348 356
349 if (selector.isSetter) { 357 if (selector.isSetter) {
350 parameterNames.add('value'); 358 parameterNames.add('value');
351 } else { 359 } else {
352 for (int i = 0; i < selector.argumentCount; i++) { 360 for (int i = 0; i < selector.argumentCount; i++) {
353 parameterNames.add('a$i'); 361 parameterNames.add('a$i');
354 } 362 }
355 } 363 }
356 364
357 jsAst.Name invocationName = backend.namer.invocationName(selector); 365 jsAst.Name invocationName = backend.namer.invocationName(selector);
358 String globalObject = 366 String globalObject = namer
359 namer.globalObjectForLibrary(helpers.interceptorsLibrary); 367 .globalObjectForLibrary(compiler.commonElements.interceptorsLibrary);
360 368
361 jsAst.Statement optimizedPath = 369 jsAst.Statement optimizedPath =
362 _fastPathForOneShotInterceptor(selector, classes); 370 _fastPathForOneShotInterceptor(selector, classes);
363 if (optimizedPath == null) optimizedPath = js.statement(';'); 371 if (optimizedPath == null) optimizedPath = js.statement(';');
364 372
365 return js('function(#) { #; return #.#(receiver).#(#) }', [ 373 return js('function(#) { #; return #.#(receiver).#(#) }', [
366 parameterNames, 374 parameterNames,
367 optimizedPath, 375 optimizedPath,
368 globalObject, 376 globalObject,
369 getInterceptorName, 377 getInterceptorName,
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 } 422 }
415 423
416 var map = new jsAst.ObjectInitializer(properties); 424 var map = new jsAst.ObjectInitializer(properties);
417 elements.add(map); 425 elements.add(map);
418 } 426 }
419 } 427 }
420 428
421 return new jsAst.ArrayInitializer(elements); 429 return new jsAst.ArrayInitializer(elements);
422 } 430 }
423 } 431 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698