| 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 Pass; | 5 import '../../cps_ir/optimizers.dart' show 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; | |
| 11 import '../../cps_ir/cps_fragment.dart'; | 10 import '../../cps_ir/cps_fragment.dart'; |
| 12 import '../../common/names.dart'; | 11 import '../../common/names.dart'; |
| 13 | 12 |
| 14 class ExplicitReceiverParameterEntity implements Local { | 13 class ExplicitReceiverParameterEntity implements Local { |
| 15 String get name => 'receiver'; | 14 String get name => 'receiver'; |
| 16 final ExecutableElement executableContext; | 15 final ExecutableElement executableContext; |
| 17 ExplicitReceiverParameterEntity(this.executableContext); | 16 ExplicitReceiverParameterEntity(this.executableContext); |
| 18 toString() => 'ExplicitReceiverParameterEntity($executableContext)'; | 17 toString() => 'ExplicitReceiverParameterEntity($executableContext)'; |
| 19 } | 18 } |
| 20 | 19 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 31 /// special nodes that respect JavaScript behavior. | 30 /// special nodes that respect JavaScript behavior. |
| 32 /// | 31 /// |
| 33 /// Performs the following rewrites: | 32 /// Performs the following rewrites: |
| 34 /// - Add interceptors at call sites that use interceptor calling convention. | 33 /// - Add interceptors at call sites that use interceptor calling convention. |
| 35 /// - Add explicit receiver argument for methods that are called in interceptor | 34 /// - Add explicit receiver argument for methods that are called in interceptor |
| 36 /// calling convention. | 35 /// calling convention. |
| 37 /// - Convert two-parameter exception handlers to one-parameter ones. | 36 /// - Convert two-parameter exception handlers to one-parameter ones. |
| 38 class UnsugarVisitor extends TrampolineRecursiveVisitor implements Pass { | 37 class UnsugarVisitor extends TrampolineRecursiveVisitor implements Pass { |
| 39 Glue _glue; | 38 Glue _glue; |
| 40 | 39 |
| 41 Parameter thisParameter; | 40 FunctionDefinition function; |
| 42 Parameter explicitReceiverParameter; | 41 |
| 42 Parameter get receiverParameter => function.receiverParameter; |
| 43 |
| 44 /// The interceptor of the receiver. For some methods, this is the receiver |
| 45 /// itself, for others, it is the interceptor parameter. |
| 46 Parameter receiverInterceptor; |
| 43 | 47 |
| 44 // In a catch block, rethrow implicitly throws the block's exception | 48 // In a catch block, rethrow implicitly throws the block's exception |
| 45 // parameter. This is the exception parameter when nested in a catch | 49 // parameter. This is the exception parameter when nested in a catch |
| 46 // block and null otherwise. | 50 // block and null otherwise. |
| 47 Parameter _exceptionParameter = null; | 51 Parameter _exceptionParameter = null; |
| 48 | 52 |
| 49 UnsugarVisitor(this._glue); | 53 UnsugarVisitor(this._glue); |
| 50 | 54 |
| 51 String get passName => 'Unsugaring'; | 55 String get passName => 'Unsugaring'; |
| 52 | 56 |
| 53 bool methodUsesReceiverArgument(FunctionElement function) { | |
| 54 assert(_glue.isInterceptedMethod(function)); | |
| 55 ClassElement clazz = function.enclosingClass.declaration; | |
| 56 return _glue.isInterceptorClass(clazz) || | |
| 57 _glue.isUsedAsMixin(clazz); | |
| 58 } | |
| 59 | |
| 60 void rewrite(FunctionDefinition function) { | 57 void rewrite(FunctionDefinition function) { |
| 61 thisParameter = function.thisParameter; | 58 this.function = function; |
| 62 bool inInterceptedMethod = _glue.isInterceptedMethod(function.element); | 59 bool inInterceptedMethod = _glue.isInterceptedMethod(function.element); |
| 63 | 60 |
| 64 if (function.element.name == '==' && | 61 if (function.element.name == '==' && |
| 65 function.parameters.length == 1 && | 62 function.parameters.length == 1 && |
| 66 !_glue.operatorEqHandlesNullArgument(function.element)) { | 63 !_glue.operatorEqHandlesNullArgument(function.element)) { |
| 67 // Insert the null check that the language semantics requires us to | 64 // Insert the null check that the language semantics requires us to |
| 68 // perform before calling operator ==. | 65 // perform before calling operator ==. |
| 69 insertEqNullCheck(function); | 66 insertEqNullCheck(function); |
| 70 } | 67 } |
| 71 | 68 |
| 72 if (inInterceptedMethod) { | 69 if (inInterceptedMethod) { |
| 73 ThisParameterLocal holder = thisParameter.hint; | 70 function.interceptorParameter = new Parameter(null)..parent = function; |
| 74 explicitReceiverParameter = new Parameter( | 71 // Since the receiver won't be compiled to "this", set a hint on it |
| 75 new ExplicitReceiverParameterEntity(holder.executableContext)); | 72 // so the parameter gets a meaningful name. |
| 76 explicitReceiverParameter.parent = function; | 73 function.receiverParameter.hint = |
| 77 function.parameters.insert(0, explicitReceiverParameter); | 74 new ExplicitReceiverParameterEntity(function.element); |
| 78 } | 75 // If we need an interceptor for the receiver, use the receiver itself |
| 79 | 76 // if possible, otherwise the interceptor argument. |
| 80 if (inInterceptedMethod && methodUsesReceiverArgument(function.element)) { | 77 receiverInterceptor = _glue.methodUsesReceiverArgument(function.element) |
| 81 thisParameter.replaceUsesWith(explicitReceiverParameter); | 78 ? function.interceptorParameter |
| 79 : receiverParameter; |
| 82 } | 80 } |
| 83 | 81 |
| 84 visit(function); | 82 visit(function); |
| 85 } | 83 } |
| 86 | 84 |
| 87 Constant get trueConstant { | 85 Constant get trueConstant { |
| 88 return new Constant(new TrueConstantValue()); | 86 return new Constant(new TrueConstantValue()); |
| 89 } | 87 } |
| 90 | 88 |
| 91 Constant get falseConstant { | 89 Constant get falseConstant { |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 204 node.argumentRefs.length == 1 && | 202 node.argumentRefs.length == 1 && |
| 205 isNullConstant(node.argument(0))) { | 203 isNullConstant(node.argument(0))) { |
| 206 node.replaceWith(new ApplyBuiltinOperator( | 204 node.replaceWith(new ApplyBuiltinOperator( |
| 207 BuiltinOperator.Identical, | 205 BuiltinOperator.Identical, |
| 208 [node.receiver, node.argument(0)], | 206 [node.receiver, node.argument(0)], |
| 209 node.sourceInformation)); | 207 node.sourceInformation)); |
| 210 return; | 208 return; |
| 211 } | 209 } |
| 212 | 210 |
| 213 Primitive receiver = node.receiver; | 211 Primitive receiver = node.receiver; |
| 214 Primitive newReceiver; | 212 Primitive interceptor; |
| 215 | 213 |
| 216 if (receiver == explicitReceiverParameter) { | 214 if (receiver == receiverParameter && receiverInterceptor != null) { |
| 217 // If the receiver is the explicit receiver, we are calling a method in | 215 // TODO(asgerf): This could be done by GVN. |
| 216 // If the receiver is 'this', we are calling a method in |
| 218 // the same interceptor: | 217 // the same interceptor: |
| 219 // Change 'receiver.foo()' to 'this.foo(receiver)'. | 218 // Change 'receiver.foo()' to 'this.foo(receiver)'. |
| 220 newReceiver = thisParameter; | 219 interceptor = receiverInterceptor; |
| 221 } else { | 220 } else { |
| 222 newReceiver = new Interceptor(receiver, node.sourceInformation); | 221 interceptor = new Interceptor(receiver, node.sourceInformation); |
| 223 if (receiver.hint != null) { | 222 if (receiver.hint != null) { |
| 224 newReceiver.hint = new InterceptorEntity(receiver.hint); | 223 interceptor.hint = new InterceptorEntity(receiver.hint); |
| 225 } | 224 } |
| 226 new LetPrim(newReceiver).insertAbove(node.parent); | 225 new LetPrim(interceptor).insertAbove(node.parent); |
| 227 } | 226 } |
| 228 node.argumentRefs.insert(0, node.receiverRef); | 227 assert(node.interceptorRef == null); |
| 229 node.receiverRef = new Reference<Primitive>(newReceiver)..parent = node; | 228 node.makeIntercepted(interceptor); |
| 230 node.callingConvention = CallingConvention.Intercepted; | |
| 231 } | 229 } |
| 232 | 230 |
| 233 processInvokeMethodDirectly(InvokeMethodDirectly node) { | 231 processInvokeMethodDirectly(InvokeMethodDirectly node) { |
| 234 if (!_glue.isInterceptedMethod(node.target)) return; | 232 if (!_glue.isInterceptedMethod(node.target)) return; |
| 235 | 233 |
| 236 Primitive receiver = node.receiver; | 234 Primitive receiver = node.receiver; |
| 237 Primitive newReceiver; | 235 Primitive interceptor; |
| 238 | 236 |
| 239 if (receiver == explicitReceiverParameter) { | 237 if (receiver == receiverParameter && receiverInterceptor != null) { |
| 240 // If the receiver is the explicit receiver, we are calling a method in | 238 // If the receiver is 'this', we are calling a method in |
| 241 // the same interceptor: | 239 // the same interceptor: |
| 242 // Change 'receiver.foo()' to 'this.foo(receiver)'. | 240 // Change 'receiver.foo()' to 'this.foo(receiver)'. |
| 243 newReceiver = thisParameter; | 241 interceptor = receiverInterceptor; |
| 244 } else { | 242 } else { |
| 245 newReceiver = new Interceptor(receiver, node.sourceInformation); | 243 interceptor = new Interceptor(receiver, node.sourceInformation); |
| 246 if (receiver.hint != null) { | 244 if (receiver.hint != null) { |
| 247 newReceiver.hint = new InterceptorEntity(receiver.hint); | 245 interceptor.hint = new InterceptorEntity(receiver.hint); |
| 248 } | 246 } |
| 249 new LetPrim(newReceiver).insertAbove(node.parent); | 247 new LetPrim(interceptor).insertAbove(node.parent); |
| 250 } | 248 } |
| 251 node.argumentRefs.insert(0, node.receiverRef); | 249 assert(node.interceptorRef == null); |
| 252 node.receiverRef = new Reference<Primitive>(newReceiver)..parent = node; | 250 node.makeIntercepted(interceptor); |
| 253 node.callingConvention = CallingConvention.Intercepted; | |
| 254 } | 251 } |
| 255 } | 252 } |
| OLD | NEW |