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 a071b47a3b16a5fe29305fb9f578dfc0c252f040..555c5324257b1946d6910550e91540037b8765c7 100644 |
--- a/pkg/compiler/lib/src/js_backend/codegen/unsugar.dart |
+++ b/pkg/compiler/lib/src/js_backend/codegen/unsugar.dart |
@@ -6,23 +6,77 @@ 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, Element; |
+import '../../elements/elements.dart' show |
+ ClassElement, |
+ ExecutableElement, |
+ FieldElement, |
+ Local; |
import '../../js_backend/codegen/glue.dart'; |
import '../../dart2jslib.dart' show Selector, World; |
+ |
+class ExplicitReceiverParameterEntity implements Local { |
+ final String name; |
Kevin Millikin (Google)
2015/03/26 15:14:17
Consider hardcoding the name 'receiver' here rathe
|
+ final ExecutableElement executableContext; |
+ ExplicitReceiverParameterEntity(this.name, 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. |
+/// - Rewrite [IsTrue] in a [Branch] to do boolean conversion. |
+/// - Add explicit receiver argument for methods that are called in interceptor |
+/// calling convention. |
+/// - Use current or new interceptor at call sites taht use interceptor calling |
Kevin Millikin (Google)
2015/03/26 15:14:17
'taht' ==> 'that'
|
+/// convention. |
+/// - Rewrite operator== to test the argument for null. |
Kevin Millikin (Google)
2015/03/26 15:14:17
'first argument' or 'receiver argument'?
|
class UnsugarVisitor extends RecursiveVisitor { |
Glue _glue; |
+ bool inInterceptedMethod = false; |
+ Parameter thisParameter = null; |
+ Parameter explicitReceiverParameter = null; |
+ |
UnsugarVisitor(this._glue); |
void rewrite(FunctionDefinition function) { |
+ |
+ if (function.element.name == '==' && function.parameters.length == 1) { |
+ // If [functionElement] is `operator==` we explicitely add a null check at |
+ // the beginning of the method. This is to avoid having call sites do the |
+ // null check. |
+ if (_glue.requiresEqNullCheck(function.element)) { |
+ insertEqNullCheck(function); |
+ } |
+ } |
+ |
+ // For interceptor calling convention, the receiver is passed as a |
+ // parameter. |
+ bool inInterceptedMethod = false; |
+ if (_glue.isInterceptedMethod(function.element)) { |
+ inInterceptedMethod = true; |
+ thisParameter = function.thisParameter; |
+ explicitReceiverParameter = |
+ new Parameter( |
+ new ExplicitReceiverParameterEntity('receiver', |
+ thisParameter.hint.executableContext)); |
+ function.parameters.insert(0, explicitReceiverParameter); |
+ } |
+ |
// Set all parent pointers. |
new ParentVisitor().visit(function); |
+ |
+ // Replace all references to This with the explicit receiver parameter. |
+ if (inInterceptedMethod) { |
+ explicitReceiverParameter.substituteFor(thisParameter); |
+ explicitReceiverParameter.firstRef = thisParameter.firstRef; |
Kevin Millikin (Google)
2015/03/26 15:14:17
substituteFor will do this (and doing it here is o
|
+ thisParameter.firstRef = null; |
Kevin Millikin (Google)
2015/03/26 15:14:17
And we should almost certainly do this in substitu
|
+ } |
+ |
visit(function); |
} |
@@ -32,12 +86,24 @@ class UnsugarVisitor extends RecursiveVisitor { |
return result != null ? result : node; |
} |
- Constant get trueConstant { |
+ Constant makeTrue() { |
Kevin Millikin (Google)
2015/03/26 15:14:17
Oooh, these seem like good utilities to have in cl
|
return new Constant( |
new PrimitiveConstantExpression( |
new TrueConstantValue())); |
} |
+ Constant makeFalse() { |
+ return new Constant( |
+ new PrimitiveConstantExpression( |
+ new FalseConstantValue())); |
+ } |
+ |
+ Primitive makeNull() { |
+ return new Constant( |
+ new PrimitiveConstantExpression( |
+ new NullConstantValue())); |
+ } |
+ |
void insertLetPrim(Primitive primitive, Expression node) { |
LetPrim let = new LetPrim(primitive); |
InteriorNode parent = node.parent; |
@@ -47,12 +113,65 @@ 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 = makeFalse(); |
+ returnFalse.body = |
+ new LetPrim(falsePrimitive, |
+ new InvokeContinuation( |
+ function.body.returnContinuation, <Primitive>[falsePrimitive])); |
+ |
+ Primitive nullPrimitive = makeNull(); |
+ 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; |
+ } |
+ |
processInvokeMethod(InvokeMethod node) { |
Selector selector = node.selector; |
- // TODO(karlklose): should we rewrite all selectors? |
+ // TODO(karlklose): should we rewrite all selectors? sra: No. It would add |
Kevin Millikin (Google)
2015/03/26 15:14:17
I don't understand the question or the answer. Ma
|
+ // a lot of code. We would need to do so only if we permit native classes |
+ // to implement noSuchMethod. |
if (!_glue.isInterceptedSelector(selector)) return; |
Primitive receiver = node.receiver.definition; |
+ |
+ // If the receiver is the explicit receiver, we are calling a method in the |
+ // same interceptor. |
+ if (receiver == explicitReceiverParameter) { |
+ // receiver.foo() --> this.foo(receiver); |
+ node.arguments.insert(0, node.receiver); |
+ node.callingConvention = CallingConvention.JS_INTERCEPTED; |
+ Reference<Primitive> receiverRef = node.receiver; |
Kevin Millikin (Google)
2015/03/26 15:14:17
This is unused.
|
+ node.receiver = new Reference<Primitive>(thisParameter); |
+ assert(node.isValid); |
+ return; |
+ } |
+ |
+ // 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 = |
Kevin Millikin (Google)
2015/03/26 15:14:17
I think there's enough shared code here and above
|
_glue.getInterceptedClassesOn(selector); |
_glue.registerSpecializedGetInterceptor(interceptedClasses); |
@@ -61,13 +180,8 @@ class UnsugarVisitor extends RecursiveVisitor { |
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 PrimitiveConstantExpression(nullConst)); |
+ assert(node.isValid); |
} |
processInvokeMethodDirectly(InvokeMethodDirectly node) { |
@@ -76,6 +190,12 @@ class UnsugarVisitor extends RecursiveVisitor { |
insertLetPrim(nullPrim, node); |
node.arguments.insert(0, node.receiver); |
node.receiver = new Reference<Primitive>(nullPrim); |
+ |
+ // 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`. |
} |
} |
@@ -83,13 +203,15 @@ class UnsugarVisitor extends RecursiveVisitor { |
// TODO(karlklose): implement the checked mode part of boolean conversion. |
InteriorNode parent = node.parent; |
IsTrue condition = node.condition; |
- Primitive t = trueConstant; |
+ Primitive t = makeTrue(); |
Primitive i = new Identical(condition.value.definition, t); |
- LetPrim newNode = new LetPrim(t, |
- new LetPrim(i, |
- new Branch(new IsTrue(i), |
- node.trueContinuation.definition, |
- node.falseContinuation.definition))); |
+ LetPrim newNode = |
+ new LetPrim(t, |
+ new LetPrim(i, |
+ new Branch( |
+ new IsTrue(i), |
+ node.trueContinuation.definition, |
+ node.falseContinuation.definition))); |
condition.value.unlink(); |
node.trueContinuation.unlink(); |
node.falseContinuation.unlink(); |