OLD | NEW |
1 library dart2js.unsugar_cps; | 1 library dart2js.unsugar_cps; |
2 | 2 |
3 import '../../cps_ir/cps_ir_nodes.dart'; | 3 import '../../cps_ir/cps_ir_nodes.dart'; |
4 | 4 |
5 import '../../cps_ir/optimizers.dart' show ParentVisitor; | 5 import '../../cps_ir/optimizers.dart' show ParentVisitor; |
6 import '../../constants/values.dart'; | 6 import '../../constants/values.dart'; |
7 import '../../elements/elements.dart'; | 7 import '../../elements/elements.dart'; |
8 import '../../io/source_information.dart'; | 8 import '../../io/source_information.dart'; |
9 import '../../js_backend/codegen/glue.dart'; | 9 import '../../js_backend/codegen/glue.dart'; |
10 import '../../universe/universe.dart' show Selector; | 10 import '../../universe/universe.dart' show Selector; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
45 | 45 |
46 Map<Primitive, Interceptor> interceptors = <Primitive, Interceptor>{}; | 46 Map<Primitive, Interceptor> interceptors = <Primitive, Interceptor>{}; |
47 | 47 |
48 // In a catch block, rethrow implicitly throws the block's exception | 48 // In a catch block, rethrow implicitly throws the block's exception |
49 // parameter. This is the exception parameter when nested in a catch | 49 // parameter. This is the exception parameter when nested in a catch |
50 // block and null otherwise. | 50 // block and null otherwise. |
51 Parameter _exceptionParameter = null; | 51 Parameter _exceptionParameter = null; |
52 | 52 |
53 UnsugarVisitor(this._glue); | 53 UnsugarVisitor(this._glue); |
54 | 54 |
| 55 bool methodUsesReceiverArgument(FunctionElement function) { |
| 56 assert(_glue.isInterceptedMethod(function)); |
| 57 ClassElement clazz = function.enclosingClass.declaration; |
| 58 return _glue.isInterceptorClass(clazz) || |
| 59 _glue.isUsedAsMixin(clazz); |
| 60 } |
| 61 |
55 void rewrite(FunctionDefinition function) { | 62 void rewrite(FunctionDefinition function) { |
| 63 thisParameter = function.thisParameter; |
56 bool inInterceptedMethod = _glue.isInterceptedMethod(function.element); | 64 bool inInterceptedMethod = _glue.isInterceptedMethod(function.element); |
57 | 65 |
58 if (function.element.name == '==' && | 66 if (function.element.name == '==' && |
59 function.parameters.length == 1 && | 67 function.parameters.length == 1 && |
60 !_glue.operatorEqHandlesNullArgument(function.element)) { | 68 !_glue.operatorEqHandlesNullArgument(function.element)) { |
61 // Insert the null check that the language semantics requires us to | 69 // Insert the null check that the language semantics requires us to |
62 // perform before calling operator ==. | 70 // perform before calling operator ==. |
63 insertEqNullCheck(function); | 71 insertEqNullCheck(function); |
64 } | 72 } |
65 | 73 |
66 if (inInterceptedMethod) { | 74 if (inInterceptedMethod) { |
67 thisParameter = function.thisParameter; | |
68 ThisParameterLocal holder = thisParameter.hint; | 75 ThisParameterLocal holder = thisParameter.hint; |
69 explicitReceiverParameter = new Parameter( | 76 explicitReceiverParameter = new Parameter( |
70 new ExplicitReceiverParameterEntity( | 77 new ExplicitReceiverParameterEntity( |
71 holder.executableContext)); | 78 holder.executableContext)); |
72 function.parameters.insert(0, explicitReceiverParameter); | 79 function.parameters.insert(0, explicitReceiverParameter); |
73 } | 80 } |
74 | 81 |
75 // Set all parent pointers. | 82 // Set all parent pointers. |
76 _parentVisitor.visit(function); | 83 _parentVisitor.visit(function); |
77 | 84 |
78 if (inInterceptedMethod) { | 85 if (inInterceptedMethod && methodUsesReceiverArgument(function.element)) { |
79 explicitReceiverParameter.substituteFor(thisParameter); | 86 explicitReceiverParameter.substituteFor(thisParameter); |
80 } | 87 } |
81 | 88 |
82 visit(function); | 89 visit(function); |
83 } | 90 } |
84 | 91 |
85 Constant get trueConstant { | 92 Constant get trueConstant { |
86 return new Constant(new TrueConstantValue()); | 93 return new Constant(new TrueConstantValue()); |
87 } | 94 } |
88 | 95 |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 | 235 |
229 /// Returns an interceptor for the given value, capable of responding to | 236 /// Returns an interceptor for the given value, capable of responding to |
230 /// [selector]. | 237 /// [selector]. |
231 /// | 238 /// |
232 /// A single getInterceptor call will be created per primitive, bound | 239 /// A single getInterceptor call will be created per primitive, bound |
233 /// immediately after the primitive is bound. | 240 /// immediately after the primitive is bound. |
234 /// | 241 /// |
235 /// The type propagation pass will later narrow the set of interceptors | 242 /// The type propagation pass will later narrow the set of interceptors |
236 /// based on the input type, and the let sinking pass will propagate the | 243 /// based on the input type, and the let sinking pass will propagate the |
237 /// getInterceptor call closer to its use when this is profitable. | 244 /// getInterceptor call closer to its use when this is profitable. |
238 Interceptor getInterceptorFor(Primitive prim, Selector selector, | 245 Primitive getInterceptorFor(Primitive prim, Selector selector, |
239 SourceInformation sourceInformation) { | 246 SourceInformation sourceInformation) { |
| 247 if (prim == explicitReceiverParameter) { |
| 248 // If the receiver is the explicit receiver, we are calling a method in |
| 249 // the same interceptor. |
| 250 return thisParameter; |
| 251 } |
240 assert(prim is! Interceptor); | 252 assert(prim is! Interceptor); |
241 Interceptor interceptor = interceptors[prim]; | 253 Interceptor interceptor = interceptors[prim]; |
242 if (interceptor == null) { | 254 if (interceptor == null) { |
243 interceptor = new Interceptor(prim, sourceInformation); | 255 interceptor = new Interceptor(prim, sourceInformation); |
244 interceptors[prim] = interceptor; | 256 interceptors[prim] = interceptor; |
245 InteriorNode parent = prim.parent; | 257 InteriorNode parent = prim.parent; |
246 insertLetPrim(interceptor, parent.body); | 258 insertLetPrim(interceptor, parent.body); |
247 if (prim.hint != null) { | 259 if (prim.hint != null) { |
248 interceptor.hint = new InterceptorEntity(prim.hint); | 260 interceptor.hint = new InterceptorEntity(prim.hint); |
249 } | 261 } |
250 } | 262 } |
251 // Add the interceptor classes that can respond to the given selector. | 263 // Add the interceptor classes that can respond to the given selector. |
252 interceptor.interceptedClasses.addAll( | 264 interceptor.interceptedClasses.addAll( |
253 _glue.getInterceptedClassesOn(selector)); | 265 _glue.getInterceptedClassesOn(selector)); |
254 return interceptor; | 266 return interceptor; |
255 } | 267 } |
256 | 268 |
257 processInvokeMethod(InvokeMethod node) { | 269 processInvokeMethod(InvokeMethod node) { |
258 Selector selector = node.selector; | 270 if (_glue.isInterceptedSelector(node.selector)) { |
259 if (!_glue.isInterceptedSelector(selector)) return; | 271 // Rewrite `x.foo()` => `INTERCEPTOR.foo(x, ..)`. |
260 | 272 Primitive receiver = node.receiver.definition; |
261 Primitive receiver = node.receiver.definition; | 273 Primitive newReceiver = |
262 Primitive newReceiver; | 274 getInterceptorFor(receiver, node.selector, node.sourceInformation); |
263 | 275 node.arguments.insert(0, node.receiver); |
264 if (receiver == explicitReceiverParameter) { | 276 node.receiver = new Reference<Primitive>(newReceiver); |
265 // If the receiver is the explicit receiver, we are calling a method in | |
266 // the same interceptor: | |
267 // Change 'receiver.foo()' to 'this.foo(receiver)'. | |
268 newReceiver = thisParameter; | |
269 } else { | |
270 newReceiver = getInterceptorFor( | |
271 receiver, node.selector, node.sourceInformation); | |
272 } | 277 } |
273 | |
274 node.arguments.insert(0, node.receiver); | |
275 node.receiver = new Reference<Primitive>(newReceiver); | |
276 } | 278 } |
277 | 279 |
278 processInvokeMethodDirectly(InvokeMethodDirectly node) { | 280 processInvokeMethodDirectly(InvokeMethodDirectly node) { |
279 if (_glue.isInterceptedMethod(node.target)) { | 281 if (_glue.isInterceptedMethod(node.target)) { |
280 Primitive nullPrim = nullConstant; | 282 // Rewrite `x.foo()` => `INTERCEPTOR.foo(x, ..)`. |
281 insertLetPrim(nullPrim, node); | 283 Primitive receiver = node.receiver.definition; |
| 284 Primitive newReceiver = |
| 285 getInterceptorFor(receiver, node.selector, node.sourceInformation); |
282 node.arguments.insert(0, node.receiver); | 286 node.arguments.insert(0, node.receiver); |
283 // TODO(sra): `null` is not adequate. Interceptors project the class | 287 node.receiver = new Reference<Primitive>(newReceiver); |
284 // hierarchy onto an interceptor hierarchy. A super call that does a | |
285 // method call will use the javascript 'this' parameter to avoid calling | |
286 // getInterceptor again, so the receiver must be the interceptor (likely | |
287 // `this`), not `null`. | |
288 node.receiver = new Reference<Primitive>(nullPrim); | |
289 } | 288 } |
290 } | 289 } |
291 | 290 |
292 processBranch(Branch node) { | 291 processBranch(Branch node) { |
293 // TODO(karlklose): implement the checked mode part of boolean conversion. | 292 // TODO(karlklose): implement the checked mode part of boolean conversion. |
294 InteriorNode parent = node.parent; | 293 InteriorNode parent = node.parent; |
295 IsTrue condition = node.condition; | 294 IsTrue condition = node.condition; |
296 | 295 |
297 // Do not rewrite conditions that are foreign code. | 296 // Do not rewrite conditions that are foreign code. |
298 // It is redundant, and causes infinite recursion (if not optimized) | 297 // It is redundant, and causes infinite recursion (if not optimized) |
(...skipping 22 matching lines...) Expand all Loading... |
321 condition.value.unlink(); | 320 condition.value.unlink(); |
322 node.trueContinuation.unlink(); | 321 node.trueContinuation.unlink(); |
323 node.falseContinuation.unlink(); | 322 node.falseContinuation.unlink(); |
324 parent.body = newNode; | 323 parent.body = newNode; |
325 } | 324 } |
326 | 325 |
327 processInterceptor(Interceptor node) { | 326 processInterceptor(Interceptor node) { |
328 _glue.registerSpecializedGetInterceptor(node.interceptedClasses); | 327 _glue.registerSpecializedGetInterceptor(node.interceptedClasses); |
329 } | 328 } |
330 } | 329 } |
OLD | NEW |