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, Pass; | 5 import '../../cps_ir/optimizers.dart' show ParentVisitor, 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; | 10 import '../../cps_ir/cps_ir_builder.dart' show ThisParameterLocal; |
| 11 import '../../cps_ir/cps_fragment.dart'; |
11 | 12 |
12 class ExplicitReceiverParameterEntity implements Local { | 13 class ExplicitReceiverParameterEntity implements Local { |
13 String get name => 'receiver'; | 14 String get name => 'receiver'; |
14 final ExecutableElement executableContext; | 15 final ExecutableElement executableContext; |
15 ExplicitReceiverParameterEntity(this.executableContext); | 16 ExplicitReceiverParameterEntity(this.executableContext); |
16 toString() => 'ExplicitReceiverParameterEntity($executableContext)'; | 17 toString() => 'ExplicitReceiverParameterEntity($executableContext)'; |
17 } | 18 } |
18 | 19 |
19 /// Suggested name for an interceptor. | 20 /// Suggested name for an interceptor. |
20 class InterceptorEntity extends Entity { | 21 class InterceptorEntity extends Entity { |
21 Entity interceptedVariable; | 22 Entity interceptedVariable; |
22 | 23 |
23 InterceptorEntity(this.interceptedVariable); | 24 InterceptorEntity(this.interceptedVariable); |
24 | 25 |
25 String get name => interceptedVariable.name + '_'; | 26 String get name => interceptedVariable.name + '_'; |
26 } | 27 } |
27 | 28 |
28 | |
29 /// Rewrites the initial CPS IR to make Dart semantics explicit and inserts | 29 /// Rewrites the initial CPS IR to make Dart semantics explicit and inserts |
30 /// special nodes that respect JavaScript behavior. | 30 /// special nodes that respect JavaScript behavior. |
31 /// | 31 /// |
32 /// Performs the following rewrites: | 32 /// Performs the following rewrites: |
33 /// - Add interceptors at call sites that use interceptor calling convention. | 33 /// - Add interceptors at call sites that use interceptor calling convention. |
34 /// - Add explicit receiver argument for methods that are called in interceptor | 34 /// - Add explicit receiver argument for methods that are called in interceptor |
35 /// calling convention. | 35 /// calling convention. |
36 /// - Convert two-parameter exception handlers to one-parameter ones. | 36 /// - Convert two-parameter exception handlers to one-parameter ones. |
37 class UnsugarVisitor extends RecursiveVisitor implements Pass { | 37 class UnsugarVisitor extends TrampolineRecursiveVisitor implements Pass { |
38 Glue _glue; | 38 Glue _glue; |
39 ParentVisitor _parentVisitor = new ParentVisitor(); | |
40 | 39 |
41 Parameter thisParameter; | 40 Parameter thisParameter; |
42 Parameter explicitReceiverParameter; | 41 Parameter explicitReceiverParameter; |
43 | 42 |
44 // In a catch block, rethrow implicitly throws the block's exception | 43 // In a catch block, rethrow implicitly throws the block's exception |
45 // parameter. This is the exception parameter when nested in a catch | 44 // parameter. This is the exception parameter when nested in a catch |
46 // block and null otherwise. | 45 // block and null otherwise. |
47 Parameter _exceptionParameter = null; | 46 Parameter _exceptionParameter = null; |
48 | 47 |
49 UnsugarVisitor(this._glue); | 48 UnsugarVisitor(this._glue); |
(...skipping 15 matching lines...) Expand all Loading... |
65 function.parameters.length == 1 && | 64 function.parameters.length == 1 && |
66 !_glue.operatorEqHandlesNullArgument(function.element)) { | 65 !_glue.operatorEqHandlesNullArgument(function.element)) { |
67 // Insert the null check that the language semantics requires us to | 66 // Insert the null check that the language semantics requires us to |
68 // perform before calling operator ==. | 67 // perform before calling operator ==. |
69 insertEqNullCheck(function); | 68 insertEqNullCheck(function); |
70 } | 69 } |
71 | 70 |
72 if (inInterceptedMethod) { | 71 if (inInterceptedMethod) { |
73 ThisParameterLocal holder = thisParameter.hint; | 72 ThisParameterLocal holder = thisParameter.hint; |
74 explicitReceiverParameter = new Parameter( | 73 explicitReceiverParameter = new Parameter( |
75 new ExplicitReceiverParameterEntity( | 74 new ExplicitReceiverParameterEntity(holder.executableContext)); |
76 holder.executableContext)); | 75 explicitReceiverParameter.parent = function; |
77 function.parameters.insert(0, explicitReceiverParameter); | 76 function.parameters.insert(0, explicitReceiverParameter); |
78 } | 77 } |
79 | 78 |
80 // Set all parent pointers. | |
81 _parentVisitor.visit(function); | |
82 | |
83 if (inInterceptedMethod && methodUsesReceiverArgument(function.element)) { | 79 if (inInterceptedMethod && methodUsesReceiverArgument(function.element)) { |
84 explicitReceiverParameter.substituteFor(thisParameter); | 80 explicitReceiverParameter.substituteFor(thisParameter); |
85 } | 81 } |
86 | 82 |
87 visit(function); | 83 visit(function); |
88 } | 84 } |
89 | 85 |
90 Constant get trueConstant { | 86 Constant get trueConstant { |
91 return new Constant(new TrueConstantValue()); | 87 return new Constant(new TrueConstantValue()); |
92 } | 88 } |
93 | 89 |
94 Constant get falseConstant { | 90 Constant get falseConstant { |
95 return new Constant(new FalseConstantValue()); | 91 return new Constant(new FalseConstantValue()); |
96 } | 92 } |
97 | 93 |
98 Constant get nullConstant { | 94 Constant get nullConstant { |
99 return new Constant(new NullConstantValue()); | 95 return new Constant(new NullConstantValue()); |
100 } | 96 } |
101 | 97 |
102 void insertLetPrim(Primitive primitive, Expression node) { | 98 void insertLetPrim(Primitive primitive, Expression node) { |
103 LetPrim let = new LetPrim(primitive); | 99 LetPrim let = new LetPrim(primitive); |
104 InteriorNode parent = node.parent; | 100 let.insertAbove(node); |
105 parent.body = let; | |
106 let.body = node; | |
107 node.parent = let; | |
108 let.parent = parent; | |
109 } | 101 } |
110 | 102 |
111 void insertEqNullCheck(FunctionDefinition function) { | 103 void insertEqNullCheck(FunctionDefinition function) { |
112 // Replace | 104 // Replace |
113 // | 105 // |
114 // body; | 106 // body; |
115 // | 107 // |
116 // with | 108 // with |
117 // | 109 // |
118 // if (identical(arg, null)) | 110 // if (identical(arg, null)) |
119 // return false; | 111 // return false; |
120 // else | 112 // else |
121 // body; | 113 // body; |
122 // | 114 // |
123 Continuation originalBody = new Continuation(<Parameter>[]); | 115 CpsFragment cps = new CpsFragment(); |
124 originalBody.body = function.body; | 116 Primitive isNull = cps.applyBuiltin( |
125 | |
126 Continuation returnFalse = new Continuation(<Parameter>[]); | |
127 Primitive falsePrimitive = falseConstant; | |
128 returnFalse.body = | |
129 new LetPrim(falsePrimitive, | |
130 new InvokeContinuation( | |
131 function.returnContinuation, <Primitive>[falsePrimitive])); | |
132 | |
133 Primitive nullPrimitive = nullConstant; | |
134 Primitive test = new ApplyBuiltinOperator( | |
135 BuiltinOperator.Identical, | 117 BuiltinOperator.Identical, |
136 <Primitive>[function.parameters.single, nullPrimitive], | 118 <Primitive>[function.parameters.single, cps.makeNull()]); |
137 function.parameters.single.sourceInformation); | 119 CpsFragment trueBranch = cps.ifTruthy(isNull); |
138 | 120 trueBranch.invokeContinuation(function.returnContinuation, |
139 Expression newBody = | 121 <Primitive>[trueBranch.makeFalse()]); |
140 new LetCont.many(<Continuation>[returnFalse, originalBody], | 122 cps.insertAbove(function.body); |
141 new LetPrim(nullPrimitive, | |
142 new LetPrim(test, | |
143 new Branch.loose(test, returnFalse, originalBody)))); | |
144 function.body = newBody; | |
145 } | 123 } |
146 | 124 |
147 /// Insert a static call to [function] at the point of [node] with result | 125 /// Insert a static call to [function] at the point of [node] with result |
148 /// [result]. | 126 /// [result]. |
149 /// | 127 /// |
150 /// Rewrite [node] to | 128 /// Rewrite [node] to |
151 /// | 129 /// |
152 /// let cont continuation(result) = node | 130 /// let cont continuation(result) = node |
153 /// in invoke function arguments continuation | 131 /// in invoke function arguments continuation |
154 void insertStaticCall(FunctionElement function, List<Primitive> arguments, | 132 void insertStaticCall(FunctionElement function, List<Primitive> arguments, |
155 Parameter result, | 133 Parameter result, Expression node) { |
156 Expression node) { | |
157 InteriorNode parent = node.parent; | 134 InteriorNode parent = node.parent; |
158 Continuation continuation = new Continuation([result]); | 135 Continuation continuation = new Continuation([result]); |
159 continuation.body = node; | |
160 _parentVisitor.processContinuation(continuation); | |
161 | 136 |
162 Selector selector = new Selector.fromElement(function); | 137 Selector selector = new Selector.fromElement(function); |
163 // TODO(johnniwinther): Come up with an implementation of SourceInformation | 138 // TODO(johnniwinther): Come up with an implementation of SourceInformation |
164 // for calls such as this one that don't appear in the original source. | 139 // for calls such as this one that don't appear in the original source. |
165 InvokeStatic invoke = new InvokeStatic( | 140 InvokeStatic invoke = new InvokeStatic( |
166 function, selector, arguments, continuation, null); | 141 function, selector, arguments, continuation, null); |
167 _parentVisitor.processInvokeStatic(invoke); | |
168 | 142 |
169 LetCont letCont = new LetCont(continuation, invoke); | 143 LetCont letCont = new LetCont(continuation, invoke); |
170 _parentVisitor.processLetCont(letCont); | |
171 | 144 |
172 parent.body = letCont; | 145 parent.body = letCont; |
173 letCont.parent = parent; | 146 letCont.parent = parent; |
| 147 continuation.body = node; |
| 148 node.parent = continuation; |
174 } | 149 } |
175 | 150 |
176 @override | 151 @override |
177 Expression traverseLetHandler(LetHandler node) { | 152 Expression traverseLetHandler(LetHandler node) { |
178 assert(node.handler.parameters.length == 2); | 153 assert(node.handler.parameters.length == 2); |
179 Parameter previousExceptionParameter = _exceptionParameter; | 154 Parameter previousExceptionParameter = _exceptionParameter; |
180 | 155 |
181 // BEFORE: Handlers have two parameters, exception and stack trace. | 156 // BEFORE: Handlers have two parameters, exception and stack trace. |
182 // AFTER: Handlers have a single parameter, which is unwrapped to get | 157 // AFTER: Handlers have a single parameter, which is unwrapped to get |
183 // the exception and stack trace. | 158 // the exception and stack trace. |
(...skipping 19 matching lines...) Expand all Loading... |
203 node.handler.parameters.removeLast(); | 178 node.handler.parameters.removeLast(); |
204 | 179 |
205 visit(node.handler); | 180 visit(node.handler); |
206 _exceptionParameter = previousExceptionParameter; | 181 _exceptionParameter = previousExceptionParameter; |
207 | 182 |
208 return node.body; | 183 return node.body; |
209 } | 184 } |
210 | 185 |
211 processThrow(Throw node) { | 186 processThrow(Throw node) { |
212 // The subexpression of throw is wrapped in the JavaScript output. | 187 // The subexpression of throw is wrapped in the JavaScript output. |
213 Parameter value = new Parameter(null); | 188 Parameter wrappedException = new Parameter(null); |
214 insertStaticCall(_glue.getWrapExceptionHelper(), [node.value.definition], | 189 insertStaticCall(_glue.getWrapExceptionHelper(), [node.value.definition], |
215 value, node); | 190 wrappedException, node); |
216 node.value.unlink(); | 191 node.value.changeTo(wrappedException); |
217 node.value = new Reference<Primitive>(value); | |
218 } | 192 } |
219 | 193 |
220 processRethrow(Rethrow node) { | 194 processRethrow(Rethrow node) { |
221 // Rethrow can only appear in a catch block. It throws that block's | 195 // Rethrow can only appear in a catch block. It throws that block's |
222 // (wrapped) caught exception. | 196 // (wrapped) caught exception. |
223 Throw replacement = new Throw(_exceptionParameter); | 197 Throw replacement = new Throw(_exceptionParameter); |
224 InteriorNode parent = node.parent; | 198 InteriorNode parent = node.parent; |
225 parent.body = replacement; | 199 parent.body = replacement; |
226 replacement.parent = parent; | 200 replacement.parent = parent; |
227 // The original rethrow does not have any references that we need to | 201 // The original rethrow does not have any references that we need to |
(...skipping 27 matching lines...) Expand all Loading... |
255 } else { | 229 } else { |
256 LetCont contBinding = node.parent; | 230 LetCont contBinding = node.parent; |
257 newReceiver = new Interceptor(receiver, node.sourceInformation) | 231 newReceiver = new Interceptor(receiver, node.sourceInformation) |
258 ..interceptedClasses.addAll(_glue.getInterceptedClassesOn(selector)); | 232 ..interceptedClasses.addAll(_glue.getInterceptedClassesOn(selector)); |
259 if (receiver.hint != null) { | 233 if (receiver.hint != null) { |
260 newReceiver.hint = new InterceptorEntity(receiver.hint); | 234 newReceiver.hint = new InterceptorEntity(receiver.hint); |
261 } | 235 } |
262 insertLetPrim(newReceiver, contBinding); | 236 insertLetPrim(newReceiver, contBinding); |
263 } | 237 } |
264 node.arguments.insert(0, node.receiver); | 238 node.arguments.insert(0, node.receiver); |
265 node.receiver = new Reference<Primitive>(newReceiver); | 239 node.receiver = new Reference<Primitive>(newReceiver)..parent = node; |
266 node.receiverIsIntercepted = true; | 240 node.receiverIsIntercepted = true; |
267 } | 241 } |
268 | 242 |
269 processInvokeMethodDirectly(InvokeMethodDirectly node) { | 243 processInvokeMethodDirectly(InvokeMethodDirectly node) { |
270 if (!_glue.isInterceptedMethod(node.target)) return; | 244 if (!_glue.isInterceptedMethod(node.target)) return; |
271 | 245 |
272 Selector selector = node.selector; | 246 Selector selector = node.selector; |
273 Primitive receiver = node.receiver.definition; | 247 Primitive receiver = node.receiver.definition; |
274 Primitive newReceiver; | 248 Primitive newReceiver; |
275 | 249 |
276 if (receiver == explicitReceiverParameter) { | 250 if (receiver == explicitReceiverParameter) { |
277 // If the receiver is the explicit receiver, we are calling a method in | 251 // If the receiver is the explicit receiver, we are calling a method in |
278 // the same interceptor: | 252 // the same interceptor: |
279 // Change 'receiver.foo()' to 'this.foo(receiver)'. | 253 // Change 'receiver.foo()' to 'this.foo(receiver)'. |
280 newReceiver = thisParameter; | 254 newReceiver = thisParameter; |
281 } else { | 255 } else { |
282 LetCont contBinding = node.parent; | 256 LetCont contBinding = node.parent; |
283 newReceiver = new Interceptor(receiver, node.sourceInformation) | 257 newReceiver = new Interceptor(receiver, node.sourceInformation) |
284 ..interceptedClasses.addAll(_glue.getInterceptedClassesOn(selector)); | 258 ..interceptedClasses.addAll(_glue.getInterceptedClassesOn(selector)); |
285 if (receiver.hint != null) { | 259 if (receiver.hint != null) { |
286 newReceiver.hint = new InterceptorEntity(receiver.hint); | 260 newReceiver.hint = new InterceptorEntity(receiver.hint); |
287 } | 261 } |
288 insertLetPrim(newReceiver, contBinding); | 262 insertLetPrim(newReceiver, contBinding); |
289 } | 263 } |
290 node.arguments.insert(0, node.receiver); | 264 node.arguments.insert(0, node.receiver); |
291 node.receiver = new Reference<Primitive>(newReceiver); | 265 node.receiver = new Reference<Primitive>(newReceiver)..parent = node; |
292 } | 266 } |
293 | 267 |
294 processInterceptor(Interceptor node) { | 268 processInterceptor(Interceptor node) { |
295 _glue.registerSpecializedGetInterceptor(node.interceptedClasses); | 269 _glue.registerSpecializedGetInterceptor(node.interceptedClasses); |
296 } | 270 } |
297 } | 271 } |
OLD | NEW |