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) { |
| 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 |
| 218 // the same interceptor: |
| 219 // Change 'receiver.foo()' to 'this.foo(receiver)'. |
| 220 newReceiver = thisParameter; |
| 221 } else { |
| 222 // TODO(sra): Move the computation of interceptedClasses to a much later |
| 223 // phase and take into account the remaining uses of the interceptor. |
| 224 Set<ClassElement> interceptedClasses = |
126 _glue.getInterceptedClassesOn(selector); | 225 _glue.getInterceptedClassesOn(selector); |
127 _glue.registerSpecializedGetInterceptor(interceptedClasses); | 226 _glue.registerSpecializedGetInterceptor(interceptedClasses); |
| 227 newReceiver = new Interceptor(receiver, interceptedClasses); |
| 228 insertLetPrim(newReceiver, node); |
| 229 } |
128 | 230 |
129 Primitive intercepted = new Interceptor(receiver, interceptedClasses); | |
130 insertLetPrim(intercepted, node); | |
131 node.arguments.insert(0, node.receiver); | 231 node.arguments.insert(0, node.receiver); |
132 node.callingConvention = CallingConvention.JS_INTERCEPTED; | 232 node.callingConvention = CallingConvention.JS_INTERCEPTED; |
133 assert(node.isValid); | 233 assert(node.isValid); |
134 node.receiver = new Reference<Primitive>(intercepted); | 234 node.receiver = new Reference<Primitive>(newReceiver); |
135 } | |
136 | |
137 Primitive makeNull() { | |
138 NullConstantValue nullConst = new NullConstantValue(); | |
139 return new Constant(new NullConstantExpression(nullConst)); | |
140 } | 235 } |
141 | 236 |
142 processInvokeMethodDirectly(InvokeMethodDirectly node) { | 237 processInvokeMethodDirectly(InvokeMethodDirectly node) { |
143 if (_glue.isInterceptedMethod(node.target)) { | 238 if (_glue.isInterceptedMethod(node.target)) { |
144 Primitive nullPrim = makeNull(); | 239 Primitive nullPrim = nullConstant; |
145 insertLetPrim(nullPrim, node); | 240 insertLetPrim(nullPrim, node); |
146 node.arguments.insert(0, node.receiver); | 241 node.arguments.insert(0, node.receiver); |
| 242 // TODO(sra): `null` is not adequate. Interceptors project the class |
| 243 // hierarchy onto an interceptor hierarchy. A super call that does a |
| 244 // method call will use the javascript 'this' parameter to avoid calling |
| 245 // getInterceptor again, so the receiver must be the interceptor (likely |
| 246 // `this`), not `null`. |
147 node.receiver = new Reference<Primitive>(nullPrim); | 247 node.receiver = new Reference<Primitive>(nullPrim); |
148 } | 248 } |
149 } | 249 } |
150 | 250 |
151 processBranch(Branch node) { | 251 processBranch(Branch node) { |
152 // TODO(karlklose): implement the checked mode part of boolean conversion. | 252 // TODO(karlklose): implement the checked mode part of boolean conversion. |
153 InteriorNode parent = node.parent; | 253 InteriorNode parent = node.parent; |
154 IsTrue condition = node.condition; | 254 IsTrue condition = node.condition; |
155 Primitive t = trueConstant; | 255 Primitive t = trueConstant; |
156 Primitive i = new Identical(condition.value.definition, t); | 256 Primitive i = new Identical(condition.value.definition, t); |
157 LetPrim newNode = new LetPrim(t, | 257 LetPrim newNode = new LetPrim(t, |
158 new LetPrim(i, | 258 new LetPrim(i, |
159 new Branch(new IsTrue(i), | 259 new Branch(new IsTrue(i), |
160 node.trueContinuation.definition, | 260 node.trueContinuation.definition, |
161 node.falseContinuation.definition))); | 261 node.falseContinuation.definition))); |
162 condition.value.unlink(); | 262 condition.value.unlink(); |
163 node.trueContinuation.unlink(); | 263 node.trueContinuation.unlink(); |
164 node.falseContinuation.unlink(); | 264 node.falseContinuation.unlink(); |
165 parent.body = newNode; | 265 parent.body = newNode; |
166 } | 266 } |
167 } | 267 } |
OLD | NEW |