diff --git a/src/main/java/net/starlark/java/eval/Starlark.java b/src/main/java/net/starlark/java/eval/Starlark.java index 7bd62c8a4775b0..cfdcb451a007c7 100644 --- a/src/main/java/net/starlark/java/eval/Starlark.java +++ b/src/main/java/net/starlark/java/eval/Starlark.java @@ -244,7 +244,7 @@ public static Iterable toIterable(Object x) throws EvalException { public static Object[] toArray(Object x) throws EvalException { // Specialize Sequence and Dict to avoid allocation and/or indirection. if (x instanceof Sequence) { - return ((Sequence) x).toArray(); + return ((Sequence) x).toArray(new Object[((Sequence) x).size()]); } else if (x instanceof Dict) { return ((Dict) x).keySet().toArray(); } else { diff --git a/src/main/java/net/starlark/java/eval/StarlarkList.java b/src/main/java/net/starlark/java/eval/StarlarkList.java index e50238c2bbf0bb..76f4f51f23b484 100644 --- a/src/main/java/net/starlark/java/eval/StarlarkList.java +++ b/src/main/java/net/starlark/java/eval/StarlarkList.java @@ -78,6 +78,7 @@ public final class StarlarkList extends AbstractList // but without the extra indirection of using ArrayList. // elems[0:size] holds the logical elements, and elems[size:] are not used. + // elems.getClass() == Object[].class. This is necessary to avoid ArrayStoreException. private int size; private int iteratorCount; // number of active iterators (unused once frozen) private Object[] elems = EMPTY_ARRAY; // elems[i] == null iff i >= size @@ -88,15 +89,16 @@ public final class StarlarkList extends AbstractList private static final Object[] EMPTY_ARRAY = {}; private StarlarkList(@Nullable Mutability mutability, Object[] elems, int size) { + Preconditions.checkArgument(elems.getClass() == Object[].class); this.elems = elems; this.size = size; this.mutability = mutability == null ? Mutability.IMMUTABLE : mutability; } /** - * Takes ownership of the supplied array and returns a new StarlarkList instance that initially - * wraps the array. The caller must not subsequently modify the array, but the StarlarkList - * instance may do so. + * Takes ownership of the supplied array of class Object[].class, and returns a new StarlarkList + * instance that initially wraps the array. The caller must not subsequently modify the array, but + * the StarlarkList instance may do so. */ static StarlarkList wrap(@Nullable Mutability mutability, Object[] elems) { return new StarlarkList<>(mutability, elems, elems.length); @@ -184,13 +186,13 @@ public static StarlarkList immutableCopyOf(Iterable elems) { */ public static StarlarkList of(@Nullable Mutability mutability, T... elems) { checkElemsValid(elems); - return wrap(mutability, Arrays.copyOf(elems, elems.length)); + return wrap(mutability, Arrays.copyOf(elems, elems.length, Object[].class)); } /** Returns an immutable {@code StarlarkList} with the given items. */ public static StarlarkList immutableOf(T... elems) { checkElemsValid(elems); - return wrap(null, Arrays.copyOf(elems, elems.length)); + return wrap(null, Arrays.copyOf(elems, elems.length, Object[].class)); } @Override diff --git a/src/test/java/net/starlark/java/eval/StarlarkListTest.java b/src/test/java/net/starlark/java/eval/StarlarkListTest.java index 4505f6d9febb34..f259445db3a619 100644 --- a/src/test/java/net/starlark/java/eval/StarlarkListTest.java +++ b/src/test/java/net/starlark/java/eval/StarlarkListTest.java @@ -321,12 +321,20 @@ public void testCopyOfTakesCopy() throws EvalException { @Test public void testWrapTakesOwnershipOfArray() throws EvalException { - String[] wrapped = {"hello"}; + Object[] wrapped = {"hello"}; Mutability mutability = Mutability.create("test"); - StarlarkList mutableList = StarlarkList.wrap(mutability, wrapped); + StarlarkList mutableList = StarlarkList.wrap(mutability, wrapped); // Big no-no, but we're proving a point. wrapped[0] = "goodbye"; - assertThat((List) mutableList).containsExactly("goodbye"); + assertThat((List) mutableList).containsExactly("goodbye"); + } + + @Test + public void testOfReturnsListWhoseArrayElementTypeIsObject() throws EvalException { + Mutability mu = Mutability.create("test"); + StarlarkList list = StarlarkList.of(mu, "a", "b"); + list.addElement(StarlarkInt.of(1)); // no ArrayStoreException + assertThat(list.toString()).isEqualTo("[\"a\", \"b\", 1]"); } }