| 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 import '../../cps_ir/optimizers.dart' show ParentVisitor, Pass; | 5 import '../../cps_ir/optimizers.dart' show ParentVisitor, Pass; |
| 6 import '../../constants/values.dart'; | 6 import '../../constants/values.dart'; |
| 7 import '../../elements/elements.dart'; | 7 import '../../elements/elements.dart'; |
| 8 import '../../js_backend/codegen/glue.dart'; | 8 import '../../js_backend/codegen/glue.dart'; |
| 9 import '../../universe/selector.dart' show Selector; | 9 import '../../universe/selector.dart' show Selector; |
| 10 import '../../cps_ir/cps_ir_builder.dart' show ThisParameterLocal; | 10 import '../../cps_ir/cps_ir_builder.dart' show ThisParameterLocal; |
| 11 import '../../cps_ir/cps_fragment.dart'; |
| 11 | 12 |
| 12 class ExplicitReceiverParameterEntity implements Local { | 13 class ExplicitReceiverParameterEntity implements Local { |
| 13 String get name => 'receiver'; | 14 String get name => 'receiver'; |
| 14 final ExecutableElement executableContext; | 15 final ExecutableElement executableContext; |
| 15 ExplicitReceiverParameterEntity(this.executableContext); | 16 ExplicitReceiverParameterEntity(this.executableContext); |
| 16 toString() => 'ExplicitReceiverParameterEntity($executableContext)'; | 17 toString() => 'ExplicitReceiverParameterEntity($executableContext)'; |
| 17 } | 18 } |
| 18 | 19 |
| 19 /// Suggested name for an interceptor. | 20 /// Suggested name for an interceptor. |
| 20 class InterceptorEntity extends Entity { | 21 class InterceptorEntity extends Entity { |
| 21 Entity interceptedVariable; | 22 Entity interceptedVariable; |
| 22 | 23 |
| 23 InterceptorEntity(this.interceptedVariable); | 24 InterceptorEntity(this.interceptedVariable); |
| 24 | 25 |
| 25 String get name => interceptedVariable.name + '_'; | 26 String get name => interceptedVariable.name + '_'; |
| 26 } | 27 } |
| 27 | 28 |
| 28 | |
| 29 /// Rewrites the initial CPS IR to make Dart semantics explicit and inserts | 29 /// Rewrites the initial CPS IR to make Dart semantics explicit and inserts |
| 30 /// special nodes that respect JavaScript behavior. | 30 /// special nodes that respect JavaScript behavior. |
| 31 /// | 31 /// |
| 32 /// Performs the following rewrites: | 32 /// Performs the following rewrites: |
| 33 /// - Add interceptors at call sites that use interceptor calling convention. | 33 /// - Add interceptors at call sites that use interceptor calling convention. |
| 34 /// - Add explicit receiver argument for methods that are called in interceptor | 34 /// - Add explicit receiver argument for methods that are called in interceptor |
| 35 /// calling convention. | 35 /// calling convention. |
| 36 /// - Convert two-parameter exception handlers to one-parameter ones. | 36 /// - Convert two-parameter exception handlers to one-parameter ones. |
| 37 class UnsugarVisitor extends RecursiveVisitor implements Pass { | 37 class UnsugarVisitor extends TrampolineRecursiveVisitor implements Pass { |
| 38 Glue _glue; | 38 Glue _glue; |
| 39 ParentVisitor _parentVisitor = new ParentVisitor(); | |
| 40 | 39 |
| 41 Parameter thisParameter; | 40 Parameter thisParameter; |
| 42 Parameter explicitReceiverParameter; | 41 Parameter explicitReceiverParameter; |
| 43 | 42 |
| 44 // In a catch block, rethrow implicitly throws the block's exception | 43 // In a catch block, rethrow implicitly throws the block's exception |
| 45 // parameter. This is the exception parameter when nested in a catch | 44 // parameter. This is the exception parameter when nested in a catch |
| 46 // block and null otherwise. | 45 // block and null otherwise. |
| 47 Parameter _exceptionParameter = null; | 46 Parameter _exceptionParameter = null; |
| 48 | 47 |
| 49 UnsugarVisitor(this._glue); | 48 UnsugarVisitor(this._glue); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 65 function.parameters.length == 1 && | 64 function.parameters.length == 1 && |
| 66 !_glue.operatorEqHandlesNullArgument(function.element)) { | 65 !_glue.operatorEqHandlesNullArgument(function.element)) { |
| 67 // Insert the null check that the language semantics requires us to | 66 // Insert the null check that the language semantics requires us to |
| 68 // perform before calling operator ==. | 67 // perform before calling operator ==. |
| 69 insertEqNullCheck(function); | 68 insertEqNullCheck(function); |
| 70 } | 69 } |
| 71 | 70 |
| 72 if (inInterceptedMethod) { | 71 if (inInterceptedMethod) { |
| 73 ThisParameterLocal holder = thisParameter.hint; | 72 ThisParameterLocal holder = thisParameter.hint; |
| 74 explicitReceiverParameter = new Parameter( | 73 explicitReceiverParameter = new Parameter( |
| 75 new ExplicitReceiverParameterEntity( | 74 new ExplicitReceiverParameterEntity(holder.executableContext)); |
| 76 holder.executableContext)); | 75 explicitReceiverParameter.parent = function; |
| 77 function.parameters.insert(0, explicitReceiverParameter); | 76 function.parameters.insert(0, explicitReceiverParameter); |
| 78 } | 77 } |
| 79 | 78 |
| 80 // Set all parent pointers. | |
| 81 _parentVisitor.visit(function); | |
| 82 | |
| 83 if (inInterceptedMethod && methodUsesReceiverArgument(function.element)) { | 79 if (inInterceptedMethod && methodUsesReceiverArgument(function.element)) { |
| 84 explicitReceiverParameter.substituteFor(thisParameter); | 80 explicitReceiverParameter.substituteFor(thisParameter); |
| 85 } | 81 } |
| 86 | 82 |
| 87 visit(function); | 83 visit(function); |
| 88 } | 84 } |
| 89 | 85 |
| 90 Constant get trueConstant { | 86 Constant get trueConstant { |
| 91 return new Constant(new TrueConstantValue()); | 87 return new Constant(new TrueConstantValue()); |
| 92 } | 88 } |
| 93 | 89 |
| 94 Constant get falseConstant { | 90 Constant get falseConstant { |
| 95 return new Constant(new FalseConstantValue()); | 91 return new Constant(new FalseConstantValue()); |
| 96 } | 92 } |
| 97 | 93 |
| 98 Constant get nullConstant { | 94 Constant get nullConstant { |
| 99 return new Constant(new NullConstantValue()); | 95 return new Constant(new NullConstantValue()); |
| 100 } | 96 } |
| 101 | 97 |
| 102 void insertLetPrim(Primitive primitive, Expression node) { | 98 void insertLetPrim(Primitive primitive, Expression node) { |
| 103 LetPrim let = new LetPrim(primitive); | 99 LetPrim let = new LetPrim(primitive); |
| 104 InteriorNode parent = node.parent; | 100 let.insertAbove(node); |
| 105 parent.body = let; | |
| 106 let.body = node; | |
| 107 node.parent = let; | |
| 108 let.parent = parent; | |
| 109 } | 101 } |
| 110 | 102 |
| 111 void insertEqNullCheck(FunctionDefinition function) { | 103 void insertEqNullCheck(FunctionDefinition function) { |
| 112 // Replace | 104 // Replace |
| 113 // | 105 // |
| 114 // body; | 106 // body; |
| 115 // | 107 // |
| 116 // with | 108 // with |
| 117 // | 109 // |
| 118 // if (identical(arg, null)) | 110 // if (identical(arg, null)) |
| 119 // return false; | 111 // return false; |
| 120 // else | 112 // else |
| 121 // body; | 113 // body; |
| 122 // | 114 // |
| 123 Continuation originalBody = new Continuation(<Parameter>[]); | 115 CpsFragment cps = new CpsFragment(); |
| 124 originalBody.body = function.body; | 116 Primitive isNull = cps.applyBuiltin( |
| 125 | |
| 126 Continuation returnFalse = new Continuation(<Parameter>[]); | |
| 127 Primitive falsePrimitive = falseConstant; | |
| 128 returnFalse.body = | |
| 129 new LetPrim(falsePrimitive, | |
| 130 new InvokeContinuation( | |
| 131 function.returnContinuation, <Primitive>[falsePrimitive])); | |
| 132 | |
| 133 Primitive nullPrimitive = nullConstant; | |
| 134 Primitive test = new ApplyBuiltinOperator( | |
| 135 BuiltinOperator.Identical, | 117 BuiltinOperator.Identical, |
| 136 <Primitive>[function.parameters.single, nullPrimitive], | 118 <Primitive>[function.parameters.single, cps.makeNull()]); |
| 137 function.parameters.single.sourceInformation); | 119 CpsFragment trueBranch = cps.ifTruthy(isNull); |
| 138 | 120 trueBranch.invokeContinuation(function.returnContinuation, |
| 139 Expression newBody = | 121 <Primitive>[trueBranch.makeFalse()]); |
| 140 new LetCont.many(<Continuation>[returnFalse, originalBody], | 122 cps.insertAbove(function.body); |
| 141 new LetPrim(nullPrimitive, | |
| 142 new LetPrim(test, | |
| 143 new Branch.loose(test, returnFalse, originalBody)))); | |
| 144 function.body = newBody; | |
| 145 } | 123 } |
| 146 | 124 |
| 147 /// Insert a static call to [function] at the point of [node] with result | 125 /// Insert a static call to [function] at the point of [node] with result |
| 148 /// [result]. | 126 /// [result]. |
| 149 /// | 127 /// |
| 150 /// Rewrite [node] to | 128 /// Rewrite [node] to |
| 151 /// | 129 /// |
| 152 /// let cont continuation(result) = node | 130 /// let cont continuation(result) = node |
| 153 /// in invoke function arguments continuation | 131 /// in invoke function arguments continuation |
| 154 void insertStaticCall(FunctionElement function, List<Primitive> arguments, | 132 void insertStaticCall(FunctionElement function, List<Primitive> arguments, |
| 155 Parameter result, | 133 Parameter result, Expression node) { |
| 156 Expression node) { | |
| 157 InteriorNode parent = node.parent; | 134 InteriorNode parent = node.parent; |
| 158 Continuation continuation = new Continuation([result]); | 135 Continuation continuation = new Continuation([result]); |
| 159 continuation.body = node; | |
| 160 _parentVisitor.processContinuation(continuation); | |
| 161 | 136 |
| 162 Selector selector = new Selector.fromElement(function); | 137 Selector selector = new Selector.fromElement(function); |
| 163 // TODO(johnniwinther): Come up with an implementation of SourceInformation | 138 // TODO(johnniwinther): Come up with an implementation of SourceInformation |
| 164 // for calls such as this one that don't appear in the original source. | 139 // for calls such as this one that don't appear in the original source. |
| 165 InvokeStatic invoke = new InvokeStatic( | 140 InvokeStatic invoke = new InvokeStatic( |
| 166 function, selector, arguments, continuation, null); | 141 function, selector, arguments, continuation, null); |
| 167 _parentVisitor.processInvokeStatic(invoke); | |
| 168 | 142 |
| 169 LetCont letCont = new LetCont(continuation, invoke); | 143 LetCont letCont = new LetCont(continuation, invoke); |
| 170 _parentVisitor.processLetCont(letCont); | |
| 171 | 144 |
| 172 parent.body = letCont; | 145 parent.body = letCont; |
| 173 letCont.parent = parent; | 146 letCont.parent = parent; |
| 147 continuation.body = node; |
| 148 node.parent = continuation; |
| 174 } | 149 } |
| 175 | 150 |
| 176 @override | 151 @override |
| 177 Expression traverseLetHandler(LetHandler node) { | 152 Expression traverseLetHandler(LetHandler node) { |
| 178 assert(node.handler.parameters.length == 2); | 153 assert(node.handler.parameters.length == 2); |
| 179 Parameter previousExceptionParameter = _exceptionParameter; | 154 Parameter previousExceptionParameter = _exceptionParameter; |
| 180 | 155 |
| 181 // BEFORE: Handlers have two parameters, exception and stack trace. | 156 // BEFORE: Handlers have two parameters, exception and stack trace. |
| 182 // AFTER: Handlers have a single parameter, which is unwrapped to get | 157 // AFTER: Handlers have a single parameter, which is unwrapped to get |
| 183 // the exception and stack trace. | 158 // the exception and stack trace. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 203 node.handler.parameters.removeLast(); | 178 node.handler.parameters.removeLast(); |
| 204 | 179 |
| 205 visit(node.handler); | 180 visit(node.handler); |
| 206 _exceptionParameter = previousExceptionParameter; | 181 _exceptionParameter = previousExceptionParameter; |
| 207 | 182 |
| 208 return node.body; | 183 return node.body; |
| 209 } | 184 } |
| 210 | 185 |
| 211 processThrow(Throw node) { | 186 processThrow(Throw node) { |
| 212 // The subexpression of throw is wrapped in the JavaScript output. | 187 // The subexpression of throw is wrapped in the JavaScript output. |
| 213 Parameter value = new Parameter(null); | 188 Parameter wrappedException = new Parameter(null); |
| 214 insertStaticCall(_glue.getWrapExceptionHelper(), [node.value.definition], | 189 insertStaticCall(_glue.getWrapExceptionHelper(), [node.value.definition], |
| 215 value, node); | 190 wrappedException, node); |
| 216 node.value.unlink(); | 191 node.value.changeTo(wrappedException); |
| 217 node.value = new Reference<Primitive>(value); | |
| 218 } | 192 } |
| 219 | 193 |
| 220 processRethrow(Rethrow node) { | 194 processRethrow(Rethrow node) { |
| 221 // Rethrow can only appear in a catch block. It throws that block's | 195 // Rethrow can only appear in a catch block. It throws that block's |
| 222 // (wrapped) caught exception. | 196 // (wrapped) caught exception. |
| 223 Throw replacement = new Throw(_exceptionParameter); | 197 Throw replacement = new Throw(_exceptionParameter); |
| 224 InteriorNode parent = node.parent; | 198 InteriorNode parent = node.parent; |
| 225 parent.body = replacement; | 199 parent.body = replacement; |
| 226 replacement.parent = parent; | 200 replacement.parent = parent; |
| 227 // The original rethrow does not have any references that we need to | 201 // The original rethrow does not have any references that we need to |
| (...skipping 27 matching lines...) Expand all Loading... |
| 255 } else { | 229 } else { |
| 256 LetCont contBinding = node.parent; | 230 LetCont contBinding = node.parent; |
| 257 newReceiver = new Interceptor(receiver, node.sourceInformation) | 231 newReceiver = new Interceptor(receiver, node.sourceInformation) |
| 258 ..interceptedClasses.addAll(_glue.getInterceptedClassesOn(selector)); | 232 ..interceptedClasses.addAll(_glue.getInterceptedClassesOn(selector)); |
| 259 if (receiver.hint != null) { | 233 if (receiver.hint != null) { |
| 260 newReceiver.hint = new InterceptorEntity(receiver.hint); | 234 newReceiver.hint = new InterceptorEntity(receiver.hint); |
| 261 } | 235 } |
| 262 insertLetPrim(newReceiver, contBinding); | 236 insertLetPrim(newReceiver, contBinding); |
| 263 } | 237 } |
| 264 node.arguments.insert(0, node.receiver); | 238 node.arguments.insert(0, node.receiver); |
| 265 node.receiver = new Reference<Primitive>(newReceiver); | 239 node.receiver = new Reference<Primitive>(newReceiver)..parent = node; |
| 266 node.receiverIsIntercepted = true; | 240 node.receiverIsIntercepted = true; |
| 267 } | 241 } |
| 268 | 242 |
| 269 processInvokeMethodDirectly(InvokeMethodDirectly node) { | 243 processInvokeMethodDirectly(InvokeMethodDirectly node) { |
| 270 if (!_glue.isInterceptedMethod(node.target)) return; | 244 if (!_glue.isInterceptedMethod(node.target)) return; |
| 271 | 245 |
| 272 Selector selector = node.selector; | 246 Selector selector = node.selector; |
| 273 Primitive receiver = node.receiver.definition; | 247 Primitive receiver = node.receiver.definition; |
| 274 Primitive newReceiver; | 248 Primitive newReceiver; |
| 275 | 249 |
| 276 if (receiver == explicitReceiverParameter) { | 250 if (receiver == explicitReceiverParameter) { |
| 277 // If the receiver is the explicit receiver, we are calling a method in | 251 // If the receiver is the explicit receiver, we are calling a method in |
| 278 // the same interceptor: | 252 // the same interceptor: |
| 279 // Change 'receiver.foo()' to 'this.foo(receiver)'. | 253 // Change 'receiver.foo()' to 'this.foo(receiver)'. |
| 280 newReceiver = thisParameter; | 254 newReceiver = thisParameter; |
| 281 } else { | 255 } else { |
| 282 LetCont contBinding = node.parent; | 256 LetCont contBinding = node.parent; |
| 283 newReceiver = new Interceptor(receiver, node.sourceInformation) | 257 newReceiver = new Interceptor(receiver, node.sourceInformation) |
| 284 ..interceptedClasses.addAll(_glue.getInterceptedClassesOn(selector)); | 258 ..interceptedClasses.addAll(_glue.getInterceptedClassesOn(selector)); |
| 285 if (receiver.hint != null) { | 259 if (receiver.hint != null) { |
| 286 newReceiver.hint = new InterceptorEntity(receiver.hint); | 260 newReceiver.hint = new InterceptorEntity(receiver.hint); |
| 287 } | 261 } |
| 288 insertLetPrim(newReceiver, contBinding); | 262 insertLetPrim(newReceiver, contBinding); |
| 289 } | 263 } |
| 290 node.arguments.insert(0, node.receiver); | 264 node.arguments.insert(0, node.receiver); |
| 291 node.receiver = new Reference<Primitive>(newReceiver); | 265 node.receiver = new Reference<Primitive>(newReceiver)..parent = node; |
| 292 } | 266 } |
| 293 | 267 |
| 294 processInterceptor(Interceptor node) { | 268 processInterceptor(Interceptor node) { |
| 295 _glue.registerSpecializedGetInterceptor(node.interceptedClasses); | 269 _glue.registerSpecializedGetInterceptor(node.interceptedClasses); |
| 296 } | 270 } |
| 297 } | 271 } |
| OLD | NEW |