test: Add support for jasmin

Adding .j files in a jasmin directory will place those classes into
the same jar as src/src2 source files.

Adding .j files in a jasmin-multidex directory will place those classes
into the same jar as src-multidex source files.

Jasmin classes have medium priority and will overwrite any .java
classes.

Smali classes have the highest priority and smali classes
will overwrite any jasmin classes.

Using jasmin is preferred for tests that can run cross-platform.

(Also convert two of the tests to use jasmin instead of smali).

Test: art/test/run-test --host --optimizing --build-with-jack 064-field-access
Test: DESUGAR=false art/test/run-test --host --optimizing --build-with-javac-dx  064-field-access
Test: DESUGAR=false art/test/run-test --host --optimizing --build-with-javac-dx 606-erroneous-class
Test: art/test/run-test --host --optimizing --build-with-jack 606-erroneous-class
Test: #(manual) run-test --jvm and check that .class is there as a build artifact
Bug: 62855082
Change-Id: I5966d37f603bb5b93f75a842e7d597721afafacd
diff --git a/test/064-field-access/jasmin/SubClassUsingInaccessibleField.j b/test/064-field-access/jasmin/SubClassUsingInaccessibleField.j
new file mode 100644
index 0000000..3422f85
--- /dev/null
+++ b/test/064-field-access/jasmin/SubClassUsingInaccessibleField.j
@@ -0,0 +1,36 @@
+; Copyright (C) 2017 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class                   public SubClassUsingInaccessibleField
+.super                   other/PublicClass
+
+.method                  public <init>()V
+   .limit stack          1
+   .limit locals         1
+   aload_0
+   invokespecial         other/PublicClass/<init>()V
+   return
+.end method
+
+; Regression test for compiler DCHECK() failure (bogus check) when referencing
+; a package-private field from an indirectly inherited package-private class,
+; using this very class as the declaring class in the FieldId, bug: 27684368 .
+.method                  public test()I
+   .limit stack          1
+   .limit locals         1
+   aload_0
+   getfield              SubClassUsingInaccessibleField/otherProtectedClassPackageIntInstanceField I
+   ireturn
+.end method
+
diff --git a/test/064-field-access/smali/SubClassUsingInaccessibleField.smali b/test/064-field-access/smali/SubClassUsingInaccessibleField.smali
deleted file mode 100644
index 224b431..0000000
--- a/test/064-field-access/smali/SubClassUsingInaccessibleField.smali
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-.class public LSubClassUsingInaccessibleField;
-
-.super Lother/PublicClass;
-
-.method public constructor <init>()V
-    .registers 1
-    invoke-direct {p0}, Lother/PublicClass;-><init>()V
-    return-void
-.end method
-
-# Regression test for compiler DCHECK() failure (bogus check) when referencing
-# a package-private field from an indirectly inherited package-private class,
-# using this very class as the declaring class in the FieldId, bug: 27684368 .
-.method public test()I
-    .registers 2
-    iget v0, p0, LSubClassUsingInaccessibleField;->otherProtectedClassPackageIntInstanceField:I
-    return v0
-.end method
diff --git a/test/606-erroneous-class/jasmin-multidex/ClassA.j b/test/606-erroneous-class/jasmin-multidex/ClassA.j
new file mode 100644
index 0000000..50c6755
--- /dev/null
+++ b/test/606-erroneous-class/jasmin-multidex/ClassA.j
@@ -0,0 +1,30 @@
+; Copyright (C) 2017 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class                   public final ClassA
+.super                   java/lang/Object
+
+.method                  public static foo()V
+   .limit stack          1
+   .limit locals         0
+   ; Obtain the ErrClass type from Dex cache of the first Dex file. Note that
+   ; because the first Dex file has already been verified, we know the class
+   ; is erroneous at this point.
+   getstatic             ClassB/g LErrClass;
+   ; Use the object in a way that will try to store the ErrClass type in
+   ; the Dex cache of the second Dex file.
+   invokevirtual         ErrClass/foo()V
+   return
+.end method
+
diff --git a/test/606-erroneous-class/smali-multidex/ClassA.smali b/test/606-erroneous-class/smali-multidex/ClassA.smali
deleted file mode 100644
index f87fcb2..0000000
--- a/test/606-erroneous-class/smali-multidex/ClassA.smali
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-.class public final LClassA;
-.super Ljava/lang/Object;
-
-.method public static foo()V
-    .registers 1
-    # Obtain the ErrClass type from Dex cache of the first Dex file. Note that
-    # because the first Dex file has already been verified, we know the class
-    # is erroneous at this point.
-    sget-object v0, LClassB;->g:LErrClass;
-    # Use the object in a way that will try to store the ErrClass type in
-    # the Dex cache of the second Dex file.
-    invoke-virtual {v0}, LErrClass;->foo()V
-.end method
diff --git a/test/etc/default-build b/test/etc/default-build
index 13f4301..bafd415 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -24,6 +24,13 @@
   HAS_SMALI=false
 fi
 
