Merge "Ensure that compile-time constants from library classes are ignored"
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index 0c8ce8c..ffff701 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -17,6 +17,11 @@
       DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods) {
     super(sourceFile, interfaces, accessFlags, superType, type,
         staticFields, instanceFields, directMethods, virtualMethods, annotations, origin);
+    // Set all static field values to unknown. We don't want to use the value from the library
+    // at compile time, as it can be different at runtime.
+    for (DexEncodedField staticField : staticFields) {
+      staticField.staticValue = DexValue.UNKNOWN;
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index 73b3242..415775d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -17,6 +17,8 @@
 
 public abstract class DexValue extends DexItem {
 
+  public static final UnknownDexValue UNKNOWN = UnknownDexValue.UNKNOWN;
+
   public static final byte VALUE_BYTE = 0x00;
   public static final byte VALUE_SHORT = 0x02;
   public static final byte VALUE_CHAR = 0x03;
@@ -43,7 +45,7 @@
   @Override
   void collectMixedSectionItems(MixedSectionCollection mixedItems) {
     // Should never be visited.
-    assert false;
+    throw new Unreachable();
   }
 
   public abstract void sort();
@@ -108,6 +110,55 @@
     return true;
   }
 
+  static public class UnknownDexValue extends DexValue {
+
+    // Singleton instance.
+    public static final UnknownDexValue UNKNOWN = new UnknownDexValue();
+
+    private UnknownDexValue() {
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      throw new Unreachable();
+    }
+
+    @Override
+    public void sort() {
+      throw new Unreachable();
+    }
+
+    @Override
+    public boolean mayTriggerAllocation() {
+      return true;
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      throw new Unreachable();
+    }
+
+    @Override
+    public int hashCode() {
+      return System.identityHashCode(this);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      return other == this;
+    }
+
+    @Override
+    public String toString() {
+      return "UNKNOWN";
+    }
+
+    @Override
+    public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+      return null;
+    }
+  }
+
   static private abstract class SimpleDexValue extends DexValue {
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java b/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
index 8e3c638..b819a84 100644
--- a/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
@@ -75,6 +75,13 @@
     return builder.build();
   }
 
+  public static ClassFileResourceProvider fromClassData(String descriptor, byte[] data)
+      throws IOException {
+    Builder builder = builder();
+    builder.addResource(descriptor, data);
+    return builder.build();
+  }
+
   // Guess class descriptor from location of the class file.
   static String guessTypeDescriptor(Path name) {
     return guessTypeDescriptor(name.toString());
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index f2c4d15..ae06040 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -7,12 +7,15 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.shaking.FilteredClassPath;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OutputMode;
+import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
 import java.io.File;
@@ -88,6 +91,15 @@
   }
 
   /**
+   * Compile an application with D8.
+   */
+  protected AndroidApp compileWithD8(AndroidApp app)
+      throws CompilationException, ExecutionException, IOException {
+    D8Command command = ToolHelper.prepareD8CommandBuilder(app).build();
+    return ToolHelper.runD8(command);
+  }
+
+  /**
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(Class... classes)
@@ -280,4 +292,14 @@
     }
     return result.stdout;
   }
+
+
+  /**
+   * Disassemble the content of an application. Only works for an application with only dex code.
+   */
+  protected void disassemble(AndroidApp app) throws Exception {
+    InternalOptions options = new InternalOptions();
+    DexApplication dexApplication = new ApplicationReader(app, options, new Timing("XX")).read();
+    System.out.println(dexApplication.smali(options));
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 09f2594..b751766 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -476,6 +476,10 @@
     return parser.getConfig();
   }
 
+  public static D8Command.Builder prepareD8CommandBuilder(AndroidApp app) {
+    return D8Command.builder(app);
+  }
+
   public static R8Command.Builder prepareR8CommandBuilder(AndroidApp app) {
     return R8Command.builder(app);
   }
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index ba5e370..8f3ace3 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -86,12 +86,20 @@
       return new MethodSignature(name, returnJavaType, argumentJavaTypes);
     }
 
