/** check types and convert as required */ protected void canonicalizeArguments(Object[] arguments, Location loc) throws EvalException { // TODO(bazel-team): maybe link syntax.SkylarkType and package.Type, // so we can simultaneously typecheck and convert? // Note that a BuiltinFunction already does typechecking of simple types. List<SkylarkType> types = getEnforcedArgumentTypes(); // Check types, if supplied if (types == null) { return; } List<String> names = signature.getSignature().getNames(); int length = types.size(); for (int i = 0; i < length; i++) { Object value = arguments[i]; SkylarkType type = types.get(i); if (value != null && type != null && !type.contains(value)) { throw new EvalException( loc, String.format( "expected %s for '%s' while calling %s but got %s instead: %s", type, names.get(i), getName(), EvalUtils.getDataTypeName(value, true), value)); } } }
@Test public void testDataTypeNames() throws Exception { assertEquals("string", EvalUtils.getDataTypeName("foo")); assertEquals("int", EvalUtils.getDataTypeName(3)); assertEquals("tuple", EvalUtils.getDataTypeName(Tuple.of(1, 2, 3))); assertEquals("list", EvalUtils.getDataTypeName(MutableList.of(null, 1, 2, 3))); assertEquals("dict", EvalUtils.getDataTypeName(makeDict())); assertEquals("NoneType", EvalUtils.getDataTypeName(Runtime.NONE)); }
/** * Prints the types of the first {@code howManyArgsToPrint} given arguments as "(type1, type2, * ...)" */ protected String printTypeString(Object[] args, int howManyArgsToPrint) { StringBuilder builder = new StringBuilder(); builder.append("("); int start = hasSelfArgument() ? 1 : 0; for (int pos = start; pos < howManyArgsToPrint; ++pos) { builder.append(EvalUtils.getDataTypeName(args[pos])); if (pos < howManyArgsToPrint - 1) { builder.append(", "); } } builder.append(")"); return builder.toString(); }
// This is safe because of the type checking @SuppressWarnings("unchecked") private SkylarkNestedSet( Order order, SkylarkType contentType, Object item, Location loc, List<Object> items, List<NestedSet<Object>> transitiveItems) throws EvalException { // Adding the item if (item instanceof SkylarkNestedSet) { SkylarkNestedSet nestedSet = (SkylarkNestedSet) item; if (!nestedSet.isEmpty()) { contentType = checkType(contentType, nestedSet.contentType, loc); transitiveItems.add((NestedSet<Object>) nestedSet.set); } } else if (item instanceof SkylarkList) { // TODO(bazel-team): we should check ImmutableList here but it screws up genrule at line 43 for (Object object : (SkylarkList) item) { contentType = checkType(contentType, SkylarkType.of(object.getClass()), loc); checkImmutable(object, loc); items.add(object); } } else { throw new EvalException( loc, String.format("cannot add value of type '%s' to a set", EvalUtils.getDataTypeName(item))); } this.contentType = Preconditions.checkNotNull(contentType, "type cannot be null"); // Initializing the real nested set NestedSetBuilder<Object> builder = new NestedSetBuilder<>(order); builder.addAll(items); try { for (NestedSet<Object> nestedSet : transitiveItems) { builder.addTransitive(nestedSet); } } catch (IllegalStateException e) { throw new EvalException(loc, e.getMessage()); } this.set = builder.build(); this.items = ImmutableList.copyOf(items); this.transitiveItems = ImmutableList.copyOf(transitiveItems); }
@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); } }