Chromium Code Reviews| 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 |