-    public FieldSignature addStaticField(String name, String type, String value) {
+    public FieldSignature addField(String flags, String name, String type, String value) {
       fields.add(
-          ".field public static " + name + " " + type + (value != null ? (" = " + value) : ""));
+          ".field " + flags + " " + name + " " + type + (value != null ? (" = " + value) : ""));
       return new FieldSignature(name, type);
     }
 
+    public FieldSignature addStaticField(String name, String type, String value) {
+      return addField("public static", name, type, value);
+    }
+
+    public FieldSignature addStaticFinalField(String name, String type, String value) {
+      return addField("public static final", name, type, value);
+    }
+
     @Override
     public String toString() {
       StringBuilder builder = new StringBuilder();
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/LibraryClass.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/LibraryClass.java
new file mode 100644
index 0000000..b124ee9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/LibraryClass.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.staticvalues.inlibraries;
+
+public class LibraryClass {
+  public static int x = 1;
+
+  static int getX() {
+    return x;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
new file mode 100644
index 0000000..445c12c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.staticvalues.inlibraries;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.shaking.FilteredClassPath;
+import com.android.tools.r8.smali.SmaliTestBase.SmaliBuilder;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.PreloadedClassFileProvider;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class StaticLibraryValuesChangeTest extends TestBase {
+  @Test
+  public void testStatic() throws Exception {
+    /*
+     * Three versions of the class LibraryClass is used in the test:
+     *
+     *   * one in Java code, where field x is "public static int x = 1" (not final)
+     *   * one in Jasmin code, where field x is "public static final int x = 2"
+     *   * int in smali code, where field x is  "public static final int x = 3"
+     *
+     * The first version is used to compile TestMain with javac. This causes the field accessor
+     * for x to be in the code for TestMain.main. As javac will inline compile-time constants
+     * it cannot be declared "final" here.
+     *
+     * The second version is used as a library class when compiling the javac compiled TestMain
+     * with R8.
+     *
+     * The third version is used for running the R8 compiled TestMain on Art.
+     */
+
+    // Build the second version of LibraryClass
+    JasminBuilder compileTimeLibrary = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = compileTimeLibrary.addClass(
+        "com.android.tools.r8.rewrite.staticvalues.inlibraries.LibraryClass");
+    clazz.addStaticFinalField("x", "I", "2");
+    clazz.addStaticMethod("getX", ImmutableList.of(), "I",
+        ".limit stack 1",
+        ".limit locals 0",
+        "  iconst_2",
+        "  ireturn");
+
+    // Compile TestMain with R8 using the second version of LibraryClass as library.
+    AndroidApp.Builder builder = AndroidApp.builder();
+    builder.addProgramFiles(
+        FilteredClassPath.unfiltered(ToolHelper.getClassFileForTestClass(TestMain.class)));
+    builder.addLibraryResourceProvider(PreloadedClassFileProvider.fromClassData(
+        "Lcom/android/tools/r8/rewrite/staticvalues/inlibraries/LibraryClass;",
+        compileTimeLibrary.buildClasses().get(0)));
+    AndroidApp app = compileWithR8(builder.build());
+
+    // Build the third version of LibraryClass
+    SmaliBuilder runtimeLibrary = new SmaliBuilder(LibraryClass.class.getCanonicalName());
+    runtimeLibrary.addStaticField("x", "I", "3");
+    runtimeLibrary.addStaticMethod(
+        "int",
+        "getX",
+        ImmutableList.of(),
+        1,
+        "    const/4             v0, 3",
+        "    return              v0"
+    );
+
+    // Merge the compiled TestMain with the runtime version of LibraryClass.
+    builder = AndroidApp.builder(app);
+    builder.addDexProgramData(runtimeLibrary.compile());
+    String result = runOnArt(builder.build(), TestMain.class);
+    assertEquals("33", result);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/TestMain.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/TestMain.java
new file mode 100644
index 0000000..83f6c16
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/TestMain.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.staticvalues.inlibraries;
+
+public class TestMain {
+
+  public static void main(String[] args) {
+    System.out.print(LibraryClass.x);
+    System.out.print(LibraryClass.getX());
+  }
+}