aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/syntax/LValue.java
diff options
context:
space:
mode:
authorGravatar Florian Weikert <fwe@google.com>2015-11-09 13:26:24 +0000
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2015-11-10 10:19:34 +0000
commitdb8b8672e2b7659a6ab890393cdd512049b07e80 (patch)
treec35fac75865a01f584cf3e69857055edfcb582b6 /src/main/java/com/google/devtools/build/lib/syntax/LValue.java
parentb487ac69185fd0080461a3c8795589fe4532f4bb (diff)
Compile assignments to byte code and throw errors.
Add EvalExceptions for cases in which the interpreter would throw them during evaluation of the function definition. -- MOS_MIGRATED_REVID=107376021
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/syntax/LValue.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/LValue.java112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/LValue.java b/src/main/java/com/google/devtools/build/lib/syntax/LValue.java
index 7fe31872dc..1b6cf65ec5 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/LValue.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/LValue.java
@@ -14,11 +14,24 @@
package com.google.devtools.build.lib.syntax;
+import static com.google.devtools.build.lib.syntax.compiler.ByteCodeUtils.append;
+
import com.google.common.base.Preconditions;
import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.syntax.compiler.ByteCodeUtils;
+import com.google.devtools.build.lib.syntax.compiler.DebugInfo.AstAccessors;
+import com.google.devtools.build.lib.syntax.compiler.Variable.InternalVariable;
+import com.google.devtools.build.lib.syntax.compiler.VariableScope;
+
+import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
+import net.bytebuddy.implementation.bytecode.Removal;
+import net.bytebuddy.implementation.bytecode.constant.IntegerConstant;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
/**
* Class representing an LValue.
@@ -121,4 +134,103 @@ public class LValue implements Serializable {
public String toString() {
return expr.toString();
}
+
+ /**
+ * Compile an assignment within the given ASTNode to these l-values.
+ *
+ * <p>The value to possibly destructure and assign must already be on the stack.
+ */
+ public ByteCodeAppender compileAssignment(ASTNode node, AstAccessors debugAccessors,
+ VariableScope scope) throws EvalException {
+ List<ByteCodeAppender> code = new ArrayList<>();
+ compileAssignment(node, debugAccessors, expr, scope, code);
+ return ByteCodeUtils.compoundAppender(code);
+ }
+
+ /**
+ * Called recursively to compile the tree of l-values we might have.
+ */
+ private static void compileAssignment(
+ ASTNode node,
+ AstAccessors debugAccessors,
+ Expression leftValue,
+ VariableScope scope,
+ List<ByteCodeAppender> code) throws EvalException {
+ if (leftValue instanceof Identifier) {
+ code.add(compileAssignment(scope, (Identifier) leftValue));
+ } else if (leftValue instanceof ListLiteral) {
+ List<Expression> lValueExpressions = ((ListLiteral) leftValue).getElements();
+ compileAssignment(node, debugAccessors, scope, lValueExpressions, code);
+ } else {
+ String message = String.format(
+ "Can't assign to expression '%s', only to variables or nested tuples of variables",
+ leftValue);
+ throw new EvalExceptionWithStackTrace(
+ new EvalException(
+ node.getLocation(),
+ message),
+ node);
+ }
+ }
+
+ /**
+ * Assumes a collection of values on the top of the stack and assigns them to the l-value
+ * expressions given.
+ */
+ private static void compileAssignment(
+ ASTNode node,
+ AstAccessors debugAccessors,
+ VariableScope scope,
+ List<Expression> lValueExpressions,
+ List<ByteCodeAppender> code) throws EvalException {
+ InternalVariable objects = scope.freshVariable(Collection.class);
+ InternalVariable iterator = scope.freshVariable(Iterator.class);
+ // convert the object on the stack into a collection and store it to a variable for loading
+ // multiple times below below
+ code.add(new ByteCodeAppender.Simple(debugAccessors.loadLocation, EvalUtils.toCollection));
+ code.add(objects.store());
+ append(code,
+ // check that we got exactly the amount of objects in the collection that we need
+ IntegerConstant.forValue(lValueExpressions.size()),
+ objects.load(),
+ debugAccessors.loadLocation, // TODO(bazel-team) load better location within tuple
+ ByteCodeUtils.invoke(
+ LValue.class, "checkSize", int.class, Collection.class, Location.class),
+ // get an iterator to assign the objects
+ objects.load(),
+ ByteCodeUtils.invoke(Collection.class, "iterator"));
+ code.add(iterator.store());
+ // assign each object to the corresponding l-value
+ for (Expression lValue : lValueExpressions) {
+ code.add(
+ new ByteCodeAppender.Simple(
+ iterator.load(), ByteCodeUtils.invoke(Iterator.class, "next")));
+ compileAssignment(node, debugAccessors, lValue, scope, code);
+ }
+ }
+
+ /**
+ * Compile assignment to a single identifier.
+ */
+ private static ByteCodeAppender compileAssignment(VariableScope scope, Identifier identifier) {
+ // don't store to/create the _ "variable" the value is not needed, just remove it
+ if (identifier.getName().equals("_")) {
+ return new ByteCodeAppender.Simple(Removal.SINGLE);
+ }
+ return scope.getVariable(identifier).store();
+ }
+
+ /**
+ * Checks that the size of a collection at runtime conforms to the amount of l-value expressions
+ * we have to assign to.
+ */
+ public static void checkSize(int expected, Collection<?> collection, Location location)
+ throws EvalException {
+ int actual = collection.size();
+ if (expected != actual) {
+ throw new EvalException(
+ location,
+ String.format("lvalue has length %d, but rvalue has has length %d", expected, actual));
+ }
+ }
}