| OLD | NEW |
| (Empty) |
| 1 library dart2js.unsugar_cps; | |
| 2 | |
| 3 import '../../common/names.dart'; | |
| 4 import '../../constants/values.dart'; | |
| 5 import '../../cps_ir/cps_fragment.dart'; | |
| 6 import '../../cps_ir/cps_ir_nodes.dart'; | |
| 7 import '../../cps_ir/optimizers.dart' show Pass; | |
| 8 import '../../elements/elements.dart'; | |
| 9 import '../../js_backend/codegen/glue.dart'; | |
| 10 import '../../universe/selector.dart' show Selector; | |
| 11 | |
| 12 class ExplicitReceiverParameterEntity implements Local { | |
| 13 String get name => 'receiver'; | |
| 14 final ExecutableElement executableContext; | |
| 15 ExplicitReceiverParameterEntity(this.executableContext); | |
| 16 toString() => 'ExplicitReceiverParameterEntity($executableContext)'; | |
| 17 } | |
| 18 | |
| 19 /// Suggested name for an interceptor. | |
| 20 class InterceptorEntity extends Entity { | |
| 21 Entity interceptedVariable; | |
| 22 | |
| 23 InterceptorEntity(this.interceptedVariable); | |
| 24 | |
| 25 String get name => interceptedVariable.name + '_'; | |
| 26 } | |
| 27 | |
| 28 /// Rewrites the initial CPS IR to make Dart semantics explicit and inserts | |
| 29 /// special nodes that respect JavaScript behavior. | |
| 30 /// | |
| 31 /// Performs the following rewrites: | |
| 32 /// - Add interceptors at call sites that use interceptor calling convention. | |
| 33 /// - Add explicit receiver argument for methods that are called in interceptor | |
| 34 /// calling convention. | |
| 35 /// - Convert two-parameter exception handlers to one-parameter ones. | |
| 36 class UnsugarVisitor extends TrampolineRecursiveVisitor implements Pass { | |
| 37 Glue _glue; | |
| 38 | |
| 39 FunctionDefinition function; | |
| 40 | |
| 41 Parameter get receiverParameter => function.receiverParameter; | |
| 42 | |
| 43 /// The interceptor of the receiver. For some methods, this is the receiver | |
| 44 /// itself, for others, it is the interceptor parameter. | |
| 45 Parameter receiverInterceptor; | |
| 46 | |
| 47 // In a catch block, rethrow implicitly throws the block's exception | |
| 48 // parameter. This is the exception parameter when nested in a catch | |
| 49 // block and null otherwise. | |
| 50 Parameter _exceptionParameter = null; | |
| 51 | |
| 52 UnsugarVisitor(this._glue); | |
| 53 | |
| 54 String get passName => 'Unsugaring'; | |
| 55 | |
| 56 void rewrite(FunctionDefinition function) { | |
| 57 this.function = function; | |
| 58 bool inInterceptedMethod = _glue.isInterceptedMethod(function.element); | |
| 59 | |
| 60 if (function.element.name == '==' && | |
| 61 function.parameters.length == 1 && | |
| 62 !_glue.operatorEqHandlesNullArgument(function.element)) { | |
| 63 // Insert the null check that the language semantics requires us to | |
| 64 // perform before calling operator ==. | |
| 65 insertEqNullCheck(function); | |
| 66 } | |
| 67 | |
| 68 if (inInterceptedMethod) { | |
| 69 function.interceptorParameter = new Parameter(null)..parent = function; | |
| 70 // Since the receiver won't be compiled to "this", set a hint on it | |
| 71 // so the parameter gets a meaningful name. | |
| 72 function.receiverParameter.hint = | |
| 73 new ExplicitReceiverParameterEntity(function.element); | |
| 74 // If we need an interceptor for the receiver, use the receiver itself | |
| 75 // if possible, otherwise the interceptor argument. | |
| 76 receiverInterceptor = _glue.methodUsesReceiverArgument(function.element) | |
| 77 ? function.interceptorParameter | |
| 78 : receiverParameter; | |
| 79 } | |
| 80 | |
| 81 visit(function); | |
| 82 } | |
| 83 | |
| 84 Constant get trueConstant { | |
| 85 return new Constant(new TrueConstantValue()); | |
| 86 } | |
| 87 | |
| 88 Constant get falseConstant { | |
| 89 return new Constant(new FalseConstantValue()); | |
| 90 } | |
| 91 | |
| 92 Constant get nullConstant { | |
| 93 return new Constant(new NullConstantValue()); | |
| 94 } | |
| 95 | |
| 96 void insertEqNullCheck(FunctionDefinition function) { | |
| 97 // Replace | |
| 98 // | |
| 99 // body; | |
| 100 // | |
| 101 // with | |
| 102 // | |
| 103 // if (identical(arg, null)) | |
| 104 // return false; | |
| 105 // else | |
| 106 // body; | |
| 107 // | |
| 108 CpsFragment cps = new CpsFragment(); | |
| 109 Primitive isNull = cps.applyBuiltin(BuiltinOperator.Identical, | |
| 110 <Primitive>[function.parameters.single, cps.makeNull()]); | |
| 111 CpsFragment trueBranch = cps.ifTruthy(isNull); | |
| 112 trueBranch.invokeContinuation( | |
| 113 function.returnContinuation, <Primitive>[trueBranch.makeFalse()]); | |
| 114 cps.insertAbove(function.body); | |
| 115 } | |
| 116 | |
| 117 /// Insert a static call to [function] immediately above [node]. | |
| 118 Primitive insertStaticCallAbove( | |
| 119 FunctionElement function, List<Primitive> arguments, Expression node) { | |
| 120 // TODO(johnniwinther): Come up with an implementation of SourceInformation | |
| 121 // for calls such as this one that don't appear in the original source. | |
| 122 InvokeStatic invoke = new InvokeStatic( | |
| 123 function, new Selector.fromElement(function), arguments, null); | |
| 124 new LetPrim(invoke).insertAbove(node); | |
| 125 return invoke; | |
| 126 } | |
| 127 | |
| 128 @override | |
| 129 Expression traverseLetHandler(LetHandler node) { | |
| 130 assert(node.handler.parameters.length == 2); | |
| 131 Parameter previousExceptionParameter = _exceptionParameter; | |
| 132 | |
| 133 // BEFORE: Handlers have two parameters, exception and stack trace. | |
| 134 // AFTER: Handlers have a single parameter, which is unwrapped to get | |
| 135 // the exception and stack trace. | |
| 136 _exceptionParameter = node.handler.parameters.first; | |
| 137 Parameter stackTraceParameter = node.handler.parameters.last; | |
| 138 Expression body = node.handler.body; | |
| 139 if (_exceptionParameter.hasAtLeastOneUse || | |
| 140 stackTraceParameter.hasAtLeastOneUse) { | |
| 141 InvokeStatic unwrapped = insertStaticCallAbove( | |
| 142 _glue.getExceptionUnwrapper(), | |
| 143 [new Parameter(null)], // Dummy argument, see below. | |
| 144 body); | |
| 145 _exceptionParameter.replaceUsesWith(unwrapped); | |
| 146 | |
| 147 // Replace the dummy with the exception parameter. It must be set after | |
| 148 // replacing all uses of [_exceptionParameter]. | |
| 149 unwrapped.argumentRefs[0].changeTo(_exceptionParameter); | |
| 150 | |
| 151 if (stackTraceParameter.hasAtLeastOneUse) { | |
| 152 InvokeStatic stackTraceValue = insertStaticCallAbove( | |
| 153 _glue.getTraceFromException(), [_exceptionParameter], body); | |
| 154 stackTraceParameter.replaceUsesWith(stackTraceValue); | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 assert(stackTraceParameter.hasNoUses); | |
| 159 node.handler.parameters.removeLast(); | |
| 160 | |
| 161 visit(node.handler); | |
| 162 _exceptionParameter = previousExceptionParameter; | |
| 163 | |
| 164 return node.body; | |
| 165 } | |
| 166 | |
| 167 processThrow(Throw node) { | |
| 168 // The subexpression of throw is wrapped in the JavaScript output. | |
| 169 Primitive wrappedException = insertStaticCallAbove( | |
| 170 _glue.getWrapExceptionHelper(), [node.value], node); | |
| 171 node.valueRef.changeTo(wrappedException); | |
| 172 } | |
| 173 | |
| 174 processRethrow(Rethrow node) { | |
| 175 // Rethrow can only appear in a catch block. It throws that block's | |
| 176 // (wrapped) caught exception. | |
| 177 Throw replacement = new Throw(_exceptionParameter); | |
| 178 InteriorNode parent = node.parent; | |
| 179 parent.body = replacement; | |
| 180 replacement.parent = parent; | |
| 181 // The original rethrow does not have any references that we need to | |
| 182 // worry about unlinking. | |
| 183 } | |
| 184 | |
| 185 bool isNullConstant(Primitive prim) { | |
| 186 return prim is Constant && prim.value.isNull; | |
| 187 } | |
| 188 | |
| 189 processInvokeMethod(InvokeMethod node) { | |
| 190 Selector selector = node.selector; | |
| 191 if (!_glue.isInterceptedSelector(selector)) return; | |
| 192 | |
| 193 // Some platform libraries will compare non-interceptable objects against | |
| 194 // null using the Dart == operator. These must be translated directly. | |
| 195 if (node.selector == Selectors.equals && | |
| 196 node.argumentRefs.length == 1 && | |
| 197 isNullConstant(node.argument(0))) { | |
| 198 node.replaceWith(new ApplyBuiltinOperator(BuiltinOperator.Identical, | |
| 199 [node.receiver, node.argument(0)], node.sourceInformation)); | |
| 200 return; | |
| 201 } | |
| 202 | |
| 203 Primitive receiver = node.receiver; | |
| 204 Primitive interceptor; | |
| 205 | |
| 206 if (receiver == receiverParameter && receiverInterceptor != null) { | |
| 207 // TODO(asgerf): This could be done by GVN. | |
| 208 // If the receiver is 'this', we are calling a method in | |
| 209 // the same interceptor: | |
| 210 // Change 'receiver.foo()' to 'this.foo(receiver)'. | |
| 211 interceptor = receiverInterceptor; | |
| 212 } else { | |
| 213 interceptor = new Interceptor(receiver, node.sourceInformation); | |
| 214 if (receiver.hint != null) { | |
| 215 interceptor.hint = new InterceptorEntity(receiver.hint); | |
| 216 } | |
| 217 new LetPrim(interceptor).insertAbove(node.parent); | |
| 218 } | |
| 219 assert(node.interceptorRef == null); | |
| 220 node.makeIntercepted(interceptor); | |
| 221 } | |
| 222 | |
| 223 processInvokeMethodDirectly(InvokeMethodDirectly node) { | |
| 224 if (!_glue.isInterceptedMethod(node.target)) return; | |
| 225 | |
| 226 Primitive receiver = node.receiver; | |
| 227 Primitive interceptor; | |
| 228 | |
| 229 if (receiver == receiverParameter && receiverInterceptor != null) { | |
| 230 // If the receiver is 'this', we are calling a method in | |
| 231 // the same interceptor: | |
| 232 // Change 'receiver.foo()' to 'this.foo(receiver)'. | |
| 233 interceptor = receiverInterceptor; | |
| 234 } else { | |
| 235 interceptor = new Interceptor(receiver, node.sourceInformation); | |
| 236 if (receiver.hint != null) { | |
| 237 interceptor.hint = new InterceptorEntity(receiver.hint); | |
| 238 } | |
| 239 new LetPrim(interceptor).insertAbove(node.parent); | |
| 240 } | |
| 241 assert(node.interceptorRef == null); | |
| 242 node.makeIntercepted(interceptor); | |
| 243 } | |
| 244 } | |
| OLD | NEW |