blob: a8158e3f32f0f267b299dd27b7b812e1493e012a [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
laurentlbcaafe302018-08-28 10:24:31 -070084 /** A phase for enabling or disabling certain builtin functions */
85 public enum Phase {
86 WORKSPACE,
87 LOADING,
88 ANALYSIS
89 }
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +000090
91 /**
laurentlbcaafe302018-08-28 10:24:31 -070092 * A mapping of bindings, either mutable or immutable according to an associated {@link
93 * Mutability}. The order of the bindings within a single {@link Frame} is deterministic but
94 * unspecified.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000095 *
nharmata68cc06b2018-03-01 10:21:57 -080096 * <p>Any non-frozen {@link Frame} must have the same {@link Mutability} as the current {@link
brandjoncc0f6a62017-05-08 13:19:21 -040097 * Environment}, to avoid interference from other evaluation contexts. For example, a {@link
nharmata68cc06b2018-03-01 10:21:57 -080098 * UserDefinedFunction} will close over the global frame of the {@link Environment} in which it
99 * was defined. When the function is called from other {@link Environment}s (possibly
100 * simultaneously), that global frame must already be frozen; a new local {@link Frame} is created
brandjoncc0f6a62017-05-08 13:19:21 -0400101 * to represent the lexical scope of the function.
brandjonadda5122017-08-29 18:29:58 +0200102 *
nharmata68cc06b2018-03-01 10:21:57 -0800103 * <p>A {@link Frame} can have an associated "parent" {@link Frame}, which is used in {@link #get}
104 * and {@link #getTransitiveBindings()}
laurentlbc8e67962018-08-31 14:20:40 -0700105 *
106 * <p>TODO(laurentlb): "parent" should be named "universe" since it contains only the builtins.
107 * The "get" method shouldn't look at the universe (so that "moduleLookup" works as expected)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100108 */
nharmata68cc06b2018-03-01 10:21:57 -0800109 public interface Frame extends Freezable {
110 /**
111 * Gets a binding from this {@link Frame} or one of its transitive parents.
112 *
113 * <p>In case of conflicts, the binding found in the {@link Frame} closest to the current one is
114 * used; the remaining bindings are shadowed.
115 *
116 * @param varname the name of the variable whose value should be retrieved
117 * @return the value bound to the variable, or null if no binding is found
118 */
119 @Nullable
120 Object get(String varname);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000121
brandjonadda5122017-08-29 18:29:58 +0200122 /**
nharmata68cc06b2018-03-01 10:21:57 -0800123 * Assigns or reassigns a binding in the current {@code Frame}.
124 *
125 * <p>If the binding has the same name as one in a transitive parent, the parent binding is
126 * shadowed (i.e., the parent is unaffected).
127 *
128 * @param env the {@link Environment} attempting the mutation
129 * @param varname the name of the variable to be bound
130 * @param value the value to bind to the variable
131 */
132 void put(Environment env, String varname, Object value) throws MutabilityException;
133
134 /**
laurentlbcaafe302018-08-28 10:24:31 -0700135 * TODO(laurentlb): Remove this method when possible. It should probably not be part of the
136 * public interface.
nharmata68cc06b2018-03-01 10:21:57 -0800137 */
138 void remove(Environment env, String varname) throws MutabilityException;
139
140 /**
laurentlbcaafe302018-08-28 10:24:31 -0700141 * Returns a map containing all bindings of this {@link Frame} and of its transitive parents,
142 * taking into account shadowing precedence.
nharmata68cc06b2018-03-01 10:21:57 -0800143 *
144 * <p>The bindings are returned in a deterministic order (for a given sequence of initial values
145 * and updates).
146 */
147 Map<String, Object> getTransitiveBindings();
148 }
149
150 interface LexicalFrame extends Frame {
151 static LexicalFrame create(Mutability mutability) {
152 return mutability.isFrozen()
153 ? ImmutableEmptyLexicalFrame.INSTANCE
154 : new MutableLexicalFrame(mutability);
155 }
nharmata62678a42018-03-08 16:43:45 -0800156
laurentlbcaafe302018-08-28 10:24:31 -0700157 static LexicalFrame create(Mutability mutability, int numArgs) {
nharmata62678a42018-03-08 16:43:45 -0800158 Preconditions.checkState(!mutability.isFrozen());
159 return new MutableLexicalFrame(mutability, /*initialCapacity=*/ numArgs);
160 }
nharmata68cc06b2018-03-01 10:21:57 -0800161 }
162
163 private static final class ImmutableEmptyLexicalFrame implements LexicalFrame {
164 private static final ImmutableEmptyLexicalFrame INSTANCE = new ImmutableEmptyLexicalFrame();
165
166 @Override
167 public Mutability mutability() {
168 return Mutability.IMMUTABLE;
169 }
170
171 @Nullable
172 @Override
173 public Object get(String varname) {
174 return null;
175 }
176
177 @Override
178 public void put(Environment env, String varname, Object value) throws MutabilityException {
179 Mutability.checkMutable(this, env.mutability());
180 throw new IllegalStateException();
181 }
182
183 @Override
184 public void remove(Environment env, String varname) throws MutabilityException {
185 Mutability.checkMutable(this, env.mutability());
186 throw new IllegalStateException();
187 }
188
189 @Override
190 public Map<String, Object> getTransitiveBindings() {
191 return ImmutableMap.of();
192 }
193
194 @Override
195 public String toString() {
196 return "<ImmutableEmptyLexicalFrame>";
197 }
198 }
199
200 private static final class MutableLexicalFrame implements LexicalFrame {
201 private final Mutability mutability;
202 /** Bindings are maintained in order of creation. */
nharmata62678a42018-03-08 16:43:45 -0800203 private final LinkedHashMap<String, Object> bindings;
nharmata68cc06b2018-03-01 10:21:57 -0800204
nharmata62678a42018-03-08 16:43:45 -0800205 private MutableLexicalFrame(Mutability mutability, int initialCapacity) {
nharmata68cc06b2018-03-01 10:21:57 -0800206 this.mutability = mutability;
nharmata62678a42018-03-08 16:43:45 -0800207 this.bindings = new LinkedHashMap<>(initialCapacity);
208 }
209
210 private MutableLexicalFrame(Mutability mutability) {
211 this.mutability = mutability;
212 this.bindings = new LinkedHashMap<>();
nharmata68cc06b2018-03-01 10:21:57 -0800213 }
214
215 @Override
216 public Mutability mutability() {
217 return mutability;
218 }
219
220 @Nullable
221 @Override
222 public Object get(String varname) {
223 return bindings.get(varname);
224 }
225
226 @Override
227 public void put(Environment env, String varname, Object value) throws MutabilityException {
228 Mutability.checkMutable(this, env.mutability());
229 bindings.put(varname, value);
230 }
231
232 @Override
233 public void remove(Environment env, String varname) throws MutabilityException {
234 Mutability.checkMutable(this, env.mutability());
235 bindings.remove(varname);
236 }
237
238 @Override
239 public Map<String, Object> getTransitiveBindings() {
240 return bindings;
241 }
242
243 @Override
244 public String toString() {
245 return String.format("<MutableLexicalFrame%s>", mutability());
246 }
247 }
248
249 /**
250 * A {@link Frame} that can have a parent {@link GlobalFrame} from which it inherits bindings.
251 *
252 * <p>Bindings in a {@link GlobalFrame} may shadow those inherited from its parents. A chain of
253 * {@link GlobalFrame}s can represent different builtin scopes with a linear precedence ordering.
254 *
255 * <p>A {@link GlobalFrame} can also be constructed in a two-phase process. To do this, call the
laurentlbcaafe302018-08-28 10:24:31 -0700256 * nullary constructor to create an uninitialized {@link GlobalFrame}, then call {@link
257 * #initialize}. It is illegal to use any other method in-between these two calls, or to call
258 * {@link #initialize} on an already initialized {@link GlobalFrame}.
nharmata68cc06b2018-03-01 10:21:57 -0800259 */
nharmata68cc06b2018-03-01 10:21:57 -0800260 public static final class GlobalFrame implements Frame {
261 /**
brandjonadda5122017-08-29 18:29:58 +0200262 * Final, except that it may be initialized after instantiation. Null mutability indicates that
263 * this Frame is uninitialized.
264 */
laurentlbcaafe302018-08-28 10:24:31 -0700265 @Nullable private Mutability mutability;
brandjoncc0f6a62017-05-08 13:19:21 -0400266
brandjonadda5122017-08-29 18:29:58 +0200267 /** Final, except that it may be initialized after instantiation. */
laurentlbcaafe302018-08-28 10:24:31 -0700268 @Nullable private GlobalFrame parent;
brandjonadda5122017-08-29 18:29:58 +0200269
270 /**
271 * If this frame is a global frame, the label for the corresponding target, e.g. {@code
272 * //foo:bar.bzl}.
273 *
274 * <p>Final, except that it may be initialized after instantiation.
275 */
laurentlbcaafe302018-08-28 10:24:31 -0700276 @Nullable private Label label;
brandjoncc0f6a62017-05-08 13:19:21 -0400277
brandjon73e768e2018-02-13 12:26:52 -0800278 /** Bindings are maintained in order of creation. */
279 private final LinkedHashMap<String, Object> bindings;
brandjoncc0f6a62017-05-08 13:19:21 -0400280
brandjonadda5122017-08-29 18:29:58 +0200281 /** Constructs an uninitialized instance; caller must call {@link #initialize} before use. */
nharmata68cc06b2018-03-01 10:21:57 -0800282 public GlobalFrame() {
brandjonadda5122017-08-29 18:29:58 +0200283 this.mutability = null;
284 this.parent = null;
285 this.label = null;
286 this.bindings = new LinkedHashMap<>();
287 }
288
brandjonb80eb182018-05-01 10:54:58 -0700289 public GlobalFrame(
290 Mutability mutability,
291 @Nullable GlobalFrame parent,
292 @Nullable Label label,
293 @Nullable Map<String, Object> bindings) {
laurentlbc8e67962018-08-31 14:20:40 -0700294 Preconditions.checkState(parent == null || parent.parent == null); // no more than 1 parent
brandjonadda5122017-08-29 18:29:58 +0200295 this.mutability = Preconditions.checkNotNull(mutability);
296 this.parent = parent;
297 this.label = label;
298 this.bindings = new LinkedHashMap<>();
brandjonb80eb182018-05-01 10:54:58 -0700299 if (bindings != null) {
300 this.bindings.putAll(bindings);
301 }
brandjonadda5122017-08-29 18:29:58 +0200302 }
303
nharmata68cc06b2018-03-01 10:21:57 -0800304 public GlobalFrame(Mutability mutability) {
brandjonb80eb182018-05-01 10:54:58 -0700305 this(mutability, null, null, null);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000306 }
307
brandjonb80eb182018-05-01 10:54:58 -0700308 public GlobalFrame(Mutability mutability, @Nullable GlobalFrame parent) {
309 this(mutability, parent, null, null);
310 }
311
312 public GlobalFrame(Mutability mutability, @Nullable GlobalFrame parent, @Nullable Label label) {
313 this(mutability, parent, label, null);
314 }
315
316 /** Constructs a global frame for the given builtin bindings. */
317 public static GlobalFrame createForBuiltins(Map<String, Object> bindings) {
318 Mutability mutability = Mutability.create("<builtins>").freeze();
319 return new GlobalFrame(mutability, null, null, bindings);
brandjoncc0f6a62017-05-08 13:19:21 -0400320 }
321
brandjonadda5122017-08-29 18:29:58 +0200322 private void checkInitialized() {
323 Preconditions.checkNotNull(mutability, "Attempted to use Frame before initializing it");
brandjoncc0f6a62017-05-08 13:19:21 -0400324 }
325
brandjonadda5122017-08-29 18:29:58 +0200326 public void initialize(
laurentlbcaafe302018-08-28 10:24:31 -0700327 Mutability mutability,
328 @Nullable GlobalFrame parent,
329 @Nullable Label label,
330 Map<String, Object> bindings) {
331 Preconditions.checkState(
332 this.mutability == null, "Attempted to initialize an already initialized Frame");
brandjonadda5122017-08-29 18:29:58 +0200333 this.mutability = Preconditions.checkNotNull(mutability);
334 this.parent = parent;
335 this.label = label;
brandjoncc0f6a62017-05-08 13:19:21 -0400336 this.bindings.putAll(bindings);
337 }
338
339 /**
laurentlbc8e67962018-08-31 14:20:40 -0700340 * Returns a new {@link GlobalFrame} with the same fields, except that {@link #label} is set to
nharmata68cc06b2018-03-01 10:21:57 -0800341 * the given value.
brandjoncc0f6a62017-05-08 13:19:21 -0400342 */
nharmata68cc06b2018-03-01 10:21:57 -0800343 public GlobalFrame withLabel(Label label) {
brandjonadda5122017-08-29 18:29:58 +0200344 checkInitialized();
laurentlbc8e67962018-08-31 14:20:40 -0700345 return new GlobalFrame(mutability, /*parent*/ null, label, bindings);
brandjoncc0f6a62017-05-08 13:19:21 -0400346 }
347
348 /**
nharmata68cc06b2018-03-01 10:21:57 -0800349 * Returns the {@link Mutability} of this {@link GlobalFrame}, which may be different from its
brandjoncc0f6a62017-05-08 13:19:21 -0400350 * parent's.
351 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000352 @Override
brandjoncc0f6a62017-05-08 13:19:21 -0400353 public Mutability mutability() {
brandjonadda5122017-08-29 18:29:58 +0200354 checkInitialized();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000355 return mutability;
356 }
357
nharmata68cc06b2018-03-01 10:21:57 -0800358 /** Returns the parent {@link GlobalFrame}, if it exists. */
brandjoncc0f6a62017-05-08 13:19:21 -0400359 @Nullable
nharmata68cc06b2018-03-01 10:21:57 -0800360 public GlobalFrame getParent() {
brandjonadda5122017-08-29 18:29:58 +0200361 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400362 return parent;
Kristina Chodorow6f153352016-03-21 16:20:06 +0000363 }
364
365 /**
brandjoncc0f6a62017-05-08 13:19:21 -0400366 * Returns the label of this {@code Frame}, which may be null. Parent labels are not consulted.
367 *
368 * <p>Usually you want to use {@link #getTransitiveLabel}; this is just an accessor for
369 * completeness.
Kristina Chodorow6f153352016-03-21 16:20:06 +0000370 */
371 @Nullable
brandjoncc0f6a62017-05-08 13:19:21 -0400372 public Label getLabel() {
brandjonadda5122017-08-29 18:29:58 +0200373 checkInitialized();
Kristina Chodorow6f153352016-03-21 16:20:06 +0000374 return label;
375 }
376
377 /**
nharmata68cc06b2018-03-01 10:21:57 -0800378 * Walks from this {@link GlobalFrame} up through transitive parents, and returns the first
379 * non-null label found, or null if all labels are null.
brandjoncc0f6a62017-05-08 13:19:21 -0400380 */
381 @Nullable
382 public Label getTransitiveLabel() {
brandjonadda5122017-08-29 18:29:58 +0200383 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400384 if (label != null) {
385 return label;
386 } else if (parent != null) {
387 return parent.getTransitiveLabel();
388 } else {
389 return null;
390 }
391 }
392
393 /**
nharmata68cc06b2018-03-01 10:21:57 -0800394 * Returns a map of direct bindings of this {@link GlobalFrame}, ignoring parents.
brandjoncc0f6a62017-05-08 13:19:21 -0400395 *
brandjon73e768e2018-02-13 12:26:52 -0800396 * <p>The bindings are returned in a deterministic order (for a given sequence of initial values
397 * and updates).
398 *
brandjoncc0f6a62017-05-08 13:19:21 -0400399 * <p>For efficiency an unmodifiable view is returned. Callers should assume that the view is
nharmata68cc06b2018-03-01 10:21:57 -0800400 * invalidated by any subsequent modification to the {@link GlobalFrame}'s bindings.
brandjoncc0f6a62017-05-08 13:19:21 -0400401 */
402 public Map<String, Object> getBindings() {
brandjonadda5122017-08-29 18:29:58 +0200403 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400404 return Collections.unmodifiableMap(bindings);
405 }
406
nharmata68cc06b2018-03-01 10:21:57 -0800407 @Override
brandjoncc0f6a62017-05-08 13:19:21 -0400408 public Map<String, Object> getTransitiveBindings() {
brandjonadda5122017-08-29 18:29:58 +0200409 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400410 // Can't use ImmutableMap.Builder because it doesn't allow duplicates.
brandjon73e768e2018-02-13 12:26:52 -0800411 LinkedHashMap<String, Object> collectedBindings = new LinkedHashMap<>();
brandjoncc0f6a62017-05-08 13:19:21 -0400412 accumulateTransitiveBindings(collectedBindings);
413 return collectedBindings;
414 }
415
brandjon73e768e2018-02-13 12:26:52 -0800416 private void accumulateTransitiveBindings(LinkedHashMap<String, Object> accumulator) {
brandjonadda5122017-08-29 18:29:58 +0200417 checkInitialized();
brandjoncc0f6a62017-05-08 13:19:21 -0400418 // Put parents first, so child bindings take precedence.
419 if (parent != null) {
420 parent.accumulateTransitiveBindings(accumulator);
421 }
422 accumulator.putAll(bindings);
423 }
424
nharmata68cc06b2018-03-01 10:21:57 -0800425 @Override
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000426 public Object get(String varname) {
brandjonadda5122017-08-29 18:29:58 +0200427 checkInitialized();
nharmata68cc06b2018-03-01 10:21:57 -0800428 Object val = bindings.get(varname);
429 if (val != null) {
430 return val;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000431 }
432 if (parent != null) {
433 return parent.get(varname);
434 }
435 return null;
436 }
437
nharmata68cc06b2018-03-01 10:21:57 -0800438 @Override
laurentlbcaafe302018-08-28 10:24:31 -0700439 public void put(Environment env, String varname, Object value) throws MutabilityException {
brandjonadda5122017-08-29 18:29:58 +0200440 checkInitialized();
brandjonf6316bf2017-08-02 16:42:56 +0200441 Mutability.checkMutable(this, env.mutability());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000442 bindings.put(varname, value);
443 }
444
nharmata68cc06b2018-03-01 10:21:57 -0800445 @Override
446 public void remove(Environment env, String varname) throws MutabilityException {
brandjonadda5122017-08-29 18:29:58 +0200447 checkInitialized();
brandjonf6316bf2017-08-02 16:42:56 +0200448 Mutability.checkMutable(this, env.mutability());
laurentlb9d5c0a02017-06-13 23:08:06 +0200449 bindings.remove(varname);
450 }
451
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000452 @Override
453 public String toString() {
brandjonadda5122017-08-29 18:29:58 +0200454 if (mutability == null) {
nharmata68cc06b2018-03-01 10:21:57 -0800455 return "<Uninitialized GlobalFrame>";
brandjonadda5122017-08-29 18:29:58 +0200456 } else {
nharmata68cc06b2018-03-01 10:21:57 -0800457 return String.format("<GlobalFrame%s>", mutability());
brandjonadda5122017-08-29 18:29:58 +0200458 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000459 }
460 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100461
462 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000463 * A Continuation contains data saved during a function call and restored when the function exits.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100464 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000465 private static final class Continuation {
466 /** The {@link BaseFunction} being evaluated that will return into this Continuation. */
brandjonc06e7462017-07-11 20:54:58 +0200467 final BaseFunction function;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000468
469 /** The {@link FuncallExpression} to which this Continuation will return. */
brendandouglasfe785e12018-06-12 09:39:38 -0700470 @Nullable final FuncallExpression caller;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000471
472 /** The next Continuation after this Continuation. */
brandjonc06e7462017-07-11 20:54:58 +0200473 @Nullable final Continuation continuation;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000474
475 /** The lexical Frame of the caller. */
laurentlb5d26ef12018-08-27 12:23:15 -0700476 final Frame lexicalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000477
478 /** The global Frame of the caller. */
nharmata68cc06b2018-03-01 10:21:57 -0800479 final GlobalFrame globalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000480
laurentlb9dc4e202018-09-06 09:34:13 -0700481 /**
482 * The set of known global variables of the caller.
483 *
484 * <p>TODO(laurentlb): Remove this when we use static name resolution.
485 */
brandjon73e768e2018-02-13 12:26:52 -0800486 @Nullable final LinkedHashSet<String> knownGlobalVariables;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000487
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000488 Continuation(
brendandouglasfe785e12018-06-12 09:39:38 -0700489 @Nullable Continuation continuation,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000490 BaseFunction function,
brendandouglasfe785e12018-06-12 09:39:38 -0700491 @Nullable FuncallExpression caller,
laurentlb5d26ef12018-08-27 12:23:15 -0700492 Frame lexicalFrame,
nharmata68cc06b2018-03-01 10:21:57 -0800493 GlobalFrame globalFrame,
brendandouglasfe785e12018-06-12 09:39:38 -0700494 @Nullable LinkedHashSet<String> knownGlobalVariables) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000495 this.continuation = continuation;
496 this.function = function;
497 this.caller = caller;
498 this.lexicalFrame = lexicalFrame;
499 this.globalFrame = globalFrame;
Jon Brandvein83e3acb2016-08-11 18:53:26 +0000500 this.knownGlobalVariables = knownGlobalVariables;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000501 }
502 }
503
brandjon357ed7a2017-05-09 11:37:22 -0400504 /** An Extension to be imported with load() into a BUILD or .bzl file. */
505 @Immutable
janakr9ba46f82018-03-13 13:07:52 -0700506 // TODO(janakr,brandjon): Do Extensions actually have to start their own memoization? Or can we
507 // have a node higher up in the hierarchy inject the mutability?
shahan9012e6f2018-04-02 13:26:14 -0700508 @AutoCodec
brandjone821fb92017-07-18 21:00:22 +0200509 public static final class Extension {
510
511 private final ImmutableMap<String, Object> bindings;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100512
brandjon357ed7a2017-05-09 11:37:22 -0400513 /**
514 * Cached hash code for the transitive content of this {@code Extension} and its dependencies.
brandjon2c98fff2018-02-06 01:32:46 -0800515 *
516 * <p>Note that "content" refers to the AST content, not the evaluated bindings.
brandjon357ed7a2017-05-09 11:37:22 -0400517 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000518 private final String transitiveContentHashCode;
519
brandjone821fb92017-07-18 21:00:22 +0200520 /** Constructs with the given hash code and bindings. */
janakr9ba46f82018-03-13 13:07:52 -0700521 @AutoCodec.Instantiator
brandjone821fb92017-07-18 21:00:22 +0200522 public Extension(ImmutableMap<String, Object> bindings, String transitiveContentHashCode) {
523 this.bindings = bindings;
brandjon357ed7a2017-05-09 11:37:22 -0400524 this.transitiveContentHashCode = transitiveContentHashCode;
525 }
526
527 /**
528 * Constructs using the bindings from the global definitions of the given {@link Environment},
529 * and that {@code Environment}'s transitive hash code.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000530 */
531 public Extension(Environment env) {
brandjone821fb92017-07-18 21:00:22 +0200532 this(ImmutableMap.copyOf(env.globalFrame.bindings), env.getTransitiveContentHashCode());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000533 }
534
brandjon357ed7a2017-05-09 11:37:22 -0400535 public String getTransitiveContentHashCode() {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000536 return transitiveContentHashCode;
537 }
538
brandjon73e768e2018-02-13 12:26:52 -0800539 /** Retrieves all bindings, in a deterministic order. */
brandjon357ed7a2017-05-09 11:37:22 -0400540 public ImmutableMap<String, Object> getBindings() {
541 return bindings;
Laurent Le Bruna2501d52017-01-02 15:42:19 +0000542 }
brandjone821fb92017-07-18 21:00:22 +0200543
544 @Override
545 public boolean equals(Object obj) {
546 if (this == obj) {
547 return true;
548 }
549 if (!(obj instanceof Extension)) {
550 return false;
551 }
552 Extension other = (Extension) obj;
553 return transitiveContentHashCode.equals(other.getTransitiveContentHashCode())
554 && bindings.equals(other.getBindings());
555 }
556
janakr33439dc2018-03-22 13:44:05 -0700557 private static boolean skylarkObjectsProbablyEqual(Object obj1, Object obj2) {
558 // TODO(b/76154791): check this more carefully.
559 return obj1.equals(obj2)
560 || (obj1 instanceof SkylarkValue
561 && obj2 instanceof SkylarkValue
562 && Printer.repr(obj1).equals(Printer.repr(obj2)));
563 }
564
brandjon2c98fff2018-02-06 01:32:46 -0800565 /**
566 * Throws {@link IllegalStateException} if this {@link Extension} is not equal to {@code obj}.
567 *
568 * <p>The exception explains the reason for the inequality, including all unequal bindings.
569 */
570 public void checkStateEquals(Object obj) {
571 if (this == obj) {
572 return;
573 }
574 if (!(obj instanceof Extension)) {
laurentlbcaafe302018-08-28 10:24:31 -0700575 throw new IllegalStateException(
576 String.format(
577 "Expected an equal Extension, but got a %s instead of an Extension",
578 obj == null ? "null" : obj.getClass().getName()));
brandjon2c98fff2018-02-06 01:32:46 -0800579 }
580 Extension other = (Extension) obj;
581 ImmutableMap<String, Object> otherBindings = other.getBindings();
582
583 Set<String> names = bindings.keySet();
584 Set<String> otherNames = otherBindings.keySet();
585 if (!names.equals(otherNames)) {
laurentlbcaafe302018-08-28 10:24:31 -0700586 throw new IllegalStateException(
587 String.format(
588 "Expected Extensions to be equal, but they don't define the same bindings: "
589 + "in this one but not given one: [%s]; in given one but not this one: [%s]",
590 Joiner.on(", ").join(Sets.difference(names, otherNames)),
591 Joiner.on(", ").join(Sets.difference(otherNames, names))));
brandjon2c98fff2018-02-06 01:32:46 -0800592 }
593
594 ArrayList<String> badEntries = new ArrayList<>();
595 for (String name : names) {
596 Object value = bindings.get(name);
597 Object otherValue = otherBindings.get(name);
janakr889f5622018-03-16 17:49:11 -0700598 if (value.equals(otherValue)) {
599 continue;
brandjon2c98fff2018-02-06 01:32:46 -0800600 }
janakr33439dc2018-03-22 13:44:05 -0700601 if (value instanceof SkylarkNestedSet) {
602 if (otherValue instanceof SkylarkNestedSet
603 && ((SkylarkNestedSet) value)
604 .toCollection()
605 .equals(((SkylarkNestedSet) otherValue).toCollection())) {
606 continue;
607 }
608 } else if (value instanceof SkylarkDict) {
609 if (otherValue instanceof SkylarkDict) {
610 @SuppressWarnings("unchecked")
611 SkylarkDict<Object, Object> thisDict = (SkylarkDict<Object, Object>) value;
612 @SuppressWarnings("unchecked")
613 SkylarkDict<Object, Object> otherDict = (SkylarkDict<Object, Object>) otherValue;
614 if (thisDict.size() == otherDict.size()
615 && thisDict.keySet().equals(otherDict.keySet())) {
616 boolean foundProblem = false;
617 for (Object key : thisDict.keySet()) {
618 if (!skylarkObjectsProbablyEqual(
619 Preconditions.checkNotNull(thisDict.get(key), key),
620 Preconditions.checkNotNull(otherDict.get(key), key))) {
621 foundProblem = true;
622 }
623 }
624 if (!foundProblem) {
625 continue;
626 }
627 }
628 }
629 } else if (skylarkObjectsProbablyEqual(value, otherValue)) {
janakr889f5622018-03-16 17:49:11 -0700630 continue;
631 }
632 badEntries.add(
633 String.format(
janakrcfc34322018-03-26 11:10:08 -0700634 "%s: this one has %s (class %s, %s), but given one has %s (class %s, %s)",
janakr889f5622018-03-16 17:49:11 -0700635 name,
636 Printer.repr(value),
637 value.getClass().getName(),
janakrcfc34322018-03-26 11:10:08 -0700638 value,
janakr889f5622018-03-16 17:49:11 -0700639 Printer.repr(otherValue),
janakrcfc34322018-03-26 11:10:08 -0700640 otherValue.getClass().getName(),
641 otherValue));
brandjon2c98fff2018-02-06 01:32:46 -0800642 }
643 if (!badEntries.isEmpty()) {
644 throw new IllegalStateException(
645 "Expected Extensions to be equal, but the following bindings are unequal: "
646 + Joiner.on("; ").join(badEntries));
647 }
648
649 if (!transitiveContentHashCode.equals(other.getTransitiveContentHashCode())) {
laurentlbcaafe302018-08-28 10:24:31 -0700650 throw new IllegalStateException(
651 String.format(
652 "Expected Extensions to be equal, but transitive content hashes don't match:"
653 + " %s != %s",
654 transitiveContentHashCode, other.getTransitiveContentHashCode()));
brandjon2c98fff2018-02-06 01:32:46 -0800655 }
656 }
657
brandjone821fb92017-07-18 21:00:22 +0200658 @Override
659 public int hashCode() {
660 return Objects.hash(bindings, transitiveContentHashCode);
661 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000662 }
663
664 /**
laurentlbcaafe302018-08-28 10:24:31 -0700665 * Static Frame for lexical variables that are always looked up in the current Environment or for
666 * the definition Environment of the function currently being evaluated.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000667 */
laurentlb5d26ef12018-08-27 12:23:15 -0700668 private Frame lexicalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000669
670 /**
671 * Static Frame for global variables; either the current lexical Frame if evaluation is currently
672 * happening at the global scope of a BUILD file, or the global Frame at the time of function
673 * definition if evaluation is currently happening in the body of a function. Thus functions can
674 * close over other functions defined in the same file.
675 */
nharmata68cc06b2018-03-01 10:21:57 -0800676 private GlobalFrame globalFrame;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000677
678 /**
laurentlbcaafe302018-08-28 10:24:31 -0700679 * Dynamic Frame for variables that are always looked up in the runtime Environment, and never in
680 * the lexical or "global" Environment as it was at the time of function definition. For instance,
681 * PACKAGE_NAME.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000682 */
683 private final Frame dynamicFrame;
684
laurentlbcaafe302018-08-28 10:24:31 -0700685 /** The semantics options that affect how Skylark code is evaluated. */
brandjon3c161912017-10-05 05:06:05 +0200686 private final SkylarkSemantics semantics;
brandjonb712f332017-04-29 16:03:32 +0200687
688 /**
laurentlbcaafe302018-08-28 10:24:31 -0700689 * An EventHandler for errors and warnings. This is not used in the BUILD language, however it
690 * might be used in Skylark code called from the BUILD language, so shouldn't be null.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000691 */
692 private final EventHandler eventHandler;
693
694 /**
John Field1ea7fc32015-12-22 19:37:19 +0000695 * For each imported extension, a global Skylark frame from which to load() individual bindings.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000696 */
John Field1ea7fc32015-12-22 19:37:19 +0000697 private final Map<String, Extension> importedExtensions;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000698
699 /**
Laurent Le Brune51a4d22016-10-11 18:04:16 +0000700 * Is this Environment being executed during the loading phase? Many builtin functions are only
laurentlbcaafe302018-08-28 10:24:31 -0700701 * enabled during the loading phase, and check this flag. TODO(laurentlb): Remove from Environment
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000702 */
Laurent Le Brune51a4d22016-10-11 18:04:16 +0000703 private final Phase phase;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000704
705 /**
laurentlbcaafe302018-08-28 10:24:31 -0700706 * When in a lexical (Skylark) Frame, this set contains the variable names that are global, as
707 * determined not by global declarations (not currently supported), but by previous lookups that
708 * ended being global or dynamic. This is necessary because if in a function definition something
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000709 * reads a global variable after which a local variable with the same name is assigned an
710 * Exception needs to be thrown.
711 */
brandjon73e768e2018-02-13 12:26:52 -0800712 @Nullable private LinkedHashSet<String> knownGlobalVariables;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000713
714 /**
laurentlbcaafe302018-08-28 10:24:31 -0700715 * When in a lexical (Skylark) frame, this lists the names of the functions in the call stack. We
716 * currently use it to artificially disable recursion.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000717 */
718 @Nullable private Continuation continuation;
719
720 /**
laurentlbcaafe302018-08-28 10:24:31 -0700721 * Gets the label of the BUILD file that is using this environment. For example, if a target //foo
722 * has a dependency on //bar which is a Skylark rule defined in //rules:my_rule.bzl being
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000723 * evaluated in this environment, then this would return //foo.
724 */
725 @Nullable private final Label callerLabel;
726
727 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000728 * Enters a scope by saving state to a new Continuation
brendandouglasfe785e12018-06-12 09:39:38 -0700729 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000730 * @param function the function whose scope to enter
nharmata68cc06b2018-03-01 10:21:57 -0800731 * @param lexical the lexical frame to use
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000732 * @param caller the source AST node for the caller
733 * @param globals the global Frame that this function closes over from its definition Environment
734 */
nharmata68cc06b2018-03-01 10:21:57 -0800735 void enterScope(
brendandouglasfe785e12018-06-12 09:39:38 -0700736 BaseFunction function,
laurentlb5d26ef12018-08-27 12:23:15 -0700737 Frame lexical,
brendandouglasfe785e12018-06-12 09:39:38 -0700738 @Nullable FuncallExpression caller,
739 GlobalFrame globals) {
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +0000740 continuation =
Laurent Le Brun4db50642016-10-26 14:25:37 +0000741 new Continuation(
742 continuation, function, caller, lexicalFrame, globalFrame, knownGlobalVariables);
nharmata68cc06b2018-03-01 10:21:57 -0800743 lexicalFrame = lexical;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000744 globalFrame = globals;
brandjon73e768e2018-02-13 12:26:52 -0800745 knownGlobalVariables = new LinkedHashSet<>();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000746 }
747
laurentlbcaafe302018-08-28 10:24:31 -0700748 /** Exits a scope by restoring state from the current continuation */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000749 void exitScope() {
750 Preconditions.checkNotNull(continuation);
751 lexicalFrame = continuation.lexicalFrame;
752 globalFrame = continuation.globalFrame;
753 knownGlobalVariables = continuation.knownGlobalVariables;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000754 continuation = continuation.continuation;
755 }
756
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +0000757 private final String transitiveHashCode;
Francois-Rene Rideau3a8ddcc2015-08-26 22:30:38 +0000758
759 /**
laurentlbcaafe302018-08-28 10:24:31 -0700760 * Checks that the current Environment is in the loading or the workspace phase. TODO(laurentlb):
761 * Move to SkylarkUtils
Laurent Le Brun27d0b322016-11-22 14:48:44 +0000762 *
Damien Martin-Guillerez074b9572016-05-23 12:33:07 +0000763 * @param symbol name of the function being only authorized thus.
764 */
765 public void checkLoadingOrWorkspacePhase(String symbol, Location loc) throws EvalException {
766 if (phase == Phase.ANALYSIS) {
767 throw new EvalException(loc, symbol + "() cannot be called during the analysis phase");
768 }
769 }
770
771 /**
laurentlbcaafe302018-08-28 10:24:31 -0700772 * Checks that the current Environment is in the loading phase. TODO(laurentlb): Move to
773 * SkylarkUtils
Laurent Le Brun27d0b322016-11-22 14:48:44 +0000774 *
Francois-Rene Rideau3a8ddcc2015-08-26 22:30:38 +0000775 * @param symbol name of the function being only authorized thus.
776 */
777 public void checkLoadingPhase(String symbol, Location loc) throws EvalException {
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +0000778 if (phase != Phase.LOADING) {
Francois-Rene Rideau3a8ddcc2015-08-26 22:30:38 +0000779 throw new EvalException(loc, symbol + "() can only be called during the loading phase");
780 }
781 }
782
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100783 /**
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000784 * Is this a global Environment?
laurentlbcaafe302018-08-28 10:24:31 -0700785 *
786 * @return true if the current code is being executed at the top-level, as opposed to inside the
787 * body of a function.
Francois-Rene Rideau6e7160d2015-08-26 17:22:35 +0000788 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000789 boolean isGlobal() {
laurentlb5d26ef12018-08-27 12:23:15 -0700790 return lexicalFrame instanceof GlobalFrame;
Francois-Rene Rideau6e7160d2015-08-26 17:22:35 +0000791 }
792
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000793 @Override
794 public Mutability mutability() {
795 // the mutability of the environment is that of its dynamic frame.
796 return dynamicFrame.mutability();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100797 }
798
brandjon73e768e2018-02-13 12:26:52 -0800799 /** Returns the global variables for the Environment (not including dynamic bindings). */
nharmata68cc06b2018-03-01 10:21:57 -0800800 public GlobalFrame getGlobals() {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000801 return globalFrame;
802 }
803
804 /**
laurentlbcaafe302018-08-28 10:24:31 -0700805 * Returns an EventHandler for errors and warnings. The BUILD language doesn't use it directly,
806 * but can call Skylark code that does use it.
807 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000808 * @return an EventHandler
809 */
Francois-Rene Rideaue6a46b82015-04-10 18:31:43 +0000810 public EventHandler getEventHandler() {
811 return eventHandler;
812 }
813
michajlocbb33472017-10-23 20:30:18 +0200814 /**
815 * Returns if calling the supplied function would be a recursive call, or in other words if the
816 * supplied function is already on the stack.
817 */
818 boolean isRecursiveCall(UserDefinedFunction function) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000819 for (Continuation k = continuation; k != null; k = k.continuation) {
michajlocbb33472017-10-23 20:30:18 +0200820 if (k.function.equals(function)) {
821 return true;
822 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000823 }
michajlocbb33472017-10-23 20:30:18 +0200824 return false;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100825 }
826
michajlocbb33472017-10-23 20:30:18 +0200827 /** Returns the current function call, if it exists. */
828 @Nullable
829 BaseFunction getCurrentFunction() {
830 return continuation != null ? continuation.function : null;
831 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000832
laurentlbcaafe302018-08-28 10:24:31 -0700833 /** Returns the FuncallExpression and the BaseFunction for the top-level call being evaluated. */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000834 public Pair<FuncallExpression, BaseFunction> getTopCall() {
835 Continuation continuation = this.continuation;
836 if (continuation == null) {
837 return null;
838 }
839 while (continuation.continuation != null) {
840 continuation = continuation.continuation;
841 }
842 return new Pair<>(continuation.caller, continuation.function);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100843 }
844
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000845 /**
Laurent Le Bruna31bc4e2016-10-27 12:48:22 +0000846 * Constructs an Environment. This is the main, most basic constructor.
847 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000848 * @param globalFrame a frame for the global Environment
849 * @param dynamicFrame a frame for the dynamic Environment
850 * @param eventHandler an EventHandler for warnings, errors, etc
851 * @param importedExtensions Extension-s from which to import bindings with load()
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000852 * @param fileContentHashCode a hash for the source file being evaluated, if any
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +0000853 * @param phase the current phase
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000854 * @param callerLabel the label this environment came from
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000855 */
856 private Environment(
nharmata68cc06b2018-03-01 10:21:57 -0800857 GlobalFrame globalFrame,
858 LexicalFrame dynamicFrame,
brandjon3c161912017-10-05 05:06:05 +0200859 SkylarkSemantics semantics,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000860 EventHandler eventHandler,
John Field1ea7fc32015-12-22 19:37:19 +0000861 Map<String, Extension> importedExtensions,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000862 @Nullable String fileContentHashCode,
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000863 Phase phase,
864 @Nullable Label callerLabel) {
laurentlb5d26ef12018-08-27 12:23:15 -0700865 this.lexicalFrame = Preconditions.checkNotNull(globalFrame);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000866 this.globalFrame = Preconditions.checkNotNull(globalFrame);
867 this.dynamicFrame = Preconditions.checkNotNull(dynamicFrame);
brandjon33b49432017-05-04 18:58:52 +0200868 Preconditions.checkArgument(!globalFrame.mutability().isFrozen());
869 Preconditions.checkArgument(!dynamicFrame.mutability().isFrozen());
brandjonb712f332017-04-29 16:03:32 +0200870 this.semantics = semantics;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000871 this.eventHandler = eventHandler;
872 this.importedExtensions = importedExtensions;
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +0000873 this.phase = phase;
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000874 this.callerLabel = callerLabel;
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +0000875 this.transitiveHashCode =
876 computeTransitiveContentHashCode(fileContentHashCode, importedExtensions);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000877 }
878
879 /**
brandjond9a1a432017-10-19 23:47:08 +0200880 * A Builder class for Environment.
881 *
882 * <p>The caller must explicitly set the semantics by calling either {@link #setSemantics} or
883 * {@link #useDefaultSemantics}.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000884 */
885 public static class Builder {
886 private final Mutability mutability;
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +0000887 private Phase phase = Phase.ANALYSIS;
nharmata68cc06b2018-03-01 10:21:57 -0800888 @Nullable private GlobalFrame parent;
brandjon3c161912017-10-05 05:06:05 +0200889 @Nullable private SkylarkSemantics semantics;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000890 @Nullable private EventHandler eventHandler;
John Field1ea7fc32015-12-22 19:37:19 +0000891 @Nullable private Map<String, Extension> importedExtensions;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000892 @Nullable private String fileContentHashCode;
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000893 private Label label;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000894
895 Builder(Mutability mutability) {
896 this.mutability = mutability;
897 }
898
Luis Fernando Pino Duque3fedf9e2016-04-28 15:47:29 +0000899 /** Enables loading or workspace phase only functions in this Environment. */
900 public Builder setPhase(Phase phase) {
901 Preconditions.checkState(this.phase == Phase.ANALYSIS);
902 this.phase = phase;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000903 return this;
904 }
905
906 /** Inherits global bindings from the given parent Frame. */
nharmata6070ba72018-03-01 13:35:33 -0800907 public Builder setGlobals(GlobalFrame parent) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000908 Preconditions.checkState(this.parent == null);
laurentlbc8e67962018-08-31 14:20:40 -0700909 // Make sure that the global frame does at most two lookups: one for the module definitions
910 // and one for the builtins.
nharmata6070ba72018-03-01 13:35:33 -0800911 this.parent = parent;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000912 return this;
913 }
914
brandjon3c161912017-10-05 05:06:05 +0200915 public Builder setSemantics(SkylarkSemantics semantics) {
brandjonb712f332017-04-29 16:03:32 +0200916 this.semantics = semantics;
917 return this;
918 }
919
brandjond9a1a432017-10-19 23:47:08 +0200920 public Builder useDefaultSemantics() {
921 this.semantics = SkylarkSemantics.DEFAULT_SEMANTICS;
922 return this;
923 }
924
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000925 /** Sets an EventHandler for errors and warnings. */
926 public Builder setEventHandler(EventHandler eventHandler) {
927 Preconditions.checkState(this.eventHandler == null);
928 this.eventHandler = eventHandler;
929 return this;
930 }
931
932 /** Declares imported extensions for load() statements. */
laurentlbcaafe302018-08-28 10:24:31 -0700933 public Builder setImportedExtensions(Map<String, Extension> importMap) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000934 Preconditions.checkState(this.importedExtensions == null);
John Field1ea7fc32015-12-22 19:37:19 +0000935 this.importedExtensions = importMap;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000936 return this;
937 }
938
939 /** Declares content hash for the source file for this Environment. */
940 public Builder setFileContentHashCode(String fileContentHashCode) {
941 this.fileContentHashCode = fileContentHashCode;
942 return this;
943 }
944
945 /** Builds the Environment. */
946 public Environment build() {
brandjon33b49432017-05-04 18:58:52 +0200947 Preconditions.checkArgument(!mutability.isFrozen());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000948 if (parent != null) {
brandjon73e768e2018-02-13 12:26:52 -0800949 Preconditions.checkArgument(parent.mutability().isFrozen(), "parent frame must be frozen");
laurentlbc8e67962018-08-31 14:20:40 -0700950 if (parent.parent != null) { // This code path doesn't happen in Bazel.
951
952 // Flatten the frame, ensure all builtins are in the same frame.
953 parent =
954 new GlobalFrame(
955 parent.mutability(),
956 null /* parent */,
957 parent.label,
958 parent.getTransitiveBindings());
959 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000960 }
nharmata68cc06b2018-03-01 10:21:57 -0800961 GlobalFrame globalFrame = new GlobalFrame(mutability, parent);
962 LexicalFrame dynamicFrame = LexicalFrame.create(mutability);
brandjonb712f332017-04-29 16:03:32 +0200963 if (semantics == null) {
brandjonb368b392017-10-20 22:02:02 +0200964 throw new IllegalArgumentException("must call either setSemantics or useDefaultSemantics");
brandjonb712f332017-04-29 16:03:32 +0200965 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000966 if (importedExtensions == null) {
967 importedExtensions = ImmutableMap.of();
968 }
Laurent Le Brun46171092015-12-23 14:04:23 +0000969 return new Environment(
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000970 globalFrame,
971 dynamicFrame,
brandjonb712f332017-04-29 16:03:32 +0200972 semantics,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000973 eventHandler,
974 importedExtensions,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000975 fileContentHashCode,
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000976 phase,
977 label);
978 }
979
980 public Builder setCallerLabel(Label label) {
981 this.label = label;
982 return this;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000983 }
984 }
985
986 public static Builder builder(Mutability mutability) {
987 return new Builder(mutability);
988 }
989
laurentlbcaafe302018-08-28 10:24:31 -0700990 /** Returns the caller's label. */
Kristina Chodorow704d8d92016-12-01 17:47:52 +0000991 public Label getCallerLabel() {
992 return callerLabel;
993 }
994
995 /**
laurentlbcaafe302018-08-28 10:24:31 -0700996 * Sets a binding for a special dynamic variable in this Environment. This is not for end-users,
997 * and will throw an AssertionError in case of conflict.
998 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000999 * @param varname the name of the dynamic variable to be bound
1000 * @param value a value to bind to the variable
1001 * @return this Environment, in fluid style
1002 */
1003 public Environment setupDynamic(String varname, Object value) {
laurentlb5d26ef12018-08-27 12:23:15 -07001004 if (lookup(varname) != null) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001005 throw new AssertionError(
laurentlbcaafe302018-08-28 10:24:31 -07001006 String.format("Trying to bind dynamic variable '%s' but it is already bound", varname));
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001007 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001008 try {
1009 dynamicFrame.put(this, varname, value);
1010 } catch (MutabilityException e) {
1011 // End users don't have access to setupDynamic, and it is an implementation error
1012 // if we encounter a mutability exception.
1013 throw new AssertionError(
vladmos46907932017-06-30 14:01:45 +02001014 String.format(
1015 "Trying to bind dynamic variable '%s' in frozen environment %s", varname, this),
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001016 e);
1017 }
1018 return this;
1019 }
1020
laurentlb9d5c0a02017-06-13 23:08:06 +02001021 /** Remove variable from local bindings. */
1022 void removeLocalBinding(String varname) {
1023 try {
laurentlb5d26ef12018-08-27 12:23:15 -07001024 lexicalFrame.remove(this, varname);
laurentlb9d5c0a02017-06-13 23:08:06 +02001025 } catch (MutabilityException e) {
1026 throw new AssertionError(e);
1027 }
1028 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001029
nharmataf1013482018-03-07 10:48:31 -08001030 /**
laurentlbcaafe302018-08-28 10:24:31 -07001031 * Modifies a binding in the current Frame of this Environment, as would an {@link
1032 * AssignmentStatement}. Does not try to modify an inherited binding. This will shadow any
1033 * inherited binding, which may be an error that you want to guard against before calling this
1034 * function.
1035 *
nharmataf1013482018-03-07 10:48:31 -08001036 * @param varname the name of the variable to be bound
1037 * @param value the value to bind to the variable
1038 * @return this Environment, in fluid style
1039 */
1040 public Environment update(String varname, Object value) throws EvalException {
laurentlb9dc4e202018-09-06 09:34:13 -07001041 Preconditions.checkNotNull(value, "trying to assign null to '%s'", varname);
nharmata6e233f02018-03-07 16:02:22 -08001042 if (isKnownGlobalVariable(varname)) {
1043 throw new EvalException(
laurentlb9dc4e202018-09-06 09:34:13 -07001044 null,
1045 String.format(
1046 "Variable '%s' is referenced before assignment. "
1047 + "The variable is defined in the global scope.",
1048 varname));
nharmata6e233f02018-03-07 16:02:22 -08001049 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001050 try {
laurentlb9dc4e202018-09-06 09:34:13 -07001051 lexicalFrame.put(this, varname, value);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001052 } catch (MutabilityException e) {
1053 // Note that since at this time we don't accept the global keyword, and don't have closures,
1054 // end users should never be able to mutate a frozen Environment, and a MutabilityException
1055 // is therefore a failed assertion for Bazel. However, it is possible to shadow a binding
1056 // imported from a parent Environment by updating the current Environment, which will not
1057 // trigger a MutabilityException.
1058 throw new AssertionError(
laurentlbcaafe302018-08-28 10:24:31 -07001059 Printer.format("Can't update %s to %r in frozen environment", varname, value), e);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001060 }
1061 return this;
1062 }
1063
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001064 /**
1065 * Initializes a binding in this Environment. It is an error if the variable is already bound.
1066 * This is not for end-users, and will throw an AssertionError in case of conflict.
laurentlbcaafe302018-08-28 10:24:31 -07001067 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001068 * @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 setup(String varname, Object value) {
laurentlb1434b172018-08-27 11:32:34 -07001073 if (lookup(varname) != null) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001074 throw new AssertionError(String.format("variable '%s' already bound", varname));
1075 }
1076 return setupOverride(varname, value);
1077 }
1078
1079 /**
laurentlbcaafe302018-08-28 10:24:31 -07001080 * Initializes a binding in this environment. Overrides any previous binding. This is not for
1081 * end-users, and will throw an AssertionError in case of conflict.
1082 *
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001083 * @param varname the name of the variable to be bound
1084 * @param value the value to bind to the variable
1085 * @return this Environment, in fluid style
1086 */
1087 public Environment setupOverride(String varname, Object value) {
1088 try {
1089 return update(varname, value);
1090 } catch (EvalException ee) {
1091 throw new AssertionError(ee);
1092 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001093 }
1094
1095 /**
laurentlbebb40712018-08-29 11:54:27 -07001096 * Returns the value of a variable defined in Local scope. Do not search in any parent scope. This
1097 * function should be used once the AST has been analysed and we know which variables are local.
laurentlbcaafe302018-08-28 10:24:31 -07001098 */
1099 public Object localLookup(String varname) {
1100 return lexicalFrame.get(varname);
1101 }
1102
1103 /**
laurentlbc8e67962018-08-31 14:20:40 -07001104 * Returns the value of a variable defined in the Module scope (e.g. global variables, functions).
1105 *
1106 * <p>TODO(laurentlb): This method may also return values from the universe. We should fix that.
laurentlbebb40712018-08-29 11:54:27 -07001107 */
1108 public Object moduleLookup(String varname) {
1109 return globalFrame.get(varname);
1110 }
1111
1112 /** Returns the value of a variable defined in the Universe scope (builtins). */
1113 public Object universeLookup(String varname) {
laurentlbc8e67962018-08-31 14:20:40 -07001114 // TODO(laurentlb): look only at globalFrame.parent.
laurentlbebb40712018-08-29 11:54:27 -07001115 return globalFrame.get(varname);
1116 }
1117
1118 /** Returns the value of a variable defined with setupDynamic. */
1119 public Object dynamicLookup(String varname) {
1120 return dynamicFrame.get(varname);
1121 }
1122
1123 /**
brandjon73e768e2018-02-13 12:26:52 -08001124 * Returns the value from the environment whose name is "varname" if it exists, otherwise null.
laurentlbebb40712018-08-29 11:54:27 -07001125 *
1126 * <p>TODO(laurentlb): Remove this method. Callers should know where the value is defined and use
1127 * the corresponding method (e.g. localLookup or moduleLookup).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001128 */
laurentlbc3e58512018-09-04 10:11:32 -07001129 Object lookup(String varname) {
brandjoncc0f6a62017-05-08 13:19:21 -04001130 // Lexical frame takes precedence, then globals, then dynamics.
laurentlb5d26ef12018-08-27 12:23:15 -07001131 Object lexicalValue = lexicalFrame.get(varname);
1132 if (lexicalValue != null) {
1133 return lexicalValue;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001134 }
1135 Object globalValue = globalFrame.get(varname);
1136 Object dynamicValue = dynamicFrame.get(varname);
1137 if (globalValue == null && dynamicValue == null) {
Vladimir Moskva4994cb32016-08-10 15:44:31 +00001138 return null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001139 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001140 if (knownGlobalVariables != null) {
1141 knownGlobalVariables.add(varname);
1142 }
1143 if (globalValue != null) {
1144 return globalValue;
1145 }
1146 return dynamicValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001147 }
1148
1149 /**
brandjon73e768e2018-02-13 12:26:52 -08001150 * Returns true if varname is a known global variable (i.e., it has been read in the context of
1151 * the current function).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001152 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001153 boolean isKnownGlobalVariable(String varname) {
laurentlb9dc4e202018-09-06 09:34:13 -07001154 return !semantics.incompatibleStaticNameResolution()
1155 && knownGlobalVariables != null
1156 && knownGlobalVariables.contains(varname);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001157 }
1158
brandjon3c161912017-10-05 05:06:05 +02001159 public SkylarkSemantics getSemantics() {
brandjonb712f332017-04-29 16:03:32 +02001160 return semantics;
1161 }
1162
Vladimir Moskvadfad9e32016-09-15 18:22:01 +00001163 public void handleEvent(Event event) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001164 eventHandler.handle(event);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001165 }
1166
brandjon73e768e2018-02-13 12:26:52 -08001167 /**
1168 * Returns a set of all names of variables that are accessible in this {@code Environment}, in a
1169 * deterministic order.
1170 */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001171 public Set<String> getVariableNames() {
brandjon73e768e2018-02-13 12:26:52 -08001172 LinkedHashSet<String> vars = new LinkedHashSet<>();
laurentlb5d26ef12018-08-27 12:23:15 -07001173 vars.addAll(lexicalFrame.getTransitiveBindings().keySet());
1174 // No-op when globalFrame = lexicalFrame
brandjoncc0f6a62017-05-08 13:19:21 -04001175 vars.addAll(globalFrame.getTransitiveBindings().keySet());
1176 vars.addAll(dynamicFrame.getTransitiveBindings().keySet());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001177 return vars;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001178 }
1179
Googler29eafdf2018-05-23 12:32:07 -07001180 private static final class EvalEventHandler implements EventHandler {
1181 List<String> messages = new ArrayList<>();
1182
1183 @Override
1184 public void handle(Event event) {
1185 if (event.getKind() == EventKind.ERROR) {
1186 messages.add(event.getMessage());
1187 }
1188 }
1189 }
1190
1191 @Override
brendandouglas01683d32018-06-26 08:53:10 -07001192 public Object evaluate(String contents) throws EvalException, InterruptedException {
1193 ParserInputSource input =
1194 ParserInputSource.create(contents, PathFragment.create("<debug eval>"));
Googler29eafdf2018-05-23 12:32:07 -07001195 EvalEventHandler eventHandler = new EvalEventHandler();
brendandouglas01683d32018-06-26 08:53:10 -07001196 Statement statement = Parser.parseStatement(input, eventHandler, ParsingLevel.LOCAL_LEVEL);
Googler29eafdf2018-05-23 12:32:07 -07001197 if (!eventHandler.messages.isEmpty()) {
brendandouglas01683d32018-06-26 08:53:10 -07001198 throw new EvalException(statement.getLocation(), eventHandler.messages.get(0));
Googler29eafdf2018-05-23 12:32:07 -07001199 }
brendandouglas01683d32018-06-26 08:53:10 -07001200 // TODO(bazel-team): move statement handling code to Eval
1201 // deal with the most common case first
1202 if (statement.kind() == Statement.Kind.EXPRESSION) {
1203 return ((ExpressionStatement) statement).getExpression().doEval(this);
1204 }
1205 // all other statement types are executed directly
1206 Eval.fromEnvironment(this).exec(statement);
1207 switch (statement.kind()) {
1208 case ASSIGNMENT:
1209 case AUGMENTED_ASSIGNMENT:
1210 return ((AssignmentStatement) statement).getLValue().getExpression().doEval(this);
1211 case RETURN:
1212 Expression expr = ((ReturnStatement) statement).getReturnExpression();
1213 return expr != null ? expr.doEval(this) : Runtime.NONE;
1214 default:
1215 return Runtime.NONE;
1216 }
Googler29eafdf2018-05-23 12:32:07 -07001217 }
1218
1219 @Override
1220 public ImmutableList<DebugFrame> listFrames(Location currentLocation) {
1221 ImmutableList.Builder<DebugFrame> frameListBuilder = ImmutableList.builder();
1222
1223 Continuation currentContinuation = continuation;
laurentlb5d26ef12018-08-27 12:23:15 -07001224 Frame currentFrame = lexicalFrame;
Googler29eafdf2018-05-23 12:32:07 -07001225
1226 // if there's a continuation then the current frame is a lexical frame
1227 while (currentContinuation != null) {
1228 frameListBuilder.add(
1229 DebugFrame.builder()
1230 .setLexicalFrameBindings(ImmutableMap.copyOf(currentFrame.getTransitiveBindings()))
1231 .setGlobalBindings(ImmutableMap.copyOf(getGlobals().getTransitiveBindings()))
1232 .setFunctionName(currentContinuation.function.getFullName())
1233 .setLocation(currentLocation)
1234 .build());
1235
1236 currentFrame = currentContinuation.lexicalFrame;
brendandouglasfe785e12018-06-12 09:39:38 -07001237 currentLocation =
1238 currentContinuation.caller != null ? currentContinuation.caller.getLocation() : null;
Googler29eafdf2018-05-23 12:32:07 -07001239 currentContinuation = currentContinuation.continuation;
1240 }
1241
1242 frameListBuilder.add(
1243 DebugFrame.builder()
1244 .setGlobalBindings(ImmutableMap.copyOf(getGlobals().getTransitiveBindings()))
1245 .setFunctionName("<top level>")
1246 .setLocation(currentLocation)
1247 .build());
1248
1249 return frameListBuilder.build();
1250 }
1251
1252 @Override
1253 @Nullable
1254 public ReadyToPause stepControl(Stepping stepping) {
1255 final Continuation pausedContinuation = continuation;
1256
1257 switch (stepping) {
1258 case NONE:
1259 return null;
1260 case INTO:
1261 // pause at the very next statement
1262 return env -> true;
1263 case OVER:
1264 return env -> isAt(env, pausedContinuation) || isOutside(env, pausedContinuation);
1265 case OUT:
1266 // if we're at the outer-most frame, same as NONE
1267 return pausedContinuation == null ? null : env -> isOutside(env, pausedContinuation);
1268 }
1269 throw new IllegalArgumentException("Unsupported stepping type: " + stepping);
1270 }
1271
1272 /** Returns true if {@code env} is in a parent frame of {@code pausedContinuation}. */
1273 private static boolean isOutside(Environment env, @Nullable Continuation pausedContinuation) {
1274 return pausedContinuation != null && env.continuation == pausedContinuation.continuation;
1275 }
1276
1277 /** Returns true if {@code env} is at the same frame as {@code pausedContinuation. */
1278 private static boolean isAt(Environment env, @Nullable Continuation pausedContinuation) {
1279 return env.continuation == pausedContinuation;
1280 }
1281
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001282 @Override
1283 public int hashCode() {
1284 throw new UnsupportedOperationException(); // avoid nondeterminism
1285 }
1286
1287 @Override
1288 public boolean equals(Object that) {
1289 throw new UnsupportedOperationException();
1290 }
1291
1292 @Override
1293 public String toString() {
Francois-Rene Rideaubd0c7bb2015-09-21 16:54:19 +00001294 return String.format("<Environment%s>", mutability());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001295 }
1296
1297 /**
laurentlbcaafe302018-08-28 10:24:31 -07001298 * An Exception thrown when an attempt is made to import a symbol from a file that was not
1299 * properly loaded.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001300 */
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001301 static class LoadFailedException extends Exception {
John Field1ea7fc32015-12-22 19:37:19 +00001302 LoadFailedException(String importString) {
laurentlbcaafe302018-08-28 10:24:31 -07001303 super(
1304 String.format(
1305 "file '%s' was not correctly loaded. "
1306 + "Make sure the 'load' statement appears in the global scope in your file",
1307 importString));
Vladimir Moskva4994cb32016-08-10 15:44:31 +00001308 }
1309
Laurent Le Bruna2501d52017-01-02 15:42:19 +00001310 LoadFailedException(String importString, String symbolString, Iterable<String> allKeys) {
1311 super(
1312 String.format(
1313 "file '%s' does not contain symbol '%s'%s",
1314 importString, symbolString, SpellChecker.didYouMean(symbolString, allKeys)));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001315 }
1316 }
1317
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001318 void importSymbol(String importString, Identifier symbol, String nameInLoadedFile)
Vladimir Moskva4994cb32016-08-10 15:44:31 +00001319 throws LoadFailedException {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001320 Preconditions.checkState(isGlobal()); // loading is only allowed at global scope.
1321
John Field1ea7fc32015-12-22 19:37:19 +00001322 if (!importedExtensions.containsKey(importString)) {
1323 throw new LoadFailedException(importString);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001324 }
Florian Weikert9d659ad2015-07-23 14:44:36 +00001325
John Field1ea7fc32015-12-22 19:37:19 +00001326 Extension ext = importedExtensions.get(importString);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001327
brandjon357ed7a2017-05-09 11:37:22 -04001328 Map<String, Object> bindings = ext.getBindings();
1329 if (!bindings.containsKey(nameInLoadedFile)) {
1330 throw new LoadFailedException(importString, nameInLoadedFile, bindings.keySet());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001331 }
1332
brandjon357ed7a2017-05-09 11:37:22 -04001333 Object value = bindings.get(nameInLoadedFile);
Florian Weikert9d659ad2015-07-23 14:44:36 +00001334
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001335 try {
1336 update(symbol.getName(), value);
1337 } catch (EvalException e) {
John Field1ea7fc32015-12-22 19:37:19 +00001338 throw new LoadFailedException(importString);
Francois-Rene Rideau5a94e592015-09-04 19:13:47 +00001339 }
Florian Weikert6a663392015-09-02 14:04:33 +00001340 }
1341
brandjon73e768e2018-02-13 12:26:52 -08001342 /**
1343 * Computes a deterministic hash for the given base hash code and extension map (the map's order
1344 * does not matter).
1345 */
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001346 private static String computeTransitiveContentHashCode(
1347 @Nullable String baseHashCode, Map<String, Extension> importedExtensions) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001348 // Calculate a new hash from the hash of the loaded Extension-s.
1349 Fingerprint fingerprint = new Fingerprint();
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001350 if (baseHashCode != null) {
1351 fingerprint.addString(Preconditions.checkNotNull(baseHashCode));
1352 }
John Field1ea7fc32015-12-22 19:37:19 +00001353 TreeSet<String> importStrings = new TreeSet<>(importedExtensions.keySet());
1354 for (String importString : importStrings) {
1355 fingerprint.addString(importedExtensions.get(importString).getTransitiveContentHashCode());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001356 }
1357 return fingerprint.hexDigestAndReset();
Janak Ramakrishnanb6e33bc2015-09-06 21:05:23 +00001358 }
1359
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001360 /**
laurentlbcaafe302018-08-28 10:24:31 -07001361 * Returns a hash code calculated from the hash code of this Environment and the transitive
1362 * closure of other Environments it loads.
Janak Ramakrishnan9b12f9c2016-05-20 16:33:02 +00001363 */
1364 public String getTransitiveContentHashCode() {
1365 return transitiveHashCode;
1366 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001367
brandjonb80eb182018-05-01 10:54:58 -07001368 /** A read-only {@link Environment.GlobalFrame} with False/True/None constants only. */
janakra1f17612018-08-17 06:01:51 -07001369 @AutoCodec static final GlobalFrame CONSTANTS_ONLY = createConstantsGlobals();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001370
brandjonb80eb182018-05-01 10:54:58 -07001371 /**
janakra1f17612018-08-17 06:01:51 -07001372 * A read-only {@link Environment.GlobalFrame} with initial globals as defined in MethodLibrary.
brandjonb80eb182018-05-01 10:54:58 -07001373 */
janakra1f17612018-08-17 06:01:51 -07001374 @AutoCodec public static final GlobalFrame DEFAULT_GLOBALS = createDefaultGlobals();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001375
Laurent Le Brun5e991982016-10-14 13:39:45 +00001376 /** To be removed when all call-sites are updated. */
nharmata68cc06b2018-03-01 10:21:57 -08001377 public static final GlobalFrame SKYLARK = DEFAULT_GLOBALS;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001378
nharmata68cc06b2018-03-01 10:21:57 -08001379 private static Environment.GlobalFrame createConstantsGlobals() {
brandjonb80eb182018-05-01 10:54:58 -07001380 ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
1381 Runtime.addConstantsToBuilder(builder);
1382 return GlobalFrame.createForBuiltins(builder.build());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001383 }
1384
nharmata68cc06b2018-03-01 10:21:57 -08001385 private static Environment.GlobalFrame createDefaultGlobals() {
brandjonb80eb182018-05-01 10:54:58 -07001386 ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
1387 Runtime.addConstantsToBuilder(builder);
1388 MethodLibrary.addBindingsToBuilder(builder);
1389 return GlobalFrame.createForBuiltins(builder.build());
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001390 }
1391
brandjone5d95fb2017-07-13 17:23:09 +02001392 /** An exception thrown by {@link #FAIL_FAST_HANDLER}. */
1393 // TODO(bazel-team): Possibly extend RuntimeException instead of IllegalArgumentException.
1394 public static class FailFastException extends IllegalArgumentException {
1395 public FailFastException(String s) {
1396 super(s);
1397 }
1398 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001399
1400 /**
brandjone5d95fb2017-07-13 17:23:09 +02001401 * A handler that immediately throws {@link FailFastException} whenever an error or warning
1402 * occurs.
1403 *
laurentlbcaafe302018-08-28 10:24:31 -07001404 * <p>We do not reuse an existing unchecked exception type, because callers (e.g., test
1405 * assertions) need to be able to distinguish between organically occurring exceptions and
1406 * exceptions thrown by this handler.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001407 */
laurentlbcaafe302018-08-28 10:24:31 -07001408 public static final EventHandler FAIL_FAST_HANDLER =
1409 new EventHandler() {
1410 @Override
1411 public void handle(Event event) {
1412 if (EventKind.ERRORS_AND_WARNINGS.contains(event.getKind())) {
1413 throw new FailFastException(event.toString());
1414 }
1415 }
1416 };
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001417}