/** 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)); } } }
/** Configure the reflection mechanism */ @Override protected void configure() { invokeMethod = findMethod("invoke"); int arguments = signature.getSignature().getShape().getArguments(); innerArgumentCount = arguments + (extraArgs == null ? 0 : extraArgs.length); Class<?>[] parameterTypes = invokeMethod.getParameterTypes(); Preconditions.checkArgument( innerArgumentCount == parameterTypes.length, "bad argument count for %s: method has %s arguments, type list has %s", getName(), innerArgumentCount, parameterTypes.length); if (enforcedArgumentTypes != null) { for (int i = 0; i < arguments; i++) { SkylarkType enforcedType = enforcedArgumentTypes.get(i); if (enforcedType != null) { Class<?> parameterType = parameterTypes[i]; String msg = String.format( "fun %s(%s), param %s, enforcedType: %s (%s); parameterType: %s", getName(), signature, signature.getSignature().getNames().get(i), enforcedType, enforcedType.getType(), parameterType); if (enforcedType instanceof SkylarkType.Simple || enforcedType instanceof SkylarkFunctionType) { Preconditions.checkArgument(enforcedType.getType() == parameterType, msg); // No need to enforce Simple types on the Skylark side, the JVM will do it for us. enforcedArgumentTypes.set(i, null); } else if (enforcedType instanceof SkylarkType.Combination) { Preconditions.checkArgument(enforcedType.getType() == parameterType, msg); } else { Preconditions.checkArgument( parameterType == Object.class || parameterType == null, msg); } } } } // No need for the enforcedArgumentTypes List if all the types were Simple enforcedArgumentTypes = FunctionSignature.<SkylarkType>valueListOrNull(enforcedArgumentTypes); if (returnType != null) { Class<?> type = returnType; if (type == HackHackEitherList.class) { type = Object.class; } Class<?> methodReturnType = invokeMethod.getReturnType(); Preconditions.checkArgument( type == methodReturnType, "signature for function %s says it returns %s but its invoke method returns %s", getName(), returnType, methodReturnType); } }
private static SkylarkType checkType(SkylarkType builderType, SkylarkType itemType, Location loc) throws EvalException { if (SkylarkType.intersection(SkylarkType.Union.of(SkylarkType.DICT, SkylarkType.LIST), itemType) != SkylarkType.BOTTOM) { throw new EvalException( loc, String.format("sets cannot contain items of type '%s'", itemType)); } SkylarkType newType = SkylarkType.intersection(builderType, itemType); if (newType == SkylarkType.BOTTOM) { throw new EvalException( loc, String.format("cannot add an item of type '%s' to a set of '%s'", itemType, builderType)); } return newType; }
// Ensure that values are all acceptable to Skylark before to stuff them in a ClassObject private ImmutableMap<String, Object> copyValues(Map<String, Object> values) { ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder(); for (Map.Entry<String, Object> e : values.entrySet()) { builder.put(e.getKey(), SkylarkType.convertToSkylark(e.getValue(), null)); } return builder.build(); }
/** Returns a value and try to cast it into specified type */ public <TYPE> TYPE getValue(String key, Class<TYPE> type) throws EvalException { Object obj = values.get(key); if (obj == null) { return null; } SkylarkType.checkType(obj, type, key); return type.cast(obj); }
// 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; }
// 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); }
/** * A not type safe constructor for SkylarkNestedSet, specifying type as a Java class. It's * discouraged to use it unless type generic safety is guaranteed from the caller side. */ public SkylarkNestedSet(Class<?> contentType, NestedSet<?> set) { this(SkylarkType.of(contentType), set); }
/** Returns a type safe SkylarkNestedSet. Use this instead of the constructor if possible. */ public static <T> SkylarkNestedSet of(Class<T> contentType, NestedSet<T> set) { return of(SkylarkType.of(contentType), set); }