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.
    */