+# .j files in jasmin get compiled into classes.jar
+if [ -d jasmin ]; then
+  HAS_JASMIN=true
+else
+  HAS_JASMIN=false
+fi
+
 if [ -d src ]; then
   HAS_SRC=true
 else
@@ -55,6 +62,13 @@
   HAS_SMALI_MULTIDEX=false
 fi
 
+# .j files in jasmin-multidex get compiled into classes2.jar
+if [ -d jasmin-multidex ]; then
+  HAS_JASMIN_MULTIDEX=true
+else
+  HAS_JASMIN_MULTIDEX=false
+fi
+
 if [ -d src-ex ]; then
   HAS_SRC_EX=true
 else
@@ -80,7 +94,6 @@
 
 DX_FLAGS="--min-sdk-version=24"
 DX_VM_FLAGS=""
-SKIP_DX_MERGER="false"
 EXPERIMENTAL=""
 
 BUILD_MODE="target"
@@ -219,6 +232,21 @@
   fi
 }
 
+function make_jasmin() {
+  local out_directory="$1"
+  shift
+  local jasmin_sources=("$@")
+
+  mkdir -p "$out_directory"
+
+  if [[ $DEV_MODE == yes ]]; then
+    echo ${JASMIN} -d "$out_directory" "${jasmin_sources[@]}"
+    ${JASMIN} -d "$out_directory" "${jasmin_sources[@]}"
+  else
+    ${JASMIN} -d "$out_directory" "${jasmin_sources[@]}" >/dev/null
+  fi
+}
+
 function desugar() {
   local desugar_args=--mode=host
   if [[ $BUILD_MODE == target ]]; then
@@ -268,6 +296,26 @@
   ${DX} -JXmx256m ${DX_VM_FLAGS} --debug --dex --dump-to=${name}.lst --output=${name}.dex --dump-width=1000 ${DX_FLAGS} "${dx_input}"
 }
 
