| 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..dff9f1519bc19c09c1af521328a9cd33baa82013 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) {
|
| + // 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,42 @@ 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 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);
|
| }
|
| }
|
|
|