diff options
author | 2015-11-09 13:26:24 +0000 | |
---|---|---|
committer | 2015-11-10 10:19:34 +0000 | |
commit | db8b8672e2b7659a6ab890393cdd512049b07e80 (patch) | |
tree | c35fac75865a01f584cf3e69857055edfcb582b6 /src/main/java/com/google/devtools/build/lib/syntax/LValue.java | |
parent | b487ac69185fd0080461a3c8795589fe4532f4bb (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.java | 112 |
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)); + } + } } |