public void visit(FuncallExpression node) { if (node.getObject() != null) { visit(node.getObject()); } visit(node.getFunction()); visitAll(node.getArguments()); }
/** * The outer calling convention to a BaseFunction. * * @param args a list of all positional arguments (as in *starArg) * @param kwargs a map for key arguments (as in **kwArgs) * @param ast the expression for this function's definition * @param env the Environment in the function is called * @return the value resulting from evaluating the function with the given arguments * @throws EvalException-s containing source information. */ public Object call( List<Object> args, @Nullable Map<String, Object> kwargs, @Nullable FuncallExpression ast, Environment env) throws EvalException, InterruptedException { Preconditions.checkState(isConfigured(), "Function %s was not configured", getName()); // ast is null when called from Java (as there's no Skylark call site). Location loc = ast == null ? Location.BUILTIN : ast.getLocation(); Object[] arguments = processArguments(args, kwargs, loc); canonicalizeArguments(arguments, loc); return call(arguments, ast, env); }
@Override @Nullable public Object call(Object[] args, @Nullable FuncallExpression ast, @Nullable Environment env) throws EvalException, InterruptedException { final Location loc = (ast == null) ? location : ast.getLocation(); // Add extra arguments, if needed if (extraArgs != null) { int i = args.length - extraArgs.length; for (BuiltinFunction.ExtraArgKind extraArg : extraArgs) { switch (extraArg) { case LOCATION: args[i] = loc; break; case SYNTAX_TREE: args[i] = ast; break; case ENVIRONMENT: args[i] = env; break; } i++; } } // Last but not least, actually make an inner call to the function with the resolved arguments. try { return invokeMethod.invoke(this, args); } catch (InvocationTargetException x) { Throwable e = x.getCause(); if (e instanceof EvalException) { throw ((EvalException) e).ensureLocation(loc); } else if (e instanceof InterruptedException) { throw (InterruptedException) e; } else if (e instanceof ClassCastException || e instanceof ExecutionException || e instanceof IllegalStateException) { throw new EvalException(loc, "in call to " + getName(), e); } else if (e instanceof IllegalArgumentException) { throw new EvalException(loc, "Illegal argument in call to " + getName(), e); } else { throw badCallException(loc, e, args); } } catch (IllegalArgumentException e) { // Either this was thrown by Java itself, or it's a bug // To cover the first case, let's manually check the arguments. final int len = args.length - ((extraArgs == null) ? 0 : extraArgs.length); final Class<?>[] types = invokeMethod.getParameterTypes(); for (int i = 0; i < args.length; i++) { if (args[i] != null && !types[i].isAssignableFrom(args[i].getClass())) { String paramName = i < len ? signature.getSignature().getNames().get(i) : extraArgs[i - len].name(); int extraArgsCount = (extraArgs == null) ? 0 : extraArgs.length; throw new EvalException( loc, String.format( "Method %s is not applicable for arguments %s: '%s' is %s, but should be %s", getShortSignature(true), printTypeString(args, args.length - extraArgsCount), paramName, EvalUtils.getDataTypeName(args[i]), EvalUtils.getDataTypeNameFromClass(types[i]))); } } throw badCallException(loc, e, args); } catch (IllegalAccessException e) { throw badCallException(loc, e, args); } }
@Override public Object call( List<Object> args, Map<String, Object> kwargs, FuncallExpression ast, Environment env) throws EvalException, InterruptedException { // ast is null when called from Java (as there's no Skylark call site). Location loc = ast == null ? location : ast.getLocation(); if (onlyNamedArguments && !args.isEmpty()) { throw new EvalException(loc, getSignature() + " does not accept positional arguments"); } if (kwargs == null) { kwargs = ImmutableMap.<String, Object>of(); } int numParams = parameters.size(); int numArgs = args.size(); Object[] namedArguments = new Object[numParams]; // first, positional arguments: if (numArgs > numParams) { throw new EvalException(loc, "too many positional arguments in call to " + getSignature()); } for (int ii = 0; ii < numArgs; ++ii) { namedArguments[ii] = args.get(ii); } // TODO(bazel-team): here, support *varargs splicing // second, keyword arguments: for (Map.Entry<String, Object> entry : kwargs.entrySet()) { String keyword = entry.getKey(); int pos = parameters.indexOf(keyword); if (pos == -1) { List<String> unexpected = listDifference(new ArrayList<>(kwargs.keySet()), parameters); Collections.sort(unexpected); // issue stable error messages. throw new EvalException( loc, "unexpected keyword" + (unexpected.size() > 1 ? "s" : "") + " '" + Joiner.on("', '").join(unexpected) + "' in call to " + getSignature()); } else { if (namedArguments[pos] != null) { throw new EvalException( loc, getSignature() + " got multiple values for keyword argument '" + keyword + "'"); } namedArguments[pos] = kwargs.get(keyword); } } // third, check mandatory parameters: for (int ii = 0; ii < numMandatoryParameters; ++ii) { if (namedArguments[ii] == null) { throw new EvalException(loc, getSignature() + " received insufficient arguments"); } } // fourth, fill in defaults from the signature, if any List<Object> defaults = signature.getDefaultValues(); if (defaults != null) { int jj = 0; for (int ii = numMandatoryParameters; ii < numParams; ++ii) { if (namedArguments[ii] == null) { namedArguments[ii] = defaults.get(jj); } jj++; } } try { return call(namedArguments, ast, env); } catch (ConversionException | IllegalArgumentException | IllegalStateException | ClassCastException e) { throw new EvalException(loc, e.getMessage()); } }
// Don't make it abstract, so that subclasses may be defined that @Override the outer call() only. protected Object call(Object[] args, @Nullable FuncallExpression ast, @Nullable Environment env) throws EvalException, ConversionException, InterruptedException { throw new EvalException( (ast == null) ? Location.BUILTIN : ast.getLocation(), String.format("function %s not implemented", getName())); }