Compile dot and not expressions to byte code. -- MOS_MIGRATED_REVID=107378197
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java index e8482a7..84805e9 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java
@@ -16,7 +16,15 @@ import com.google.common.collect.Iterables; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.syntax.FuncallExpression.MethodDescriptor; +import com.google.devtools.build.lib.syntax.compiler.ByteCodeUtils; +import com.google.devtools.build.lib.syntax.compiler.DebugInfo; +import com.google.devtools.build.lib.syntax.compiler.VariableScope; +import net.bytebuddy.implementation.bytecode.ByteCodeAppender; +import net.bytebuddy.implementation.bytecode.Duplication; +import net.bytebuddy.implementation.bytecode.constant.TextConstant; + +import java.util.ArrayList; import java.util.List; /** @@ -52,14 +60,22 @@ Object objValue = obj.eval(env); String name = field.getName(); Object result = eval(objValue, name, getLocation(), env); + return checkResult(objValue, result, name, getLocation()); + } + + /** + * Throws the correct error message if the result is null depending on the objValue. + */ + public static Object checkResult(Object objValue, Object result, String name, Location loc) + throws EvalException { if (result == null) { if (objValue instanceof ClassObject) { String customErrorMessage = ((ClassObject) objValue).errorMessage(name); if (customErrorMessage != null) { - throw new EvalException(getLocation(), customErrorMessage); + throw new EvalException(loc, customErrorMessage); } } - throw new EvalException(getLocation(), Printer.format("Object of type '%s' has no field %r", + throw new EvalException(loc, Printer.format("Object of type '%s' has no field %r", EvalUtils.getDataTypeName(objValue), name)); } return result; @@ -96,6 +112,7 @@ return FuncallExpression.callMethod(method, name, objValue, new Object[] {}, loc, env); } } + return null; } @@ -108,4 +125,26 @@ void validate(ValidationEnvironment env) throws EvalException { obj.validate(env); } + + @Override + ByteCodeAppender compile(VariableScope scope, DebugInfo debugInfo) throws EvalException { + List<ByteCodeAppender> code = new ArrayList<>(); + code.add(obj.compile(scope, debugInfo)); + TextConstant name = new TextConstant(field.getName()); + ByteCodeUtils.append( + code, + Duplication.SINGLE, + name, + debugInfo.add(this).loadLocation, + scope.loadEnvironment(), + ByteCodeUtils.invoke(DotExpression.class, "eval", Object.class, String.class, + Location.class, Environment.class), + // at this point we have the value of obj and the result of eval on the stack + name, + debugInfo.add(this).loadLocation, + ByteCodeUtils.invoke(DotExpression.class, "checkResult", Object.class, Object.class, + String.class, Location.class) + ); + return ByteCodeUtils.compoundAppender(code); + } }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/NotExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/NotExpression.java index 72aca2b..75b93e7 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/NotExpression.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/NotExpression.java
@@ -13,6 +13,13 @@ // limitations under the License. package com.google.devtools.build.lib.syntax; +import com.google.devtools.build.lib.syntax.compiler.ByteCodeMethodCalls; +import com.google.devtools.build.lib.syntax.compiler.ByteCodeUtils; +import com.google.devtools.build.lib.syntax.compiler.DebugInfo; +import com.google.devtools.build.lib.syntax.compiler.VariableScope; + +import net.bytebuddy.implementation.bytecode.ByteCodeAppender; + /** * As syntax node for the not boolean operation. */ @@ -47,4 +54,16 @@ void validate(ValidationEnvironment env) throws EvalException { expression.validate(env); } + + @Override + ByteCodeAppender compile(VariableScope scope, DebugInfo debugInfo) throws EvalException { + // since there is no byte code logical negation + // compile expression and convert to boolean then negate and convert back to Boolean + return new ByteCodeAppender.Compound( + expression.compile(scope, debugInfo), + new ByteCodeAppender.Simple( + EvalUtils.toBoolean, + ByteCodeUtils.intLogicalNegation(), + ByteCodeMethodCalls.BCBoolean.valueOf)); + } }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/compiler/ByteCodeMethodCalls.java b/src/main/java/com/google/devtools/build/lib/syntax/compiler/ByteCodeMethodCalls.java index a699a17..8fe0346 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/compiler/ByteCodeMethodCalls.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/compiler/ByteCodeMethodCalls.java
@@ -28,6 +28,14 @@ public class ByteCodeMethodCalls { /** + * Byte code invocations for {@link Boolean}. + */ + public static class BCBoolean { + public static final StackManipulation valueOf = + ByteCodeUtils.invoke(Boolean.class, "valueOf", boolean.class); + } + + /** * Byte code invocations for {@link ImmutableList}. */ public static class BCImmutableList {
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/compiler/ByteCodeUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/compiler/ByteCodeUtils.java index 832d4e3..4e7abf8 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/compiler/ByteCodeUtils.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/compiler/ByteCodeUtils.java
@@ -13,6 +13,8 @@ // limitations under the License. package com.google.devtools.build.lib.syntax.compiler; +import com.google.devtools.build.lib.syntax.compiler.Jump.PrimitiveComparison; + import net.bytebuddy.description.method.MethodDescription.ForLoadedMethod; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.generic.GenericTypeDescription; @@ -20,6 +22,7 @@ import net.bytebuddy.implementation.bytecode.ByteCodeAppender.Compound; import net.bytebuddy.implementation.bytecode.Removal; import net.bytebuddy.implementation.bytecode.StackManipulation; +import net.bytebuddy.implementation.bytecode.constant.IntegerConstant; import net.bytebuddy.implementation.bytecode.member.FieldAccess; import net.bytebuddy.implementation.bytecode.member.MethodInvocation; @@ -70,6 +73,34 @@ } /** + * Build a {@link StackManipulation} that logically negates an integer on the stack. + * + * <p>Java byte code does not have an instruction for this, so this produces a conditional jump + * which puts 0/1 on the stack. + */ + public static StackManipulation intLogicalNegation() { + return intToPrimitiveBoolean(PrimitiveComparison.NOT_EQUAL); + } + + /** + * Build a {@link StackManipulation} that converts an integer to 0/1 depending on a comparison + * with 0. + */ + public static StackManipulation intToPrimitiveBoolean(PrimitiveComparison operator) { + LabelAdder afterLabel = new LabelAdder(); + LabelAdder putFalseLabel = new LabelAdder(); + return new StackManipulation.Compound( + Jump.ifIntOperandToZero(operator).to(putFalseLabel), + // otherwise put "false" on the stack and jump to end + IntegerConstant.ONE, + Jump.to(afterLabel.getLabel()), + // add label for "else" and put "true" on the stack + putFalseLabel, + IntegerConstant.ZERO, + afterLabel); + } + + /** * Builds a {@link StackManipulation} that invokes the method identified via reflection on the * given class, method and parameter types. */