diff --git a/src/com/google/javascript/jscomp/ConformanceRules.java b/src/com/google/javascript/jscomp/ConformanceRules.java index cb24fa66355..b849ca2219f 100644 --- a/src/com/google/javascript/jscomp/ConformanceRules.java +++ b/src/com/google/javascript/jscomp/ConformanceRules.java @@ -586,13 +586,12 @@ static TypeI evaluateTypeString( * @see TypeCheck#visitParameterList */ static boolean validateCall( - AbstractCompiler compiler, Node callOrNew, FunctionTypeI functionType, boolean isCallInvocation) { Preconditions.checkState(callOrNew.isCall() || callOrNew.isNew()); - return validateParameterList(compiler, callOrNew, functionType, isCallInvocation) + return validateParameterList(callOrNew, functionType, isCallInvocation) && validateThis(callOrNew, functionType, isCallInvocation); } @@ -619,7 +618,6 @@ private static boolean validateThis( } private static boolean validateParameterList( - AbstractCompiler compiler, Node callOrNew, FunctionTypeI functionType, boolean isCallInvocation) { @@ -629,61 +627,13 @@ private static boolean validateParameterList( arguments.next(); } - Iterator parameters = functionType.getParameters().iterator(); - Node parameter = null; - Node argument = null; - while (arguments.hasNext() - && (parameters.hasNext() || parameter != null && parameter.isVarArgs())) { - // If there are no parameters left in the list, then the while loop - // above implies that this must be a var_args function. - if (parameters.hasNext()) { - parameter = parameters.next(); - } - argument = arguments.next(); - - if (!validateParameter( - getTypeI(compiler, argument), getTypeI(compiler, parameter))) { - return false; - } - } - - int numArgs = callOrNew.getChildCount() - 1; - if (isCallInvocation && numArgs > 0) { - numArgs -= 1; - } - int minArgs = functionType.getMinArguments(); - int maxArgs = functionType.getMaxArguments(); - return minArgs <= numArgs && numArgs <= maxArgs; - } - - /** - * Expect that the type of an argument matches the type of the parameter - * that it's fulfilling. - * - * @param argType The type of the argument. - * @param paramType The type of the parameter. - */ - static boolean validateParameter(TypeI argType, TypeI paramType) { - return argType.isSubtypeOf(paramType); - } - - /** - * This method gets the TypeI from the Node argument and verifies that it is - * present. - */ - static TypeI getTypeI(AbstractCompiler compiler, Node n) { - TypeI type = n.getTypeI(); - if (type == null) { - return getNativeType(compiler, JSTypeNative.UNKNOWN_TYPE); - } else { - return type; + // Get all the annotated types of the argument nodes + ImmutableList.Builder argumentTypes = ImmutableList.builder(); + while (arguments.hasNext()) { + argumentTypes.add(arguments.next().getTypeI()); } + return functionType.acceptsArguments(argumentTypes.build()); } - - static TypeI getNativeType(AbstractCompiler compiler, JSTypeNative typeId) { - return compiler.getTypeIRegistry().getNativeType(typeId); - } - } /** @@ -736,13 +686,13 @@ protected ConformanceResult checkConformance(NodeTraversal t, Node n) { if (n.matchesQualifiedName(r.name)) { if (!ConformanceUtil.validateCall( - compiler, n.getParent(), r.restrictedCallType, false)) { + n.getParent(), r.restrictedCallType, false)) { return ConformanceResult.VIOLATION; } } else if (n.isGetProp() && n.getLastChild().getString().equals("call") && n.getFirstChild().matchesQualifiedName(r.name)) { if (!ConformanceUtil.validateCall( - compiler, n.getParent(), r.restrictedCallType, true)) { + n.getParent(), r.restrictedCallType, true)) { return ConformanceResult.VIOLATION; } } @@ -858,14 +808,12 @@ private ConformanceResult checkConformance( || targetType.isEquivalentTo( registry.getNativeType(JSTypeNative.OBJECT_TYPE))) { if (!ConformanceUtil.validateCall( - compiler, n.getParent(), r.restrictedCallType, - isCallInvocation)) { + n.getParent(), r.restrictedCallType, isCallInvocation)) { return ConformanceResult.POSSIBLE_VIOLATION_DUE_TO_LOOSE_TYPES; } } else if (targetType.isSubtypeOf(methodClassType)) { if (!ConformanceUtil.validateCall( - compiler, n.getParent(), r.restrictedCallType, - isCallInvocation)) { + n.getParent(), r.restrictedCallType, isCallInvocation)) { return ConformanceResult.VIOLATION; } } diff --git a/src/com/google/javascript/jscomp/newtypes/JSType.java b/src/com/google/javascript/jscomp/newtypes/JSType.java index 3119e0d67ce..97f7b3219af 100644 --- a/src/com/google/javascript/jscomp/newtypes/JSType.java +++ b/src/com/google/javascript/jscomp/newtypes/JSType.java @@ -1674,18 +1674,25 @@ public TypeI getTypeOfThis() { } @Override - public int getMinArguments() { - throw new UnsupportedOperationException("getMinArguments not implemented yet"); - } + public boolean acceptsArguments(List argumentTypes) { + Preconditions.checkState(this.isFunctionType()); - @Override - public int getMaxArguments() { - throw new UnsupportedOperationException("getMaxArguments not implemented yet"); - } + int numArgs = argumentTypes.size(); + FunctionType fnType = this.getFunTypeIfSingletonObj(); - @Override - public Iterable getParameters() { - throw new UnsupportedOperationException("getParameters not implemented yet"); + if (numArgs < fnType.getMinArity() || numArgs > fnType.getMaxArity()) { + return false; + } + + for (int i = 0; i < numArgs; i++) { + TypeI ithArgType = argumentTypes.get(i); + JSType ithParamType = fnType.getFormalType(i); + if (!ithArgType.isSubtypeOf(ithParamType)) { + return false; + } + } + + return true; } @Override diff --git a/src/com/google/javascript/rhino/FunctionTypeI.java b/src/com/google/javascript/rhino/FunctionTypeI.java index 7e5d9bbe6f1..9cdd081b0d8 100644 --- a/src/com/google/javascript/rhino/FunctionTypeI.java +++ b/src/com/google/javascript/rhino/FunctionTypeI.java @@ -77,9 +77,6 @@ public interface FunctionTypeI extends TypeI { void setSource(Node n); - int getMinArguments(); - - int getMaxArguments(); - - Iterable getParameters(); + /** Checks if a call to this function with the given list of arguments is valid. */ + boolean acceptsArguments(List argumentTypes); } diff --git a/src/com/google/javascript/rhino/jstype/FunctionType.java b/src/com/google/javascript/rhino/jstype/FunctionType.java index f662e6c4dbb..2f4f204a57b 100644 --- a/src/com/google/javascript/rhino/jstype/FunctionType.java +++ b/src/com/google/javascript/rhino/jstype/FunctionType.java @@ -53,6 +53,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -300,7 +301,6 @@ public boolean hasImplementedInterfaces() { return false; } - @Override public Iterable getParameters() { Node n = getParametersNode(); if (n != null) { @@ -316,7 +316,6 @@ public Node getParametersNode() { } /** Gets the minimum number of arguments that this function requires. */ - @Override public int getMinArguments() { // NOTE(nicksantos): There are some native functions that have optional // parameters before required parameters. This algorithm finds the position @@ -336,7 +335,6 @@ public int getMinArguments() { * Gets the maximum number of arguments that this function requires, * or Integer.MAX_VALUE if this is a variable argument function. */ - @Override public int getMaxArguments() { Node params = getParametersNode(); if (params != null) { @@ -1510,4 +1508,30 @@ public List checkExtendsLoop(HashSet cache, } return null; } + + @Override + public boolean acceptsArguments(List argumentTypes) { + // NOTE(aravindpg): This code is essentially lifted from TypeCheck::visitParameterList, + // but what small differences there are make it very painful to refactor out the shared code. + Iterator arguments = argumentTypes.iterator(); + Iterator parameters = this.getParameters().iterator(); + Node parameter = null; + TypeI argument = null; + while (arguments.hasNext() + && (parameters.hasNext() || parameter != null && parameter.isVarArgs())) { + // If there are no parameters left in the list, then the while loop + // above implies that this must be a var_args function. + if (parameters.hasNext()) { + parameter = parameters.next(); + } + argument = arguments.next(); + + if (!argument.isSubtypeOf(parameter.getTypeI())) { + return false; + } + } + + int numArgs = argumentTypes.size(); + return this.getMinArguments() <= numArgs && numArgs <= this.getMaxArguments(); + } }