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