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' | 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 |