+# Merge all the dex files in $1..$N into $1. Skip non-existing files, but at least 1 file must exist.
+function make_dexmerge() {
+  # Dex file that acts as the destination.
+  local dst_file="$1"
+
+  # Dex files that act as the source.
+  local dex_files_to_merge=()
+
+  # Skip any non-existing files.
+  while [[ $# -gt 0 ]]; do
+    if [[ -e "$1" ]]; then
+      dex_files_to_merge+=("$1")
+    fi
+    shift
+  done
+
+  # Should have at least 1 dex_files_to_merge here, otherwise dxmerger will print the help.
+  ${DXMERGER} "$dst_file" "${dex_files_to_merge[@]}"
+}
+
 # Print the directory name only if it exists.
 function maybe_dir() {
   local dirname="$1"
@@ -281,11 +329,6 @@
   exit 0
 fi
 
-if ! [ "${HAS_SRC}" = "true" ] && ! [ "${HAS_SRC2}" = "true" ] && ! [ "${HAS_SRC_ART}" = "true" ]; then
-  # No src directory? Then forget about trying to run dx.
-  SKIP_DX_MERGER="true"
-fi
-
 if [ ${HAS_SRC_DEX2OAT_UNRESOLVED} = "true" ]; then
   mkdir classes
   mkdir classes-ex
@@ -332,7 +375,7 @@
     fi
 
     # Compile jack files into a DEX file.
-    if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ] || [ "${HAS_SRC_ART}" ]; then
+    if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ] || [ "${HAS_SRC_ART}" = "true" ]; then
       ${JACK} ${JACK_ARGS} ${jack_extra_args} --output-dex .
     fi
   else
@@ -361,22 +404,49 @@
     fi
 
     if [[ "${HAS_SRC}" == "true" || "${HAS_SRC2}" == "true" || "${HAS_SRC_ART}" == "true" ]]; then
-      if [ ${NEED_DEX} = "true" -a ${SKIP_DX_MERGER} = "false" ]; then
+      if [ ${NEED_DEX} = "true" ]; then
         make_dex classes
       fi
     fi
   fi
 fi
 
+if [[ "${HAS_JASMIN}" == true ]]; then
+  # Compile Jasmin classes as if they were part of the classes.dex file.
+  make_jasmin jasmin_classes $(find 'jasmin' -name '*.j')
+  if [[ "${NEED_DEX}" == "true" ]]; then
+    # Disable desugar because it won't handle intentional linkage errors.
+    USE_DESUGAR=false make_dex jasmin_classes
+    make_dexmerge classes.dex jasmin_classes.dex
+  else
+    # Move jasmin classes into classes directory so that they are picked up with -cp classes.
+    mkdir -p classes
+    mv jasmin_classes/* classes
+  fi
+fi
+
 if [ "${HAS_SMALI}" = "true" -a ${NEED_DEX} = "true" ]; then
   # Compile Smali classes
   ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'`
 
-  # Don't bother with dexmerger if we provide our own main function in a smali file.
-  if [ ${SKIP_DX_MERGER} = "false" ]; then
-    ${DXMERGER} classes.dex classes.dex smali_classes.dex
+  # Merge smali files into classes.dex, this takes priority over any jasmin files.
+  make_dexmerge classes.dex smali_classes.dex
+fi
+
+# Compile Jasmin classes in jasmin-multidex as if they were part of the classes2.jar
+if [[ "$HAS_JASMIN_MULTIDEX" == true ]]; then
+  make_jasmin jasmin_classes2 $(find 'jasmin-multidex' -name '*.j')
+
+  if [[ "${NEED_DEX}" == "true" ]]; then
+    # Disable desugar because it won't handle intentional linkage errors.
+    USE_DESUGAR=false make_dex jasmin_classes2
+
+    # Merge jasmin_classes2.dex into classes2.dex
+    make_dexmerge classes2.dex jasmin_classes2.dex
   else
-    mv smali_classes.dex classes.dex
+    # Move jasmin classes into classes2 directory so that they are picked up with -cp classes2.
+    mkdir -p classes2
+    mv jasmin_classes2/* classes2
   fi
 fi
 
@@ -384,12 +454,8 @@
   # Compile Smali classes
   ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes2.dex `find smali-multidex -name '*.smali'`
 
-  # Don't bother with dexmerger if we provide our own main function in a smali file.
-  if [ ${HAS_SRC_MULTIDEX} = "true" ]; then
-    ${DXMERGER} classes2.dex classes2.dex smali_classes2.dex
-  else
-    mv smali_classes2.dex classes2.dex
-  fi
+  # Merge smali_classes2.dex into classes2.dex
+  make_dexmerge classes2.dex smali_classes2.dex
 fi
 
 
@@ -430,9 +496,9 @@
   fi
 fi
 
-# Create a single jar with two dex files for multidex.
+# Create a single dex jar with two dex files for multidex.
 if [ ${NEED_DEX} = "true" ]; then
-  if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then
+  if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_JASMIN_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then
     zip $TEST_NAME.jar classes.dex classes2.dex
   else
     zip $TEST_NAME.jar classes.dex