blob: 4a6d014f16de12e731739f361ce4c3a79f1691f5 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.syntax;
16
brandjon2c98fff2018-02-06 01:32:46 -080017import com.google.common.base.Joiner;
tomlua155b532017-11-08 20:12:47 +010018import com.google.common.base.Preconditions;
Googler29eafdf2018-05-23 12:32:07 -070019import com.google.common.collect.ImmutableList;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000020import com.google.common.collect.ImmutableMap;
laurentlbe5894f02018-10-25 13:02:00 -070021import com.google.common.collect.Maps;
brandjon2c98fff2018-02-06 01:32:46 -080022import com.google.common.collect.Sets;
Kristina Chodorow6f153352016-03-21 16:20:06 +000023import com.google.devtools.build.lib.cmdline.Label;
brandjon357ed7a2017-05-09 11:37:22 -040024import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000025import com.google.devtools.build.lib.events.Event;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import com.google.devtools.build.lib.events.EventHandler;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000027import com.google.devtools.build.lib.events.EventKind;
Francois-Rene Rideau3a8ddcc2015-08-26 22:30:38 +000028import com.google.devtools.build.lib.events.Location;
janakr9ba46f82018-03-13 13:07:52 -070029import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
janakr33439dc2018-03-22 13:44:05 -070030import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
cparsonsb380dc92018-12-05 13:57:39 -080031import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000032import com.google.devtools.build.lib.syntax.Mutability.Freezable;
33import com.google.devtools.build.lib.syntax.Mutability.MutabilityException;
brendandouglas01683d32018-06-26 08:53:10 -070034import com.google.devtools.build.lib.syntax.Parser.ParsingLevel;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000035import com.google.devtools.build.lib.util.Fingerprint;
36import com.google.devtools.build.lib.util.Pair;
Laurent Le Bruna2501d52017-01-02 15:42:19 +000037import com.google.devtools.build.lib.util.SpellChecker;
Googler29eafdf2018-05-23 12:32:07 -070038import com.google.devtools.build.lib.vfs.PathFragment;
brandjon2c98fff2018-02-06 01:32:46 -080039import java.util.ArrayList;
brandjoncc0f6a62017-05-08 13:19:21 -040040import java.util.Collections;
laurentlb9d179e12018-09-27 08:15:42 -070041import java.util.HashSet;
Jon Brandveinded4fbb2017-01-18 22:21:04 +000042import java.util.LinkedHashMap;
brandjon73e768e2018-02-13 12:26:52 -080043import java.util.LinkedHashSet;
Googler29eafdf2018-05-23 12:32:07 -070044import java.util.List;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010045import java.util.Map;
cparsons6622e6f2018-10-17 15:00:09 -070046import java.util.Map.Entry;
brandjone821fb92017-07-18 21:00:22 +020047import java.util.Objects;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010048import java.util.Set;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000049import java.util.TreeSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010050import javax.annotation.Nullable;
51
52/**
brandjon0adb7842017-09-28 10:58:43 -040053 * An {@code Environment} is the main entry point to evaluating Skylark code. It embodies all the
54 * state that is required to evaluate such code, except for the current instruction pointer, which
55 * is an {@link ASTNode} that is evaluated (for expressions) or executed (for statements) with
56 * respect to this {@code Environment}.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000057 *
brandjon0adb7842017-09-28 10:58:43 -040058 * <p>{@link Continuation}-s are explicitly represented, but only partly, with another part being
59 * implicit in a series of try-catch statements, to maintain the direct style. One notable trick is
60 * how a {@link UserDefinedFunction} implements returning values as the function catching a {@link
61 * ReturnStatement.ReturnException} thrown by a {@link ReturnStatement} in the body.
62 *
63 * <p>Every {@code Environment} has a {@link Mutability} field, and must be used within a function
64 * that creates and closes this {@link Mutability} with the try-with-resource pattern. This {@link
65 * Mutability} is also used when initializing mutable objects within that {@code Environment}. When
66 * the {@code Mutability} is closed at the end of the computation, it freezes the {@code
67 * Environment} along with all of those objects. This pattern enforces the discipline that there
68 * should be no dangling mutable {@code Environment}, or concurrency between interacting {@code
69 * Environment}s. It is a Skylark-level error to attempt to mutate a frozen {@code Environment} or
70 * its objects, but it is a Java-level error to attempt to mutate an unfrozen {@code Environment} or
71 * its objects from within a different {@code Environment}.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000072 *
Laurent Le Bruna6d8fe42016-11-28 18:14:54 +000073 * <p>One creates an Environment using the {@link #builder} function, then populates it with {@link
74 * #setup}, {@link #setupDynamic} and sometimes {@link #setupOverride}, before to evaluate code in
75 * it with {@link BuildFileAST#eval}, or with {@link BuildFileAST#exec} (where the AST was obtained
76 * by passing a {@link ValidationEnvironment} constructed from the Environment to {@link
77 * BuildFileAST#parseBuildFile} or {@link BuildFileAST#parseSkylarkFile}). When the computation is
78 * over, the frozen Environment can still be queried with {@link #lookup}.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000079 *
80 * <p>Final fields of an Environment represent its dynamic state, i.e. state that remains the same
Laurent Le Bruna6d8fe42016-11-28 18:14:54 +000081 * throughout a given evaluation context, and don't change with source code location, while mutable
82 * fields embody its static state, that change with source code location. The seeming paradox is
83 * that the words "dynamic" and "static" refer to the point of view of the source code, and here we
84 * have a dual point of view.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010085 */
Googler29eafdf2018-05-23 12:32:07 -070086public final class Environment implements Freezable, Debuggable {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010087
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +000088 /**
laurentlbcaafe302018-08-28 10:24:31 -070089 * A mapping of bindings, either mutable or immutable according to an associated {@link
90 * Mutability}. The order of the bindings within a single {@link Frame} is deterministic but
91 * unspecified.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000092 *
nharmata68cc06b2018-03-01 10:21:57 -080093 * <p>Any non-frozen {@link Frame} must have the same {@link Mutability} as the current {@link
brandjoncc0f6a62017-05-08 13:19:21 -040094 * Environment}, to avoid interference from other evaluation contexts. For example, a {@link
nharmata68cc06b2018-03-01 10:21:57 -080095 * UserDefinedFunction} will close over the global frame of the {@link Environment} in which it
96 * was defined. When the function is called from other {@link Environment}s (possibly
97 * simultaneously), that global frame must already be frozen; a new local {@link Frame} is created
brandjoncc0f6a62017-05-08 13:19:21 -040098 * to represent the lexical scope of the function.
brandjonadda5122017-08-29 18:29:58 +020099 *
nharmata68cc06b2018-03-01 10:21:57 -0800100 * <p>A {@link Frame} can have an associated "parent" {@link Frame}, which is used in {@link #get}
101 * and {@link #getTransitiveBindings()}
laurentlbc8e67962018-08-31 14:20:40 -0700102 *
103 * <p>TODO(laurentlb): "parent" should be named "universe" since it contains only the builtins.
104 * The "get" method shouldn't look at the universe (so that "moduleLookup" works as expected)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100105 */
nharmata68cc06b2018-03-01 10:21:57 -0800106 public interface Frame extends Freezable {
107 /**
108 * Gets a binding from this {@link Frame} or one of its transitive parents.
109 *
110 * <p>In case of conflicts, the binding found in the {@link Frame} closest to the current one is
111 * used; the remaining bindings are shadowed.
112 *
113 * @param varname the name of the variable whose value should be retrieved
114 * @return the value bound to the variable, or null if no binding is found
115 */
116 @Nullable
117 Object get(String varname);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000118
brandjonadda5122017-08-29 18:29:58 +0200119 /**
nharmata68cc06b2018-03-01 10:21:57 -0800120 * Assigns or reassigns a binding in the current {@code Frame}.
121 *
122 * <p>If the binding has the same name as one in a transitive parent, the parent binding is
123 * shadowed (i.e., the parent is unaffected).
124 *
125 * @param env the {@link Environment} attempting the mutation
126 * @param varname the name of the variable to be bound
127 * @param value the value to bind to the variable
128 */
129 void put(Environment env, String varname, Object value) throws MutabilityException;
130
131 /**
laurentlbcaafe302018-08-28 10:24:31 -0700132 * TODO(laurentlb): Remove this method when possible. It should probably not be part of the
133 * public interface.
nharmata68cc06b2018-03-01 10:21:57 -0800134 */
135 void remove(Environment env, String varname) throws MutabilityException;
136
137 /**
laurentlbcaafe302018-08-28 10:24:31 -0700138 * Returns a map containing all bindings of this {@link Frame} and of its transitive parents,
139 * taking into account shadowing precedence.
nharmata68cc06b2018-03-01 10:21:57 -0800140 *
141 * <p>The bindings are returned in a deterministic order (for a given sequence of initial values
142 * and updates).
143 */
144 Map<String, Object> getTransitiveBindings();
145 }
146
147 interface LexicalFrame extends Frame {
148 static LexicalFrame create(Mutability mutability) {
149 return mutability.isFrozen()
150 ? ImmutableEmptyLexicalFrame.INSTANCE
151 : new MutableLexicalFrame(mutability);
152 }
nharmata62678a42018-03-08 16:43:45 -0800153
laurentlbcaafe302018-08-28 10:24:31 -0700154 static LexicalFrame create(Mutability mutability, int numArgs) {
nharmata62678a42018-03-08 16:43:45 -0800155 Preconditions.checkState(!mutability.isFrozen());
156 return new MutableLexicalFrame(mutability, /*initialCapacity=*/ numArgs);
157 }
nharmata68cc06b2018-03-01 10:21:57 -0800158 }
159
160 private static final class ImmutableEmptyLexicalFrame implements LexicalFrame {
161 private static final ImmutableEmptyLexicalFrame INSTANCE = new ImmutableEmptyLexicalFrame();
162
163 @Override
164 public Mutability mutability() {
165 return Mutability.IMMUTABLE;
166 }
167
168 @Nullable
169 @Override
170 public Object get(String varname) {
171 return null;
172 }
173
174 @Override
175 public void put(Environment env, String varname, Object value) throws MutabilityException {
176 Mutability.checkMutable(this, env.mutability());
177 throw new IllegalStateException();
178 }
179
180 @Override
181 public void remove(Environment env, String varname) throws MutabilityException {
182 Mutability.checkMutable(this, env.mutability());
183 throw new IllegalStateException();
184 }
185
186 @Override
187 public Map<String, Object> getTransitiveBindings() {
188 return ImmutableMap.of();
189 }
190
191 @Override
192 public String toString() {
193 return "<ImmutableEmptyLexicalFrame>";
194 }
195 }
196
197 private static final class MutableLexicalFrame implements LexicalFrame {
198 private final Mutability mutability;
199 /** Bindings are maintained in order of creation. */
nharmata62678a42018-03-08 16:43:45 -0800200 private final LinkedHashMap<String, Object> bindings;
nharmata68cc06b2018-03-01 10:21:57 -0800201
nharmata62678a42018-03-08 16:43:45 -0800202 private MutableLexicalFrame(Mutability mutability, int initialCapacity) {
nharmata68cc06b2018-03-01 10:21:57 -0800203 this.mutability = mutability;
laurentlbe5894f02018-10-25 13:02:00 -0700204 this.bindings = Maps.newLinkedHashMapWithExpectedSize(initialCapacity);
nharmata62678a42018-03-08 16:43:45 -0800205 }
206
207 private MutableLexicalFrame(Mutability mutability) {
208 this.mutability = mutability;
209 this.bindings = new LinkedHashMap<>();
nharmata68cc06b2018-03-01 10:21:57 -0800210 }
211
212 @Override
213 public Mutability mutability() {
214 return mutability;
215 }
216
217 @Nullable
218 @Override
219 public Object get(String varname) {
220 return bindings.get(varname);
221 }
222
223 @Override
224 public void put(Environment env, String varname, Object value) throws MutabilityException {
225 Mutability.checkMutable(this, env.mutability());
226 bindings.put(varname, value);
227 }
228
229 @Override
230 public void remove(Environment env, String varname) throws MutabilityException {
231 Mutability.checkMutable(this, env.mutability());
232 bindings.remove(varname);
233 }
234
235 @Override
236 public Map<String, Object> getTransitiveBindings() {
237 return bindings;
238 }
239
240 @Override
241 public String toString() {
242 return String.format("<MutableLexicalFrame%s>", mutability());
243 }
244 }
245
246 /**
laurentlbf6350622018-09-19 10:15:34 -0700247 * A {@link Frame} that represents the top-level definitions of a file. It contains the
248 * module-scope variables and has a reference to the universe.
nharmata68cc06b2018-03-01 10:21:57 -0800249 *
laurentlbf6350622018-09-19 10:15:34 -0700250 * <p>Bindings in a {@link GlobalFrame} may shadow those inherited from its universe.
nharmata68cc06b2018-03-01 10:21:57 -0800251 *
252 * <p>A {@link GlobalFrame} can also be constructed in a two-phase process. To do this, call the
laurentlbcaafe302018-08-28 10:24:31 -0700253 * nullary constructor to create an uninitialized {@link GlobalFrame}, then call {@link
254 * #initialize}. It is illegal to use any other method in-between these two calls, or to call
255 * {@link #initialize} on an already initialized {@link GlobalFrame}.
nharmata68cc06b2018-03-01 10:21:57 -0800256 */
nharmata68cc06b2018-03-01 10:21:57 -0800257 public static final class GlobalFrame implements Frame {
258 /**
brandjonadda5122017-08-29 18:29:58 +0200259 * Final, except that it may be initialized after instantiation. Null mutability indicates that
260 * this Frame is uninitialized.
261 */
laurentlbcaafe302018-08-28 10:24:31 -0700262 @Nullable private Mutability mutability;
brandjoncc0f6a62017-05-08 13:19:21 -0400263
brandjonadda5122017-08-29 18:29:58 +0200264 /** Final, except that it may be initialized after instantiation. */
laurentlbf6350622018-09-19 10:15:34 -0700265 @Nullable private Frame universe;
brandjonadda5122017-08-29 18:29:58 +0200266
267 /**
268 * If this frame is a global frame, the label for the corresponding target, e.g. {@code
269 * //foo:bar.bzl}.
270 *
271 * <p>Final, except that it may be initialized after instantiation.
272 */
laurentlbcaafe302018-08-28 10:24:31 -0700273 @Nullable private Label label;
brandjoncc0f6a62017-05-08 13:19:21 -0400274
brandjon73e768e2018-02-13 12:26:52 -0800275 /** Bindings are maintained in order of creation. */
276 private final LinkedHashMap<String, Object> bindings;
brandjoncc0f6a62017-05-08 13:19:21 -0400277
cparsons6622e6f2018-10-17 15:00:09 -0700278 /**
279 * A list of bindings which *would* exist in this global frame under certain semantic
280 * flags, but do not exist using the semantic flags used in this frame's creation.
281 * This map should not be used for lookups; it should only be used to throw descriptive
282 * error messages when a lookup of a restricted object is attempted.
283 **/
284 private final LinkedHashMap<String, FlagGuardedValue> restrictedBindings;
285
laurentlb9d179e12018-09-27 08:15:42 -0700286 /** Set of bindings that are exported (can be loaded from other modules). */
287 private final HashSet<String> exportedBindings;
288
brandjonadda5122017-08-29 18:29:58 +0200289 /** Constructs an uninitialized instance; caller must call {@link #initialize} before use. */
nharmata68cc06b2018-03-01 10:21:57 -0800290 public GlobalFrame() {
brandjonadda5122017-08-29 18:29:58 +0200291 this.mutability = null;
laurentlbf6350622018-09-19 10:15:34 -0700292 this.universe = null;
brandjonadda5122017-08-29 18:29:58 +0200293 this.label = null;
294 this.bindings = new LinkedHashMap<>();
cparsons6622e6f2018-10-17 15:00:09 -0700295 this.restrictedBindings = new LinkedHashMap<>();
laurentlb9d179e12018-09-27 08:15:42 -0700296 this.exportedBindings = new HashSet<>();
brandjonadda5122017-08-29 18:29:58 +0200297 }
298
brandjonb80eb182018-05-01 10:54:58 -0700299 public GlobalFrame(
300 Mutability mutability,
laurentlbf6350622018-09-19 10:15:34 -0700301 @Nullable GlobalFrame universe,
brandjonb80eb182018-05-01 10:54:58 -0700302 @Nullable Label label,
cparsons6622e6f2018-10-17 15:00:09 -0700303 @Nullable Map<String, Object> bindings,
304 @Nullable Map<String, FlagGuardedValue> restrictedBindings) {
laurentlbf6350622018-09-19 10:15:34 -0700305 Preconditions.checkState(universe == null || universe.universe == null);
brandjonadda5122017-08-29 18:29:58 +0200306 this.mutability = Preconditions.checkNotNull(mutability);
laurentlbf6350622018-09-19 10:15:34 -0700307 this.universe = universe;
308 if (label != null) {
309 this.label = label;
310 } else if (universe != null) {
311 this.label = universe.label;
312 } else {
313 this.label = null;
314 }
brandjonadda5122017-08-29 18:29:58 +0200315 this.bindings = new LinkedHashMap<>();
brandjonb80eb182018-05-01 10:54:58 -0700316 if (bindings != null) {
317 this.bindings.putAll(bindings);
318 }
cparsons6622e6f2018-10-17 15:00:09 -0700319 this.restrictedBindings = new LinkedHashMap<>();
320 if (restrictedBindings != null) {
321 this.restrictedBindings.putAll(restrictedBindings);
322 }
323 if (universe != null) {
324 this.restrictedBindings.putAll(universe.restrictedBindings);
325 }
laurentlb9d179e12018-09-27 08:15:42 -0700326 this.exportedBindings = new HashSet<>();
brandjonadda5122017-08-29 18:29:58 +0200327 }
328
nharmata68cc06b2018-03-01 10:21:57 -0800329 public GlobalFrame(Mutability mutability) {
cparsons6622e6f2018-10-17 15:00:09 -0700330 this(mutability, null, null, null, null);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000331 }
332
laurentlbf6350622018-09-19 10:15:34 -0700333 public GlobalFrame(Mutability mutability, @Nullable GlobalFrame universe) {
cparsons6622e6f2018-10-17 15:00:09 -0700334 this(mutability, universe, null, null, null);
brandjonb80eb182018-05-01 10:54:58 -0700335 }
336
laurentlbf6350622018-09-19 10:15:34 -0700337 public GlobalFrame(
338 Mutability mutability, @Nullable GlobalFrame universe, @Nullable Label label) {
cparsons6622e6f2018-10-17 15:00:09 -0700339 this(mutability, universe, label, null, null);
brandjonb80eb182018-05-01 10:54:58 -0700340 }
341
342 /** Constructs a global frame for the given builtin bindings. */
343 public static GlobalFrame createForBuiltins(Map<String, Object> bindings) {
344 Mutability mutability = Mutability.create("<builtins>").freeze();
cparsons6622e6f2018-10-17 15:00:09 -0700345 return new GlobalFrame(mutability, null, null, bindings, null);
346 }
347
348 /**
349 * Constructs a global frame based on the given parent frame, filtering out flag-restricted
350 * global objects.
351 */
352 public static GlobalFrame filterOutRestrictedBindings(
laurentlb6659b4c2019-02-18 07:23:36 -0800353 Mutability mutability, GlobalFrame parent, StarlarkSemantics semantics) {
cparsons6622e6f2018-10-17 15:00:09 -0700354 if (parent == null) {
355 return new GlobalFrame(mutability);
356 }
357 Map<String, Object> filteredBindings = new LinkedHashMap<>();
358 Map<String, FlagGuardedValue> restrictedBindings = new LinkedHashMap<>();
359
360 for (Entry<String, Object> binding : parent.getTransitiveBindings().entrySet()) {
361 if (binding.getValue() instanceof FlagGuardedValue) {
362 FlagGuardedValue val = (FlagGuardedValue) binding.getValue();
363 if (val.isObjectAccessibleUsingSemantics(semantics)) {
364 filteredBindings.put(binding.getKey(), val.getObject(semantics));
365 } else {
366 restrictedBindings.put(binding.getKey(), val);
367 }
368 } else {
369 filteredBindings.put(binding.getKey(), binding.getValue());
370 }
371 }
372
373 restrictedBindings.putAll(parent.restrictedBindings);
374
375 return new GlobalFrame(
376 mutability,
377 null /*parent */,
378 parent.label,
379 filteredBindings,
380 restrictedBindings);
brandjoncc0f6a62017-05-08 13:19:21 -0400381 }
382
brandjonadda5122017-08-29 18:29:58 +0200383 private void checkInitialized() {
384 Preconditions.checkNotNull(mutability, "Attempted to use Frame before initializing it");
brandjoncc0f6a62017-05-08 13:19:21 -0400385 }
386
brandjonadda5122017-08-29 18:29:58 +0200387 public void initialize(
laurentlbcaafe302018-08-28 10:24:31 -0700388 Mutability mutability,
laurentlbf6350622018-09-19 10:15:34 -0700389 @Nullable GlobalFrame universe,
laurentlbcaafe302018-08-28 10:24:31 -0700390 @Nullable Label label,
391 Map<String, Object> bindings) {
392 Preconditions.checkState(
laurentlbf6350622018-09-19 10:15:34 -0700393 universe == null || universe.universe == null); // no more than 1 universe
394 Preconditions.checkState(
laurentlbcaafe302018-08-28 10:24:31 -0700395 this.mutability == null, "Attempted to initialize an already initialized Frame");
brandjonadda5122017-08-29 18:29:58 +0200396 this.mutability = Preconditions.checkNotNull(mutability);
laurentlbf6350622018-09-19 10:15:34 -0700397 this.universe = universe;
398 if (label != null) {
399 this.label = label;
400 } else if (universe != null) {
401 this.label = universe.label;
402 } else {
403 this.label = null;
404 }
brandjoncc0f6a62017-05-08 13:19:21 -0400405 this.bindings.putAll(bindings);
406 }
407
408 /**
laurentlbc8e67962018-08-31 14:20:40 -0700409 * Returns a new {@link GlobalFrame} with the same fields, except that {@link #label} is set to
nharmata68cc06b2018-03-01 10:21:57 -0800410 * the given value.
brandjoncc0f6a62017-05-08 13:19:21 -0400411 */
nharmata68cc06b2018-03-01 10:21:57 -0800412 public GlobalFrame withLabel(Label label) {
brandjonadda5122017-08-29 18:29:58 +0200413 checkInitialized();
cparsons6622e6f2018-10-17 15:00:09 -0700414 return new GlobalFrame(mutability, /*universe*/ null, label, bindings,
415 /*restrictedBindings*/ null);
brandjoncc0f6a62017-05-08 13:19:21 -0400416 }
417
laurentlbf6350622018-09-19 10:15:34 -0700418 /** Returns the {@link Mutability} of this {@link GlobalFrame}. */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000419 @Override
brandjoncc0f6a62017-05-08 13:19:21 -0400420 public Mutability mutability() {
brandjonadda5122017-08-29 18:29:58 +0200421 checkInitialized();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000422 return mutability;
423 }
424
laurentlbf6350622018-09-19 10:15:34 -0700425 /**
426 * Returns the parent {@link GlobalFrame}, if it exists.
427 *
428 * <p>TODO(laurentlb): Should be called getUniverse.
429 */
brandjoncc0f6a62017-05-08 13:19:21 -0400430 @Nullable
laurentlbf6350622018-09-19 10:15:34 -0700431 public Frame getParent() {
brandjonadda5122017-08-29 18:29:58 +0200432 checkInitialized();
laurentlbf6350622018-09-19 10:15:34 -0700433 return universe;
Kristina Chodorow6f153352016-03-21 16:20:06 +0000434 }
435
laurentlbf6350622018-09-19 10:15:34 -0700436 /** Returns the label of this {@code Frame}, which may be null. */
Kristina Chodorow6f153352016-03-21 16:20:06 +0000437 @Nullable
brandjoncc0f6a62017-05-08 13:19:21 -0400438 public Label getLabel() {
brandjonadda5122017-08-29 18:29:58 +0200439 checkInitialized();
Kristina Chodorow6f153352016-03-21 16:20:06 +0000440 return label;
441 }
442
brandjoncc0f6a62017-05-08 13:19:21 -0400443 /**
laurentlbf6350622018-09-19 10:15:34 -0700444 * Returns a map of direct bindings of this {@link GlobalFrame}, ignoring universe.
brandjoncc0f6a62017-05-08 13:19:21 -0400445 *
brandjon73e768e2018-02-13 12:26:52 -0800446 * <p>The bindings are returned in a deterministic order (for a given sequence of initial values
447 * and updates).
448 *
brandjoncc0f6a62017-05-08 13:19:21 -0400449 * <p>For efficiency an unmodifiable view is returned. Callers should assume that the view is
nharmata68cc06b2018-03-01 10:21:57 -0800450 * invalidated by any subsequent modification to the {@link GlobalFrame}'s bindings.
brandjoncc0f6a62017-05-08 13:19:21 -0400451 */
452 public Map<String, Object> getBindings() {
brandjonadda5122017-08-29 18:29:58 +0200453 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400454 return Collections.unmodifiableMap(bindings);
455 }
456
laurentlb9d179e12018-09-27 08:15:42 -0700457 /**
458 * Returns a map of bindings that are exported (i.e. symbols declared using `=` and
459 * `def`, but not `load`).
460 */
461 public Map<String, Object> getExportedBindings() {
462 checkInitialized();
463 ImmutableMap.Builder<String, Object> result = new ImmutableMap.Builder<>();
464 for (Map.Entry<String, Object> entry : bindings.entrySet()) {
465 if (exportedBindings.contains(entry.getKey())) {
466 result.put(entry);
467 }
468 }
469 return result.build();
470 }
471
nharmata68cc06b2018-03-01 10:21:57 -0800472 @Override
brandjoncc0f6a62017-05-08 13:19:21 -0400473 public Map<String, Object> getTransitiveBindings() {
brandjonadda5122017-08-29 18:29:58 +0200474 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400475 // Can't use ImmutableMap.Builder because it doesn't allow duplicates.
brandjon73e768e2018-02-13 12:26:52 -0800476 LinkedHashMap<String, Object> collectedBindings = new LinkedHashMap<>();
laurentlbf6350622018-09-19 10:15:34 -0700477 if (universe != null) {
478 collectedBindings.putAll(universe.getTransitiveBindings());
479 }
480 collectedBindings.putAll(getBindings());
brandjoncc0f6a62017-05-08 13:19:21 -0400481 return collectedBindings;
482 }
483
laurentlbf6350622018-09-19 10:15:34 -0700484 public Object getDirectBindings(String varname) {
brandjonadda5122017-08-29 18:29:58 +0200485 checkInitialized();
laurentlbf6350622018-09-19 10:15:34 -0700486 return bindings.get(varname);
brandjoncc0f6a62017-05-08 13:19:21 -0400487 }
488
nharmata68cc06b2018-03-01 10:21:57 -0800489 @Override
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000490 public Object get(String varname) {
brandjonadda5122017-08-29 18:29:58 +0200491 checkInitialized();
nharmata68cc06b2018-03-01 10:21:57 -0800492 Object val = bindings.get(varname);
493 if (val != null) {
494 return val;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000495 }
laurentlbf6350622018-09-19 10:15:34 -0700496 if (universe != null) {
497 return universe.get(varname);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000498 }
499 return null;
500 }
501
nharmata68cc06b2018-03-01 10:21:57 -0800502 @Override
laurentlbcaafe302018-08-28 10:24:31 -0700503 public void put(Environment env, String varname, Object value) throws MutabilityException {
brandjonadda5122017-08-29 18:29:58 +0200504 checkInitialized();
brandjonf6316bf2017-08-02 16:42:56 +0200505 Mutability.checkMutable(this, env.mutability());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000506 bindings.put(varname, value);
507 }
508
nharmata68cc06b2018-03-01 10:21:57 -0800509 @Override
510 public void remove(Environment env, String varname) throws MutabilityException {
brandjonadda5122017-08-29 18:29:58 +0200511 checkInitialized();
brandjonf6316bf2017-08-02 16:42:56 +0200512 Mutability.checkMutable(this, env.mutability());
laurentlb9d5c0a02017-06-13 23:08:06 +0200513 bindings.remove(varname);
514 }
515
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000516 @Override
517 public String toString() {
brandjonadda5122017-08-29 18:29:58 +0200518 if (mutability == null) {
nharmata68cc06b2018-03-01 10:21:57 -0800519 return "<Uninitialized GlobalFrame>";
brandjonadda5122017-08-29 18:29:58 +0200520 } else {
nharmata68cc06b2018-03-01 10:21:57 -0800521 return String.format("<GlobalFrame%s>", mutability());
brandjonadda5122017-08-29 18:29:58 +0200522 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000523 }
524 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100525
526 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000527 * A Continuation contains data saved during a function call and restored when the function exits.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100528 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000529 private static final class Continuation {
530 /** The {@link BaseFunction} being evaluated that will return into this Continuation. */
brandjonc06e7462017-07-11 20:54:58 +0200531 final BaseFunction function;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000532
533 /** The {@link FuncallExpression} to which this Continuation will return. */
brendandouglasfe785e12018-06-12 09:39:38 -0700534 @Nullable final FuncallExpression caller;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000535
536 /** The next Continuation after this Continuation. */
brandjonc06e7462017-07-11 20:54:58 +0200537 @Nullable final Continuation continuation;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000538
539 /** The lexical Frame of the caller. */
laurentlb5d26ef12018-08-27 12:23:15 -0700540 final Frame lexicalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000541
542 /** The global Frame of the caller. */
nharmata68cc06b2018-03-01 10:21:57 -0800543 final GlobalFrame globalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000544
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000545 Continuation(
brendandouglasfe785e12018-06-12 09:39:38 -0700546 @Nullable Continuation continuation,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000547 BaseFunction function,
brendandouglasfe785e12018-06-12 09:39:38 -0700548 @Nullable FuncallExpression caller,
laurentlb5d26ef12018-08-27 12:23:15 -0700549 Frame lexicalFrame,
laurentlb2b3ad182018-12-05 05:49:45 -0800550 GlobalFrame globalFrame) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000551 this.continuation = continuation;
552 this.function = function;
553 this.caller = caller;
554 this.lexicalFrame = lexicalFrame;
555 this.globalFrame = globalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000556 }
557 }
558
brandjon357ed7a2017-05-09 11:37:22 -0400559 /** An Extension to be imported with load() into a BUILD or .bzl file. */
560 @Immutable
janakr9ba46f82018-03-13 13:07:52 -0700561 // TODO(janakr,brandjon): Do Extensions actually have to start their own memoization? Or can we
562 // have a node higher up in the hierarchy inject the mutability?
shahan9012e6f2018-04-02 13:26:14 -0700563 @AutoCodec
brandjone821fb92017-07-18 21:00:22 +0200564 public static final class Extension {
565
566 private final ImmutableMap<String, Object> bindings;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100567
brandjon357ed7a2017-05-09 11:37:22 -0400568 /**
569 * Cached hash code for the transitive content of this {@code Extension} and its dependencies.
brandjon2c98fff2018-02-06 01:32:46 -0800570 *
571 * <p>Note that "content" refers to the AST content, not the evaluated bindings.
brandjon357ed7a2017-05-09 11:37:22 -0400572 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000573 private final String transitiveContentHashCode;
574
brandjone821fb92017-07-18 21:00:22 +0200575 /** Constructs with the given hash code and bindings. */
janakr9ba46f82018-03-13 13:07:52 -0700576 @AutoCodec.Instantiator
brandjone821fb92017-07-18 21:00:22 +0200577 public Extension(ImmutableMap<String, Object> bindings, String transitiveContentHashCode) {
578 this.bindings = bindings;
brandjon357ed7a2017-05-09 11:37:22 -0400579 this.transitiveContentHashCode = transitiveContentHashCode;
580 }
581
582 /**
583 * Constructs using the bindings from the global definitions of the given {@link Environment},
584 * and that {@code Environment}'s transitive hash code.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000585 */
586 public Extension(Environment env) {
laurentlb9d179e12018-09-27 08:15:42 -0700587 // Legacy behavior: all symbols from the global Frame are exported (including symbols
588 // introduced by load).
589 this(
590 ImmutableMap.copyOf(
591 env.getSemantics().incompatibleNoTransitiveLoads()
592 ? env.globalFrame.getExportedBindings()
593 : env.globalFrame.getBindings()),
594 env.getTransitiveContentHashCode());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000595 }
596
brandjon357ed7a2017-05-09 11:37:22 -0400597 public String getTransitiveContentHashCode() {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000598 return transitiveContentHashCode;
599 }
600
brandjon73e768e2018-02-13 12:26:52 -0800601 /** Retrieves all bindings, in a deterministic order. */
brandjon357ed7a2017-05-09 11:37:22 -0400602 public ImmutableMap<String, Object> getBindings() {
603 return bindings;
Laurent Le Bruna2501d52017-01-02 15:42:19 +0000604 }
brandjone821fb92017-07-18 21:00:22 +0200605
606 @Override
607 public boolean equals(Object obj) {
608 if (this == obj) {
609 return true;
610 }
611 if (!(obj instanceof Extension)) {
612 return false;
613 }
614 Extension other = (Extension) obj;
615 return transitiveContentHashCode.equals(other.getTransitiveContentHashCode())
616 && bindings.equals(other.getBindings());
617 }
618
janakr33439dc2018-03-22 13:44:05 -0700619 private static boolean skylarkObjectsProbablyEqual(Object obj1, Object obj2) {
620 // TODO(b/76154791): check this more carefully.
621 return obj1.equals(obj2)
622 || (obj1 instanceof SkylarkValue
623 && obj2 instanceof SkylarkValue
624 && Printer.repr(obj1).equals(Printer.repr(obj2)));
625 }
626
brandjon2c98fff2018-02-06 01:32:46 -0800627 /**
628 * Throws {@link IllegalStateException} if this {@link Extension} is not equal to {@code obj}.
629 *
630 * <p>The exception explains the reason for the inequality, including all unequal bindings.
631 */
632 public void checkStateEquals(Object obj) {
633 if (this == obj) {
634 return;
635 }
636 if (!(obj instanceof Extension)) {
laurentlbcaafe302018-08-28 10:24:31 -0700637 throw new IllegalStateException(
638 String.format(
639 "Expected an equal Extension, but got a %s instead of an Extension",
640 obj == null ? "null" : obj.getClass().getName()));
brandjon2c98fff2018-02-06 01:32:46 -0800641 }
642 Extension other = (Extension) obj;
643 ImmutableMap<String, Object> otherBindings = other.getBindings();
644
645 Set<String> names = bindings.keySet();
646 Set<String> otherNames = otherBindings.keySet();
647 if (!names.equals(otherNames)) {
laurentlbcaafe302018-08-28 10:24:31 -0700648 throw new IllegalStateException(
649 String.format(
650 "Expected Extensions to be equal, but they don't define the same bindings: "
651 + "in this one but not given one: [%s]; in given one but not this one: [%s]",
652 Joiner.on(", ").join(Sets.difference(names, otherNames)),
653 Joiner.on(", ").join(Sets.difference(otherNames, names))));
brandjon2c98fff2018-02-06 01:32:46 -0800654 }
655
656 ArrayList<String> badEntries = new ArrayList<>();
657 for (String name : names) {
658 Object value = bindings.get(name);
659 Object otherValue = otherBindings.get(name);
janakr889f5622018-03-16 17:49:11 -0700660 if (value.equals(otherValue)) {
661 continue;
brandjon2c98fff2018-02-06 01:32:46 -0800662 }
janakr33439dc2018-03-22 13:44:05 -0700663 if (value instanceof SkylarkNestedSet) {
664 if (otherValue instanceof SkylarkNestedSet
665 && ((SkylarkNestedSet) value)
666 .toCollection()
667 .equals(((SkylarkNestedSet) otherValue).toCollection())) {
668 continue;
669 }
670 } else if (value instanceof SkylarkDict) {
671 if (otherValue instanceof SkylarkDict) {
672 @SuppressWarnings("unchecked")
673 SkylarkDict<Object, Object> thisDict = (SkylarkDict<Object, Object>) value;
674 @SuppressWarnings("unchecked")
675 SkylarkDict<Object, Object> otherDict = (SkylarkDict<Object, Object>) otherValue;
676 if (thisDict.size() == otherDict.size()
677 && thisDict.keySet().equals(otherDict.keySet())) {
678 boolean foundProblem = false;
679 for (Object key : thisDict.keySet()) {
680 if (!skylarkObjectsProbablyEqual(
681 Preconditions.checkNotNull(thisDict.get(key), key),
682 Preconditions.checkNotNull(otherDict.get(key), key))) {
683 foundProblem = true;
684 }
685 }
686 if (!foundProblem) {
687 continue;
688 }
689 }
690 }
691 } else if (skylarkObjectsProbablyEqual(value, otherValue)) {
janakr889f5622018-03-16 17:49:11 -0700692 continue;
693 }
694 badEntries.add(
695 String.format(
janakrcfc34322018-03-26 11:10:08 -0700696 "%s: this one has %s (class %s, %s), but given one has %s (class %s, %s)",
janakr889f5622018-03-16 17:49:11 -0700697 name,
698 Printer.repr(value),
699 value.getClass().getName(),
janakrcfc34322018-03-26 11:10:08 -0700700 value,
janakr889f5622018-03-16 17:49:11 -0700701 Printer.repr(otherValue),
janakrcfc34322018-03-26 11:10:08 -0700702 otherValue.getClass().getName(),
703 otherValue));
brandjon2c98fff2018-02-06 01:32:46 -0800704 }
705 if (!badEntries.isEmpty()) {
706 throw new IllegalStateException(
707 "Expected Extensions to be equal, but the following bindings are unequal: "
708 + Joiner.on("; ").join(badEntries));
709 }
710
711 if (!transitiveContentHashCode.equals(other.getTransitiveContentHashCode())) {
laurentlbcaafe302018-08-28 10:24:31 -0700712 throw new IllegalStateException(
713 String.format(
714 "Expected Extensions to be equal, but transitive content hashes don't match:"
715 + " %s != %s",
716 transitiveContentHashCode, other.getTransitiveContentHashCode()));
brandjon2c98fff2018-02-06 01:32:46 -0800717 }
718 }
719
brandjone821fb92017-07-18 21:00:22 +0200720 @Override
721 public int hashCode() {
722 return Objects.hash(bindings, transitiveContentHashCode);
723 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000724 }
725
726 /**
laurentlbcaafe302018-08-28 10:24:31 -0700727 * Static Frame for lexical variables that are always looked up in the current Environment or for
728 * the definition Environment of the function currently being evaluated.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000729 */
laurentlb5d26ef12018-08-27 12:23:15 -0700730 private Frame lexicalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000731
732 /**
733 * Static Frame for global variables; either the current lexical Frame if evaluation is currently
734 * happening at the global scope of a BUILD file, or the global Frame at the time of function
735 * definition if evaluation is currently happening in the body of a function. Thus functions can
736 * close over other functions defined in the same file.
737 */
nharmata68cc06b2018-03-01 10:21:57 -0800738 private GlobalFrame globalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000739
740 /**
laurentlbcaafe302018-08-28 10:24:31 -0700741 * Dynamic Frame for variables that are always looked up in the runtime Environment, and never in
742 * the lexical or "global" Environment as it was at the time of function definition. For instance,
743 * PACKAGE_NAME.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000744 */
745 private final Frame dynamicFrame;
746
laurentlbcaafe302018-08-28 10:24:31 -0700747 /** The semantics options that affect how Skylark code is evaluated. */
laurentlb6659b4c2019-02-18 07:23:36 -0800748 private final StarlarkSemantics semantics;
brandjonb712f332017-04-29 16:03:32 +0200749
cparsonsb380dc92018-12-05 13:57:39 -0800750 private final StarlarkContext starlarkContext;
751
brandjonb712f332017-04-29 16:03:32 +0200752 /**
laurentlbcaafe302018-08-28 10:24:31 -0700753 * An EventHandler for errors and warnings. This is not used in the BUILD language, however it
754 * might be used in Skylark code called from the BUILD language, so shouldn't be null.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000755 */
756 private final EventHandler eventHandler;
757
758 /**
John Field1ea7fc32015-12-22 19:37:19 +0000759 * For each imported extension, a global Skylark frame from which to load() individual bindings.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000760 */
John Field1ea7fc32015-12-22 19:37:19 +0000761 private final Map<String, Extension> importedExtensions;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000762
763 /**
laurentlbcaafe302018-08-28 10:24:31 -0700764 * When in a lexical (Skylark) frame, this lists the names of the functions in the call stack. We
765 * currently use it to artificially disable recursion.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000766 */
767 @Nullable private Continuation continuation;
768
769 /**
laurentlbcaafe302018-08-28 10:24:31 -0700770 * Gets the label of the BUILD file that is using this environment. For example, if a target //foo
771 * has a dependency on //bar which is a Skylark rule defined in //rules:my_rule.bzl being
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000772 * evaluated in this environment, then this would return //foo.
773 */
774 @Nullable private final Label callerLabel;
775
776 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000777 * Enters a scope by saving state to a new Continuation
brendandouglasfe785e12018-06-12 09:39:38 -0700778 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000779 * @param function the function whose scope to enter
nharmata68cc06b2018-03-01 10:21:57 -0800780 * @param lexical the lexical frame to use
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000781 * @param caller the source AST node for the caller
782 * @param globals the global Frame that this function closes over from its definition Environment
783 */
nharmata68cc06b2018-03-01 10:21:57 -0800784 void enterScope(
brendandouglasfe785e12018-06-12 09:39:38 -0700785 BaseFunction function,
laurentlb5d26ef12018-08-27 12:23:15 -0700786 Frame lexical,
brendandouglasfe785e12018-06-12 09:39:38 -0700787 @Nullable FuncallExpression caller,
788 GlobalFrame globals) {
laurentlb2b3ad182018-12-05 05:49:45 -0800789 continuation = new Continuation(continuation, function, caller, lexicalFrame, globalFrame);
nharmata68cc06b2018-03-01 10:21:57 -0800790 lexicalFrame = lexical;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000791 globalFrame = globals;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000792 }
793
laurentlbcaafe302018-08-28 10:24:31 -0700794 /** Exits a scope by restoring state from the current continuation */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000795 void exitScope() {
796 Preconditions.checkNotNull(continuation);
797 lexicalFrame = continuation.lexicalFrame;
798 globalFrame = continuation.globalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000799 continuation = continuation.continuation;
800 }
801
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +0000802 private final String transitiveHashCode;
Francois-Rene Rideau3a8ddcc2015-08-26 22:30:38 +0000803
804 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000805 * Is this a global Environment?
laurentlbcaafe302018-08-28 10:24:31 -0700806 *
807 * @return true if the current code is being executed at the top-level, as opposed to inside the
808 * body of a function.
Francois-Rene Rideau6e7160d2015-08-26 17:22:35 +0000809 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000810 boolean isGlobal() {
laurentlb5d26ef12018-08-27 12:23:15 -0700811 return lexicalFrame instanceof GlobalFrame;
Francois-Rene Rideau6e7160d2015-08-26 17:22:35 +0000812 }
813
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000814 @Override
815 public Mutability mutability() {
816 // the mutability of the environment is that of its dynamic frame.
817 return dynamicFrame.mutability();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100818 }
819
brandjon73e768e2018-02-13 12:26:52 -0800820 /** Returns the global variables for the Environment (not including dynamic bindings). */
nharmata68cc06b2018-03-01 10:21:57 -0800821 public GlobalFrame getGlobals() {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000822 return globalFrame;
823 }
824
825 /**
laurentlbcaafe302018-08-28 10:24:31 -0700826 * Returns an EventHandler for errors and warnings. The BUILD language doesn't use it directly,
827 * but can call Skylark code that does use it.
828 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000829 * @return an EventHandler
830 */
Francois-Rene Rideaue6a46b82015-04-10 18:31:43 +0000831 public EventHandler getEventHandler() {
832 return eventHandler;
833 }
834
michajlocbb33472017-10-23 20:30:18 +0200835 /**
836 * Returns if calling the supplied function would be a recursive call, or in other words if the
837 * supplied function is already on the stack.
838 */
839 boolean isRecursiveCall(UserDefinedFunction function) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000840 for (Continuation k = continuation; k != null; k = k.continuation) {
michajlocbb33472017-10-23 20:30:18 +0200841 if (k.function.equals(function)) {
842 return true;
843 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000844 }
michajlocbb33472017-10-23 20:30:18 +0200845 return false;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100846 }
847
michajlocbb33472017-10-23 20:30:18 +0200848 /** Returns the current function call, if it exists. */
849 @Nullable
850 BaseFunction getCurrentFunction() {
851 return continuation != null ? continuation.function : null;
852 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000853
laurentlbcaafe302018-08-28 10:24:31 -0700854 /** Returns the FuncallExpression and the BaseFunction for the top-level call being evaluated. */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000855 public Pair<FuncallExpression, BaseFunction> getTopCall() {
856 Continuation continuation = this.continuation;
857 if (continuation == null) {
858 return null;
859 }
860 while (continuation.continuation != null) {
861 continuation = continuation.continuation;
862 }
863 return new Pair<>(continuation.caller, continuation.function);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100864 }
865
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000866 /**
Laurent Le Bruna31bc4e2016-10-27 12:48:22 +0000867 * Constructs an Environment. This is the main, most basic constructor.
868 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000869 * @param globalFrame a frame for the global Environment
870 * @param dynamicFrame a frame for the dynamic Environment
871 * @param eventHandler an EventHandler for warnings, errors, etc
872 * @param importedExtensions Extension-s from which to import bindings with load()
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000873 * @param fileContentHashCode a hash for the source file being evaluated, if any
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000874 * @param callerLabel the label this environment came from
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000875 */
876 private Environment(
nharmata68cc06b2018-03-01 10:21:57 -0800877 GlobalFrame globalFrame,
878 LexicalFrame dynamicFrame,
laurentlb6659b4c2019-02-18 07:23:36 -0800879 StarlarkSemantics semantics,
cparsonsb380dc92018-12-05 13:57:39 -0800880 StarlarkContext starlarkContext,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000881 EventHandler eventHandler,
John Field1ea7fc32015-12-22 19:37:19 +0000882 Map<String, Extension> importedExtensions,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000883 @Nullable String fileContentHashCode,
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000884 @Nullable Label callerLabel) {
laurentlb5d26ef12018-08-27 12:23:15 -0700885 this.lexicalFrame = Preconditions.checkNotNull(globalFrame);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000886 this.globalFrame = Preconditions.checkNotNull(globalFrame);
887 this.dynamicFrame = Preconditions.checkNotNull(dynamicFrame);
brandjon33b49432017-05-04 18:58:52 +0200888 Preconditions.checkArgument(!globalFrame.mutability().isFrozen());
889 Preconditions.checkArgument(!dynamicFrame.mutability().isFrozen());
brandjonb712f332017-04-29 16:03:32 +0200890 this.semantics = semantics;
cparsonsb380dc92018-12-05 13:57:39 -0800891 this.starlarkContext = starlarkContext;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000892 this.eventHandler = eventHandler;
893 this.importedExtensions = importedExtensions;
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000894 this.callerLabel = callerLabel;
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +0000895 this.transitiveHashCode =
896 computeTransitiveContentHashCode(fileContentHashCode, importedExtensions);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000897 }
898
899 /**
brandjond9a1a432017-10-19 23:47:08 +0200900 * A Builder class for Environment.
901 *
902 * <p>The caller must explicitly set the semantics by calling either {@link #setSemantics} or
903 * {@link #useDefaultSemantics}.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000904 */
905 public static class Builder {
906 private final Mutability mutability;
nharmata68cc06b2018-03-01 10:21:57 -0800907 @Nullable private GlobalFrame parent;
laurentlb6659b4c2019-02-18 07:23:36 -0800908 @Nullable private StarlarkSemantics semantics;
cparsonsb380dc92018-12-05 13:57:39 -0800909 @Nullable private StarlarkContext starlarkContext;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000910 @Nullable private EventHandler eventHandler;
John Field1ea7fc32015-12-22 19:37:19 +0000911 @Nullable private Map<String, Extension> importedExtensions;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000912 @Nullable private String fileContentHashCode;
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000913 private Label label;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000914
915 Builder(Mutability mutability) {
916 this.mutability = mutability;
cparsonsb380dc92018-12-05 13:57:39 -0800917 // TODO(cparsons): Require specifying a starlarkContext (or declaring use of an empty stub).
918 this.starlarkContext = new StarlarkContext() {};
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000919 }
920
laurentlbf6350622018-09-19 10:15:34 -0700921 /**
922 * Inherits global bindings from the given parent Frame.
923 *
924 * <p>TODO(laurentlb): this should be called setUniverse.
925 */
nharmata6070ba72018-03-01 13:35:33 -0800926 public Builder setGlobals(GlobalFrame parent) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000927 Preconditions.checkState(this.parent == null);
nharmata6070ba72018-03-01 13:35:33 -0800928 this.parent = parent;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000929 return this;
930 }
931
laurentlb6659b4c2019-02-18 07:23:36 -0800932 public Builder setSemantics(StarlarkSemantics semantics) {
brandjonb712f332017-04-29 16:03:32 +0200933 this.semantics = semantics;
934 return this;
935 }
936
brandjond9a1a432017-10-19 23:47:08 +0200937 public Builder useDefaultSemantics() {
laurentlb6659b4c2019-02-18 07:23:36 -0800938 this.semantics = StarlarkSemantics.DEFAULT_SEMANTICS;
brandjond9a1a432017-10-19 23:47:08 +0200939 return this;
940 }
941
cparsonsb380dc92018-12-05 13:57:39 -0800942 public Builder setStarlarkContext(StarlarkContext starlarkContext) {
943 this.starlarkContext = starlarkContext;
944 return this;
945 }
946
947 public Builder useEmptyStarlarkContext() {
948 this.starlarkContext = new StarlarkContext() {};
949 return this;
950 }
951
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000952 /** Sets an EventHandler for errors and warnings. */
953 public Builder setEventHandler(EventHandler eventHandler) {
954 Preconditions.checkState(this.eventHandler == null);
955 this.eventHandler = eventHandler;
956 return this;
957 }
958
959 /** Declares imported extensions for load() statements. */
laurentlbcaafe302018-08-28 10:24:31 -0700960 public Builder setImportedExtensions(Map<String, Extension> importMap) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000961 Preconditions.checkState(this.importedExtensions == null);
John Field1ea7fc32015-12-22 19:37:19 +0000962 this.importedExtensions = importMap;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000963 return this;
964 }
965
966 /** Declares content hash for the source file for this Environment. */
967 public Builder setFileContentHashCode(String fileContentHashCode) {
968 this.fileContentHashCode = fileContentHashCode;
969 return this;
970 }
971
972 /** Builds the Environment. */
973 public Environment build() {
brandjon33b49432017-05-04 18:58:52 +0200974 Preconditions.checkArgument(!mutability.isFrozen());
cparsons6622e6f2018-10-17 15:00:09 -0700975 if (semantics == null) {
976 throw new IllegalArgumentException("must call either setSemantics or useDefaultSemantics");
977 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000978 if (parent != null) {
brandjon73e768e2018-02-13 12:26:52 -0800979 Preconditions.checkArgument(parent.mutability().isFrozen(), "parent frame must be frozen");
laurentlbf6350622018-09-19 10:15:34 -0700980 if (parent.universe != null) { // This code path doesn't happen in Bazel.
laurentlbc8e67962018-08-31 14:20:40 -0700981
982 // Flatten the frame, ensure all builtins are in the same frame.
983 parent =
984 new GlobalFrame(
985 parent.mutability(),
986 null /* parent */,
987 parent.label,
cparsons6622e6f2018-10-17 15:00:09 -0700988 parent.getTransitiveBindings(),
989 parent.restrictedBindings);
laurentlbc8e67962018-08-31 14:20:40 -0700990 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000991 }
cparsons6622e6f2018-10-17 15:00:09 -0700992
993 // Filter out restricted objects from the universe scope. This cannot be done in-place in
994 // creation of the input global universe scope, because this environment's semantics may not
995 // have been available during its creation. Thus, create a new universe scope for this
996 // environment which is equivalent in every way except that restricted bindings are
997 // filtered out.
998 parent = GlobalFrame.filterOutRestrictedBindings(mutability, parent, semantics);
999
nharmata68cc06b2018-03-01 10:21:57 -08001000 GlobalFrame globalFrame = new GlobalFrame(mutability, parent);
1001 LexicalFrame dynamicFrame = LexicalFrame.create(mutability);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001002 if (importedExtensions == null) {
1003 importedExtensions = ImmutableMap.of();
1004 }
Laurent Le Brun46171092015-12-23 14:04:23 +00001005 return new Environment(
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001006 globalFrame,
1007 dynamicFrame,
brandjonb712f332017-04-29 16:03:32 +02001008 semantics,
cparsonsb380dc92018-12-05 13:57:39 -08001009 starlarkContext,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001010 eventHandler,
1011 importedExtensions,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001012 fileContentHashCode,
Kristina Chodorow704d8d92016-12-01 17:47:52 +00001013 label);
1014 }
1015
1016 public Builder setCallerLabel(Label label) {
1017 this.label = label;
1018 return this;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001019 }
1020 }
1021
1022 public static Builder builder(Mutability mutability) {
1023 return new Builder(mutability);
1024 }
1025
laurentlbcaafe302018-08-28 10:24:31 -07001026 /** Returns the caller's label. */
Kristina Chodorow704d8d92016-12-01 17:47:52 +00001027 public Label getCallerLabel() {
1028 return callerLabel;
1029 }
1030
1031 /**
laurentlbcaafe302018-08-28 10:24:31 -07001032 * Sets a binding for a special dynamic variable in this Environment. This is not for end-users,
1033 * and will throw an AssertionError in case of conflict.
1034 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001035 * @param varname the name of the dynamic variable to be bound
1036 * @param value a value to bind to the variable
1037 * @return this Environment, in fluid style
1038 */
1039 public Environment setupDynamic(String varname, Object value) {
laurentlb5d26ef12018-08-27 12:23:15 -07001040 if (lookup(varname) != null) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001041 throw new AssertionError(
laurentlbcaafe302018-08-28 10:24:31 -07001042 String.format("Trying to bind dynamic variable '%s' but it is already bound", varname));
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001043 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001044 try {
1045 dynamicFrame.put(this, varname, value);
1046 } catch (MutabilityException e) {
1047 // End users don't have access to setupDynamic, and it is an implementation error
1048 // if we encounter a mutability exception.
1049 throw new AssertionError(
vladmos46907932017-06-30 14:01:45 +02001050 String.format(
1051 "Trying to bind dynamic variable '%s' in frozen environment %s", varname, this),
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001052 e);
1053 }
1054 return this;
1055 }
1056
laurentlb9d5c0a02017-06-13 23:08:06 +02001057 /** Remove variable from local bindings. */
1058 void removeLocalBinding(String varname) {
1059 try {
laurentlb5d26ef12018-08-27 12:23:15 -07001060 lexicalFrame.remove(this, varname);
laurentlb9d5c0a02017-06-13 23:08:06 +02001061 } catch (MutabilityException e) {
1062 throw new AssertionError(e);
1063 }
1064 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001065
laurentlb9d179e12018-09-27 08:15:42 -07001066 /** Modifies a binding in the current Frame. If it is the module Frame, also export it. */
1067 public Environment updateAndExport(String varname, Object value) throws EvalException {
1068 update(varname, value);
1069 if (isGlobal()) {
1070 globalFrame.exportedBindings.add(varname);
1071 }
1072 return this;
1073 }
1074
nharmataf1013482018-03-07 10:48:31 -08001075 /**
laurentlbcaafe302018-08-28 10:24:31 -07001076 * Modifies a binding in the current Frame of this Environment, as would an {@link
1077 * AssignmentStatement}. Does not try to modify an inherited binding. This will shadow any
1078 * inherited binding, which may be an error that you want to guard against before calling this
1079 * function.
1080 *
nharmataf1013482018-03-07 10:48:31 -08001081 * @param varname the name of the variable to be bound
1082 * @param value the value to bind to the variable
1083 * @return this Environment, in fluid style
1084 */
1085 public Environment update(String varname, Object value) throws EvalException {
laurentlb9dc4e202018-09-06 09:34:13 -07001086 Preconditions.checkNotNull(value, "trying to assign null to '%s'", varname);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001087 try {
laurentlb9dc4e202018-09-06 09:34:13 -07001088 lexicalFrame.put(this, varname, value);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001089 } catch (MutabilityException e) {
1090 // Note that since at this time we don't accept the global keyword, and don't have closures,
1091 // end users should never be able to mutate a frozen Environment, and a MutabilityException
1092 // is therefore a failed assertion for Bazel. However, it is possible to shadow a binding
1093 // imported from a parent Environment by updating the current Environment, which will not
1094 // trigger a MutabilityException.
1095 throw new AssertionError(
laurentlbcaafe302018-08-28 10:24:31 -07001096 Printer.format("Can't update %s to %r in frozen environment", varname, value), e);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001097 }
1098 return this;
1099 }
1100
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001101 /**
1102 * Initializes a binding in this Environment. It is an error if the variable is already bound.
1103 * This is not for end-users, and will throw an AssertionError in case of conflict.
laurentlbcaafe302018-08-28 10:24:31 -07001104 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001105 * @param varname the name of the variable to be bound
1106 * @param value the value to bind to the variable
1107 * @return this Environment, in fluid style
1108 */
1109 public Environment setup(String varname, Object value) {
laurentlb1434b172018-08-27 11:32:34 -07001110 if (lookup(varname) != null) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001111 throw new AssertionError(String.format("variable '%s' already bound", varname));
1112 }
1113 return setupOverride(varname, value);
1114 }
1115
1116 /**
laurentlbcaafe302018-08-28 10:24:31 -07001117 * Initializes a binding in this environment. Overrides any previous binding. This is not for
1118 * end-users, and will throw an AssertionError in case of conflict.
1119 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001120 * @param varname the name of the variable to be bound
1121 * @param value the value to bind to the variable
1122 * @return this Environment, in fluid style
1123 */
1124 public Environment setupOverride(String varname, Object value) {
1125 try {
1126 return update(varname, value);
1127 } catch (EvalException ee) {
1128 throw new AssertionError(ee);
1129 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001130 }
1131
1132 /**
laurentlbebb40712018-08-29 11:54:27 -07001133 * Returns the value of a variable defined in Local scope. Do not search in any parent scope. This
1134 * function should be used once the AST has been analysed and we know which variables are local.
laurentlbcaafe302018-08-28 10:24:31 -07001135 */
1136 public Object localLookup(String varname) {
1137 return lexicalFrame.get(varname);
1138 }
1139
1140 /**
laurentlbc8e67962018-08-31 14:20:40 -07001141 * Returns the value of a variable defined in the Module scope (e.g. global variables, functions).
laurentlbebb40712018-08-29 11:54:27 -07001142 */
1143 public Object moduleLookup(String varname) {
laurentlbf6350622018-09-19 10:15:34 -07001144 return globalFrame.getDirectBindings(varname);
laurentlbebb40712018-08-29 11:54:27 -07001145 }
1146
1147 /** Returns the value of a variable defined in the Universe scope (builtins). */
1148 public Object universeLookup(String varname) {
laurentlbf6350622018-09-19 10:15:34 -07001149 // TODO(laurentlb): look only at globalFrame.universe.
laurentlb2b3ad182018-12-05 05:49:45 -08001150 return globalFrame.get(varname);
laurentlbebb40712018-08-29 11:54:27 -07001151 }
1152
1153 /** Returns the value of a variable defined with setupDynamic. */
1154 public Object dynamicLookup(String varname) {
1155 return dynamicFrame.get(varname);
1156 }
1157
1158 /**
brandjon73e768e2018-02-13 12:26:52 -08001159 * Returns the value from the environment whose name is "varname" if it exists, otherwise null.
laurentlbebb40712018-08-29 11:54:27 -07001160 *
1161 * <p>TODO(laurentlb): Remove this method. Callers should know where the value is defined and use
1162 * the corresponding method (e.g. localLookup or moduleLookup).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001163 */
laurentlbc3e58512018-09-04 10:11:32 -07001164 Object lookup(String varname) {
brandjoncc0f6a62017-05-08 13:19:21 -04001165 // Lexical frame takes precedence, then globals, then dynamics.
laurentlb5d26ef12018-08-27 12:23:15 -07001166 Object lexicalValue = lexicalFrame.get(varname);
1167 if (lexicalValue != null) {
1168 return lexicalValue;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001169 }
1170 Object globalValue = globalFrame.get(varname);
laurentlb2b3ad182018-12-05 05:49:45 -08001171 if (globalValue == null) {
Vladimir Moskva4994cb32016-08-10 15:44:31 +00001172 return null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001173 }
laurentlb2b3ad182018-12-05 05:49:45 -08001174 return globalValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001175 }
1176
1177 /**
cparsons6622e6f2018-10-17 15:00:09 -07001178 * Returns a map containing all bindings that are technically <i>present</i> but are
1179 * <i>restricted</i> in the current frame with the current semantics. Such bindings should be
1180 * treated unresolvable; this method should be invoked to prepare error messaging for
1181 * evaluation environments where access of these restricted objects may have been attempted.
1182 */
1183 public Map<String, FlagGuardedValue> getRestrictedBindings() {
1184 return globalFrame.restrictedBindings;
1185 }
1186
laurentlb6659b4c2019-02-18 07:23:36 -08001187 public StarlarkSemantics getSemantics() {
brandjonb712f332017-04-29 16:03:32 +02001188 return semantics;
1189 }
1190
cparsonsb380dc92018-12-05 13:57:39 -08001191 public StarlarkContext getStarlarkContext() {
1192 return starlarkContext;
1193 }
1194
Vladimir Moskvadfad9e32016-09-15 18:22:01 +00001195 public void handleEvent(Event event) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001196 eventHandler.handle(event);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001197 }
1198
brandjon73e768e2018-02-13 12:26:52 -08001199 /**
1200 * Returns a set of all names of variables that are accessible in this {@code Environment}, in a
1201 * deterministic order.
1202 */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001203 public Set<String> getVariableNames() {
brandjon73e768e2018-02-13 12:26:52 -08001204 LinkedHashSet<String> vars = new LinkedHashSet<>();
laurentlb5d26ef12018-08-27 12:23:15 -07001205 vars.addAll(lexicalFrame.getTransitiveBindings().keySet());
1206 // No-op when globalFrame = lexicalFrame
brandjoncc0f6a62017-05-08 13:19:21 -04001207 vars.addAll(globalFrame.getTransitiveBindings().keySet());
1208 vars.addAll(dynamicFrame.getTransitiveBindings().keySet());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001209 return vars;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001210 }
1211
Googler29eafdf2018-05-23 12:32:07 -07001212 private static final class EvalEventHandler implements EventHandler {
1213 List<String> messages = new ArrayList<>();
1214
1215 @Override
1216 public void handle(Event event) {
1217 if (event.getKind() == EventKind.ERROR) {
1218 messages.add(event.getMessage());
1219 }
1220 }
1221 }
1222
1223 @Override
brendandouglas01683d32018-06-26 08:53:10 -07001224 public Object evaluate(String contents) throws EvalException, InterruptedException {
1225 ParserInputSource input =
1226 ParserInputSource.create(contents, PathFragment.create("<debug eval>"));
Googler29eafdf2018-05-23 12:32:07 -07001227 EvalEventHandler eventHandler = new EvalEventHandler();
brendandouglas01683d32018-06-26 08:53:10 -07001228 Statement statement = Parser.parseStatement(input, eventHandler, ParsingLevel.LOCAL_LEVEL);
Googler29eafdf2018-05-23 12:32:07 -07001229 if (!eventHandler.messages.isEmpty()) {
brendandouglas01683d32018-06-26 08:53:10 -07001230 throw new EvalException(statement.getLocation(), eventHandler.messages.get(0));
Googler29eafdf2018-05-23 12:32:07 -07001231 }
brendandouglas01683d32018-06-26 08:53:10 -07001232 // TODO(bazel-team): move statement handling code to Eval
1233 // deal with the most common case first
1234 if (statement.kind() == Statement.Kind.EXPRESSION) {
1235 return ((ExpressionStatement) statement).getExpression().doEval(this);
1236 }
1237 // all other statement types are executed directly
1238 Eval.fromEnvironment(this).exec(statement);
1239 switch (statement.kind()) {
1240 case ASSIGNMENT:
1241 case AUGMENTED_ASSIGNMENT:
1242 return ((AssignmentStatement) statement).getLValue().getExpression().doEval(this);
1243 case RETURN:
1244 Expression expr = ((ReturnStatement) statement).getReturnExpression();
1245 return expr != null ? expr.doEval(this) : Runtime.NONE;
1246 default:
1247 return Runtime.NONE;
1248 }
Googler29eafdf2018-05-23 12:32:07 -07001249 }
1250
1251 @Override
1252 public ImmutableList<DebugFrame> listFrames(Location currentLocation) {
1253 ImmutableList.Builder<DebugFrame> frameListBuilder = ImmutableList.builder();
1254
1255 Continuation currentContinuation = continuation;
laurentlb5d26ef12018-08-27 12:23:15 -07001256 Frame currentFrame = lexicalFrame;
Googler29eafdf2018-05-23 12:32:07 -07001257
1258 // if there's a continuation then the current frame is a lexical frame
1259 while (currentContinuation != null) {
1260 frameListBuilder.add(
1261 DebugFrame.builder()
1262 .setLexicalFrameBindings(ImmutableMap.copyOf(currentFrame.getTransitiveBindings()))
1263 .setGlobalBindings(ImmutableMap.copyOf(getGlobals().getTransitiveBindings()))
1264 .setFunctionName(currentContinuation.function.getFullName())
1265 .setLocation(currentLocation)
1266 .build());
1267
1268 currentFrame = currentContinuation.lexicalFrame;
brendandouglasfe785e12018-06-12 09:39:38 -07001269 currentLocation =
1270 currentContinuation.caller != null ? currentContinuation.caller.getLocation() : null;
Googler29eafdf2018-05-23 12:32:07 -07001271 currentContinuation = currentContinuation.continuation;
1272 }
1273
1274 frameListBuilder.add(
1275 DebugFrame.builder()
1276 .setGlobalBindings(ImmutableMap.copyOf(getGlobals().getTransitiveBindings()))
1277 .setFunctionName("<top level>")
1278 .setLocation(currentLocation)
1279 .build());
1280
1281 return frameListBuilder.build();
1282 }
1283
1284 @Override
1285 @Nullable
1286 public ReadyToPause stepControl(Stepping stepping) {
1287 final Continuation pausedContinuation = continuation;
1288
1289 switch (stepping) {
1290 case NONE:
1291 return null;
1292 case INTO:
1293 // pause at the very next statement
1294 return env -> true;
1295 case OVER:
1296 return env -> isAt(env, pausedContinuation) || isOutside(env, pausedContinuation);
1297 case OUT:
1298 // if we're at the outer-most frame, same as NONE
1299 return pausedContinuation == null ? null : env -> isOutside(env, pausedContinuation);
1300 }
1301 throw new IllegalArgumentException("Unsupported stepping type: " + stepping);
1302 }
1303
1304 /** Returns true if {@code env} is in a parent frame of {@code pausedContinuation}. */
1305 private static boolean isOutside(Environment env, @Nullable Continuation pausedContinuation) {
1306 return pausedContinuation != null && env.continuation == pausedContinuation.continuation;
1307 }
1308
1309 /** Returns true if {@code env} is at the same frame as {@code pausedContinuation. */
1310 private static boolean isAt(Environment env, @Nullable Continuation pausedContinuation) {
1311 return env.continuation == pausedContinuation;
1312 }
1313
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001314 @Override
1315 public int hashCode() {
1316 throw new UnsupportedOperationException(); // avoid nondeterminism
1317 }
1318
1319 @Override
1320 public boolean equals(Object that) {
1321 throw new UnsupportedOperationException();
1322 }
1323
1324 @Override
1325 public String toString() {
Francois-Rene Rideaubd0c7bb2015-09-21 16:54:19 +00001326 return String.format("<Environment%s>", mutability());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001327 }
1328
1329 /**
laurentlbcaafe302018-08-28 10:24:31 -07001330 * An Exception thrown when an attempt is made to import a symbol from a file that was not
1331 * properly loaded.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001332 */
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001333 static class LoadFailedException extends Exception {
John Field1ea7fc32015-12-22 19:37:19 +00001334 LoadFailedException(String importString) {
laurentlbcaafe302018-08-28 10:24:31 -07001335 super(
1336 String.format(
1337 "file '%s' was not correctly loaded. "
1338 + "Make sure the 'load' statement appears in the global scope in your file",
1339 importString));
Vladimir Moskva4994cb32016-08-10 15:44:31 +00001340 }
1341
Laurent Le Bruna2501d52017-01-02 15:42:19 +00001342 LoadFailedException(String importString, String symbolString, Iterable<String> allKeys) {
1343 super(
1344 String.format(
1345 "file '%s' does not contain symbol '%s'%s",
1346 importString, symbolString, SpellChecker.didYouMean(symbolString, allKeys)));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001347 }
1348 }
1349
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001350 void importSymbol(String importString, Identifier symbol, String nameInLoadedFile)
Vladimir Moskva4994cb32016-08-10 15:44:31 +00001351 throws LoadFailedException {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001352 Preconditions.checkState(isGlobal()); // loading is only allowed at global scope.
1353
John Field1ea7fc32015-12-22 19:37:19 +00001354 if (!importedExtensions.containsKey(importString)) {
1355 throw new LoadFailedException(importString);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001356 }
Florian Weikert9d659ad2015-07-23 14:44:36 +00001357
John Field1ea7fc32015-12-22 19:37:19 +00001358 Extension ext = importedExtensions.get(importString);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001359
brandjon357ed7a2017-05-09 11:37:22 -04001360 Map<String, Object> bindings = ext.getBindings();
1361 if (!bindings.containsKey(nameInLoadedFile)) {
1362 throw new LoadFailedException(importString, nameInLoadedFile, bindings.keySet());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001363 }
1364
brandjon357ed7a2017-05-09 11:37:22 -04001365 Object value = bindings.get(nameInLoadedFile);
Florian Weikert9d659ad2015-07-23 14:44:36 +00001366
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001367 try {
1368 update(symbol.getName(), value);
1369 } catch (EvalException e) {
John Field1ea7fc32015-12-22 19:37:19 +00001370 throw new LoadFailedException(importString);
Francois-Rene Rideau5a94e592015-09-04 19:13:47 +00001371 }
Florian Weikert6a663392015-09-02 14:04:33 +00001372 }
1373
brandjon73e768e2018-02-13 12:26:52 -08001374 /**
1375 * Computes a deterministic hash for the given base hash code and extension map (the map's order
1376 * does not matter).
1377 */
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001378 private static String computeTransitiveContentHashCode(
1379 @Nullable String baseHashCode, Map<String, Extension> importedExtensions) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001380 // Calculate a new hash from the hash of the loaded Extension-s.
1381 Fingerprint fingerprint = new Fingerprint();
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001382 if (baseHashCode != null) {
1383 fingerprint.addString(Preconditions.checkNotNull(baseHashCode));
1384 }
John Field1ea7fc32015-12-22 19:37:19 +00001385 TreeSet<String> importStrings = new TreeSet<>(importedExtensions.keySet());
1386 for (String importString : importStrings) {
1387 fingerprint.addString(importedExtensions.get(importString).getTransitiveContentHashCode());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001388 }
1389 return fingerprint.hexDigestAndReset();
Janak Ramakrishnanb6e33bc2015-09-06 21:05:23 +00001390 }
1391
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001392 /**
laurentlbcaafe302018-08-28 10:24:31 -07001393 * Returns a hash code calculated from the hash code of this Environment and the transitive
1394 * closure of other Environments it loads.
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001395 */
1396 public String getTransitiveContentHashCode() {
1397 return transitiveHashCode;
1398 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001399
brandjonb80eb182018-05-01 10:54:58 -07001400 /** A read-only {@link Environment.GlobalFrame} with False/True/None constants only. */
janakra1f17612018-08-17 06:01:51 -07001401 @AutoCodec static final GlobalFrame CONSTANTS_ONLY = createConstantsGlobals();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001402
brandjonb80eb182018-05-01 10:54:58 -07001403 /**
janakra1f17612018-08-17 06:01:51 -07001404 * A read-only {@link Environment.GlobalFrame} with initial globals as defined in MethodLibrary.
brandjonb80eb182018-05-01 10:54:58 -07001405 */
janakra1f17612018-08-17 06:01:51 -07001406 @AutoCodec public static final GlobalFrame DEFAULT_GLOBALS = createDefaultGlobals();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001407
Laurent Le Brun5e991982016-10-14 13:39:45 +00001408 /** To be removed when all call-sites are updated. */
nharmata68cc06b2018-03-01 10:21:57 -08001409 public static final GlobalFrame SKYLARK = DEFAULT_GLOBALS;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001410
nharmata68cc06b2018-03-01 10:21:57 -08001411 private static Environment.GlobalFrame createConstantsGlobals() {
brandjonb80eb182018-05-01 10:54:58 -07001412 ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
1413 Runtime.addConstantsToBuilder(builder);
1414 return GlobalFrame.createForBuiltins(builder.build());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001415 }
1416
nharmata68cc06b2018-03-01 10:21:57 -08001417 private static Environment.GlobalFrame createDefaultGlobals() {
brandjonb80eb182018-05-01 10:54:58 -07001418 ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
1419 Runtime.addConstantsToBuilder(builder);
1420 MethodLibrary.addBindingsToBuilder(builder);
1421 return GlobalFrame.createForBuiltins(builder.build());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001422 }
1423
brandjone5d95fb2017-07-13 17:23:09 +02001424 /** An exception thrown by {@link #FAIL_FAST_HANDLER}. */
1425 // TODO(bazel-team): Possibly extend RuntimeException instead of IllegalArgumentException.
1426 public static class FailFastException extends IllegalArgumentException {
1427 public FailFastException(String s) {
1428 super(s);
1429 }
1430 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001431
1432 /**
brandjone5d95fb2017-07-13 17:23:09 +02001433 * A handler that immediately throws {@link FailFastException} whenever an error or warning
1434 * occurs.
1435 *
laurentlbcaafe302018-08-28 10:24:31 -07001436 * <p>We do not reuse an existing unchecked exception type, because callers (e.g., test
1437 * assertions) need to be able to distinguish between organically occurring exceptions and
1438 * exceptions thrown by this handler.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001439 */
laurentlbcaafe302018-08-28 10:24:31 -07001440 public static final EventHandler FAIL_FAST_HANDLER =
1441 new EventHandler() {
1442 @Override
1443 public void handle(Event event) {
1444 if (EventKind.ERRORS_AND_WARNINGS.contains(event.getKind())) {
1445 throw new FailFastException(event.toString());
1446 }
1447 }
1448 };
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001449}