bazel syntax: tweaks to Module
This change contains a set of embarrassingly small steps towards
disentangling Module and StarlarkThread, and hiding Module.get{,TransitiveBindings}.
(Every larger step has been into a briar patch.)
- delete testVarOrderDeterminism. The property it ensures is unneeded,
and the way it uses the production code is unnatural.
- remove incorrect comment in Extension constructor.
- work around warning about Collection.equals being unordered.
- replace unreachable code (universe.universe != null) in
StarlarkThread.Builder.build by an assertion, and move it into Module.
PiperOrigin-RevId: 294725154
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Module.java b/src/main/java/com/google/devtools/build/lib/syntax/Module.java
index 2411fd2..96e5be8 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Module.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Module.java
@@ -21,7 +21,6 @@
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nullable;
@@ -166,10 +165,13 @@
if (parent == null) {
return new Module(mutability);
}
+ Preconditions.checkArgument(parent.mutability().isFrozen(), "parent frame must be frozen");
+ Preconditions.checkArgument(parent.universe == null);
+
Map<String, Object> filteredBindings = new LinkedHashMap<>();
Map<String, FlagGuardedValue> restrictedBindings = new LinkedHashMap<>();
- for (Entry<String, Object> binding : parent.getTransitiveBindings().entrySet()) {
+ for (Map.Entry<String, Object> binding : parent.bindings.entrySet()) {
if (binding.getValue() instanceof FlagGuardedValue) {
FlagGuardedValue val = (FlagGuardedValue) binding.getValue();
if (val.isObjectAccessibleUsingSemantics(semantics)) {
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
index b354107..d70f47d 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
@@ -196,8 +196,6 @@
* StarlarkThread}, and that {@code StarlarkThread}'s transitive hash code.
*/
public Extension(StarlarkThread thread) {
- // Legacy behavior: all symbols from the global Frame are exported (including symbols
- // introduced by load).
this(
ImmutableMap.copyOf(thread.module.getExportedBindings()),
thread.getTransitiveContentHashCode());
@@ -270,9 +268,16 @@
continue;
}
if (value instanceof Depset) {
- if (otherValue instanceof Depset
- && ((Depset) value).toCollection().equals(((Depset) otherValue).toCollection())) {
- continue;
+ if (otherValue instanceof Depset) {
+ // Widen to Object to avoid static checker warning
+ // about Collection.equals(Collection). We may assume
+ // these collections have the same class, even if we
+ // can't assume its equality is List-like or Set-like.
+ Object x = ((Depset) value).toCollection();
+ Object y = ((Depset) otherValue).toCollection();
+ if (x.equals(y)) {
+ continue;
+ }
}
} else if (value instanceof Dict) {
if (otherValue instanceof Dict) {
@@ -605,21 +610,6 @@
if (semantics == null) {
throw new IllegalArgumentException("must call either setSemantics or useDefaultSemantics");
}
- if (parent != null) {
- Preconditions.checkArgument(parent.mutability().isFrozen(), "parent frame must be frozen");
- if (parent.universe != null) { // This code path doesn't happen in Bazel.
-
- // Flatten the frame, ensure all builtins are in the same frame.
- parent =
- new Module(
- parent.mutability(),
- null /* parent */,
- parent.label,
- parent.getTransitiveBindings(),
- parent.restrictedBindings);
- }
- }
-
// Filter out restricted objects from the universe scope. This cannot be done in-place in
// creation of the input global universe scope, because this environment's semantics may not
// have been available during its creation. Thus, create a new universe scope for this
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java b/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
index 167be97..5b8d0ed 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
@@ -40,9 +40,9 @@
* nodes. (In the future, it will attach additional information to functions to support lexical
* scope, and even compilation of the trees to bytecode.) Validation errors are reported in the
* analogous manner to scan/parse errors: for a StarlarkFile, they are appended to {@code
- * StarlarkFile.errors}; for an expression they will be [TODO(adonovan): implement] reported by an
- * SyntaxError exception. It is legal to validate a file that already contains scan/parse errors,
- * though it may lead to secondary validation errors.
+ * StarlarkFile.errors}; for an expression they are reported by an SyntaxError exception. It is
+ * legal to validate a file that already contains scan/parse errors, though it may lead to secondary
+ * validation errors.
*/
// TODO(adonovan): make this class private. Call it through the EvalUtils facade.
public final class ValidationEnvironment extends NodeVisitor {
@@ -71,6 +71,16 @@
* use during name resolution.
*/
public interface Module {
+
+ // TODO(adonovan): opt: for efficiency, turn this into a predicate, not an enumerable set,
+ // and look up bindings as they are needed, not preemptively.
+ // Otherwise we must do work proportional to the number of bindings in the
+ // environment, not the number of free variables of the file/expression.
+ //
+ // A single method will then suffice:
+ // Scope resolve(String name) throws Undeclared
+ // This requires that the Module retain its semantics.
+
/** Returns the set of names defined by this module. The caller must not modify the set. */
Set<String> getNames();
@@ -492,9 +502,7 @@
ValidationEnvironment venv =
new ValidationEnvironment(errors, module, semantics, /*isBuildFile=*/ false);
- venv.openBlock(Scope.Local); // needed?
venv.visit(expr);
- venv.closeBlock();
if (!errors.isEmpty()) {
throw new SyntaxError(errors);
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadTest.java b/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadTest.java
index 12fb0d7..700d9e3 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadTest.java
@@ -196,31 +196,6 @@
}
@Test
- public void testVarOrderDeterminism() throws Exception {
- Mutability parentMutability = Mutability.create("parent env");
- StarlarkThread parentThread =
- StarlarkThread.builder(parentMutability).useDefaultSemantics().build();
- parentThread.getGlobals().put("a", 1);
- parentThread.getGlobals().put("c", 2);
- parentThread.getGlobals().put("b", 3);
- Module parentFrame = parentThread.getGlobals();
- parentMutability.freeze();
- Mutability mutability = Mutability.create("testing");
- StarlarkThread thread =
- StarlarkThread.builder(mutability).useDefaultSemantics().setGlobals(parentFrame).build();
- thread.getGlobals().put("x", 4);
- thread.getGlobals().put("z", 5);
- thread.getGlobals().put("y", 6);
- // The order just has to be deterministic, but for definiteness this test spells out the exact
- // order returned by the implementation: parent frame before current environment, and bindings
- // within a frame ordered by when they were added.
- assertThat(thread.getVariableNames()).containsExactly("a", "c", "b", "x", "z", "y").inOrder();
- assertThat(thread.getGlobals().getTransitiveBindings())
- .containsExactly("a", 1, "c", 2, "b", 3, "x", 4, "z", 5, "y", 6)
- .inOrder();
- }
-
- @Test
public void testTransitiveHashCodeDeterminism() throws Exception {
// As a proxy for determinism, test that changing the order of imports doesn't change the hash
// code (within any one execution).