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