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' show ClassElement, FieldElement, Element; | 9 import '../../elements/elements.dart' show |
10 ClassElement, | |
11 ExecutableElement, | |
12 FieldElement, | |
13 Local; | |
10 import '../../js_backend/codegen/glue.dart'; | 14 import '../../js_backend/codegen/glue.dart'; |
11 import '../../dart2jslib.dart' show Selector, World; | 15 import '../../dart2jslib.dart' show Selector, World; |
12 | 16 |
17 | |
18 class ExplicitReceiverParameterEntity implements Local { | |
19 final String name; | |
Kevin Millikin (Google)
2015/03/26 15:14:17
Consider hardcoding the name 'receiver' here rathe
| |
20 final ExecutableElement executableContext; | |
21 ExplicitReceiverParameterEntity(this.name, this.executableContext); | |
22 toString() => 'ExplicitReceiverParameterEntity($executableContext)'; | |
23 } | |
24 | |
25 | |
26 | |
13 /// Rewrites the initial CPS IR to make Dart semantics explicit and inserts | 27 /// Rewrites the initial CPS IR to make Dart semantics explicit and inserts |
14 /// special nodes that respect JavaScript behavior. | 28 /// special nodes that respect JavaScript behavior. |
15 /// | 29 /// |
16 /// Performs the following rewrites: | 30 /// Performs the following rewrites: |
17 /// - rewrite [IsTrue] in a [Branch] to do boolean conversion. | 31 /// - Rewrite [IsTrue] in a [Branch] to do boolean conversion. |
32 /// - Add explicit receiver argument for methods that are called in interceptor | |
33 /// calling convention. | |
34 /// - Use current or new interceptor at call sites taht use interceptor calling | |
Kevin Millikin (Google)
2015/03/26 15:14:17
'taht' ==> 'that'
| |
35 /// convention. | |
36 /// - Rewrite operator== to test the argument for null. | |
Kevin Millikin (Google)
2015/03/26 15:14:17
'first argument' or 'receiver argument'?
| |
18 class UnsugarVisitor extends RecursiveVisitor { | 37 class UnsugarVisitor extends RecursiveVisitor { |
19 Glue _glue; | 38 Glue _glue; |
20 | 39 |
40 bool inInterceptedMethod = false; | |
41 Parameter thisParameter = null; | |
42 Parameter explicitReceiverParameter = null; | |
43 | |
21 UnsugarVisitor(this._glue); | 44 UnsugarVisitor(this._glue); |
22 | 45 |
23 void rewrite(FunctionDefinition function) { | 46 void rewrite(FunctionDefinition function) { |
47 | |
48 if (function.element.name == '==' && function.parameters.length == 1) { | |
49 // If [functionElement] is `operator==` we explicitely add a null check at | |
50 // the beginning of the method. This is to avoid having call sites do the | |
51 // null check. | |
52 if (_glue.requiresEqNullCheck(function.element)) { | |
53 insertEqNullCheck(function); | |
54 } | |
55 } | |
56 | |
57 // For interceptor calling convention, the receiver is passed as a | |
58 // parameter. | |
59 bool inInterceptedMethod = false; | |
60 if (_glue.isInterceptedMethod(function.element)) { | |
61 inInterceptedMethod = true; | |
62 thisParameter = function.thisParameter; | |
63 explicitReceiverParameter = | |
64 new Parameter( | |
65 new ExplicitReceiverParameterEntity('receiver', | |
66 thisParameter.hint.executableContext)); | |
67 function.parameters.insert(0, explicitReceiverParameter); | |
68 } | |
69 | |
24 // Set all parent pointers. | 70 // Set all parent pointers. |
25 new ParentVisitor().visit(function); | 71 new ParentVisitor().visit(function); |
72 | |
73 // Replace all references to This with the explicit receiver parameter. | |
74 if (inInterceptedMethod) { | |
75 explicitReceiverParameter.substituteFor(thisParameter); | |
76 explicitReceiverParameter.firstRef = thisParameter.firstRef; | |
Kevin Millikin (Google)
2015/03/26 15:14:17
substituteFor will do this (and doing it here is o
| |
77 thisParameter.firstRef = null; | |
Kevin Millikin (Google)
2015/03/26 15:14:17
And we should almost certainly do this in substitu
| |
78 } | |
79 | |
26 visit(function); | 80 visit(function); |
27 } | 81 } |
28 | 82 |
29 @override | 83 @override |
30 visit(Node node) { | 84 visit(Node node) { |
31 Node result = node.accept(this); | 85 Node result = node.accept(this); |
32 return result != null ? result : node; | 86 return result != null ? result : node; |
33 } | 87 } |
34 | 88 |
35 Constant get trueConstant { | 89 Constant makeTrue() { |
Kevin Millikin (Google)
2015/03/26 15:14:17
Oooh, these seem like good utilities to have in cl
| |
36 return new Constant( | 90 return new Constant( |
37 new PrimitiveConstantExpression( | 91 new PrimitiveConstantExpression( |
38 new TrueConstantValue())); | 92 new TrueConstantValue())); |
39 } | 93 } |
40 | 94 |
95 Constant makeFalse() { | |
96 return new Constant( | |
97 new PrimitiveConstantExpression( | |
98 new FalseConstantValue())); | |
99 } | |
100 | |
101 Primitive makeNull() { | |
102 return new Constant( | |
103 new PrimitiveConstantExpression( | |
104 new NullConstantValue())); | |
105 } | |
106 | |
41 void insertLetPrim(Primitive primitive, Expression node) { | 107 void insertLetPrim(Primitive primitive, Expression node) { |
42 LetPrim let = new LetPrim(primitive); | 108 LetPrim let = new LetPrim(primitive); |
43 InteriorNode parent = node.parent; | 109 InteriorNode parent = node.parent; |
44 parent.body = let; | 110 parent.body = let; |
45 let.body = node; | 111 let.body = node; |
46 node.parent = let; | 112 node.parent = let; |
47 let.parent = parent; | 113 let.parent = parent; |
48 } | 114 } |
49 | 115 |
116 void insertEqNullCheck(FunctionDefinition function) { | |
117 // Replace | |
118 // | |
119 // body; | |
120 // | |
121 // with | |
122 // | |
123 // if (identical(arg, null)) | |
124 // return false; | |
125 // else | |
126 // body; | |
127 // | |
128 Continuation originalBody = new Continuation(<Parameter>[]); | |
129 originalBody.body = function.body.body; | |
130 | |
131 Continuation returnFalse = new Continuation(<Parameter>[]); | |
132 Primitive falsePrimitive = makeFalse(); | |
133 returnFalse.body = | |
134 new LetPrim(falsePrimitive, | |
135 new InvokeContinuation( | |
136 function.body.returnContinuation, <Primitive>[falsePrimitive])); | |
137 | |
138 Primitive nullPrimitive = makeNull(); | |
139 Primitive test = new Identical(function.parameters.single, nullPrimitive); | |
140 | |
141 Expression newBody = | |
142 new LetCont.many(<Continuation>[returnFalse, originalBody], | |
143 new LetPrim(nullPrimitive, | |
144 new LetPrim(test, | |
145 new Branch( | |
146 new IsTrue(test), | |
147 returnFalse, | |
148 originalBody)))); | |
149 function.body.body = newBody; | |
150 } | |
151 | |
50 processInvokeMethod(InvokeMethod node) { | 152 processInvokeMethod(InvokeMethod node) { |
51 Selector selector = node.selector; | 153 Selector selector = node.selector; |
52 // TODO(karlklose): should we rewrite all selectors? | 154 // TODO(karlklose): should we rewrite all selectors? sra: No. It would add |
Kevin Millikin (Google)
2015/03/26 15:14:17
I don't understand the question or the answer. Ma
| |
155 // a lot of code. We would need to do so only if we permit native classes | |
156 // to implement noSuchMethod. | |
53 if (!_glue.isInterceptedSelector(selector)) return; | 157 if (!_glue.isInterceptedSelector(selector)) return; |
54 | 158 |
55 Primitive receiver = node.receiver.definition; | 159 Primitive receiver = node.receiver.definition; |
160 | |
161 // If the receiver is the explicit receiver, we are calling a method in the | |
162 // same interceptor. | |
163 if (receiver == explicitReceiverParameter) { | |
164 // receiver.foo() --> this.foo(receiver); | |
165 node.arguments.insert(0, node.receiver); | |
166 node.callingConvention = CallingConvention.JS_INTERCEPTED; | |
167 Reference<Primitive> receiverRef = node.receiver; | |
Kevin Millikin (Google)
2015/03/26 15:14:17
This is unused.
| |
168 node.receiver = new Reference<Primitive>(thisParameter); | |
169 assert(node.isValid); | |
170 return; | |
171 } | |
172 | |
173 // TODO(sra): Move the computation of interceptedClasses to a much later | |
174 // phase and take into account the remaining uses of the interceptor. | |
56 Set<ClassElement> interceptedClasses = | 175 Set<ClassElement> interceptedClasses = |
Kevin Millikin (Google)
2015/03/26 15:14:17
I think there's enough shared code here and above
| |
57 _glue.getInterceptedClassesOn(selector); | 176 _glue.getInterceptedClassesOn(selector); |
58 _glue.registerSpecializedGetInterceptor(interceptedClasses); | 177 _glue.registerSpecializedGetInterceptor(interceptedClasses); |
59 | 178 |
60 Primitive intercepted = new Interceptor(receiver, interceptedClasses); | 179 Primitive intercepted = new Interceptor(receiver, interceptedClasses); |
61 insertLetPrim(intercepted, node); | 180 insertLetPrim(intercepted, node); |
62 node.arguments.insert(0, node.receiver); | 181 node.arguments.insert(0, node.receiver); |
63 node.callingConvention = CallingConvention.JS_INTERCEPTED; | 182 node.callingConvention = CallingConvention.JS_INTERCEPTED; |
183 node.receiver = new Reference<Primitive>(intercepted); | |
64 assert(node.isValid); | 184 assert(node.isValid); |
65 node.receiver = new Reference<Primitive>(intercepted); | |
66 } | |
67 | |
68 Primitive makeNull() { | |
69 NullConstantValue nullConst = new NullConstantValue(); | |
70 return new Constant(new PrimitiveConstantExpression(nullConst)); | |
71 } | 185 } |
72 | 186 |
73 processInvokeMethodDirectly(InvokeMethodDirectly node) { | 187 processInvokeMethodDirectly(InvokeMethodDirectly node) { |
74 if (_glue.isInterceptedMethod(node.target)) { | 188 if (_glue.isInterceptedMethod(node.target)) { |
75 Primitive nullPrim = makeNull(); | 189 Primitive nullPrim = makeNull(); |
76 insertLetPrim(nullPrim, node); | 190 insertLetPrim(nullPrim, node); |
77 node.arguments.insert(0, node.receiver); | 191 node.arguments.insert(0, node.receiver); |
78 node.receiver = new Reference<Primitive>(nullPrim); | 192 node.receiver = new Reference<Primitive>(nullPrim); |
193 | |
194 // TODO(sra): `null` is not adequate. Interceptors project the class | |
195 // hierarchy onto an interceptor hierarchy. A super call that does a | |
196 // method call will use the javascript 'this' parameter to avoid calling | |
197 // getInterceptor again, so the receiver must be the interceptor (likely | |
198 // `this`), not `null`. | |
79 } | 199 } |
80 } | 200 } |
81 | 201 |
82 processBranch(Branch node) { | 202 processBranch(Branch node) { |
83 // TODO(karlklose): implement the checked mode part of boolean conversion. | 203 // TODO(karlklose): implement the checked mode part of boolean conversion. |
84 InteriorNode parent = node.parent; | 204 InteriorNode parent = node.parent; |
85 IsTrue condition = node.condition; | 205 IsTrue condition = node.condition; |
86 Primitive t = trueConstant; | 206 Primitive t = makeTrue(); |
87 Primitive i = new Identical(condition.value.definition, t); | 207 Primitive i = new Identical(condition.value.definition, t); |
88 LetPrim newNode = new LetPrim(t, | 208 LetPrim newNode = |
89 new LetPrim(i, | 209 new LetPrim(t, |
90 new Branch(new IsTrue(i), | 210 new LetPrim(i, |
91 node.trueContinuation.definition, | 211 new Branch( |
92 node.falseContinuation.definition))); | 212 new IsTrue(i), |
213 node.trueContinuation.definition, | |
214 node.falseContinuation.definition))); | |
93 condition.value.unlink(); | 215 condition.value.unlink(); |
94 node.trueContinuation.unlink(); | 216 node.trueContinuation.unlink(); |
95 node.falseContinuation.unlink(); | 217 node.falseContinuation.unlink(); |
96 parent.body = newNode; | 218 parent.body = newNode; |
97 } | 219 } |
98 } | 220 } |
OLD | NEW |