blob: 387f6ef8c0bf3b910f5502ff46782a6e57dda9a5 [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;
brandjon2c98fff2018-02-06 01:32:46 -080021import com.google.common.collect.Sets;
Kristina Chodorow6f153352016-03-21 16:20:06 +000022import com.google.devtools.build.lib.cmdline.Label;
brandjon357ed7a2017-05-09 11:37:22 -040023import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000024import com.google.devtools.build.lib.events.Event;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import com.google.devtools.build.lib.events.EventHandler;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000026import com.google.devtools.build.lib.events.EventKind;
Francois-Rene Rideau3a8ddcc2015-08-26 22:30:38 +000027import com.google.devtools.build.lib.events.Location;
janakr9ba46f82018-03-13 13:07:52 -070028import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
janakr33439dc2018-03-22 13:44:05 -070029import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000030import com.google.devtools.build.lib.syntax.Mutability.Freezable;
31import com.google.devtools.build.lib.syntax.Mutability.MutabilityException;
brendandouglas01683d32018-06-26 08:53:10 -070032import com.google.devtools.build.lib.syntax.Parser.ParsingLevel;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000033import com.google.devtools.build.lib.util.Fingerprint;
34import com.google.devtools.build.lib.util.Pair;
Laurent Le Bruna2501d52017-01-02 15:42:19 +000035import com.google.devtools.build.lib.util.SpellChecker;
Googler29eafdf2018-05-23 12:32:07 -070036import com.google.devtools.build.lib.vfs.PathFragment;
brandjon2c98fff2018-02-06 01:32:46 -080037import java.util.ArrayList;
brandjoncc0f6a62017-05-08 13:19:21 -040038import java.util.Collections;
Jon Brandveinded4fbb2017-01-18 22:21:04 +000039import java.util.LinkedHashMap;
brandjon73e768e2018-02-13 12:26:52 -080040import java.util.LinkedHashSet;
Googler29eafdf2018-05-23 12:32:07 -070041import java.util.List;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042import java.util.Map;
brandjone821fb92017-07-18 21:00:22 +020043import java.util.Objects;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010044import java.util.Set;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000045import java.util.TreeSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010046import javax.annotation.Nullable;
47
48/**
brandjon0adb7842017-09-28 10:58:43 -040049 * An {@code Environment} is the main entry point to evaluating Skylark code. It embodies all the
50 * state that is required to evaluate such code, except for the current instruction pointer, which
51 * is an {@link ASTNode} that is evaluated (for expressions) or executed (for statements) with
52 * respect to this {@code Environment}.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000053 *
brandjon0adb7842017-09-28 10:58:43 -040054 * <p>{@link Continuation}-s are explicitly represented, but only partly, with another part being
55 * implicit in a series of try-catch statements, to maintain the direct style. One notable trick is
56 * how a {@link UserDefinedFunction} implements returning values as the function catching a {@link
57 * ReturnStatement.ReturnException} thrown by a {@link ReturnStatement} in the body.
58 *
59 * <p>Every {@code Environment} has a {@link Mutability} field, and must be used within a function
60 * that creates and closes this {@link Mutability} with the try-with-resource pattern. This {@link
61 * Mutability} is also used when initializing mutable objects within that {@code Environment}. When
62 * the {@code Mutability} is closed at the end of the computation, it freezes the {@code
63 * Environment} along with all of those objects. This pattern enforces the discipline that there
64 * should be no dangling mutable {@code Environment}, or concurrency between interacting {@code
65 * Environment}s. It is a Skylark-level error to attempt to mutate a frozen {@code Environment} or
66 * its objects, but it is a Java-level error to attempt to mutate an unfrozen {@code Environment} or
67 * its objects from within a different {@code Environment}.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000068 *
Laurent Le Bruna6d8fe42016-11-28 18:14:54 +000069 * <p>One creates an Environment using the {@link #builder} function, then populates it with {@link
70 * #setup}, {@link #setupDynamic} and sometimes {@link #setupOverride}, before to evaluate code in
71 * it with {@link BuildFileAST#eval}, or with {@link BuildFileAST#exec} (where the AST was obtained
72 * by passing a {@link ValidationEnvironment} constructed from the Environment to {@link
73 * BuildFileAST#parseBuildFile} or {@link BuildFileAST#parseSkylarkFile}). When the computation is
74 * over, the frozen Environment can still be queried with {@link #lookup}.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000075 *
76 * <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 +000077 * throughout a given evaluation context, and don't change with source code location, while mutable
78 * fields embody its static state, that change with source code location. The seeming paradox is
79 * that the words "dynamic" and "static" refer to the point of view of the source code, and here we
80 * have a dual point of view.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010081 */
Googler29eafdf2018-05-23 12:32:07 -070082public final class Environment implements Freezable, Debuggable {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010083
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010084 /**
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +000085 * A phase for enabling or disabling certain builtin functions
86 */
87 public enum Phase { WORKSPACE, LOADING, ANALYSIS }
88
89 /**
nharmata68cc06b2018-03-01 10:21:57 -080090 * A mapping of bindings, either mutable or immutable according to an associated
91 * {@link Mutability}. The order of the bindings within a single {@link Frame} is deterministic
92 * but unspecified.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000093 *
nharmata68cc06b2018-03-01 10:21:57 -080094 * <p>Any non-frozen {@link Frame} must have the same {@link Mutability} as the current {@link
brandjoncc0f6a62017-05-08 13:19:21 -040095 * Environment}, to avoid interference from other evaluation contexts. For example, a {@link
nharmata68cc06b2018-03-01 10:21:57 -080096 * UserDefinedFunction} will close over the global frame of the {@link Environment} in which it
97 * was defined. When the function is called from other {@link Environment}s (possibly
98 * simultaneously), that global frame must already be frozen; a new local {@link Frame} is created
brandjoncc0f6a62017-05-08 13:19:21 -040099 * to represent the lexical scope of the function.
brandjonadda5122017-08-29 18:29:58 +0200100 *
nharmata68cc06b2018-03-01 10:21:57 -0800101 * <p>A {@link Frame} can have an associated "parent" {@link Frame}, which is used in {@link #get}
102 * and {@link #getTransitiveBindings()}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100103 */
nharmata68cc06b2018-03-01 10:21:57 -0800104 public interface Frame extends Freezable {
105 /**
106 * Gets a binding from this {@link Frame} or one of its transitive parents.
107 *
108 * <p>In case of conflicts, the binding found in the {@link Frame} closest to the current one is
109 * used; the remaining bindings are shadowed.
110 *
111 * @param varname the name of the variable whose value should be retrieved
112 * @return the value bound to the variable, or null if no binding is found
113 */
114 @Nullable
115 Object get(String varname);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000116
brandjonadda5122017-08-29 18:29:58 +0200117 /**
nharmata68cc06b2018-03-01 10:21:57 -0800118 * Assigns or reassigns a binding in the current {@code Frame}.
119 *
120 * <p>If the binding has the same name as one in a transitive parent, the parent binding is
121 * shadowed (i.e., the parent is unaffected).
122 *
123 * @param env the {@link Environment} attempting the mutation
124 * @param varname the name of the variable to be bound
125 * @param value the value to bind to the variable
126 */
127 void put(Environment env, String varname, Object value) throws MutabilityException;
128
129 /**
130 * TODO(laurentlb): Remove this method when possible. It should probably not
131 * be part of the public interface.
132 */
133 void remove(Environment env, String varname) throws MutabilityException;
134
135 /**
136 * Returns a map containing all bindings of this {@link Frame} and of its transitive
137 * parents, taking into account shadowing precedence.
138 *
139 * <p>The bindings are returned in a deterministic order (for a given sequence of initial values
140 * and updates).
141 */
142 Map<String, Object> getTransitiveBindings();
143 }
144
145 interface LexicalFrame extends Frame {
146 static LexicalFrame create(Mutability mutability) {
147 return mutability.isFrozen()
148 ? ImmutableEmptyLexicalFrame.INSTANCE
149 : new MutableLexicalFrame(mutability);
150 }
nharmata62678a42018-03-08 16:43:45 -0800151
152 static LexicalFrame createForUserDefinedFunctionCall(Mutability mutability, int numArgs) {
153 Preconditions.checkState(!mutability.isFrozen());
154 return new MutableLexicalFrame(mutability, /*initialCapacity=*/ numArgs);
155 }
nharmata68cc06b2018-03-01 10:21:57 -0800156 }
157
158 private static final class ImmutableEmptyLexicalFrame implements LexicalFrame {
159 private static final ImmutableEmptyLexicalFrame INSTANCE = new ImmutableEmptyLexicalFrame();
160
161 @Override
162 public Mutability mutability() {
163 return Mutability.IMMUTABLE;
164 }
165
166 @Nullable
167 @Override
168 public Object get(String varname) {
169 return null;
170 }
171
172 @Override
173 public void put(Environment env, String varname, Object value) throws MutabilityException {
174 Mutability.checkMutable(this, env.mutability());
175 throw new IllegalStateException();
176 }
177
178 @Override
179 public void remove(Environment env, String varname) throws MutabilityException {
180 Mutability.checkMutable(this, env.mutability());
181 throw new IllegalStateException();
182 }
183
184 @Override
185 public Map<String, Object> getTransitiveBindings() {
186 return ImmutableMap.of();
187 }
188
189 @Override
190 public String toString() {
191 return "<ImmutableEmptyLexicalFrame>";
192 }
193 }
194
195 private static final class MutableLexicalFrame implements LexicalFrame {
196 private final Mutability mutability;
197 /** Bindings are maintained in order of creation. */
nharmata62678a42018-03-08 16:43:45 -0800198 private final LinkedHashMap<String, Object> bindings;
nharmata68cc06b2018-03-01 10:21:57 -0800199
nharmata62678a42018-03-08 16:43:45 -0800200 private MutableLexicalFrame(Mutability mutability, int initialCapacity) {
nharmata68cc06b2018-03-01 10:21:57 -0800201 this.mutability = mutability;
nharmata62678a42018-03-08 16:43:45 -0800202 this.bindings = new LinkedHashMap<>(initialCapacity);
203 }
204
205 private MutableLexicalFrame(Mutability mutability) {
206 this.mutability = mutability;
207 this.bindings = new LinkedHashMap<>();
nharmata68cc06b2018-03-01 10:21:57 -0800208 }
209
210 @Override
211 public Mutability mutability() {
212 return mutability;
213 }
214
215 @Nullable
216 @Override
217 public Object get(String varname) {
218 return bindings.get(varname);
219 }
220
221 @Override
222 public void put(Environment env, String varname, Object value) throws MutabilityException {
223 Mutability.checkMutable(this, env.mutability());
224 bindings.put(varname, value);
225 }
226
227 @Override
228 public void remove(Environment env, String varname) throws MutabilityException {
229 Mutability.checkMutable(this, env.mutability());
230 bindings.remove(varname);
231 }
232
233 @Override
234 public Map<String, Object> getTransitiveBindings() {
235 return bindings;
236 }
237
238 @Override
239 public String toString() {
240 return String.format("<MutableLexicalFrame%s>", mutability());
241 }
242 }
243
244 /**
245 * A {@link Frame} that can have a parent {@link GlobalFrame} from which it inherits bindings.
246 *
247 * <p>Bindings in a {@link GlobalFrame} may shadow those inherited from its parents. A chain of
248 * {@link GlobalFrame}s can represent different builtin scopes with a linear precedence ordering.
249 *
250 * <p>A {@link GlobalFrame} can also be constructed in a two-phase process. To do this, call the
251 * nullary constructor to create an uninitialized {@link GlobalFrame}, then call
252 * {@link #initialize}. It is illegal to use any other method in-between these two calls, or to
253 * call {@link #initialize} on an already initialized {@link GlobalFrame}.
254 */
255
256 public static final class GlobalFrame implements Frame {
257 /**
brandjonadda5122017-08-29 18:29:58 +0200258 * Final, except that it may be initialized after instantiation. Null mutability indicates that
259 * this Frame is uninitialized.
260 */
brandjoncc0f6a62017-05-08 13:19:21 -0400261 @Nullable
brandjonadda5122017-08-29 18:29:58 +0200262 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. */
brandjoncc0f6a62017-05-08 13:19:21 -0400265 @Nullable
nharmata68cc06b2018-03-01 10:21:57 -0800266 private GlobalFrame parent;
brandjonadda5122017-08-29 18:29:58 +0200267
268 /**
269 * If this frame is a global frame, the label for the corresponding target, e.g. {@code
270 * //foo:bar.bzl}.
271 *
272 * <p>Final, except that it may be initialized after instantiation.
273 */
274 @Nullable
275 private Label label;
brandjoncc0f6a62017-05-08 13:19:21 -0400276
brandjon73e768e2018-02-13 12:26:52 -0800277 /** Bindings are maintained in order of creation. */
278 private final LinkedHashMap<String, Object> bindings;
brandjoncc0f6a62017-05-08 13:19:21 -0400279
brandjonadda5122017-08-29 18:29:58 +0200280 /** Constructs an uninitialized instance; caller must call {@link #initialize} before use. */
nharmata68cc06b2018-03-01 10:21:57 -0800281 public GlobalFrame() {
brandjonadda5122017-08-29 18:29:58 +0200282 this.mutability = null;
283 this.parent = null;
284 this.label = null;
285 this.bindings = new LinkedHashMap<>();
286 }
287
brandjonb80eb182018-05-01 10:54:58 -0700288 public GlobalFrame(
289 Mutability mutability,
290 @Nullable GlobalFrame parent,
291 @Nullable Label label,
292 @Nullable Map<String, Object> bindings) {
brandjonadda5122017-08-29 18:29:58 +0200293 this.mutability = Preconditions.checkNotNull(mutability);
294 this.parent = parent;
295 this.label = label;
296 this.bindings = new LinkedHashMap<>();
brandjonb80eb182018-05-01 10:54:58 -0700297 if (bindings != null) {
298 this.bindings.putAll(bindings);
299 }
brandjonadda5122017-08-29 18:29:58 +0200300 }
301
nharmata68cc06b2018-03-01 10:21:57 -0800302 public GlobalFrame(Mutability mutability) {
brandjonb80eb182018-05-01 10:54:58 -0700303 this(mutability, null, null, null);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000304 }
305
brandjonb80eb182018-05-01 10:54:58 -0700306 public GlobalFrame(Mutability mutability, @Nullable GlobalFrame parent) {
307 this(mutability, parent, null, null);
308 }
309
310 public GlobalFrame(Mutability mutability, @Nullable GlobalFrame parent, @Nullable Label label) {
311 this(mutability, parent, label, null);
312 }
313
314 /** Constructs a global frame for the given builtin bindings. */
315 public static GlobalFrame createForBuiltins(Map<String, Object> bindings) {
316 Mutability mutability = Mutability.create("<builtins>").freeze();
317 return new GlobalFrame(mutability, null, null, bindings);
brandjoncc0f6a62017-05-08 13:19:21 -0400318 }
319
brandjonadda5122017-08-29 18:29:58 +0200320 private void checkInitialized() {
321 Preconditions.checkNotNull(mutability, "Attempted to use Frame before initializing it");
brandjoncc0f6a62017-05-08 13:19:21 -0400322 }
323
brandjonadda5122017-08-29 18:29:58 +0200324 public void initialize(
nharmata68cc06b2018-03-01 10:21:57 -0800325 Mutability mutability, @Nullable GlobalFrame parent,
brandjonadda5122017-08-29 18:29:58 +0200326 @Nullable Label label, Map<String, Object> bindings) {
327 Preconditions.checkState(this.mutability == null,
328 "Attempted to initialize an already initialized Frame");
329 this.mutability = Preconditions.checkNotNull(mutability);
330 this.parent = parent;
331 this.label = label;
brandjoncc0f6a62017-05-08 13:19:21 -0400332 this.bindings.putAll(bindings);
333 }
334
335 /**
nharmata68cc06b2018-03-01 10:21:57 -0800336 * Returns a new {@link GlobalFrame} that is a descendant of this one with {@link #label} set to
337 * the given value.
brandjoncc0f6a62017-05-08 13:19:21 -0400338 */
nharmata68cc06b2018-03-01 10:21:57 -0800339 public GlobalFrame withLabel(Label label) {
brandjonadda5122017-08-29 18:29:58 +0200340 checkInitialized();
nharmata68cc06b2018-03-01 10:21:57 -0800341 return new GlobalFrame(mutability, this, label);
brandjoncc0f6a62017-05-08 13:19:21 -0400342 }
343
344 /**
nharmata68cc06b2018-03-01 10:21:57 -0800345 * Returns the {@link Mutability} of this {@link GlobalFrame}, which may be different from its
brandjoncc0f6a62017-05-08 13:19:21 -0400346 * parent's.
347 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000348 @Override
brandjoncc0f6a62017-05-08 13:19:21 -0400349 public Mutability mutability() {
brandjonadda5122017-08-29 18:29:58 +0200350 checkInitialized();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000351 return mutability;
352 }
353
nharmata68cc06b2018-03-01 10:21:57 -0800354 /** Returns the parent {@link GlobalFrame}, if it exists. */
brandjoncc0f6a62017-05-08 13:19:21 -0400355 @Nullable
nharmata68cc06b2018-03-01 10:21:57 -0800356 public GlobalFrame getParent() {
brandjonadda5122017-08-29 18:29:58 +0200357 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400358 return parent;
Kristina Chodorow6f153352016-03-21 16:20:06 +0000359 }
360
361 /**
brandjoncc0f6a62017-05-08 13:19:21 -0400362 * Returns the label of this {@code Frame}, which may be null. Parent labels are not consulted.
363 *
364 * <p>Usually you want to use {@link #getTransitiveLabel}; this is just an accessor for
365 * completeness.
Kristina Chodorow6f153352016-03-21 16:20:06 +0000366 */
367 @Nullable
brandjoncc0f6a62017-05-08 13:19:21 -0400368 public Label getLabel() {
brandjonadda5122017-08-29 18:29:58 +0200369 checkInitialized();
Kristina Chodorow6f153352016-03-21 16:20:06 +0000370 return label;
371 }
372
373 /**
nharmata68cc06b2018-03-01 10:21:57 -0800374 * Walks from this {@link GlobalFrame} up through transitive parents, and returns the first
375 * non-null label found, or null if all labels are null.
brandjoncc0f6a62017-05-08 13:19:21 -0400376 */
377 @Nullable
378 public Label getTransitiveLabel() {
brandjonadda5122017-08-29 18:29:58 +0200379 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400380 if (label != null) {
381 return label;
382 } else if (parent != null) {
383 return parent.getTransitiveLabel();
384 } else {
385 return null;
386 }
387 }
388
389 /**
nharmata68cc06b2018-03-01 10:21:57 -0800390 * Returns a map of direct bindings of this {@link GlobalFrame}, ignoring parents.
brandjoncc0f6a62017-05-08 13:19:21 -0400391 *
brandjon73e768e2018-02-13 12:26:52 -0800392 * <p>The bindings are returned in a deterministic order (for a given sequence of initial values
393 * and updates).
394 *
brandjoncc0f6a62017-05-08 13:19:21 -0400395 * <p>For efficiency an unmodifiable view is returned. Callers should assume that the view is
nharmata68cc06b2018-03-01 10:21:57 -0800396 * invalidated by any subsequent modification to the {@link GlobalFrame}'s bindings.
brandjoncc0f6a62017-05-08 13:19:21 -0400397 */
398 public Map<String, Object> getBindings() {
brandjonadda5122017-08-29 18:29:58 +0200399 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400400 return Collections.unmodifiableMap(bindings);
401 }
402
nharmata68cc06b2018-03-01 10:21:57 -0800403 @Override
brandjoncc0f6a62017-05-08 13:19:21 -0400404 public Map<String, Object> getTransitiveBindings() {
brandjonadda5122017-08-29 18:29:58 +0200405 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400406 // Can't use ImmutableMap.Builder because it doesn't allow duplicates.
brandjon73e768e2018-02-13 12:26:52 -0800407 LinkedHashMap<String, Object> collectedBindings = new LinkedHashMap<>();
brandjoncc0f6a62017-05-08 13:19:21 -0400408 accumulateTransitiveBindings(collectedBindings);
409 return collectedBindings;
410 }
411
brandjon73e768e2018-02-13 12:26:52 -0800412 private void accumulateTransitiveBindings(LinkedHashMap<String, Object> accumulator) {
brandjonadda5122017-08-29 18:29:58 +0200413 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400414 // Put parents first, so child bindings take precedence.
415 if (parent != null) {
416 parent.accumulateTransitiveBindings(accumulator);
417 }
418 accumulator.putAll(bindings);
419 }
420
nharmata68cc06b2018-03-01 10:21:57 -0800421 @Override
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000422 public Object get(String varname) {
brandjonadda5122017-08-29 18:29:58 +0200423 checkInitialized();
nharmata68cc06b2018-03-01 10:21:57 -0800424 Object val = bindings.get(varname);
425 if (val != null) {
426 return val;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000427 }
428 if (parent != null) {
429 return parent.get(varname);
430 }
431 return null;
432 }
433
nharmata68cc06b2018-03-01 10:21:57 -0800434 @Override
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000435 public void put(Environment env, String varname, Object value)
436 throws MutabilityException {
brandjonadda5122017-08-29 18:29:58 +0200437 checkInitialized();
brandjonf6316bf2017-08-02 16:42:56 +0200438 Mutability.checkMutable(this, env.mutability());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000439 bindings.put(varname, value);
440 }
441
nharmata68cc06b2018-03-01 10:21:57 -0800442 @Override
443 public void remove(Environment env, String varname) throws MutabilityException {
brandjonadda5122017-08-29 18:29:58 +0200444 checkInitialized();
brandjonf6316bf2017-08-02 16:42:56 +0200445 Mutability.checkMutable(this, env.mutability());
laurentlb9d5c0a02017-06-13 23:08:06 +0200446 bindings.remove(varname);
447 }
448
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000449 @Override
450 public String toString() {
brandjonadda5122017-08-29 18:29:58 +0200451 if (mutability == null) {
nharmata68cc06b2018-03-01 10:21:57 -0800452 return "<Uninitialized GlobalFrame>";
brandjonadda5122017-08-29 18:29:58 +0200453 } else {
nharmata68cc06b2018-03-01 10:21:57 -0800454 return String.format("<GlobalFrame%s>", mutability());
brandjonadda5122017-08-29 18:29:58 +0200455 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000456 }
457 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100458
459 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000460 * A Continuation contains data saved during a function call and restored when the function exits.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100461 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000462 private static final class Continuation {
463 /** The {@link BaseFunction} being evaluated that will return into this Continuation. */
brandjonc06e7462017-07-11 20:54:58 +0200464 final BaseFunction function;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000465
466 /** The {@link FuncallExpression} to which this Continuation will return. */
brendandouglasfe785e12018-06-12 09:39:38 -0700467 @Nullable final FuncallExpression caller;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000468
469 /** The next Continuation after this Continuation. */
brandjonc06e7462017-07-11 20:54:58 +0200470 @Nullable final Continuation continuation;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000471
472 /** The lexical Frame of the caller. */
laurentlb5d26ef12018-08-27 12:23:15 -0700473 final Frame lexicalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000474
475 /** The global Frame of the caller. */
nharmata68cc06b2018-03-01 10:21:57 -0800476 final GlobalFrame globalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000477
478 /** The set of known global variables of the caller. */
brandjon73e768e2018-02-13 12:26:52 -0800479 @Nullable final LinkedHashSet<String> knownGlobalVariables;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000480
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000481 Continuation(
brendandouglasfe785e12018-06-12 09:39:38 -0700482 @Nullable Continuation continuation,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000483 BaseFunction function,
brendandouglasfe785e12018-06-12 09:39:38 -0700484 @Nullable FuncallExpression caller,
laurentlb5d26ef12018-08-27 12:23:15 -0700485 Frame lexicalFrame,
nharmata68cc06b2018-03-01 10:21:57 -0800486 GlobalFrame globalFrame,
brendandouglasfe785e12018-06-12 09:39:38 -0700487 @Nullable LinkedHashSet<String> knownGlobalVariables) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000488 this.continuation = continuation;
489 this.function = function;
490 this.caller = caller;
491 this.lexicalFrame = lexicalFrame;
492 this.globalFrame = globalFrame;
Jon Brandvein83e3acb2016-08-11 18:53:26 +0000493 this.knownGlobalVariables = knownGlobalVariables;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000494 }
495 }
496
brandjon357ed7a2017-05-09 11:37:22 -0400497 /** An Extension to be imported with load() into a BUILD or .bzl file. */
498 @Immutable
janakr9ba46f82018-03-13 13:07:52 -0700499 // TODO(janakr,brandjon): Do Extensions actually have to start their own memoization? Or can we
500 // have a node higher up in the hierarchy inject the mutability?
shahan9012e6f2018-04-02 13:26:14 -0700501 @AutoCodec
brandjone821fb92017-07-18 21:00:22 +0200502 public static final class Extension {
503
504 private final ImmutableMap<String, Object> bindings;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100505
brandjon357ed7a2017-05-09 11:37:22 -0400506 /**
507 * Cached hash code for the transitive content of this {@code Extension} and its dependencies.
brandjon2c98fff2018-02-06 01:32:46 -0800508 *
509 * <p>Note that "content" refers to the AST content, not the evaluated bindings.
brandjon357ed7a2017-05-09 11:37:22 -0400510 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000511 private final String transitiveContentHashCode;
512
brandjone821fb92017-07-18 21:00:22 +0200513 /** Constructs with the given hash code and bindings. */
janakr9ba46f82018-03-13 13:07:52 -0700514 @AutoCodec.Instantiator
brandjone821fb92017-07-18 21:00:22 +0200515 public Extension(ImmutableMap<String, Object> bindings, String transitiveContentHashCode) {
516 this.bindings = bindings;
brandjon357ed7a2017-05-09 11:37:22 -0400517 this.transitiveContentHashCode = transitiveContentHashCode;
518 }
519
520 /**
521 * Constructs using the bindings from the global definitions of the given {@link Environment},
522 * and that {@code Environment}'s transitive hash code.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000523 */
524 public Extension(Environment env) {
brandjone821fb92017-07-18 21:00:22 +0200525 this(ImmutableMap.copyOf(env.globalFrame.bindings), env.getTransitiveContentHashCode());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000526 }
527
brandjon357ed7a2017-05-09 11:37:22 -0400528 public String getTransitiveContentHashCode() {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000529 return transitiveContentHashCode;
530 }
531
brandjon73e768e2018-02-13 12:26:52 -0800532 /** Retrieves all bindings, in a deterministic order. */
brandjon357ed7a2017-05-09 11:37:22 -0400533 public ImmutableMap<String, Object> getBindings() {
534 return bindings;
Laurent Le Bruna2501d52017-01-02 15:42:19 +0000535 }
brandjone821fb92017-07-18 21:00:22 +0200536
537 @Override
538 public boolean equals(Object obj) {
539 if (this == obj) {
540 return true;
541 }
542 if (!(obj instanceof Extension)) {
543 return false;
544 }
545 Extension other = (Extension) obj;
546 return transitiveContentHashCode.equals(other.getTransitiveContentHashCode())
547 && bindings.equals(other.getBindings());
548 }
549
janakr33439dc2018-03-22 13:44:05 -0700550 private static boolean skylarkObjectsProbablyEqual(Object obj1, Object obj2) {
551 // TODO(b/76154791): check this more carefully.
552 return obj1.equals(obj2)
553 || (obj1 instanceof SkylarkValue
554 && obj2 instanceof SkylarkValue
555 && Printer.repr(obj1).equals(Printer.repr(obj2)));
556 }
557
brandjon2c98fff2018-02-06 01:32:46 -0800558 /**
559 * Throws {@link IllegalStateException} if this {@link Extension} is not equal to {@code obj}.
560 *
561 * <p>The exception explains the reason for the inequality, including all unequal bindings.
562 */
563 public void checkStateEquals(Object obj) {
564 if (this == obj) {
565 return;
566 }
567 if (!(obj instanceof Extension)) {
568 throw new IllegalStateException(String.format(
569 "Expected an equal Extension, but got a %s instead of an Extension",
570 obj == null ? "null" : obj.getClass().getName()));
571 }
572 Extension other = (Extension) obj;
573 ImmutableMap<String, Object> otherBindings = other.getBindings();
574
575 Set<String> names = bindings.keySet();
576 Set<String> otherNames = otherBindings.keySet();
577 if (!names.equals(otherNames)) {
578 throw new IllegalStateException(String.format(
579 "Expected Extensions to be equal, but they don't define the same bindings: "
580 + "in this one but not given one: [%s]; in given one but not this one: [%s]",
581 Joiner.on(", ").join(Sets.difference(names, otherNames)),
582 Joiner.on(", ").join(Sets.difference(otherNames, names))));
583 }
584
585 ArrayList<String> badEntries = new ArrayList<>();
586 for (String name : names) {
587 Object value = bindings.get(name);
588 Object otherValue = otherBindings.get(name);
janakr889f5622018-03-16 17:49:11 -0700589 if (value.equals(otherValue)) {
590 continue;
brandjon2c98fff2018-02-06 01:32:46 -0800591 }
janakr33439dc2018-03-22 13:44:05 -0700592 if (value instanceof SkylarkNestedSet) {
593 if (otherValue instanceof SkylarkNestedSet
594 && ((SkylarkNestedSet) value)
595 .toCollection()
596 .equals(((SkylarkNestedSet) otherValue).toCollection())) {
597 continue;
598 }
599 } else if (value instanceof SkylarkDict) {
600 if (otherValue instanceof SkylarkDict) {
601 @SuppressWarnings("unchecked")
602 SkylarkDict<Object, Object> thisDict = (SkylarkDict<Object, Object>) value;
603 @SuppressWarnings("unchecked")
604 SkylarkDict<Object, Object> otherDict = (SkylarkDict<Object, Object>) otherValue;
605 if (thisDict.size() == otherDict.size()
606 && thisDict.keySet().equals(otherDict.keySet())) {
607 boolean foundProblem = false;
608 for (Object key : thisDict.keySet()) {
609 if (!skylarkObjectsProbablyEqual(
610 Preconditions.checkNotNull(thisDict.get(key), key),
611 Preconditions.checkNotNull(otherDict.get(key), key))) {
612 foundProblem = true;
613 }
614 }
615 if (!foundProblem) {
616 continue;
617 }
618 }
619 }
620 } else if (skylarkObjectsProbablyEqual(value, otherValue)) {
janakr889f5622018-03-16 17:49:11 -0700621 continue;
622 }
623 badEntries.add(
624 String.format(
janakrcfc34322018-03-26 11:10:08 -0700625 "%s: this one has %s (class %s, %s), but given one has %s (class %s, %s)",
janakr889f5622018-03-16 17:49:11 -0700626 name,
627 Printer.repr(value),
628 value.getClass().getName(),
janakrcfc34322018-03-26 11:10:08 -0700629 value,
janakr889f5622018-03-16 17:49:11 -0700630 Printer.repr(otherValue),
janakrcfc34322018-03-26 11:10:08 -0700631 otherValue.getClass().getName(),
632 otherValue));
brandjon2c98fff2018-02-06 01:32:46 -0800633 }
634 if (!badEntries.isEmpty()) {
635 throw new IllegalStateException(
636 "Expected Extensions to be equal, but the following bindings are unequal: "
637 + Joiner.on("; ").join(badEntries));
638 }
639
640 if (!transitiveContentHashCode.equals(other.getTransitiveContentHashCode())) {
641 throw new IllegalStateException(String.format(
642 "Expected Extensions to be equal, but transitive content hashes don't match: %s != %s",
643 transitiveContentHashCode,
644 other.getTransitiveContentHashCode()));
645 }
646 }
647
brandjone821fb92017-07-18 21:00:22 +0200648 @Override
649 public int hashCode() {
650 return Objects.hash(bindings, transitiveContentHashCode);
651 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000652 }
653
654 /**
655 * Static Frame for lexical variables that are always looked up in the current Environment
656 * or for the definition Environment of the function currently being evaluated.
657 */
laurentlb5d26ef12018-08-27 12:23:15 -0700658 private Frame lexicalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000659
660 /**
661 * Static Frame for global variables; either the current lexical Frame if evaluation is currently
662 * happening at the global scope of a BUILD file, or the global Frame at the time of function
663 * definition if evaluation is currently happening in the body of a function. Thus functions can
664 * close over other functions defined in the same file.
665 */
nharmata68cc06b2018-03-01 10:21:57 -0800666 private GlobalFrame globalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000667
668 /**
669 * Dynamic Frame for variables that are always looked up in the runtime Environment,
670 * and never in the lexical or "global" Environment as it was at the time of function definition.
671 * For instance, PACKAGE_NAME.
672 */
673 private final Frame dynamicFrame;
674
675 /**
brandjonb712f332017-04-29 16:03:32 +0200676 * The semantics options that affect how Skylark code is evaluated.
677 */
brandjon3c161912017-10-05 05:06:05 +0200678 private final SkylarkSemantics semantics;
brandjonb712f332017-04-29 16:03:32 +0200679
680 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000681 * An EventHandler for errors and warnings. This is not used in the BUILD language,
682 * however it might be used in Skylark code called from the BUILD language, so shouldn't be null.
683 */
684 private final EventHandler eventHandler;
685
686 /**
John Field1ea7fc32015-12-22 19:37:19 +0000687 * For each imported extension, a global Skylark frame from which to load() individual bindings.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000688 */
John Field1ea7fc32015-12-22 19:37:19 +0000689 private final Map<String, Extension> importedExtensions;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000690
691 /**
Laurent Le Brune51a4d22016-10-11 18:04:16 +0000692 * Is this Environment being executed during the loading phase? Many builtin functions are only
693 * enabled during the loading phase, and check this flag.
Laurent Le Brun8e965b82016-08-03 11:50:24 +0000694 * TODO(laurentlb): Remove from Environment
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000695 */
Laurent Le Brune51a4d22016-10-11 18:04:16 +0000696 private final Phase phase;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000697
698 /**
699 * When in a lexical (Skylark) Frame, this set contains the variable names that are global,
700 * as determined not by global declarations (not currently supported),
701 * but by previous lookups that ended being global or dynamic.
702 * This is necessary because if in a function definition something
703 * reads a global variable after which a local variable with the same name is assigned an
704 * Exception needs to be thrown.
705 */
brandjon73e768e2018-02-13 12:26:52 -0800706 @Nullable private LinkedHashSet<String> knownGlobalVariables;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000707
708 /**
709 * When in a lexical (Skylark) frame, this lists the names of the functions in the call stack.
710 * We currently use it to artificially disable recursion.
711 */
712 @Nullable private Continuation continuation;
713
714 /**
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000715 * Gets the label of the BUILD file that is using this environment. For example, if a target
716 * //foo has a dependency on //bar which is a Skylark rule defined in //rules:my_rule.bzl being
717 * evaluated in this environment, then this would return //foo.
718 */
719 @Nullable private final Label callerLabel;
720
721 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000722 * Enters a scope by saving state to a new Continuation
brendandouglasfe785e12018-06-12 09:39:38 -0700723 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000724 * @param function the function whose scope to enter
nharmata68cc06b2018-03-01 10:21:57 -0800725 * @param lexical the lexical frame to use
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000726 * @param caller the source AST node for the caller
727 * @param globals the global Frame that this function closes over from its definition Environment
728 */
nharmata68cc06b2018-03-01 10:21:57 -0800729 void enterScope(
brendandouglasfe785e12018-06-12 09:39:38 -0700730 BaseFunction function,
laurentlb5d26ef12018-08-27 12:23:15 -0700731 Frame lexical,
brendandouglasfe785e12018-06-12 09:39:38 -0700732 @Nullable FuncallExpression caller,
733 GlobalFrame globals) {
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +0000734 continuation =
Laurent Le Brun4db50642016-10-26 14:25:37 +0000735 new Continuation(
736 continuation, function, caller, lexicalFrame, globalFrame, knownGlobalVariables);
nharmata68cc06b2018-03-01 10:21:57 -0800737 lexicalFrame = lexical;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000738 globalFrame = globals;
brandjon73e768e2018-02-13 12:26:52 -0800739 knownGlobalVariables = new LinkedHashSet<>();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000740 }
741
742 /**
743 * Exits a scope by restoring state from the current continuation
744 */
745 void exitScope() {
746 Preconditions.checkNotNull(continuation);
747 lexicalFrame = continuation.lexicalFrame;
748 globalFrame = continuation.globalFrame;
749 knownGlobalVariables = continuation.knownGlobalVariables;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000750 continuation = continuation.continuation;
751 }
752
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +0000753 private final String transitiveHashCode;
Francois-Rene Rideau3a8ddcc2015-08-26 22:30:38 +0000754
755 /**
Damien Martin-Guillerez074b9572016-05-23 12:33:07 +0000756 * Checks that the current Environment is in the loading or the workspace phase.
Laurent Le Brun27d0b322016-11-22 14:48:44 +0000757 * TODO(laurentlb): Move to SkylarkUtils
758 *
Damien Martin-Guillerez074b9572016-05-23 12:33:07 +0000759 * @param symbol name of the function being only authorized thus.
760 */
761 public void checkLoadingOrWorkspacePhase(String symbol, Location loc) throws EvalException {
762 if (phase == Phase.ANALYSIS) {
763 throw new EvalException(loc, symbol + "() cannot be called during the analysis phase");
764 }
765 }
766
767 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000768 * Checks that the current Environment is in the loading phase.
Laurent Le Brun27d0b322016-11-22 14:48:44 +0000769 * TODO(laurentlb): Move to SkylarkUtils
770 *
Francois-Rene Rideau3a8ddcc2015-08-26 22:30:38 +0000771 * @param symbol name of the function being only authorized thus.
772 */
773 public void checkLoadingPhase(String symbol, Location loc) throws EvalException {
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +0000774 if (phase != Phase.LOADING) {
Francois-Rene Rideau3a8ddcc2015-08-26 22:30:38 +0000775 throw new EvalException(loc, symbol + "() can only be called during the loading phase");
776 }
777 }
778
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100779 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000780 * Is this a global Environment?
781 * @return true if the current code is being executed at the top-level,
782 * as opposed to inside the body of a function.
Francois-Rene Rideau6e7160d2015-08-26 17:22:35 +0000783 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000784 boolean isGlobal() {
laurentlb5d26ef12018-08-27 12:23:15 -0700785 return lexicalFrame instanceof GlobalFrame;
Francois-Rene Rideau6e7160d2015-08-26 17:22:35 +0000786 }
787
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000788 @Override
789 public Mutability mutability() {
790 // the mutability of the environment is that of its dynamic frame.
791 return dynamicFrame.mutability();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100792 }
793
brandjon73e768e2018-02-13 12:26:52 -0800794 /** Returns the global variables for the Environment (not including dynamic bindings). */
nharmata68cc06b2018-03-01 10:21:57 -0800795 public GlobalFrame getGlobals() {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000796 return globalFrame;
797 }
798
799 /**
800 * Returns an EventHandler for errors and warnings.
801 * The BUILD language doesn't use it directly, but can call Skylark code that does use it.
802 * @return an EventHandler
803 */
Francois-Rene Rideaue6a46b82015-04-10 18:31:43 +0000804 public EventHandler getEventHandler() {
805 return eventHandler;
806 }
807
michajlocbb33472017-10-23 20:30:18 +0200808 /**
809 * Returns if calling the supplied function would be a recursive call, or in other words if the
810 * supplied function is already on the stack.
811 */
812 boolean isRecursiveCall(UserDefinedFunction function) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000813 for (Continuation k = continuation; k != null; k = k.continuation) {
michajlocbb33472017-10-23 20:30:18 +0200814 if (k.function.equals(function)) {
815 return true;
816 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000817 }
michajlocbb33472017-10-23 20:30:18 +0200818 return false;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100819 }
820
michajlocbb33472017-10-23 20:30:18 +0200821 /** Returns the current function call, if it exists. */
822 @Nullable
823 BaseFunction getCurrentFunction() {
824 return continuation != null ? continuation.function : null;
825 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000826
827 /**
828 * Returns the FuncallExpression and the BaseFunction for the top-level call being evaluated.
829 */
830 public Pair<FuncallExpression, BaseFunction> getTopCall() {
831 Continuation continuation = this.continuation;
832 if (continuation == null) {
833 return null;
834 }
835 while (continuation.continuation != null) {
836 continuation = continuation.continuation;
837 }
838 return new Pair<>(continuation.caller, continuation.function);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100839 }
840
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000841 /**
Laurent Le Bruna31bc4e2016-10-27 12:48:22 +0000842 * Constructs an Environment. This is the main, most basic constructor.
843 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000844 * @param globalFrame a frame for the global Environment
845 * @param dynamicFrame a frame for the dynamic Environment
846 * @param eventHandler an EventHandler for warnings, errors, etc
847 * @param importedExtensions Extension-s from which to import bindings with load()
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000848 * @param fileContentHashCode a hash for the source file being evaluated, if any
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +0000849 * @param phase the current phase
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000850 * @param callerLabel the label this environment came from
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000851 */
852 private Environment(
nharmata68cc06b2018-03-01 10:21:57 -0800853 GlobalFrame globalFrame,
854 LexicalFrame dynamicFrame,
brandjon3c161912017-10-05 05:06:05 +0200855 SkylarkSemantics semantics,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000856 EventHandler eventHandler,
John Field1ea7fc32015-12-22 19:37:19 +0000857 Map<String, Extension> importedExtensions,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000858 @Nullable String fileContentHashCode,
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000859 Phase phase,
860 @Nullable Label callerLabel) {
laurentlb5d26ef12018-08-27 12:23:15 -0700861 this.lexicalFrame = Preconditions.checkNotNull(globalFrame);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000862 this.globalFrame = Preconditions.checkNotNull(globalFrame);
863 this.dynamicFrame = Preconditions.checkNotNull(dynamicFrame);
brandjon33b49432017-05-04 18:58:52 +0200864 Preconditions.checkArgument(!globalFrame.mutability().isFrozen());
865 Preconditions.checkArgument(!dynamicFrame.mutability().isFrozen());
brandjonb712f332017-04-29 16:03:32 +0200866 this.semantics = semantics;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000867 this.eventHandler = eventHandler;
868 this.importedExtensions = importedExtensions;
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +0000869 this.phase = phase;
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000870 this.callerLabel = callerLabel;
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +0000871 this.transitiveHashCode =
872 computeTransitiveContentHashCode(fileContentHashCode, importedExtensions);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000873 }
874
875 /**
brandjond9a1a432017-10-19 23:47:08 +0200876 * A Builder class for Environment.
877 *
878 * <p>The caller must explicitly set the semantics by calling either {@link #setSemantics} or
879 * {@link #useDefaultSemantics}.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000880 */
881 public static class Builder {
882 private final Mutability mutability;
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +0000883 private Phase phase = Phase.ANALYSIS;
nharmata68cc06b2018-03-01 10:21:57 -0800884 @Nullable private GlobalFrame parent;
brandjon3c161912017-10-05 05:06:05 +0200885 @Nullable private SkylarkSemantics semantics;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000886 @Nullable private EventHandler eventHandler;
John Field1ea7fc32015-12-22 19:37:19 +0000887 @Nullable private Map<String, Extension> importedExtensions;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000888 @Nullable private String fileContentHashCode;
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000889 private Label label;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000890
891 Builder(Mutability mutability) {
892 this.mutability = mutability;
893 }
894
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +0000895 /** Enables loading or workspace phase only functions in this Environment. */
896 public Builder setPhase(Phase phase) {
897 Preconditions.checkState(this.phase == Phase.ANALYSIS);
898 this.phase = phase;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000899 return this;
900 }
901
902 /** Inherits global bindings from the given parent Frame. */
nharmata6070ba72018-03-01 13:35:33 -0800903 public Builder setGlobals(GlobalFrame parent) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000904 Preconditions.checkState(this.parent == null);
nharmata6070ba72018-03-01 13:35:33 -0800905 this.parent = parent;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000906 return this;
907 }
908
brandjon3c161912017-10-05 05:06:05 +0200909 public Builder setSemantics(SkylarkSemantics semantics) {
brandjonb712f332017-04-29 16:03:32 +0200910 this.semantics = semantics;
911 return this;
912 }
913
brandjond9a1a432017-10-19 23:47:08 +0200914 public Builder useDefaultSemantics() {
915 this.semantics = SkylarkSemantics.DEFAULT_SEMANTICS;
916 return this;
917 }
918
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000919 /** Sets an EventHandler for errors and warnings. */
920 public Builder setEventHandler(EventHandler eventHandler) {
921 Preconditions.checkState(this.eventHandler == null);
922 this.eventHandler = eventHandler;
923 return this;
924 }
925
926 /** Declares imported extensions for load() statements. */
John Field1ea7fc32015-12-22 19:37:19 +0000927 public Builder setImportedExtensions (Map<String, Extension> importMap) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000928 Preconditions.checkState(this.importedExtensions == null);
John Field1ea7fc32015-12-22 19:37:19 +0000929 this.importedExtensions = importMap;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000930 return this;
931 }
932
933 /** Declares content hash for the source file for this Environment. */
934 public Builder setFileContentHashCode(String fileContentHashCode) {
935 this.fileContentHashCode = fileContentHashCode;
936 return this;
937 }
938
939 /** Builds the Environment. */
940 public Environment build() {
brandjon33b49432017-05-04 18:58:52 +0200941 Preconditions.checkArgument(!mutability.isFrozen());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000942 if (parent != null) {
brandjon73e768e2018-02-13 12:26:52 -0800943 Preconditions.checkArgument(parent.mutability().isFrozen(), "parent frame must be frozen");
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000944 }
nharmata68cc06b2018-03-01 10:21:57 -0800945 GlobalFrame globalFrame = new GlobalFrame(mutability, parent);
946 LexicalFrame dynamicFrame = LexicalFrame.create(mutability);
brandjonb712f332017-04-29 16:03:32 +0200947 if (semantics == null) {
brandjonb368b392017-10-20 22:02:02 +0200948 throw new IllegalArgumentException("must call either setSemantics or useDefaultSemantics");
brandjonb712f332017-04-29 16:03:32 +0200949 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000950 if (importedExtensions == null) {
951 importedExtensions = ImmutableMap.of();
952 }
Laurent Le Brun46171092015-12-23 14:04:23 +0000953 return new Environment(
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000954 globalFrame,
955 dynamicFrame,
brandjonb712f332017-04-29 16:03:32 +0200956 semantics,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000957 eventHandler,
958 importedExtensions,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000959 fileContentHashCode,
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000960 phase,
961 label);
962 }
963
964 public Builder setCallerLabel(Label label) {
965 this.label = label;
966 return this;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000967 }
968 }
969
970 public static Builder builder(Mutability mutability) {
971 return new Builder(mutability);
972 }
973
974 /**
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000975 * Returns the caller's label.
976 */
977 public Label getCallerLabel() {
978 return callerLabel;
979 }
980
981 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000982 * Sets a binding for a special dynamic variable in this Environment.
983 * This is not for end-users, and will throw an AssertionError in case of conflict.
984 * @param varname the name of the dynamic variable to be bound
985 * @param value a value to bind to the variable
986 * @return this Environment, in fluid style
987 */
988 public Environment setupDynamic(String varname, Object value) {
laurentlb5d26ef12018-08-27 12:23:15 -0700989 if (lookup(varname) != null) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000990 throw new AssertionError(
991 String.format("Trying to bind dynamic variable '%s' but it is already bound",
992 varname));
993 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000994 try {
995 dynamicFrame.put(this, varname, value);
996 } catch (MutabilityException e) {
997 // End users don't have access to setupDynamic, and it is an implementation error
998 // if we encounter a mutability exception.
999 throw new AssertionError(
vladmos46907932017-06-30 14:01:45 +02001000 String.format(
1001 "Trying to bind dynamic variable '%s' in frozen environment %s", varname, this),
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001002 e);
1003 }
1004 return this;
1005 }
1006
laurentlb9d5c0a02017-06-13 23:08:06 +02001007 /** Remove variable from local bindings. */
1008 void removeLocalBinding(String varname) {
1009 try {
laurentlb5d26ef12018-08-27 12:23:15 -07001010 lexicalFrame.remove(this, varname);
laurentlb9d5c0a02017-06-13 23:08:06 +02001011 } catch (MutabilityException e) {
1012 throw new AssertionError(e);
1013 }
1014 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001015
nharmataf1013482018-03-07 10:48:31 -08001016 /**
1017 * Modifies a binding in the current Frame of this Environment, as would an
1018 * {@link AssignmentStatement}. Does not try to modify an inherited binding.
1019 * This will shadow any inherited binding, which may be an error
1020 * that you want to guard against before calling this function.
1021 * @param varname the name of the variable to be bound
1022 * @param value the value to bind to the variable
1023 * @return this Environment, in fluid style
1024 */
1025 public Environment update(String varname, Object value) throws EvalException {
nharmata6e233f02018-03-07 16:02:22 -08001026 Preconditions.checkNotNull(value, "update(value == null)");
1027 // prevents clashes between static and dynamic variables.
1028 if (dynamicFrame.get(varname) != null) {
1029 throw new EvalException(
1030 null, String.format("Trying to update special read-only global variable '%s'", varname));
1031 }
1032 if (isKnownGlobalVariable(varname)) {
1033 throw new EvalException(
1034 null, String.format("Trying to update read-only global variable '%s'", varname));
1035 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001036 try {
laurentlb5d26ef12018-08-27 12:23:15 -07001037 lexicalFrame.put(this, varname, Preconditions.checkNotNull(value));
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001038 } catch (MutabilityException e) {
1039 // Note that since at this time we don't accept the global keyword, and don't have closures,
1040 // end users should never be able to mutate a frozen Environment, and a MutabilityException
1041 // is therefore a failed assertion for Bazel. However, it is possible to shadow a binding
1042 // imported from a parent Environment by updating the current Environment, which will not
1043 // trigger a MutabilityException.
1044 throw new AssertionError(
1045 Printer.format("Can't update %s to %r in frozen environment", varname, value),
1046 e);
1047 }
1048 return this;
1049 }
1050
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001051 /**
1052 * Initializes a binding in this Environment. It is an error if the variable is already bound.
1053 * This is not for end-users, and will throw an AssertionError in case of conflict.
1054 * @param varname the name of the variable to be bound
1055 * @param value the value to bind to the variable
1056 * @return this Environment, in fluid style
1057 */
1058 public Environment setup(String varname, Object value) {
laurentlb1434b172018-08-27 11:32:34 -07001059 if (lookup(varname) != null) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001060 throw new AssertionError(String.format("variable '%s' already bound", varname));
1061 }
1062 return setupOverride(varname, value);
1063 }
1064
1065 /**
1066 * Initializes a binding in this environment. Overrides any previous binding.
1067 * This is not for end-users, and will throw an AssertionError in case of conflict.
1068 * @param varname the name of the variable to be bound
1069 * @param value the value to bind to the variable
1070 * @return this Environment, in fluid style
1071 */
1072 public Environment setupOverride(String varname, Object value) {
1073 try {
1074 return update(varname, value);
1075 } catch (EvalException ee) {
1076 throw new AssertionError(ee);
1077 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001078 }
1079
1080 /**
brandjon73e768e2018-02-13 12:26:52 -08001081 * Returns the value from the environment whose name is "varname" if it exists, otherwise null.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001082 */
Vladimir Moskva4994cb32016-08-10 15:44:31 +00001083 public Object lookup(String varname) {
brandjoncc0f6a62017-05-08 13:19:21 -04001084 // Lexical frame takes precedence, then globals, then dynamics.
laurentlb5d26ef12018-08-27 12:23:15 -07001085 Object lexicalValue = lexicalFrame.get(varname);
1086 if (lexicalValue != null) {
1087 return lexicalValue;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001088 }
1089 Object globalValue = globalFrame.get(varname);
1090 Object dynamicValue = dynamicFrame.get(varname);
1091 if (globalValue == null && dynamicValue == null) {
Vladimir Moskva4994cb32016-08-10 15:44:31 +00001092 return null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001093 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001094 if (knownGlobalVariables != null) {
1095 knownGlobalVariables.add(varname);
1096 }
1097 if (globalValue != null) {
1098 return globalValue;
1099 }
1100 return dynamicValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001101 }
1102
1103 /**
brandjon73e768e2018-02-13 12:26:52 -08001104 * Returns true if varname is a known global variable (i.e., it has been read in the context of
1105 * the current function).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001106 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001107 boolean isKnownGlobalVariable(String varname) {
1108 return knownGlobalVariables != null && knownGlobalVariables.contains(varname);
1109 }
1110
brandjon3c161912017-10-05 05:06:05 +02001111 public SkylarkSemantics getSemantics() {
brandjonb712f332017-04-29 16:03:32 +02001112 return semantics;
1113 }
1114
Vladimir Moskvadfad9e32016-09-15 18:22:01 +00001115 public void handleEvent(Event event) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001116 eventHandler.handle(event);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001117 }
1118
brandjon73e768e2018-02-13 12:26:52 -08001119 /**
1120 * Returns a set of all names of variables that are accessible in this {@code Environment}, in a
1121 * deterministic order.
1122 */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001123 public Set<String> getVariableNames() {
brandjon73e768e2018-02-13 12:26:52 -08001124 LinkedHashSet<String> vars = new LinkedHashSet<>();
laurentlb5d26ef12018-08-27 12:23:15 -07001125 vars.addAll(lexicalFrame.getTransitiveBindings().keySet());
1126 // No-op when globalFrame = lexicalFrame
brandjoncc0f6a62017-05-08 13:19:21 -04001127 vars.addAll(globalFrame.getTransitiveBindings().keySet());
1128 vars.addAll(dynamicFrame.getTransitiveBindings().keySet());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001129 return vars;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001130 }
1131
Googler29eafdf2018-05-23 12:32:07 -07001132 private static final class EvalEventHandler implements EventHandler {
1133 List<String> messages = new ArrayList<>();
1134
1135 @Override
1136 public void handle(Event event) {
1137 if (event.getKind() == EventKind.ERROR) {
1138 messages.add(event.getMessage());
1139 }
1140 }
1141 }
1142
1143 @Override
brendandouglas01683d32018-06-26 08:53:10 -07001144 public Object evaluate(String contents) throws EvalException, InterruptedException {
1145 ParserInputSource input =
1146 ParserInputSource.create(contents, PathFragment.create("<debug eval>"));
Googler29eafdf2018-05-23 12:32:07 -07001147 EvalEventHandler eventHandler = new EvalEventHandler();
brendandouglas01683d32018-06-26 08:53:10 -07001148 Statement statement = Parser.parseStatement(input, eventHandler, ParsingLevel.LOCAL_LEVEL);
Googler29eafdf2018-05-23 12:32:07 -07001149 if (!eventHandler.messages.isEmpty()) {
brendandouglas01683d32018-06-26 08:53:10 -07001150 throw new EvalException(statement.getLocation(), eventHandler.messages.get(0));
Googler29eafdf2018-05-23 12:32:07 -07001151 }
brendandouglas01683d32018-06-26 08:53:10 -07001152 // TODO(bazel-team): move statement handling code to Eval
1153 // deal with the most common case first
1154 if (statement.kind() == Statement.Kind.EXPRESSION) {
1155 return ((ExpressionStatement) statement).getExpression().doEval(this);
1156 }
1157 // all other statement types are executed directly
1158 Eval.fromEnvironment(this).exec(statement);
1159 switch (statement.kind()) {
1160 case ASSIGNMENT:
1161 case AUGMENTED_ASSIGNMENT:
1162 return ((AssignmentStatement) statement).getLValue().getExpression().doEval(this);
1163 case RETURN:
1164 Expression expr = ((ReturnStatement) statement).getReturnExpression();
1165 return expr != null ? expr.doEval(this) : Runtime.NONE;
1166 default:
1167 return Runtime.NONE;
1168 }
Googler29eafdf2018-05-23 12:32:07 -07001169 }
1170
1171 @Override
1172 public ImmutableList<DebugFrame> listFrames(Location currentLocation) {
1173 ImmutableList.Builder<DebugFrame> frameListBuilder = ImmutableList.builder();
1174
1175 Continuation currentContinuation = continuation;
laurentlb5d26ef12018-08-27 12:23:15 -07001176 Frame currentFrame = lexicalFrame;
Googler29eafdf2018-05-23 12:32:07 -07001177
1178 // if there's a continuation then the current frame is a lexical frame
1179 while (currentContinuation != null) {
1180 frameListBuilder.add(
1181 DebugFrame.builder()
1182 .setLexicalFrameBindings(ImmutableMap.copyOf(currentFrame.getTransitiveBindings()))
1183 .setGlobalBindings(ImmutableMap.copyOf(getGlobals().getTransitiveBindings()))
1184 .setFunctionName(currentContinuation.function.getFullName())
1185 .setLocation(currentLocation)
1186 .build());
1187
1188 currentFrame = currentContinuation.lexicalFrame;
brendandouglasfe785e12018-06-12 09:39:38 -07001189 currentLocation =
1190 currentContinuation.caller != null ? currentContinuation.caller.getLocation() : null;
Googler29eafdf2018-05-23 12:32:07 -07001191 currentContinuation = currentContinuation.continuation;
1192 }
1193
1194 frameListBuilder.add(
1195 DebugFrame.builder()
1196 .setGlobalBindings(ImmutableMap.copyOf(getGlobals().getTransitiveBindings()))
1197 .setFunctionName("<top level>")
1198 .setLocation(currentLocation)
1199 .build());
1200
1201 return frameListBuilder.build();
1202 }
1203
1204 @Override
1205 @Nullable
1206 public ReadyToPause stepControl(Stepping stepping) {
1207 final Continuation pausedContinuation = continuation;
1208
1209 switch (stepping) {
1210 case NONE:
1211 return null;
1212 case INTO:
1213 // pause at the very next statement
1214 return env -> true;
1215 case OVER:
1216 return env -> isAt(env, pausedContinuation) || isOutside(env, pausedContinuation);
1217 case OUT:
1218 // if we're at the outer-most frame, same as NONE
1219 return pausedContinuation == null ? null : env -> isOutside(env, pausedContinuation);
1220 }
1221 throw new IllegalArgumentException("Unsupported stepping type: " + stepping);
1222 }
1223
1224 /** Returns true if {@code env} is in a parent frame of {@code pausedContinuation}. */
1225 private static boolean isOutside(Environment env, @Nullable Continuation pausedContinuation) {
1226 return pausedContinuation != null && env.continuation == pausedContinuation.continuation;
1227 }
1228
1229 /** Returns true if {@code env} is at the same frame as {@code pausedContinuation. */
1230 private static boolean isAt(Environment env, @Nullable Continuation pausedContinuation) {
1231 return env.continuation == pausedContinuation;
1232 }
1233
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001234 @Override
1235 public int hashCode() {
1236 throw new UnsupportedOperationException(); // avoid nondeterminism
1237 }
1238
1239 @Override
1240 public boolean equals(Object that) {
1241 throw new UnsupportedOperationException();
1242 }
1243
1244 @Override
1245 public String toString() {
Francois-Rene Rideaubd0c7bb2015-09-21 16:54:19 +00001246 return String.format("<Environment%s>", mutability());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001247 }
1248
1249 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001250 * An Exception thrown when an attempt is made to import a symbol from a file
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001251 * that was not properly loaded.
1252 */
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001253 static class LoadFailedException extends Exception {
John Field1ea7fc32015-12-22 19:37:19 +00001254 LoadFailedException(String importString) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001255 super(String.format("file '%s' was not correctly loaded. "
1256 + "Make sure the 'load' statement appears in the global scope in your file",
Vladimir Moskva4994cb32016-08-10 15:44:31 +00001257 importString));
1258 }
1259
Laurent Le Bruna2501d52017-01-02 15:42:19 +00001260 LoadFailedException(String importString, String symbolString, Iterable<String> allKeys) {
1261 super(
1262 String.format(
1263 "file '%s' does not contain symbol '%s'%s",
1264 importString, symbolString, SpellChecker.didYouMean(symbolString, allKeys)));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001265 }
1266 }
1267
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001268 void importSymbol(String importString, Identifier symbol, String nameInLoadedFile)
Vladimir Moskva4994cb32016-08-10 15:44:31 +00001269 throws LoadFailedException {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001270 Preconditions.checkState(isGlobal()); // loading is only allowed at global scope.
1271
John Field1ea7fc32015-12-22 19:37:19 +00001272 if (!importedExtensions.containsKey(importString)) {
1273 throw new LoadFailedException(importString);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001274 }
Florian Weikert9d659ad2015-07-23 14:44:36 +00001275
John Field1ea7fc32015-12-22 19:37:19 +00001276 Extension ext = importedExtensions.get(importString);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001277
brandjon357ed7a2017-05-09 11:37:22 -04001278 Map<String, Object> bindings = ext.getBindings();
1279 if (!bindings.containsKey(nameInLoadedFile)) {
1280 throw new LoadFailedException(importString, nameInLoadedFile, bindings.keySet());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001281 }
1282
brandjon357ed7a2017-05-09 11:37:22 -04001283 Object value = bindings.get(nameInLoadedFile);
Florian Weikert9d659ad2015-07-23 14:44:36 +00001284
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001285 try {
1286 update(symbol.getName(), value);
1287 } catch (EvalException e) {
John Field1ea7fc32015-12-22 19:37:19 +00001288 throw new LoadFailedException(importString);
Francois-Rene Rideau5a94e592015-09-04 19:13:47 +00001289 }
Florian Weikert6a663392015-09-02 14:04:33 +00001290 }
1291
brandjon73e768e2018-02-13 12:26:52 -08001292 /**
1293 * Computes a deterministic hash for the given base hash code and extension map (the map's order
1294 * does not matter).
1295 */
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001296 private static String computeTransitiveContentHashCode(
1297 @Nullable String baseHashCode, Map<String, Extension> importedExtensions) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001298 // Calculate a new hash from the hash of the loaded Extension-s.
1299 Fingerprint fingerprint = new Fingerprint();
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001300 if (baseHashCode != null) {
1301 fingerprint.addString(Preconditions.checkNotNull(baseHashCode));
1302 }
John Field1ea7fc32015-12-22 19:37:19 +00001303 TreeSet<String> importStrings = new TreeSet<>(importedExtensions.keySet());
1304 for (String importString : importStrings) {
1305 fingerprint.addString(importedExtensions.get(importString).getTransitiveContentHashCode());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001306 }
1307 return fingerprint.hexDigestAndReset();
Janak Ramakrishnanb6e33bc2015-09-06 21:05:23 +00001308 }
1309
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001310 /**
1311 * Returns a hash code calculated from the hash code of this Environment and the
1312 * transitive closure of other Environments it loads.
1313 */
1314 public String getTransitiveContentHashCode() {
1315 return transitiveHashCode;
1316 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001317
brandjonb80eb182018-05-01 10:54:58 -07001318 /** A read-only {@link Environment.GlobalFrame} with False/True/None constants only. */
janakra1f17612018-08-17 06:01:51 -07001319 @AutoCodec static final GlobalFrame CONSTANTS_ONLY = createConstantsGlobals();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001320
brandjonb80eb182018-05-01 10:54:58 -07001321 /**
janakra1f17612018-08-17 06:01:51 -07001322 * A read-only {@link Environment.GlobalFrame} with initial globals as defined in MethodLibrary.
brandjonb80eb182018-05-01 10:54:58 -07001323 */
janakra1f17612018-08-17 06:01:51 -07001324 @AutoCodec public static final GlobalFrame DEFAULT_GLOBALS = createDefaultGlobals();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001325
Laurent Le Brun5e991982016-10-14 13:39:45 +00001326 /** To be removed when all call-sites are updated. */
nharmata68cc06b2018-03-01 10:21:57 -08001327 public static final GlobalFrame SKYLARK = DEFAULT_GLOBALS;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001328
nharmata68cc06b2018-03-01 10:21:57 -08001329 private static Environment.GlobalFrame createConstantsGlobals() {
brandjonb80eb182018-05-01 10:54:58 -07001330 ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
1331 Runtime.addConstantsToBuilder(builder);
1332 return GlobalFrame.createForBuiltins(builder.build());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001333 }
1334
nharmata68cc06b2018-03-01 10:21:57 -08001335 private static Environment.GlobalFrame createDefaultGlobals() {
brandjonb80eb182018-05-01 10:54:58 -07001336 ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
1337 Runtime.addConstantsToBuilder(builder);
1338 MethodLibrary.addBindingsToBuilder(builder);
1339 return GlobalFrame.createForBuiltins(builder.build());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001340 }
1341
brandjone5d95fb2017-07-13 17:23:09 +02001342 /** An exception thrown by {@link #FAIL_FAST_HANDLER}. */
1343 // TODO(bazel-team): Possibly extend RuntimeException instead of IllegalArgumentException.
1344 public static class FailFastException extends IllegalArgumentException {
1345 public FailFastException(String s) {
1346 super(s);
1347 }
1348 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001349
1350 /**
brandjone5d95fb2017-07-13 17:23:09 +02001351 * A handler that immediately throws {@link FailFastException} whenever an error or warning
1352 * occurs.
1353 *
1354 * We do not reuse an existing unchecked exception type, because callers (e.g., test assertions)
1355 * need to be able to distinguish between organically occurring exceptions and exceptions thrown
1356 * by this handler.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001357 */
1358 public static final EventHandler FAIL_FAST_HANDLER = new EventHandler() {
brandjone5d95fb2017-07-13 17:23:09 +02001359 @Override
1360 public void handle(Event event) {
1361 if (EventKind.ERRORS_AND_WARNINGS.contains(event.getKind())) {
1362 throw new FailFastException(event.toString());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001363 }
brandjone5d95fb2017-07-13 17:23:09 +02001364 }
1365 };
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001366}