aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/syntax/EvaluationContext.java
blob: 4966fb871777cf6844704ee55b0e3f67e3b8d3f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.devtools.build.lib.syntax;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.EventKind;
import com.google.devtools.build.lib.packages.CachingPackageLocator;
import com.google.devtools.build.lib.syntax.Environment.NoSuchVariableException;
import com.google.devtools.build.lib.vfs.Path;

import java.util.List;

import javax.annotation.Nullable;

/**
 * Context for the evaluation of programs.
 */
public final class EvaluationContext {

  @Nullable private EventHandler eventHandler;
  private Environment env;
  @Nullable private ValidationEnvironment validationEnv;
  private boolean parsePython;

  private EvaluationContext(EventHandler eventHandler, Environment env,
      @Nullable ValidationEnvironment validationEnv, boolean parsePython) {
    this.eventHandler = eventHandler;
    this.env = env;
    this.validationEnv = validationEnv;
    this.parsePython = parsePython;
  }

  /**
   * The fail fast handler, which throws a runtime exception whenever we encounter an error event.
   */
  public static final EventHandler FAIL_FAST_HANDLER = new EventHandler() {
      @Override
      public void handle(Event event) {
        Preconditions.checkArgument(
            !EventKind.ERRORS_AND_WARNINGS.contains(event.getKind()), event);
      }
    };

  public static EvaluationContext newBuildContext(EventHandler eventHandler, Environment env,
      boolean parsePython) {
    return new EvaluationContext(eventHandler, env, null, parsePython);
  }

  public static EvaluationContext newBuildContext(EventHandler eventHandler, Environment env) {
    return newBuildContext(eventHandler, env, false);
  }

  public static EvaluationContext newBuildContext(EventHandler eventHandler) {
    return newBuildContext(eventHandler, new Environment());
  }

  public static EvaluationContext newSkylarkContext(
      Environment env, ValidationEnvironment validationEnv) {
    return new EvaluationContext(env.getEventHandler(), env, validationEnv, false);
  }

  public static EvaluationContext newSkylarkContext(EventHandler eventHandler) {
    return newSkylarkContext(new SkylarkEnvironment(eventHandler), new ValidationEnvironment());
  }

  /** Base context for Skylark evaluation for internal use only, while initializing builtins */
  static final EvaluationContext SKYLARK_INITIALIZATION = newSkylarkContext(FAIL_FAST_HANDLER);

  @VisibleForTesting
  public Environment getEnvironment() {
    return env;
  }

  /** Mock package locator */
  private static final class EmptyPackageLocator implements CachingPackageLocator {
    @Override
    public Path getBuildFileForPackage(String packageName) {
      return null;
    }
  }

  /** An empty package locator */
  private static final CachingPackageLocator EMPTY_PACKAGE_LOCATOR = new EmptyPackageLocator();

  /** Create a Lexer without a supporting file */
  @VisibleForTesting
  Lexer createLexer(String... input) {
    return new Lexer(ParserInputSource.create(Joiner.on("\n").join(input), null),
        eventHandler);
  }

  /** Is this a Skylark evaluation context? */
  public boolean isSkylark() {
    return env.isSkylarkEnabled();
  }

  /** Parse a string without a supporting file, returning statements and comments */
  @VisibleForTesting
  Parser.ParseResult parseFileWithComments(String... input) {
    return isSkylark()
        ? Parser.parseFileForSkylark(createLexer(input), eventHandler, null, validationEnv)
        : Parser.parseFile(createLexer(input), eventHandler, EMPTY_PACKAGE_LOCATOR, parsePython);
  }

  /** Parse a string without a supporting file, returning statements only */
  @VisibleForTesting
  List<Statement> parseFile(String... input) {
    return parseFileWithComments(input).statements;
  }

  /** Parse an Expression from string without a supporting file */
  @VisibleForTesting
  Expression parseExpression(String... input) {
    return Parser.parseExpression(createLexer(input), eventHandler);
  }

  /** Evaluate an Expression */
  @VisibleForTesting
  Object evalExpression(Expression expression) throws EvalException, InterruptedException {
    return expression.eval(env);
  }

  /** Evaluate an Expression as parsed from String-s */
  Object evalExpression(String... input) throws EvalException, InterruptedException {
    return evalExpression(parseExpression(input));
  }

  /** Parse a build (not Skylark) Statement from string without a supporting file */
  @VisibleForTesting
  Statement parseStatement(String... input) {
    return Parser.parseStatement(createLexer(input), eventHandler);
  }

  /**
   * Evaluate a Statement
   * @param statement the Statement
   * @return the value of the evaluation, if it's an Expression, or else null
   */
  @Nullable private Object eval(Statement statement) throws EvalException, InterruptedException {
    if (statement instanceof ExpressionStatement) {
      return evalExpression(((ExpressionStatement) statement).getExpression());
    }
    statement.exec(env);
    return null;
  }

  /**
   * Evaluate a list of Statement-s
   * @return the value of the last statement if it's an Expression or else null
   */
  @Nullable private Object eval(List<Statement> statements)
      throws EvalException, InterruptedException {
    Object last = null;
    for (Statement statement : statements) {
      last = eval(statement);
    }
    return last;
  }

  /** Update a variable in the environment, in fluent style */
  public EvaluationContext update(String varname, Object value) throws EvalException {
    env.update(varname, value);
    if (validationEnv != null) {
      validationEnv.declare(varname, null);
    }
    return this;
  }

  /** Lookup a variable in the environment */
  public Object lookup(String varname) throws NoSuchVariableException {
    return env.lookup(varname);
  }

  /** Evaluate a series of statements */
  public Object eval(String... input) throws EvalException, InterruptedException {
    return eval(parseFile(input));
  }
}