Skip to content

Commit b103168

Browse files
Bazel Contributorcopybara-github
authored andcommitted
Vendor starlark code from github.com/bazelbuild/bazel master branch
Included changes: - 04c01dada4aa6508a2ff8b319cb84dd4db245138 Store `BuiltinManager` in `StarlarkThread` to avoid map l... by brandjon <brandjon@google.com> - 8ad0dbf9b7fd9ffa520233cc50d8d0a6dc7ecb57 Move `CallUtils` methods into `BuiltinManager` by brandjon <brandjon@google.com> - 6edc9a8b61395ca54794479d2e9fee47a68dd4b6 unbreak bazel_bootstrap_distfile_tar_test by arostovtsev <arostovtsev@google.com> - a4089349c753b4244070c48c3ada86249a330f7f Refactor `Class`->`ClassDescriptor` map into `BuiltinMana... by brandjon <brandjon@google.com> VENDOR_STARLARK_REV_ID: 04c01dada4aa6508a2ff8b319cb84dd4db245138 Change-Id: If1d0cb142599575427dda6a4e2196164dc84d95a
1 parent fab6c04 commit b103168

File tree

7 files changed

+204
-133
lines changed

7 files changed

+204
-133
lines changed

third_party/bazel/main/java/net/starlark/java/eval/BuiltinFunction.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,17 @@ private void checkParamValue(ParamDescriptor param, Object value) throws EvalExc
523523
}
524524
}
525525

526-
private static final class BuiltinTypeFunction extends BuiltinFunction
527-
implements TypeConstructor {
526+
/**
527+
* A {@link BuiltinFunction} whose symbol is also a type constructor; for example, {@code list} is
528+
* used both as a function that returns list values ({@code l = list((1, 2, 3))}) and a
529+
* constructor for list types ({@code type T = list[int]}).
530+
*/
531+
// Non-private due to what appears to be a javac bug (present at least in JDK 21) causing
532+
// scripts/bootstrap/compile.sh and bazel_bootstrap_distfile_tar_test to spuriously fail with
533+
// "error: BuiltinTypeFunction has private access in BuiltinFunction".
534+
// TODO(bazel-team): check if we can make this class private once Bazel starts using JDK 25 or
535+
// newer to bootstrap
536+
static final class BuiltinTypeFunction extends BuiltinFunction implements TypeConstructor {
528537

529538
private final StarlarkSemantics semantics;
530539

@@ -542,7 +551,7 @@ public StarlarkType createStarlarkType(ImmutableList<Arg> argsTuple) throws Fail
542551
// and because it complicates unit tests where these preconditions fail.
543552
Class<?> tcProxy = desc.getTypeConstructorProxy();
544553
Preconditions.checkNotNull(tcProxy);
545-
TypeConstructor tc = CallUtils.getTypeConstructor(semantics, tcProxy);
554+
TypeConstructor tc = CallUtils.getBuiltinManager(semantics).getTypeConstructor(tcProxy);
546555
Preconditions.checkArgument(tc != null, "invalid type constructor proxy: %s", tcProxy);
547556
return tc.createStarlarkType(argsTuple);
548557
}

third_party/bazel/main/java/net/starlark/java/eval/CallUtils.java

Lines changed: 117 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -32,62 +32,31 @@ final class CallUtils {
3232

3333
private CallUtils() {} // uninstantiable
3434

35-
/**
36-
* Returns the {@link ClassDescriptor} for the given {@link StarlarkSemantics} and {@link Class}.
37-
*
38-
* <p>This method is a hotspot! It's called on every function call and field access. A single
39-
* `bazel build` invocation can make tens or even hundreds of millions of calls to this method.
40-
*/
41-
private static ClassDescriptor getClassDescriptor(StarlarkSemantics semantics, Class<?> clazz) {
42-
if (clazz == String.class) {
43-
clazz = StringModule.class;
44-
}
35+
/** A map for obtaining a {@link BuiltinManager} from a {@link StarlarkSemantics}. */
36+
// Historically, this code used to have a big map from (StarlarkSemantics, Class) pairs to
37+
// ClassDescriptors. This caused unnecessary GC churn and method call overhead for the dedicated
38+
// tuple objects, which became observable at scale. It was subsequently rewritten to be a
39+
// double-layer map from Semantics to Class to ClassDescriptor, which optimized for the common
40+
// case of few (typically just one) StarlarkSemantics instances. The inner map was then abstracted
41+
// into BuiltinManager.
42+
//
43+
// Avoid ConcurrentHashMap#computeIfAbsent because it is not reentrant: If a ClassDescriptor is
44+
// looked up before Starlark.UNIVERSE is initialized then the computation will re-enter the cache
45+
// and have a cycle; see b/161479826 for history.
46+
// TODO(bazel-team): Does the above cycle concern still exist?
47+
private static final ConcurrentHashMap<StarlarkSemantics, BuiltinManager> managerForSemantics =
48+
new ConcurrentHashMap<>();
4549

46-
// We use two layers of caches, with the first layer being keyed by StarlarkSemantics and the
47-
// second layer being keyed by Class. This optimizes for the common case of very few different
48-
// StarlarkSemantics instances (typically, one) being in play. In contrast, if we used a single
49-
// cache data structure then we'd need to use a dedicated tuple object for the keys of that data
50-
// structure, and the GC churn and method call overhead become meaningful at scale.
51-
//
52-
// We implement each cache ourselves using CHM#get and CHM#putIfAbsent. We don't use
53-
// CHM#computeIfAbsent since it is not reentrant: If #getClassDescriptor is called
54-
// before Starlark.UNIVERSE is initialized then the computation will re-enter the cache and have
55-
// a cycle; see b/161479826 for history.
56-
// TODO(bazel-team): Maybe the above cycle concern doesn't exist now that CallUtils is private.
57-
ConcurrentHashMap<Class<?>, ClassDescriptor> classDescriptorCache =
58-
classDescriptorCachesBySemantics.get(semantics.getClassDescriptorCacheKey());
59-
if (classDescriptorCache == null) {
60-
classDescriptorCache =
61-
new ConcurrentHashMap<>(
62-
// In May 2023, typical Bazel usage results in ~150 entries in this cache. Therefore
63-
// we presize the CHM accordingly to reduce the chance two entries use the same hash
64-
// bucket (in May 2023 this strategy was completely effective!). We used to use the
65-
// default capacity, and then the CHM would get dynamically resized to have 256
66-
// buckets, many of which had at least 2 entries which is suboptimal for such a hot
67-
// data structure.
68-
// TODO(bazel-team): Better would be to precompute the entire lookup table on server
69-
// startup (best would be to do this at compile time via an annotation processor),
70-
// rather than rely on it getting built-up dynamically as Starlark code gets
71-
// evaluated over the lifetime of the server. This way there are no concurrency
72-
// concerns, so we can use a more efficient data structure that doesn't need to
73-
// handle concurrent writes.
74-
/* initialCapacity= */ 1000);
75-
ConcurrentHashMap<Class<?>, ClassDescriptor> prev =
76-
classDescriptorCachesBySemantics.putIfAbsent(semantics, classDescriptorCache);
50+
static BuiltinManager getBuiltinManager(StarlarkSemantics semantics) {
51+
BuiltinManager manager = managerForSemantics.get(semantics.getBuiltinManagerCacheKey());
52+
if (manager == null) {
53+
manager = new BuiltinManager(semantics);
54+
BuiltinManager prev = managerForSemantics.putIfAbsent(semantics, manager);
7755
if (prev != null) {
78-
classDescriptorCache = prev; // first thread wins
56+
manager = prev; // first thread wins
7957
}
8058
}
81-
82-
ClassDescriptor classDescriptor = classDescriptorCache.get(clazz);
83-
if (classDescriptor == null) {
84-
classDescriptor = buildClassDescriptor(semantics, clazz);
85-
ClassDescriptor prev = classDescriptorCache.putIfAbsent(clazz, classDescriptor);
86-
if (prev != null) {
87-
classDescriptor = prev; // first thread wins
88-
}
89-
}
90-
return classDescriptor;
59+
return manager;
9160
}
9261

9362
/**
@@ -137,10 +106,102 @@ private static class ClassDescriptor {
137106
@Nullable TypeConstructor typeConstructor;
138107
}
139108

140-
/** Two-layer cache of {@link #buildClassDescriptor}, managed by {@link #getClassDescriptor}. */
141-
private static final ConcurrentHashMap<
142-
StarlarkSemantics, ConcurrentHashMap<Class<?>, ClassDescriptor>>
143-
classDescriptorCachesBySemantics = new ConcurrentHashMap<>();
109+
/**
110+
* A manager for obtaining descriptors for native-defined Starlark objects and methods, under a
111+
* specific {@code StarlarkSemantics}.
112+
*/
113+
static class BuiltinManager {
114+
115+
private final StarlarkSemantics semantics;
116+
117+
// In May 2023, typical Bazel usage results in ~150 entries in this cache. Therefore
118+
// we presize the CHM accordingly to reduce the chance two entries use the same hash
119+
// bucket (in May 2023 this strategy was completely effective!). We used to use the
120+
// default capacity, and then the CHM would get dynamically resized to have 256
121+
// buckets, many of which had at least 2 entries which is suboptimal for such a hot
122+
// data structure.
123+
// TODO(bazel-team): Better would be to precompute the entire lookup table on server
124+
// startup (best would be to do this at compile time via an annotation processor),
125+
// rather than rely on it getting built-up dynamically as Starlark code gets
126+
// evaluated over the lifetime of the server. This way there are no concurrency
127+
// concerns, so we can use a more efficient data structure that doesn't need to
128+
// handle concurrent writes.
129+
private final ConcurrentHashMap<Class<?>, ClassDescriptor> classDescriptorCache =
130+
new ConcurrentHashMap<>(/* initialCapacity= */ 1000);
131+
132+
private BuiltinManager(StarlarkSemantics semantics) {
133+
this.semantics = semantics;
134+
}
135+
136+
/**
137+
* Returns the {@link ClassDescriptor} for the given {@link StarlarkSemantics} and {@link
138+
* Class}.
139+
*
140+
* <p>This method is a hotspot! It's called on every function call and field access. A single
141+
* `bazel build` invocation can make tens or even hundreds of millions of calls to this method.
142+
*/
143+
private ClassDescriptor getClassDescriptor(Class<?> clazz) {
144+
if (clazz == String.class) {
145+
clazz = StringModule.class;
146+
}
147+
148+
ClassDescriptor classDescriptor = classDescriptorCache.get(clazz);
149+
if (classDescriptor == null) {
150+
classDescriptor = buildClassDescriptor(semantics, clazz);
151+
ClassDescriptor prev = classDescriptorCache.putIfAbsent(clazz, classDescriptor);
152+
if (prev != null) {
153+
classDescriptor = prev; // first thread wins
154+
}
155+
}
156+
return classDescriptor;
157+
}
158+
159+
/**
160+
* Returns the type constructor associated with the given Java class under a given {@code
161+
* StarlarkSemantics}, or null if there is none.
162+
*
163+
* <p>An example would be getting the type constructor for the {@code list} type from the class
164+
* {@code StarlarkList}.
165+
*
166+
* <p>The returned constructor has complete type information about the available Starlark
167+
* methods of the class.
168+
*/
169+
@Nullable
170+
TypeConstructor getTypeConstructor(Class<?> clazz) {
171+
return getClassDescriptor(clazz).typeConstructor;
172+
}
173+
174+
/**
175+
* Returns the set of all StarlarkMethod-annotated Java methods (excluding the self-call method)
176+
* of the specified class.
177+
*/
178+
ImmutableMap<String, MethodDescriptor> getAnnotatedMethods(Class<?> objClass) {
179+
return getClassDescriptor(objClass).methods;
180+
}
181+
182+
/**
183+
* Returns a {@link MethodDescriptor} object representing a function which calls the selfCall
184+
* java method of the given object (the {@link StarlarkMethod} method with {@link
185+
* StarlarkMethod#selfCall()} set to true). Returns null if no such method exists.
186+
*/
187+
@Nullable
188+
MethodDescriptor getSelfCallMethodDescriptor(Class<?> objClass) {
189+
return getClassDescriptor(objClass).selfCall;
190+
}
191+
192+
/**
193+
* Returns a {@code selfCall=true} method for the given class under the given Starlark
194+
* semantics, or null if no such method exists.
195+
*/
196+
@Nullable
197+
Method getSelfCallMethod(Class<?> objClass) {
198+
MethodDescriptor descriptor = getClassDescriptor(objClass).selfCall;
199+
if (descriptor == null) {
200+
return null;
201+
}
202+
return descriptor.getMethod();
203+
}
204+
}
144205

145206
private static ClassDescriptor buildClassDescriptor(StarlarkSemantics semantics, Class<?> clazz) {
146207
MethodDescriptor selfCall = null;
@@ -246,52 +307,4 @@ private static TypeConstructor getBaseTypeConstructor(Class<?> clazz) {
246307
String.format("Error invoking %s#getBaseTypeConstructor", clazz.getName()), e);
247308
}
248309
}
249-
250-
/**
251-
* Returns the type constructor associated with the given Java class under a given {@code
252-
* StarlarkSemantics}, or null if there is none.
253-
*
254-
* <p>An example would be getting the type constructor for the {@code list} type from the class
255-
* {@code StarlarkList}.
256-
*
257-
* <p>The returned constructor has complete type information about the available Starlark methods
258-
* of the class.
259-
*/
260-
@Nullable
261-
static TypeConstructor getTypeConstructor(StarlarkSemantics semantics, Class<?> clazz) {
262-
return getClassDescriptor(semantics, clazz).typeConstructor;
263-
}
264-
265-
/**
266-
* Returns the set of all StarlarkMethod-annotated Java methods (excluding the self-call method)
267-
* of the specified class.
268-
*/
269-
static ImmutableMap<String, MethodDescriptor> getAnnotatedMethods(
270-
StarlarkSemantics semantics, Class<?> objClass) {
271-
return getClassDescriptor(semantics, objClass).methods;
272-
}
273-
274-
/**
275-
* Returns a {@link MethodDescriptor} object representing a function which calls the selfCall java
276-
* method of the given object (the {@link StarlarkMethod} method with {@link
277-
* StarlarkMethod#selfCall()} set to true). Returns null if no such method exists.
278-
*/
279-
@Nullable
280-
static MethodDescriptor getSelfCallMethodDescriptor(
281-
StarlarkSemantics semantics, Class<?> objClass) {
282-
return getClassDescriptor(semantics, objClass).selfCall;
283-
}
284-
285-
/**
286-
* Returns a {@code selfCall=true} method for the given class under the given Starlark semantics,
287-
* or null if no such method exists.
288-
*/
289-
@Nullable
290-
static Method getSelfCallMethod(StarlarkSemantics semantics, Class<?> objClass) {
291-
MethodDescriptor descriptor = getClassDescriptor(semantics, objClass).selfCall;
292-
if (descriptor == null) {
293-
return null;
294-
}
295-
return descriptor.getMethod();
296-
}
297310
}

third_party/bazel/main/java/net/starlark/java/eval/Eval.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -456,13 +456,7 @@ private static void execAugmentedAssignment(StarlarkThread.Frame fr, AssignmentS
456456
Object object = eval(fr, dot.getObject());
457457
String field = dot.getField().getName();
458458
try {
459-
Object x =
460-
Starlark.getattr(
461-
fr.thread.mutability(),
462-
fr.thread.getSemantics(),
463-
object,
464-
field,
465-
/* defaultValue= */ null);
459+
Object x = Starlark.getattr(fr.thread, object, field, /* defaultValue= */ null);
466460
Object y = eval(fr, rhs);
467461
Object z;
468462
try {
@@ -662,8 +656,7 @@ private static Object evalDot(StarlarkThread.Frame fr, DotExpression dot)
662656
Object object = eval(fr, dot.getObject());
663657
String name = dot.getField().getName();
664658
try {
665-
return Starlark.getattr(
666-
fr.thread.mutability(), fr.thread.getSemantics(), object, name, /* defaultValue= */ null);
659+
return Starlark.getattr(fr.thread, object, name, /* defaultValue= */ null);
667660
} catch (EvalException ex) {
668661
fr.setErrorLocation(dot.getDotLocation());
669662
throw ex;

third_party/bazel/main/java/net/starlark/java/eval/MethodLibrary.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ public Sequence<StarlarkInt> range(
804804
},
805805
useStarlarkThread = true)
806806
public boolean hasattr(Object obj, String name, StarlarkThread thread) throws EvalException {
807-
return Starlark.hasattr(thread.getSemantics(), obj, name);
807+
return Starlark.hasattr(thread, obj, name);
808808
}
809809

810810
@StarlarkMethod(
@@ -829,11 +829,7 @@ public boolean hasattr(Object obj, String name, StarlarkThread thread) throws Ev
829829
public Object getattr(Object obj, String name, Object defaultValue, StarlarkThread thread)
830830
throws EvalException, InterruptedException {
831831
return Starlark.getattr(
832-
thread.mutability(),
833-
thread.getSemantics(),
834-
obj,
835-
name,
836-
defaultValue == Starlark.UNBOUND ? null : defaultValue);
832+
thread, obj, name, defaultValue == Starlark.UNBOUND ? null : defaultValue);
837833
}
838834

839835
@StarlarkMethod(
@@ -844,7 +840,7 @@ public Object getattr(Object obj, String name, Object defaultValue, StarlarkThre
844840
parameters = {@Param(name = "x", doc = "The object to check.")},
845841
useStarlarkThread = true)
846842
public StarlarkList<String> dir(Object object, StarlarkThread thread) throws EvalException {
847-
return Starlark.dir(thread.mutability(), thread.getSemantics(), object);
843+
return Starlark.dir(thread, object);
848844
}
849845

850846
@StarlarkMethod(

0 commit comments

Comments
 (0)