Index: lib/compiler/implementation/ssa/builder.dart |
=================================================================== |
--- lib/compiler/implementation/ssa/builder.dart (revision 11792) |
+++ lib/compiler/implementation/ssa/builder.dart (working copy) |
@@ -806,13 +806,14 @@ |
LocalsHandler localsHandler; |
HInstruction rethrowableException; |
Map<Element, HParameterValue> parameters; |
+ Map<Element, HInstruction> parametersWithSentinel; |
Map<TargetElement, JumpHandler> jumpTargets; |
/** |
* Variables stored in the current activation. These variables are |
* being updated in try/catch blocks, and should be |
- * accessed indirectly through HFieldGet and HFieldSet. |
+ * accessed indirectly through [HLocalGet] and [HLocalSet]. |
*/ |
Map<Element, HLocalValue> activationVariables; |
@@ -841,6 +842,7 @@ |
activationVariables = new Map<Element, HLocalValue>(), |
jumpTargets = new Map<TargetElement, JumpHandler>(), |
parameters = new Map<Element, HParameterValue>(), |
+ parametersWithSentinel = new Map<Element, HInstruction>(), |
inliningStack = <InliningState>[], |
super(work.resolutionTree) { |
localsHandler = new LocalsHandler(this); |
@@ -1226,6 +1228,59 @@ |
return closeFunction(); |
} |
+ void addParameterCheckInstruction(Element element) { |
+ // This is the code we emit for a parameter that is being checked |
+ // on whether it was given at value at the call site: |
+ // |
+ // foo([a = 42) { |
+ // if (?a) print('parameter passed $a'); |
+ // } |
+ // |
+ // foo([a = 42]) { |
+ // var t1 = a === sentinel; |
+ // if (t1) a = 42; |
+ // if (!t1) print('parameter passed ' + a); |
+ // } |
+ |
+ // Fetch the original default value of [element]; |
+ ConstantHandler handler = compiler.constantHandler; |
+ Constant constant = handler.compileVariable(element); |
+ HConstant defaultValue = constant == null |
+ ? graph.addConstantNull() |
+ : graph.addConstant(constant); |
+ |
+ // Emit the equality check with the sentinel. |
+ HConstant sentinel = graph.addConstant(SentinelConstant.SENTINEL); |
+ Element equalsHelper = interceptors.getTripleEqualsInterceptor(); |
+ HInstruction target = new HStatic(equalsHelper); |
+ add(target); |
+ HInstruction operand = parameters[element]; |
+ HInstruction check = new HIdentity(target, sentinel, operand); |
+ add(check); |
+ |
+ // If the check succeeds, we must update the parameter with the |
+ // default value. |
+ handleIf(element.parseNode(compiler), |
+ () => stack.add(check), |
+ () => localsHandler.updateLocal(element, defaultValue), |
+ null); |
+ |
+ // Create the instruction that parameter checks will use. |
+ check = new HNot(check); |
+ add(check); |
+ |
+ // If the parameter check is also used by closures, |
+ // we need to update the closure field holding that check. |
+ ClosureClassMap closureData = localsHandler.closureData; |
+ Element redirect = closureData.parametersWithSentinel[element]; |
+ if (redirect != null) { |
+ localsHandler.updateLocal(redirect, check); |
+ } |
+ // Cache the check so that uses in this method share the same |
+ // check instruction. |
+ parametersWithSentinel[element] = check; |
+ } |
+ |
void openFunction(FunctionElement functionElement, |
FunctionExpression node) { |
HBasicBlock block = graph.addNewBlock(); |
@@ -1236,11 +1291,17 @@ |
open(block); |
+ FunctionSignature params = functionElement.computeSignature(compiler); |
+ params.forEachParameter((Element element) { |
+ if (elements.isParameterChecked(element)) { |
+ addParameterCheckInstruction(element); |
+ } |
+ }); |
+ |
// Put the type checks in the first successor of the entry, |
// because that is where the type guards will also be inserted. |
// This way we ensure that a type guard will dominate the type |
// check. |
- FunctionSignature params = functionElement.computeSignature(compiler); |
params.forEachParameter((Element element) { |
HInstruction newParameter = potentiallyCheckType( |
localsHandler.directLocals[element], element); |
@@ -1780,8 +1841,24 @@ |
void visitUnary(Send node, Operator op) { |
String value = op.source.stringValue; |
if (value === '?') { |
kasperl
2012/09/04 10:50:08
node.isParameterCheck?
ngeoffray
2012/09/04 10:53:44
Done.
|
- // TODO(ahe): Implement argument definition test. |
- stack.add(graph.addConstantBool(true)); |
+ Element element = elements[node.receiver]; |
+ // If the parameter we're accessing is from the current |
+ // function, we can just fetch the cached instruction. |
+ HInstruction check = parametersWithSentinel[element]; |
+ if (check !== null) { |
+ stack.add(check); |
+ } else { |
+ // The parameter is from an outer function. Lookup the |
+ // element in the closure data of the outer function and read |
+ // it. |
+ assert(element.enclosingElement != work.element); |
+ Node node = element.enclosingElement.parseNode(compiler); |
+ ClosureClassMap parameterClosureData = |
+ compiler.closureToClassMapper.getMappingForNestedFunction(node); |
+ Element fieldCheck = |
+ parameterClosureData.parametersWithSentinel[element]; |
+ stack.add(localsHandler.readLocal(fieldCheck)); |
+ } |
return; |
} |
assert(node.argumentsNode is Prefix); |
@@ -2165,8 +2242,15 @@ |
return pop(); |
} |
- HInstruction compileConstant(Element constantElement) { |
- Constant constant = compiler.compileVariable(constantElement); |
+ HInstruction compileConstant(Element parameter) { |
+ Constant constant; |
+ TreeElements calleeElements = |
+ compiler.enqueuer.resolution.getCachedElements(element); |
+ if (calleeElements.isParameterChecked(parameter)) { |
+ constant = SentinelConstant.SENTINEL; |
+ } else { |
+ constant = compiler.compileVariable(parameter); |
+ } |
return graph.addConstant(constant); |
} |
@@ -3669,7 +3753,11 @@ |
tooDifficult = true; |
} |
- void visitSend(Node node) { |
+ void visitSend(Send node) { |
+ if (node.isParameterCheck) { |
+ tooDifficult = true; |
+ return; |
+ } |
node.visitChildren(this); |
} |