@Test public void testDatatypeMutability() throws Exception { assertTrue(EvalUtils.isImmutable("foo")); assertTrue(EvalUtils.isImmutable(3)); assertTrue(EvalUtils.isImmutable(Tuple.of(1, 2, 3))); assertFalse(EvalUtils.isImmutable(MutableList.of(null, 1, 2, 3))); assertFalse(EvalUtils.isImmutable(makeDict())); }
protected String getObjectTypeString() { Class<?> clazz = getObjectType(); if (clazz == null) { return ""; } return EvalUtils.getDataTypeNameFromClass(clazz, false) + "."; }
/** 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)); } } }
// The precondition ensures generic type safety @SuppressWarnings("unchecked") public <T> NestedSet<T> getSet(Class<T> type) { // Empty sets don't need have to have a type since they don't have items if (set.isEmpty()) { return (NestedSet<T>) set; } Preconditions.checkArgument( contentType.canBeCastTo(type), String.format( "Expected a set of '%s' but got a set of '%s'", EvalUtils.getDataTypeNameFromClass(type), contentType)); return (NestedSet<T>) set; }
@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); } }
private static void checkImmutable(Object o, Location loc) throws EvalException { if (!EvalUtils.isImmutable(o)) { throw new EvalException(loc, "sets cannot contain mutable items"); } }
@Test public void testStringToIterable() throws Exception { assertThat(EvalUtils.toIterable("abc", null)).hasSize(3); }
@Test public void testEmptyStringToIterable() throws Exception { assertThat(EvalUtils.toIterable("", null)).isEmpty(); }
/** * Print an official representation of object x. For regular data structures, the value should be * parsable back into an equal data structure. * * @param buffer the Appendable to write to. * @param o the string a representation of which to write. * @param quotationMark The quotation mark to be used (' or ") * @return the Appendable, in fluent style. */ public static Appendable write(Appendable buffer, Object o, char quotationMark) { if (o == null) { throw new NullPointerException(); // Java null is not a build language value. } else if (o instanceof String) { writeString(buffer, (String) o, quotationMark); } else if (o instanceof Integer || o instanceof Double) { append(buffer, o.toString()); } else if (o == Runtime.NONE) { append(buffer, "None"); } else if (o == Boolean.TRUE) { append(buffer, "True"); } else if (o == Boolean.FALSE) { append(buffer, "False"); } else if (o instanceof List<?>) { List<?> seq = (List<?>) o; printList(buffer, seq, EvalUtils.isImmutable(seq), quotationMark); } else if (o instanceof SkylarkList) { SkylarkList list = (SkylarkList) o; printList(buffer, list.toList(), list.isTuple(), quotationMark); } else if (o instanceof Map<?, ?>) { Map<?, ?> dict = (Map<?, ?>) o; printList(buffer, getSortedEntrySet(dict), "{", ", ", "}", null, quotationMark); } else if (o instanceof Map.Entry<?, ?>) { Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o; write(buffer, entry.getKey(), quotationMark); append(buffer, ": "); write(buffer, entry.getValue(), quotationMark); } else if (o instanceof SkylarkNestedSet) { SkylarkNestedSet set = (SkylarkNestedSet) o; append(buffer, "set("); printList(buffer, set, "[", ", ", "]", null, quotationMark); Order order = set.getOrder(); if (order != Order.STABLE_ORDER) { append(buffer, ", order = \"" + order.getName() + "\""); } append(buffer, ")"); } else if (o instanceof BaseFunction) { BaseFunction func = (BaseFunction) o; append(buffer, "<function " + func.getName() + ">"); } else if (o instanceof Label) { write(buffer, o.toString(), quotationMark); } else if (o instanceof FilesetEntry) { FilesetEntry entry = (FilesetEntry) o; append(buffer, "FilesetEntry(srcdir = "); write(buffer, entry.getSrcLabel().toString(), quotationMark); append(buffer, ", files = "); write(buffer, makeStringList(entry.getFiles()), quotationMark); append(buffer, ", excludes = "); write(buffer, makeList(entry.getExcludes()), quotationMark); append(buffer, ", destdir = "); write(buffer, entry.getDestDir().getPathString(), quotationMark); append(buffer, ", strip_prefix = "); write(buffer, entry.getStripPrefix(), quotationMark); append(buffer, ", symlinks = "); append(buffer, quotationMark); append(buffer, entry.getSymlinkBehavior().toString()); append(buffer, quotationMark); append(buffer, ")"); } else if (o instanceof PathFragment) { append(buffer, ((PathFragment) o).getPathString()); } else { append(buffer, o.toString()); } return buffer; }