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 // TODO(karlklose): share the [ParentVisitor]. | 5 // TODO(karlklose): share the [ParentVisitor]. |
6 import '../../cps_ir/optimizers.dart'; | 6 import '../../cps_ir/optimizers.dart'; |
7 import '../../constants/expressions.dart'; | 7 import '../../constants/expressions.dart'; |
8 import '../../constants/values.dart'; | 8 import '../../constants/values.dart'; |
9 import '../../elements/elements.dart' | 9 import '../../elements/elements.dart' show |
10 show ClassElement, FieldElement, FunctionElement, Element; | 10 ClassElement, |
11 FieldElement, | |
12 FunctionElement, | |
13 Local, | |
14 ExecutableElement; | |
11 import '../../js_backend/codegen/glue.dart'; | 15 import '../../js_backend/codegen/glue.dart'; |
12 import '../../dart2jslib.dart' show Selector, World; | 16 import '../../dart2jslib.dart' show Selector, World; |
13 | 17 |
18 class ExplicitReceiverParameterEntity implements Local { | |
19 String get name => 'receiver'; | |
20 final ExecutableElement executableContext; | |
21 ExplicitReceiverParameterEntity(this.executableContext); | |
22 toString() => 'ExplicitReceiverParameterEntity($executableContext)'; | |
23 } | |
24 | |
14 /// Rewrites the initial CPS IR to make Dart semantics explicit and inserts | 25 /// Rewrites the initial CPS IR to make Dart semantics explicit and inserts |
15 /// special nodes that respect JavaScript behavior. | 26 /// special nodes that respect JavaScript behavior. |
16 /// | 27 /// |
17 /// Performs the following rewrites: | 28 /// Performs the following rewrites: |
18 /// - rewrite [IsTrue] in a [Branch] to do boolean conversion. | 29 /// - Rewrite [IsTrue] in a [Branch] to do boolean conversion. |
19 /// - converts two-parameter exception handlers to one-parameter ones. | 30 /// - Add interceptors at call sites that use interceptor calling convention. |
31 /// - Add explicit receiver argument for methods that are called in interceptor | |
32 /// calling convention. | |
33 /// - Convert two-parameter exception handlers to one-parameter ones. | |
20 class UnsugarVisitor extends RecursiveVisitor { | 34 class UnsugarVisitor extends RecursiveVisitor { |
21 Glue _glue; | 35 Glue _glue; |
22 ParentVisitor _parentVisitor = new ParentVisitor(); | 36 ParentVisitor _parentVisitor = new ParentVisitor(); |
23 | 37 |
38 Parameter thisParameter; | |
39 Parameter explicitReceiverParameter; | |
40 | |
24 UnsugarVisitor(this._glue); | 41 UnsugarVisitor(this._glue); |
25 | 42 |
26 void rewrite(FunctionDefinition function) { | 43 void rewrite(FunctionDefinition function) { |
44 bool inInterceptedMethod = _glue.isInterceptedMethod(function.element); | |
45 | |
46 if (function.element.name == '==' && | |
47 function.parameters.length == 1 && | |
48 !_glue.operatorEqHandlesNullArgument(function.element)) { | |
49 // Insert the null check that the language semantics requires us to | |
50 // perform before calling operator ==. | |
51 insertEqNullCheck(function); | |
52 } | |
53 | |
54 if (inInterceptedMethod) { | |
55 thisParameter = function.thisParameter; | |
56 ThisParameterLocal holder = thisParameter.hint; | |
57 explicitReceiverParameter = new Parameter( | |
58 new ExplicitReceiverParameterEntity( | |
59 holder.executableContext)); | |
60 function.parameters.insert(0, explicitReceiverParameter); | |
61 } | |
62 | |
27 // Set all parent pointers. | 63 // Set all parent pointers. |
28 _parentVisitor.visit(function); | 64 _parentVisitor.visit(function); |
65 | |
66 if (inInterceptedMethod) { | |
67 explicitReceiverParameter.substituteFor(thisParameter); | |
68 } | |
69 | |
29 visit(function); | 70 visit(function); |
30 } | 71 } |
31 | 72 |
32 @override | 73 @override |
33 visit(Node node) { | 74 visit(Node node) { |
34 Node result = node.accept(this); | 75 Node result = node.accept(this); |
35 return result != null ? result : node; | 76 return result != null ? result : node; |
36 } | 77 } |
37 | 78 |
38 Constant get trueConstant { | 79 Constant get trueConstant { |
39 return new Constant( | 80 return new Constant( |
40 new BoolConstantExpression( | 81 new BoolConstantExpression( |
41 true, | 82 true, |
42 new TrueConstantValue())); | 83 new TrueConstantValue())); |
43 } | 84 } |
44 | 85 |
86 Constant get falseConstant { | |
87 return new Constant( | |
88 new BoolConstantExpression( | |
89 false, | |
90 new FalseConstantValue())); | |
91 } | |
92 | |
93 Constant get nullConstant { | |
94 return new Constant( | |
95 new NullConstantExpression( | |
96 new NullConstantValue())); | |
97 } | |
98 | |
45 void insertLetPrim(Primitive primitive, Expression node) { | 99 void insertLetPrim(Primitive primitive, Expression node) { |
46 LetPrim let = new LetPrim(primitive); | 100 LetPrim let = new LetPrim(primitive); |
47 InteriorNode parent = node.parent; | 101 InteriorNode parent = node.parent; |
48 parent.body = let; | 102 parent.body = let; |
49 let.body = node; | 103 let.body = node; |
50 node.parent = let; | 104 node.parent = let; |
51 let.parent = parent; | 105 let.parent = parent; |
52 } | 106 } |
53 | 107 |
108 void insertEqNullCheck(FunctionDefinition function) { | |
sigurdm
2015/04/30 08:57:44
Is this rewrite correct in the presence of mirrors
| |
109 // Replace | |
110 // | |
111 // body; | |
112 // | |
113 // with | |
114 // | |
115 // if (identical(arg, null)) | |
116 // return false; | |
117 // else | |
118 // body; | |
119 // | |
120 Continuation originalBody = new Continuation(<Parameter>[]); | |
121 originalBody.body = function.body.body; | |
122 | |
123 Continuation returnFalse = new Continuation(<Parameter>[]); | |
124 Primitive falsePrimitive = falseConstant; | |
125 returnFalse.body = | |
126 new LetPrim(falsePrimitive, | |
127 new InvokeContinuation( | |
128 function.body.returnContinuation, <Primitive>[falsePrimitive])); | |
129 | |
130 Primitive nullPrimitive = nullConstant; | |
131 Primitive test = new Identical(function.parameters.single, nullPrimitive); | |
132 | |
133 Expression newBody = | |
134 new LetCont.many(<Continuation>[returnFalse, originalBody], | |
135 new LetPrim(nullPrimitive, | |
136 new LetPrim(test, | |
137 new Branch( | |
138 new IsTrue(test), | |
139 returnFalse, | |
140 originalBody)))); | |
141 function.body.body = newBody; | |
142 } | |
143 | |
54 /// Insert a static call to [function] at the point of [node] with result | 144 /// Insert a static call to [function] at the point of [node] with result |
55 /// [result]. | 145 /// [result]. |
56 /// | 146 /// |
57 /// Rewrite [node] to | 147 /// Rewrite [node] to |
58 /// | 148 /// |
59 /// let cont continuation(result) = node | 149 /// let cont continuation(result) = node |
60 /// in invoke function arguments continuation | 150 /// in invoke function arguments continuation |
61 void insertStaticCall(FunctionElement function, List<Primitive> arguments, | 151 void insertStaticCall(FunctionElement function, List<Primitive> arguments, |
62 Parameter result, | 152 Parameter result, |
63 Expression node) { | 153 Expression node) { |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
111 // The subexpression of throw is wrapped in the JavaScript output. | 201 // The subexpression of throw is wrapped in the JavaScript output. |
112 Parameter value = new Parameter(null); | 202 Parameter value = new Parameter(null); |
113 insertStaticCall(_glue.getWrapExceptionHelper(), [node.value.definition], | 203 insertStaticCall(_glue.getWrapExceptionHelper(), [node.value.definition], |
114 value, node); | 204 value, node); |
115 node.value.unlink(); | 205 node.value.unlink(); |
116 node.value = new Reference<Primitive>(value); | 206 node.value = new Reference<Primitive>(value); |
117 } | 207 } |
118 | 208 |
119 processInvokeMethod(InvokeMethod node) { | 209 processInvokeMethod(InvokeMethod node) { |
120 Selector selector = node.selector; | 210 Selector selector = node.selector; |
121 // TODO(karlklose): should we rewrite all selectors? | |
122 if (!_glue.isInterceptedSelector(selector)) return; | 211 if (!_glue.isInterceptedSelector(selector)) return; |
123 | 212 |
124 Primitive receiver = node.receiver.definition; | 213 Primitive receiver = node.receiver.definition; |
125 Set<ClassElement> interceptedClasses = | 214 Primitive newReceiver; |
215 | |
216 if (receiver == explicitReceiverParameter) { | |
217 // If the receiver is the explicit receiver, we are calling a method in th e | |
sigurdm
2015/04/30 08:57:44
Long line
| |
218 // same interceptor: Change 'receiver.foo()' to 'this.foo(receiver)'. | |
219 newReceiver = thisParameter; | |
220 } else { | |
221 // TODO(sra): Move the computation of interceptedClasses to a much later | |
222 // phase and take into account the remaining uses of the interceptor. | |
223 Set<ClassElement> interceptedClasses = | |
126 _glue.getInterceptedClassesOn(selector); | 224 _glue.getInterceptedClassesOn(selector); |
127 _glue.registerSpecializedGetInterceptor(interceptedClasses); | 225 _glue.registerSpecializedGetInterceptor(interceptedClasses); |
226 newReceiver = new Interceptor(receiver, interceptedClasses); | |
227 insertLetPrim(newReceiver, node); | |
228 } | |
128 | 229 |
129 Primitive intercepted = new Interceptor(receiver, interceptedClasses); | |
130 insertLetPrim(intercepted, node); | |
131 node.arguments.insert(0, node.receiver); | 230 node.arguments.insert(0, node.receiver); |
132 node.callingConvention = CallingConvention.JS_INTERCEPTED; | 231 node.callingConvention = CallingConvention.JS_INTERCEPTED; |
133 assert(node.isValid); | 232 assert(node.isValid); |
134 node.receiver = new Reference<Primitive>(intercepted); | 233 node.receiver = new Reference<Primitive>(newReceiver); |
135 } | |
136 | |
137 Primitive makeNull() { | |
138 NullConstantValue nullConst = new NullConstantValue(); | |
139 return new Constant(new NullConstantExpression(nullConst)); | |
140 } | 234 } |
141 | 235 |
142 processInvokeMethodDirectly(InvokeMethodDirectly node) { | 236 processInvokeMethodDirectly(InvokeMethodDirectly node) { |
143 if (_glue.isInterceptedMethod(node.target)) { | 237 if (_glue.isInterceptedMethod(node.target)) { |
144 Primitive nullPrim = makeNull(); | 238 Primitive nullPrim = nullConstant; |
145 insertLetPrim(nullPrim, node); | 239 insertLetPrim(nullPrim, node); |
146 node.arguments.insert(0, node.receiver); | 240 node.arguments.insert(0, node.receiver); |
241 // TODO(sra): `null` is not adequate. Interceptors project the class | |
242 // hierarchy onto an interceptor hierarchy. A super call that does a | |
243 // method call will use the javascript 'this' parameter to avoid calling | |
244 // getInterceptor again, so the receiver must be the interceptor (likely | |
245 // `this`), not `null`. | |
147 node.receiver = new Reference<Primitive>(nullPrim); | 246 node.receiver = new Reference<Primitive>(nullPrim); |
148 } | 247 } |
149 } | 248 } |
150 | 249 |
151 processBranch(Branch node) { | 250 processBranch(Branch node) { |
152 // TODO(karlklose): implement the checked mode part of boolean conversion. | 251 // TODO(karlklose): implement the checked mode part of boolean conversion. |
153 InteriorNode parent = node.parent; | 252 InteriorNode parent = node.parent; |
154 IsTrue condition = node.condition; | 253 IsTrue condition = node.condition; |
155 Primitive t = trueConstant; | 254 Primitive t = trueConstant; |
156 Primitive i = new Identical(condition.value.definition, t); | 255 Primitive i = new Identical(condition.value.definition, t); |
157 LetPrim newNode = new LetPrim(t, | 256 LetPrim newNode = new LetPrim(t, |
158 new LetPrim(i, | 257 new LetPrim(i, |
159 new Branch(new IsTrue(i), | 258 new Branch(new IsTrue(i), |
160 node.trueContinuation.definition, | 259 node.trueContinuation.definition, |
161 node.falseContinuation.definition))); | 260 node.falseContinuation.definition))); |
162 condition.value.unlink(); | 261 condition.value.unlink(); |
163 node.trueContinuation.unlink(); | 262 node.trueContinuation.unlink(); |
164 node.falseContinuation.unlink(); | 263 node.falseContinuation.unlink(); |
165 parent.body = newNode; | 264 parent.body = newNode; |
166 } | 265 } |
167 } | 266 } |
OLD | NEW |