aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/syntax/Environment.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/Environment.java90
1 files changed, 89 insertions, 1 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
index c536a688b3..6f84188b4b 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
@@ -16,6 +16,7 @@ package com.google.devtools.build.lib.syntax;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.devtools.build.lib.cmdline.Label;
@@ -31,10 +32,12 @@ import com.google.devtools.build.lib.syntax.Mutability.MutabilityException;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.SpellChecker;
+import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -75,7 +78,7 @@ import javax.annotation.Nullable;
* that the words "dynamic" and "static" refer to the point of view of the source code, and here we
* have a dual point of view.
*/
-public final class Environment implements Freezable {
+public final class Environment implements Freezable, Debuggable {
/**
* A phase for enabling or disabling certain builtin functions
@@ -1150,6 +1153,91 @@ public final class Environment implements Freezable {
return vars;
}
+ private static final class EvalEventHandler implements EventHandler {
+ List<String> messages = new ArrayList<>();
+
+ @Override
+ public void handle(Event event) {
+ if (event.getKind() == EventKind.ERROR) {
+ messages.add(event.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public Object evaluate(String expression) throws EvalException, InterruptedException {
+ ParserInputSource inputSource =
+ ParserInputSource.create(expression, PathFragment.create("<debug eval>"));
+ EvalEventHandler eventHandler = new EvalEventHandler();
+ Expression expr = Parser.parseExpression(inputSource, eventHandler);
+ if (!eventHandler.messages.isEmpty()) {
+ throw new EvalException(expr.getLocation(), eventHandler.messages.get(0));
+ }
+ return expr.eval(this);
+ }
+
+ @Override
+ public ImmutableList<DebugFrame> listFrames(Location currentLocation) {
+ ImmutableList.Builder<DebugFrame> frameListBuilder = ImmutableList.builder();
+
+ Continuation currentContinuation = continuation;
+ Frame currentFrame = currentFrame();
+
+ // if there's a continuation then the current frame is a lexical frame
+ while (currentContinuation != null) {
+ frameListBuilder.add(
+ DebugFrame.builder()
+ .setLexicalFrameBindings(ImmutableMap.copyOf(currentFrame.getTransitiveBindings()))
+ .setGlobalBindings(ImmutableMap.copyOf(getGlobals().getTransitiveBindings()))
+ .setFunctionName(currentContinuation.function.getFullName())
+ .setLocation(currentLocation)
+ .build());
+
+ currentFrame = currentContinuation.lexicalFrame;
+ currentLocation = currentContinuation.caller.getLocation();
+ currentContinuation = currentContinuation.continuation;
+ }
+
+ frameListBuilder.add(
+ DebugFrame.builder()
+ .setGlobalBindings(ImmutableMap.copyOf(getGlobals().getTransitiveBindings()))
+ .setFunctionName("<top level>")
+ .setLocation(currentLocation)
+ .build());
+
+ return frameListBuilder.build();
+ }
+
+ @Override
+ @Nullable
+ public ReadyToPause stepControl(Stepping stepping) {
+ final Continuation pausedContinuation = continuation;
+
+ switch (stepping) {
+ case NONE:
+ return null;
+ case INTO:
+ // pause at the very next statement
+ return env -> true;
+ case OVER:
+ return env -> isAt(env, pausedContinuation) || isOutside(env, pausedContinuation);
+ case OUT:
+ // if we're at the outer-most frame, same as NONE
+ return pausedContinuation == null ? null : env -> isOutside(env, pausedContinuation);
+ }
+ throw new IllegalArgumentException("Unsupported stepping type: " + stepping);
+ }
+
+ /** Returns true if {@code env} is in a parent frame of {@code pausedContinuation}. */
+ private static boolean isOutside(Environment env, @Nullable Continuation pausedContinuation) {
+ return pausedContinuation != null && env.continuation == pausedContinuation.continuation;
+ }
+
+ /** Returns true if {@code env} is at the same frame as {@code pausedContinuation. */
+ private static boolean isAt(Environment env, @Nullable Continuation pausedContinuation) {
+ return env.continuation == pausedContinuation;
+ }
+
@Override
public int hashCode() {
throw new UnsupportedOperationException(); // avoid nondeterminism