Chromium Code Reviews| Index: pkg/compiler/lib/src/js_backend/codegen/unsugar.dart |
| diff --git a/pkg/compiler/lib/src/js_backend/codegen/unsugar.dart b/pkg/compiler/lib/src/js_backend/codegen/unsugar.dart |
| index f439e95de4fdab81de1f84f64b2903319cbc4feb..23c87a783acc4a35cedaf318c5a37022a61d1ef3 100644 |
| --- a/pkg/compiler/lib/src/js_backend/codegen/unsugar.dart |
| +++ b/pkg/compiler/lib/src/js_backend/codegen/unsugar.dart |
| @@ -6,26 +6,67 @@ import '../../cps_ir/cps_ir_nodes.dart'; |
| import '../../cps_ir/optimizers.dart'; |
| import '../../constants/expressions.dart'; |
| import '../../constants/values.dart'; |
| -import '../../elements/elements.dart' |
| - show ClassElement, FieldElement, FunctionElement, Element; |
| +import '../../elements/elements.dart' show |
| + ClassElement, |
| + FieldElement, |
| + FunctionElement, |
| + Local, |
| + ExecutableElement; |
| import '../../js_backend/codegen/glue.dart'; |
| import '../../dart2jslib.dart' show Selector, World; |
| +class ExplicitReceiverParameterEntity implements Local { |
| + String get name => 'receiver'; |
| + final ExecutableElement executableContext; |
| + ExplicitReceiverParameterEntity(this.executableContext); |
| + toString() => 'ExplicitReceiverParameterEntity($executableContext)'; |
| +} |
| + |
| /// Rewrites the initial CPS IR to make Dart semantics explicit and inserts |
| /// special nodes that respect JavaScript behavior. |
| /// |
| /// Performs the following rewrites: |
| -/// - rewrite [IsTrue] in a [Branch] to do boolean conversion. |
| -/// - converts two-parameter exception handlers to one-parameter ones. |
| +/// - Rewrite [IsTrue] in a [Branch] to do boolean conversion. |
| +/// - Add interceptors at call sites that use interceptor calling convention. |
| +/// - Add explicit receiver argument for methods that are called in interceptor |
| +/// calling convention. |
| +/// - Convert two-parameter exception handlers to one-parameter ones. |
| class UnsugarVisitor extends RecursiveVisitor { |
| Glue _glue; |
| ParentVisitor _parentVisitor = new ParentVisitor(); |
| + Parameter thisParameter; |
| + Parameter explicitReceiverParameter; |
| + |
| UnsugarVisitor(this._glue); |
| void rewrite(FunctionDefinition function) { |
| + bool inInterceptedMethod = _glue.isInterceptedMethod(function.element); |
| + |
| + if (function.element.name == '==' && |
| + function.parameters.length == 1 && |
| + !_glue.operatorEqHandlesNullArgument(function.element)) { |
| + // Insert the null check that the language semantics requires us to |
| + // perform before calling operator ==. |
| + insertEqNullCheck(function); |
| + } |
| + |
| + if (inInterceptedMethod) { |
| + thisParameter = function.thisParameter; |
| + ThisParameterLocal holder = thisParameter.hint; |
| + explicitReceiverParameter = new Parameter( |
| + new ExplicitReceiverParameterEntity( |
| + holder.executableContext)); |
| + function.parameters.insert(0, explicitReceiverParameter); |
| + } |
| + |
| // Set all parent pointers. |
| _parentVisitor.visit(function); |
| + |
| + if (inInterceptedMethod) { |
| + explicitReceiverParameter.substituteFor(thisParameter); |
| + } |
| + |
| visit(function); |
| } |
| @@ -42,6 +83,19 @@ class UnsugarVisitor extends RecursiveVisitor { |
| new TrueConstantValue())); |
| } |
| + Constant get falseConstant { |
| + return new Constant( |
| + new BoolConstantExpression( |
| + false, |
| + new FalseConstantValue())); |
| + } |
| + |
| + Constant get nullConstant { |
| + return new Constant( |
| + new NullConstantExpression( |
| + new NullConstantValue())); |
| + } |
| + |
| void insertLetPrim(Primitive primitive, Expression node) { |
| LetPrim let = new LetPrim(primitive); |
| InteriorNode parent = node.parent; |
| @@ -51,6 +105,42 @@ class UnsugarVisitor extends RecursiveVisitor { |
| let.parent = parent; |
| } |
| + void insertEqNullCheck(FunctionDefinition function) { |
|
sigurdm
2015/04/30 08:57:44
Is this rewrite correct in the presence of mirrors
|
| + // Replace |
| + // |
| + // body; |
| + // |
| + // with |
| + // |
| + // if (identical(arg, null)) |
| + // return false; |
| + // else |
| + // body; |
| + // |
| + Continuation originalBody = new Continuation(<Parameter>[]); |
| + originalBody.body = function.body.body; |
| + |
| + Continuation returnFalse = new Continuation(<Parameter>[]); |
| + Primitive falsePrimitive = falseConstant; |
| + returnFalse.body = |
| + new LetPrim(falsePrimitive, |
| + new InvokeContinuation( |
| + function.body.returnContinuation, <Primitive>[falsePrimitive])); |
| + |
| + Primitive nullPrimitive = nullConstant; |
| + Primitive test = new Identical(function.parameters.single, nullPrimitive); |
| + |
| + Expression newBody = |
| + new LetCont.many(<Continuation>[returnFalse, originalBody], |
| + new LetPrim(nullPrimitive, |
| + new LetPrim(test, |
| + new Branch( |
| + new IsTrue(test), |
| + returnFalse, |
| + originalBody)))); |
| + function.body.body = newBody; |
| + } |
| + |
| /// Insert a static call to [function] at the point of [node] with result |
| /// [result]. |
| /// |
| @@ -118,32 +208,41 @@ class UnsugarVisitor extends RecursiveVisitor { |
| processInvokeMethod(InvokeMethod node) { |
| Selector selector = node.selector; |
| - // TODO(karlklose): should we rewrite all selectors? |
| if (!_glue.isInterceptedSelector(selector)) return; |
| Primitive receiver = node.receiver.definition; |
| - Set<ClassElement> interceptedClasses = |
| + Primitive newReceiver; |
| + |
| + if (receiver == explicitReceiverParameter) { |
| + // If the receiver is the explicit receiver, we are calling a method in the |
|
sigurdm
2015/04/30 08:57:44
Long line
|
| + // same interceptor: Change 'receiver.foo()' to 'this.foo(receiver)'. |
| + newReceiver = thisParameter; |
| + } else { |
| + // TODO(sra): Move the computation of interceptedClasses to a much later |
| + // phase and take into account the remaining uses of the interceptor. |
| + Set<ClassElement> interceptedClasses = |
| _glue.getInterceptedClassesOn(selector); |
| - _glue.registerSpecializedGetInterceptor(interceptedClasses); |
| + _glue.registerSpecializedGetInterceptor(interceptedClasses); |
| + newReceiver = new Interceptor(receiver, interceptedClasses); |
| + insertLetPrim(newReceiver, node); |
| + } |
| - Primitive intercepted = new Interceptor(receiver, interceptedClasses); |
| - insertLetPrim(intercepted, node); |
| node.arguments.insert(0, node.receiver); |
| node.callingConvention = CallingConvention.JS_INTERCEPTED; |
| assert(node.isValid); |
| - node.receiver = new Reference<Primitive>(intercepted); |
| - } |
| - |
| - Primitive makeNull() { |
| - NullConstantValue nullConst = new NullConstantValue(); |
| - return new Constant(new NullConstantExpression(nullConst)); |
| + node.receiver = new Reference<Primitive>(newReceiver); |
| } |
| processInvokeMethodDirectly(InvokeMethodDirectly node) { |
| if (_glue.isInterceptedMethod(node.target)) { |
| - Primitive nullPrim = makeNull(); |
| + Primitive nullPrim = nullConstant; |
| insertLetPrim(nullPrim, node); |
| node.arguments.insert(0, node.receiver); |
| + // TODO(sra): `null` is not adequate. Interceptors project the class |
| + // hierarchy onto an interceptor hierarchy. A super call that does a |
| + // method call will use the javascript 'this' parameter to avoid calling |
| + // getInterceptor again, so the receiver must be the interceptor (likely |
| + // `this`), not `null`. |
| node.receiver = new Reference<Primitive>(nullPrim); |
| } |
| } |