starlark: simplify implementation of dict() and dict.update()
The new logic avoids several copies.
PiperOrigin-RevId: 340709379
diff --git a/src/main/java/net/starlark/java/eval/Dict.java b/src/main/java/net/starlark/java/eval/Dict.java
index efb441b..e39f8ce 100644
--- a/src/main/java/net/starlark/java/eval/Dict.java
+++ b/src/main/java/net/starlark/java/eval/Dict.java
@@ -14,17 +14,13 @@
package net.starlark.java.eval;
-import static java.lang.Math.max;
-
import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
@@ -118,10 +114,6 @@
this(mutability, new LinkedHashMap<>());
}
- private Dict(@Nullable Mutability mutability, int initialCapacity) {
- this(mutability, new LinkedHashMap<>(initialCapacity));
- }
-
/**
* Takes ownership of the supplied LinkedHashMap and returns a new Dict that wraps it. The caller
* must not subsequently modify the map, but the Dict may do so.
@@ -302,21 +294,51 @@
},
extraKeywords = @Param(name = "kwargs", doc = "Dictionary of additional entries."),
useStarlarkThread = true)
- @SuppressWarnings("unchecked")
- public NoneType update(Object args, Dict<String, Object> kwargs, StarlarkThread thread)
+ public NoneType update(Object pairs, Dict<String, Object> kwargs, StarlarkThread thread)
throws EvalException {
- // TODO(adonovan): opt: don't materialize dict; call put directly.
-
- // These types and casts are unsafe; see class doc comment.
- Dict<K, V> dict =
- args instanceof Dict
- ? (Dict<K, V>) args
- : getDictFromArgs("update", args, thread.mutability());
- dict = Dict.plus(dict, (Dict<K, V>) kwargs, thread.mutability());
- putEntries(dict);
+ Starlark.checkMutable(this);
+ @SuppressWarnings("unchecked")
+ Dict<Object, Object> dict = (Dict) this; // see class doc comment
+ update("update", dict, pairs, kwargs);
return Starlark.NONE;
}
+ // Common implementation of dict(pairs, **kwargs) and dict.update(pairs, **kwargs).
+ static void update(
+ String funcname, Dict<Object, Object> dict, Object pairs, Dict<String, Object> kwargs)
+ throws EvalException {
+ if (pairs instanceof Dict) { // common case
+ dict.putEntries((Dict<?, ?>) pairs);
+ } else {
+ Iterable<?> iterable;
+ try {
+ iterable = Starlark.toIterable(pairs);
+ } catch (EvalException unused) {
+ throw Starlark.errorf("in %s, got %s, want iterable", funcname, Starlark.type(pairs));
+ }
+ int pos = 0;
+ for (Object item : iterable) {
+ Object[] pair;
+ try {
+ pair = Starlark.toArray(item);
+ } catch (EvalException unused) {
+ throw Starlark.errorf(
+ "in %s, dictionary update sequence element #%d is not iterable (%s)",
+ funcname, pos, Starlark.type(item));
+ }
+ if (pair.length != 2) {
+ throw Starlark.errorf(
+ "in %s, item #%d has length %d, but exactly two elements are required",
+ funcname, pos, pair.length);
+ }
+ dict.putEntry(pair[0], pair[1]);
+ pos++;
+ }
+ }
+
+ dict.putEntries(kwargs);
+ }
+
@StarlarkMethod(
name = "values",
doc =
@@ -571,52 +593,6 @@
return this.containsKey(key);
}
- static <K, V> Dict<K, V> plus(
- Dict<? extends K, ? extends V> left,
- Dict<? extends K, ? extends V> right,
- @Nullable Mutability mu) {
- Dict<K, V> result = new Dict<>(mu, max(left.size(), right.size()));
- // Update underlying map contents directly, input dicts already contain valid objects
- result.contents.putAll(left.contents);
- result.contents.putAll(right.contents);
- return result;
- }
-
- @SuppressWarnings("unchecked")
- static <K, V> Dict<K, V> getDictFromArgs(String funcname, Object args, @Nullable Mutability mu)
- throws EvalException {
- Iterable<?> seq;
- try {
- seq = Starlark.toIterable(args);
- } catch (EvalException ex) {
- throw Starlark.errorf("in %s, got %s, want iterable", funcname, Starlark.type(args));
- }
- Dict<K, V> result = Dict.of(mu);
- int pos = 0;
- for (Object item : seq) {
- Iterable<?> seq2;
- try {
- seq2 = Starlark.toIterable(item);
- } catch (EvalException ex) {
- throw Starlark.errorf(
- "in %s, dictionary update sequence element #%d is not iterable (%s)",
- funcname, pos, Starlark.type(item));
- }
- // TODO(adonovan): opt: avoid unnecessary allocations and copies.
- // Why is there no operator to compute len(x), following the spec, without iterating??
- List<Object> pair = Lists.newArrayList(seq2);
- if (pair.size() != 2) {
- throw Starlark.errorf(
- "in %s, item #%d has length %d, but exactly two elements are required",
- funcname, pos, pair.size());
- }
- // These casts are lies
- result.putEntry((K) pair.get(0), (V) pair.get(1));
- pos++;
- }
- return result;
- }
-
// java.util.Map accessors
@Override
diff --git a/src/main/java/net/starlark/java/eval/MethodLibrary.java b/src/main/java/net/starlark/java/eval/MethodLibrary.java
index a1893c4..14ea5a8e 100644
--- a/src/main/java/net/starlark/java/eval/MethodLibrary.java
+++ b/src/main/java/net/starlark/java/eval/MethodLibrary.java
@@ -481,21 +481,21 @@
+ "positional argument.",
parameters = {
@Param(
- name = "args",
+ name = "pairs",
defaultValue = "[]",
- doc =
- "Either a dictionary or a list of entries. Entries must be tuples or lists with "
- + "exactly two elements: key, value."),
+ doc = "A dict, or an iterable whose elements are each of length 2 (key, value)."),
},
extraKeywords = @Param(name = "kwargs", doc = "Dictionary of additional entries."),
useStarlarkThread = true)
- public Dict<?, ?> dict(Object args, Dict<String, Object> kwargs, StarlarkThread thread)
+ public Dict<?, ?> dict(Object pairs, Dict<String, Object> kwargs, StarlarkThread thread)
throws EvalException {
- Dict<?, ?> dict =
- args instanceof Dict
- ? (Dict) args
- : Dict.getDictFromArgs("dict", args, thread.mutability());
- return Dict.plus(dict, kwargs, thread.mutability());
+ // common case: dict(k=v, ...)
+ if (pairs instanceof StarlarkList && ((StarlarkList) pairs).isEmpty()) {
+ return kwargs;
+ }
+ Dict<Object, Object> dict = Dict.of(thread.mutability());
+ Dict.update("dict", dict, pairs, kwargs);
+ return dict;
}
@StarlarkMethod(