// 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 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 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)); } }