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 Pass; | 5 import '../../cps_ir/optimizers.dart' show Pass; |
6 import '../../constants/values.dart'; | 6 import '../../constants/values.dart'; |
7 import '../../elements/elements.dart'; | 7 import '../../elements/elements.dart'; |
8 import '../../js_backend/codegen/glue.dart'; | 8 import '../../js_backend/codegen/glue.dart'; |
9 import '../../universe/selector.dart' show Selector; | 9 import '../../universe/selector.dart' show Selector; |
10 import '../../cps_ir/cps_ir_builder.dart' show ThisParameterLocal; | |
11 import '../../cps_ir/cps_fragment.dart'; | 10 import '../../cps_ir/cps_fragment.dart'; |
12 import '../../common/names.dart'; | 11 import '../../common/names.dart'; |
13 | 12 |
14 class ExplicitReceiverParameterEntity implements Local { | 13 class ExplicitReceiverParameterEntity implements Local { |
15 String get name => 'receiver'; | 14 String get name => 'receiver'; |
16 final ExecutableElement executableContext; | 15 final ExecutableElement executableContext; |
17 ExplicitReceiverParameterEntity(this.executableContext); | 16 ExplicitReceiverParameterEntity(this.executableContext); |
18 toString() => 'ExplicitReceiverParameterEntity($executableContext)'; | 17 toString() => 'ExplicitReceiverParameterEntity($executableContext)'; |
19 } | 18 } |
20 | 19 |
(...skipping 10 matching lines...) Expand all Loading... |
31 /// special nodes that respect JavaScript behavior. | 30 /// special nodes that respect JavaScript behavior. |
32 /// | 31 /// |
33 /// Performs the following rewrites: | 32 /// Performs the following rewrites: |
34 /// - Add interceptors at call sites that use interceptor calling convention. | 33 /// - Add interceptors at call sites that use interceptor calling convention. |
35 /// - Add explicit receiver argument for methods that are called in interceptor | 34 /// - Add explicit receiver argument for methods that are called in interceptor |
36 /// calling convention. | 35 /// calling convention. |
37 /// - Convert two-parameter exception handlers to one-parameter ones. | 36 /// - Convert two-parameter exception handlers to one-parameter ones. |
38 class UnsugarVisitor extends TrampolineRecursiveVisitor implements Pass { | 37 class UnsugarVisitor extends TrampolineRecursiveVisitor implements Pass { |
39 Glue _glue; | 38 Glue _glue; |
40 | 39 |
41 Parameter thisParameter; | 40 FunctionDefinition function; |
42 Parameter explicitReceiverParameter; | 41 |
| 42 Parameter get receiverParameter => function.receiverParameter; |
| 43 |
| 44 /// The interceptor of the receiver. For some methods, this is the receiver |
| 45 /// itself, for others, it is the interceptor parameter. |
| 46 Parameter receiverInterceptor; |
43 | 47 |
44 // In a catch block, rethrow implicitly throws the block's exception | 48 // In a catch block, rethrow implicitly throws the block's exception |
45 // parameter. This is the exception parameter when nested in a catch | 49 // parameter. This is the exception parameter when nested in a catch |
46 // block and null otherwise. | 50 // block and null otherwise. |
47 Parameter _exceptionParameter = null; | 51 Parameter _exceptionParameter = null; |
48 | 52 |
49 UnsugarVisitor(this._glue); | 53 UnsugarVisitor(this._glue); |
50 | 54 |
51 String get passName => 'Unsugaring'; | 55 String get passName => 'Unsugaring'; |
52 | 56 |
53 bool methodUsesReceiverArgument(FunctionElement function) { | |
54 assert(_glue.isInterceptedMethod(function)); | |
55 ClassElement clazz = function.enclosingClass.declaration; | |
56 return _glue.isInterceptorClass(clazz) || | |
57 _glue.isUsedAsMixin(clazz); | |
58 } | |
59 | |
60 void rewrite(FunctionDefinition function) { | 57 void rewrite(FunctionDefinition function) { |
61 thisParameter = function.thisParameter; | 58 this.function = function; |
62 bool inInterceptedMethod = _glue.isInterceptedMethod(function.element); | 59 bool inInterceptedMethod = _glue.isInterceptedMethod(function.element); |
63 | 60 |
64 if (function.element.name == '==' && | 61 if (function.element.name == '==' && |
65 function.parameters.length == 1 && | 62 function.parameters.length == 1 && |
66 !_glue.operatorEqHandlesNullArgument(function.element)) { | 63 !_glue.operatorEqHandlesNullArgument(function.element)) { |
67 // Insert the null check that the language semantics requires us to | 64 // Insert the null check that the language semantics requires us to |
68 // perform before calling operator ==. | 65 // perform before calling operator ==. |
69 insertEqNullCheck(function); | 66 insertEqNullCheck(function); |
70 } | 67 } |
71 | 68 |
72 if (inInterceptedMethod) { | 69 if (inInterceptedMethod) { |
73 ThisParameterLocal holder = thisParameter.hint; | 70 function.interceptorParameter = new Parameter(null)..parent = function; |
74 explicitReceiverParameter = new Parameter( | 71 // Since the receiver won't be compiled to "this", set a hint on it |
75 new ExplicitReceiverParameterEntity(holder.executableContext)); | 72 // so the parameter gets a meaningful name. |
76 explicitReceiverParameter.parent = function; | 73 function.receiverParameter.hint = |
77 function.parameters.insert(0, explicitReceiverParameter); | 74 new ExplicitReceiverParameterEntity(function.element); |
78 } | 75 // If we need an interceptor for the receiver, use the receiver itself |
79 | 76 // if possible, otherwise the interceptor argument. |
80 if (inInterceptedMethod && methodUsesReceiverArgument(function.element)) { | 77 receiverInterceptor = _glue.methodUsesReceiverArgument(function.element) |
81 thisParameter.replaceUsesWith(explicitReceiverParameter); | 78 ? function.interceptorParameter |
| 79 : receiverParameter; |
82 } | 80 } |
83 | 81 |
84 visit(function); | 82 visit(function); |
85 } | 83 } |
86 | 84 |
87 Constant get trueConstant { | 85 Constant get trueConstant { |
88 return new Constant(new TrueConstantValue()); | 86 return new Constant(new TrueConstantValue()); |
89 } | 87 } |
90 | 88 |
91 Constant get falseConstant { | 89 Constant get falseConstant { |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
204 node.argumentRefs.length == 1 && | 202 node.argumentRefs.length == 1 && |
205 isNullConstant(node.argument(0))) { | 203 isNullConstant(node.argument(0))) { |
206 node.replaceWith(new ApplyBuiltinOperator( | 204 node.replaceWith(new ApplyBuiltinOperator( |
207 BuiltinOperator.Identical, | 205 BuiltinOperator.Identical, |
208 [node.receiver, node.argument(0)], | 206 [node.receiver, node.argument(0)], |
209 node.sourceInformation)); | 207 node.sourceInformation)); |
210 return; | 208 return; |
211 } | 209 } |
212 | 210 |
213 Primitive receiver = node.receiver; | 211 Primitive receiver = node.receiver; |
214 Primitive newReceiver; | 212 Primitive interceptor; |
215 | 213 |
216 if (receiver == explicitReceiverParameter) { | 214 if (receiver == receiverParameter && receiverInterceptor != null) { |
217 // If the receiver is the explicit receiver, we are calling a method in | 215 // TODO(asgerf): This could be done by GVN. |
| 216 // If the receiver is 'this', we are calling a method in |
218 // the same interceptor: | 217 // the same interceptor: |
219 // Change 'receiver.foo()' to 'this.foo(receiver)'. | 218 // Change 'receiver.foo()' to 'this.foo(receiver)'. |
220 newReceiver = thisParameter; | 219 interceptor = receiverInterceptor; |
221 } else { | 220 } else { |
222 newReceiver = new Interceptor(receiver, node.sourceInformation); | 221 interceptor = new Interceptor(receiver, node.sourceInformation); |
223 if (receiver.hint != null) { | 222 if (receiver.hint != null) { |
224 newReceiver.hint = new InterceptorEntity(receiver.hint); | 223 interceptor.hint = new InterceptorEntity(receiver.hint); |
225 } | 224 } |
226 new LetPrim(newReceiver).insertAbove(node.parent); | 225 new LetPrim(interceptor).insertAbove(node.parent); |
227 } | 226 } |
228 node.argumentRefs.insert(0, node.receiverRef); | 227 assert(node.interceptorRef == null); |
229 node.receiverRef = new Reference<Primitive>(newReceiver)..parent = node; | 228 node.makeIntercepted(interceptor); |
230 node.callingConvention = CallingConvention.Intercepted; | |
231 } | 229 } |
232 | 230 |
233 processInvokeMethodDirectly(InvokeMethodDirectly node) { | 231 processInvokeMethodDirectly(InvokeMethodDirectly node) { |
234 if (!_glue.isInterceptedMethod(node.target)) return; | 232 if (!_glue.isInterceptedMethod(node.target)) return; |
235 | 233 |
236 Primitive receiver = node.receiver; | 234 Primitive receiver = node.receiver; |
237 Primitive newReceiver; | 235 Primitive interceptor; |
238 | 236 |
239 if (receiver == explicitReceiverParameter) { | 237 if (receiver == receiverParameter && receiverInterceptor != null) { |
240 // If the receiver is the explicit receiver, we are calling a method in | 238 // If the receiver is 'this', we are calling a method in |
241 // the same interceptor: | 239 // the same interceptor: |
242 // Change 'receiver.foo()' to 'this.foo(receiver)'. | 240 // Change 'receiver.foo()' to 'this.foo(receiver)'. |
243 newReceiver = thisParameter; | 241 interceptor = receiverInterceptor; |
244 } else { | 242 } else { |
245 newReceiver = new Interceptor(receiver, node.sourceInformation); | 243 interceptor = new Interceptor(receiver, node.sourceInformation); |
246 if (receiver.hint != null) { | 244 if (receiver.hint != null) { |
247 newReceiver.hint = new InterceptorEntity(receiver.hint); | 245 interceptor.hint = new InterceptorEntity(receiver.hint); |
248 } | 246 } |
249 new LetPrim(newReceiver).insertAbove(node.parent); | 247 new LetPrim(interceptor).insertAbove(node.parent); |
250 } | 248 } |
251 node.argumentRefs.insert(0, node.receiverRef); | 249 assert(node.interceptorRef == null); |
252 node.receiverRef = new Reference<Primitive>(newReceiver)..parent = node; | 250 node.makeIntercepted(interceptor); |
253 node.callingConvention = CallingConvention.Intercepted; | |
254 } | 251 } |
255 } | 252 } |
OLD | NEW |