Chromium Code Reviews| 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); |
| } |