From e2f3e57f37c852e5baa4cd828f9e450fa1eebaae Mon Sep 17 00:00:00 2001 From: Makoto Onuki <omakoto@google.com> Date: Thu, 28 Mar 2024 14:43:17 -0700 Subject: [PATCH] Ravenwood/HostStubGen refactor and bug fixes [Fixed the NPE in the sysui test] - Bug fix: Now native substitution methods can handle bytes correctly. - Spec change: disallow using DisabledOnNonRavenwood on types. It turned out the device side test runner can't handle it if a class rule uses Assume. We could still support it with a non-class rule, but to avoid confusion, let's disallow using it on classes for the time being. - Add more tests - Some refactoring for future changes. Test: ./ravenwood/scripts/run-ravenwood-tests.sh Test: v2/sysui/unit_test on ABTD: https://android-build.corp.google.com/abtd/run/L40000030002931752/ Bug: 292141694 Bug: 332973325 Change-Id: I6d8df1e879840b60326e2beeeee2d8ee7b2ff925 --- ravenwood/TEST_MAPPING | 11 ++ .../bivalenttest/RavenwoodAndroidApiTest.java | 47 ++++++++ .../RavenwoodClassRuleDeviceOnlyTest.java | 39 ++++++ .../RavenwoodClassRuleRavenwoodOnlyTest.java | 43 +++++++ .../RavenwoodTestRunnerValidationTest.java | 2 +- .../annotations/DisabledOnNonRavenwood.java | 6 +- .../test/ravenwood/RavenwoodClassRule.java | 26 ++-- .../test/ravenwood/RavenwoodRule.java | 35 +++--- tools/hoststubgen/hoststubgen/Android.bp | 23 +++- .../com/android/hoststubgen/HostStubGen.kt | 54 +-------- .../{Main.kt => HostStubGenMain.kt} | 12 +- .../android/hoststubgen/HostStubGenOptions.kt | 2 +- .../src/com/android/hoststubgen/Utils.kt | 5 + .../com/android/hoststubgen/asm/AsmUtils.kt | 11 +- .../com/android/hoststubgen/asm/ClassNodes.kt | 113 ++++++++++++++---- .../filters/ImplicitOutputFilter.kt | 4 +- ...tstubgen-test-tiny-framework-orig-dump.txt | 24 +++- ...gen-test-tiny-framework-host-stub-dump.txt | 11 +- ...gen-test-tiny-framework-host-impl-dump.txt | 46 ++++++- ...test-tiny-framework-host-ext-stub-dump.txt | 11 +- ...test-tiny-framework-host-ext-impl-dump.txt | 56 ++++++++- .../tinyframework/TinyFrameworkNative.java | 2 + .../TinyFrameworkNative_host.java | 4 + .../tinyframework/TinyFrameworkClassTest.java | 11 +- 24 files changed, 462 insertions(+), 136 deletions(-) create mode 100644 ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodAndroidApiTest.java create mode 100644 ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java create mode 100644 ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java rename tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/{Main.kt => HostStubGenMain.kt} (87%) diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING index a5b28ad550ba..e77f846ffaa6 100644 --- a/ravenwood/TEST_MAPPING +++ b/ravenwood/TEST_MAPPING @@ -5,6 +5,17 @@ }, { "name": "RavenwoodBivalentTest_device" + }, + { + "name": "SystemUIGoogleTests", + "options": [ + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] } ], "ravenwood-presubmit": [ diff --git a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodAndroidApiTest.java b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodAndroidApiTest.java new file mode 100644 index 000000000000..c11c1bba25a7 --- /dev/null +++ b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodAndroidApiTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 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. + */ +package com.android.platform.test.ravenwood.bivalenttest; + +import static org.junit.Assert.assertEquals; + +import android.util.ArrayMap; +import android.util.Size; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; + +// Tests for calling simple Android APIs. +@RunWith(AndroidJUnit4.class) +public class RavenwoodAndroidApiTest { + @Test + public void testArrayMapSimple() { + final Map<String, String> map = new ArrayMap<>(); + + map.put("key1", "value1"); + assertEquals("value1", map.get("key1")); + } + + @Test + public void testSizeSimple() { + final var size = new Size(1, 2); + + assertEquals(2, size.getHeight()); + } +} diff --git a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java new file mode 100644 index 000000000000..6f2465c406d3 --- /dev/null +++ b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 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. + */ +package com.android.platform.test.ravenwood.bivalenttest; + +import android.platform.test.annotations.DisabledOnRavenwood; +import android.platform.test.ravenwood.RavenwoodClassRule; +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@DisabledOnRavenwood +public class RavenwoodClassRuleDeviceOnlyTest { + @ClassRule + public static final RavenwoodClassRule sRavenwood = new RavenwoodClassRule(); + + @Test + public void testDeviceOnly() { + Assert.assertFalse(RavenwoodRule.isOnRavenwood()); + } +} diff --git a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java new file mode 100644 index 000000000000..21b31d1ca3e8 --- /dev/null +++ b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 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. + */ +package com.android.platform.test.ravenwood.bivalenttest; + +import android.platform.test.ravenwood.RavenwoodClassRule; +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +// TODO: atest RavenwoodBivalentTest_device fails with the following message. +// `RUNNER ERROR: Instrumentation reported numtests=7 but only ran 6` +// @android.platform.test.annotations.DisabledOnNonRavenwood +// Figure it out and then make DisabledOnNonRavenwood support TYPEs as well. +@Ignore +public class RavenwoodClassRuleRavenwoodOnlyTest { + @ClassRule + public static final RavenwoodClassRule sRavenwood = new RavenwoodClassRule(); + + @Test + public void testRavenwoodOnly() { + Assert.assertTrue(RavenwoodRule.isOnRavenwood()); + } +} diff --git a/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java index 2cd585ff6c9c..4ee9a9c94826 100644 --- a/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java +++ b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java @@ -40,7 +40,7 @@ public class RavenwoodTestRunnerValidationTest { public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood); public RavenwoodTestRunnerValidationTest() { - Assume.assumeTrue(mRavenwood._ravenwood_private$isOptionalValidationEnabled()); + Assume.assumeTrue(RavenwoodRule._$RavenwoodPrivate.isOptionalValidationEnabled()); // Because RavenwoodRule will throw this error before executing the test method, // we can't do it in the test method itself. // So instead, we initialize it here. diff --git a/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java index 8ca34bafc2c6..9d47f3a6bc5d 100644 --- a/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java +++ b/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java @@ -31,13 +31,17 @@ import java.lang.annotation.Target; * which means if a test class has this annotation, you can't negate it in subclasses or * on a per-method basis. * + * THIS ANNOTATION CANNOT BE ADDED TO CLASSES AT THIS PONINT. + * See {@link com.android.platform.test.ravenwood.bivalenttest.RavenwoodClassRuleRavenwoodOnlyTest} + * for the reason. + * * The {@code RAVENWOOD_RUN_DISABLED_TESTS} environmental variable won't work because it won't be * propagated to the device. (We may support it in the future, possibly using a debug. sysprop.) * * @hide */ @Inherited -@Target({ElementType.METHOD, ElementType.TYPE}) +@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface DisabledOnNonRavenwood { /** diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java index 9a4d4886d6f0..f4b7ec360dbf 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java @@ -25,6 +25,7 @@ import static android.platform.test.ravenwood.RavenwoodRule.shouldStillIgnoreInP import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.annotations.EnabledOnRavenwood; +import org.junit.Assert; import org.junit.Assume; import org.junit.rules.TestRule; import org.junit.runner.Description; @@ -41,27 +42,16 @@ import org.junit.runners.model.Statement; public class RavenwoodClassRule implements TestRule { @Override public Statement apply(Statement base, Description description) { - // No special treatment when running outside Ravenwood; run tests as-is if (!IS_ON_RAVENWOOD) { - Assume.assumeTrue(shouldEnableOnDevice(description)); - return base; - } - - if (ENABLE_PROBE_IGNORED) { + // This should be "Assume", not Assert, but if we use assume here, the device side + // test runner would complain. + // See the TODO comment in RavenwoodClassRuleRavenwoodOnlyTest. + Assert.assertTrue(shouldEnableOnDevice(description)); + } else if (ENABLE_PROBE_IGNORED) { Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description)); - // Pass through to possible underlying RavenwoodRule for both environment - // configuration and handling method-level annotations - return base; } else { - return new Statement() { - @Override - public void evaluate() throws Throwable { - Assume.assumeTrue(shouldEnableOnRavenwood(description)); - // Pass through to possible underlying RavenwoodRule for both environment - // configuration and handling method-level annotations - base.evaluate(); - } - }; + Assume.assumeTrue(shouldEnableOnRavenwood(description)); } + return base; } } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index 52ea3402fa62..21d8019fcd87 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -28,7 +28,6 @@ import android.platform.test.annotations.DisabledOnNonRavenwood; import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.annotations.EnabledOnRavenwood; import android.platform.test.annotations.IgnoreUnderRavenwood; -import android.util.ArraySet; import org.junit.Assume; import org.junit.rules.TestRule; @@ -278,6 +277,12 @@ public class RavenwoodRule implements TestRule { return false; } } + final var clazz = description.getTestClass(); + if (clazz != null) { + if (clazz.getAnnotation(DisabledOnNonRavenwood.class) != null) { + return false; + } + } return true; } @@ -308,14 +313,17 @@ public class RavenwoodRule implements TestRule { } // Otherwise, consult any class-level annotations - if (description.getTestClass().getAnnotation(EnabledOnRavenwood.class) != null) { - return true; - } - if (description.getTestClass().getAnnotation(DisabledOnRavenwood.class) != null) { - return false; - } - if (description.getTestClass().getAnnotation(IgnoreUnderRavenwood.class) != null) { - return false; + final var clazz = description.getTestClass(); + if (clazz != null) { + if (description.getTestClass().getAnnotation(EnabledOnRavenwood.class) != null) { + return true; + } + if (description.getTestClass().getAnnotation(DisabledOnRavenwood.class) != null) { + return false; + } + if (description.getTestClass().getAnnotation(IgnoreUnderRavenwood.class) != null) { + return false; + } } // When no annotations have been requested, assume test should be included @@ -413,10 +421,9 @@ public class RavenwoodRule implements TestRule { }; } - /** - * Do not use it outside ravenwood core classes. - */ - public boolean _ravenwood_private$isOptionalValidationEnabled() { - return ENABLE_OPTIONAL_VALIDATION; + public static class _$RavenwoodPrivate { + public static boolean isOptionalValidationEnabled() { + return ENABLE_OPTIONAL_VALIDATION; + } } } diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp index 30333da5e86c..682adbc86d06 100644 --- a/tools/hoststubgen/hoststubgen/Android.bp +++ b/tools/hoststubgen/hoststubgen/Android.bp @@ -82,13 +82,30 @@ java_library { jarjar_rules: "jarjar-rules.txt", } +// For sharing the code with other tools +java_library_host { + name: "hoststubgen-lib", + defaults: ["ravenwood-internal-only-visibility-java"], + srcs: ["src/**/*.kt"], + static_libs: [ + "hoststubgen-helper-runtime", + ], + libs: [ + "junit", + "ow2-asm", + "ow2-asm-analysis", + "ow2-asm-commons", + "ow2-asm-tree", + "ow2-asm-util", + ], +} + // Host-side stub generator tool. java_binary_host { name: "hoststubgen", - main_class: "com.android.hoststubgen.Main", - srcs: ["src/**/*.kt"], + main_class: "com.android.hoststubgen.HostStubGenMain", static_libs: [ - "hoststubgen-helper-runtime", + "hoststubgen-lib", "junit", "ow2-asm", "ow2-asm-analysis", diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt index 1089f82b6472..803dc283b8c7 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt @@ -32,7 +32,6 @@ import com.android.hoststubgen.visitors.PackageRedirectRemapper import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassVisitor import org.objectweb.asm.ClassWriter -import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.util.CheckClassAdapter import java.io.BufferedInputStream import java.io.FileOutputStream @@ -52,7 +51,7 @@ class HostStubGen(val options: HostStubGenOptions) { val stats = HostStubGenStats() // Load all classes. - val allClasses = loadClassStructures(options.inJar.get) + val allClasses = ClassNodes.loadClassStructures(options.inJar.get) // Dump the classes, if specified. options.inputJarDumpFile.ifSet { @@ -91,55 +90,6 @@ class HostStubGen(val options: HostStubGenOptions) { } } - /** - * Load all the classes, without code. - */ - private fun loadClassStructures(inJar: String): ClassNodes { - log.i("Reading class structure from $inJar ...") - val start = System.currentTimeMillis() - - val allClasses = ClassNodes() - - log.withIndent { - ZipFile(inJar).use { inZip -> - val inEntries = inZip.entries() - - while (inEntries.hasMoreElements()) { - val entry = inEntries.nextElement() - - BufferedInputStream(inZip.getInputStream(entry)).use { bis -> - if (entry.name.endsWith(".class")) { - val cr = ClassReader(bis) - val cn = ClassNode() - cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG - or ClassReader.SKIP_FRAMES) - if (!allClasses.addClass(cn)) { - log.w("Duplicate class found: ${cn.name}") - } - } else if (entry.name.endsWith(".dex")) { - // Seems like it's an ART jar file. We can't process it. - // It's a fatal error. - throw InvalidJarFileException( - "$inJar is not a desktop jar file. It contains a *.dex file.") - } else { - // Unknown file type. Skip. - while (bis.available() > 0) { - bis.skip((1024 * 1024).toLong()) - } - } - } - } - } - } - if (allClasses.size == 0) { - log.w("$inJar contains no *.class files.") - } - - val end = System.currentTimeMillis() - log.i("Done reading class structure in %.1f second(s).", (end - start) / 1000.0) - return allClasses - } - /** * Build the filter, which decides what classes/methods/fields should be put in stub or impl * jars, and "how". (e.g. with substitution?) @@ -229,7 +179,7 @@ class HostStubGen(val options: HostStubGenOptions) { val intersectingJars = mutableMapOf<String, ClassNodes>() filenames.forEach { filename -> - intersectingJars[filename] = loadClassStructures(filename) + intersectingJars[filename] = ClassNodes.loadClassStructures(filename) } return intersectingJars } diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt similarity index 87% rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt rename to tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt index 4882c00d2b3c..45e7e301c0d1 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:JvmName("Main") +@file:JvmName("HostStubGenMain") package com.android.hoststubgen import java.io.PrintWriter -const val COMMAND_NAME = "HostStubGen" - /** * Entry point. */ fun main(args: Array<String>) { + executableName = "HostStubGen" + var success = false var clanupOnError = false @@ -33,7 +33,7 @@ fun main(args: Array<String>) { val options = HostStubGenOptions.parseArgs(args) clanupOnError = options.cleanUpOnError.get - log.v("HostStubGen started") + log.v("$executableName started") log.v("Options: $options") // Run. @@ -41,7 +41,7 @@ fun main(args: Array<String>) { success = true } catch (e: Throwable) { - log.e("$COMMAND_NAME: Error: ${e.message}") + log.e("$executableName: Error: ${e.message}") if (e !is UserErrorException) { e.printStackTrace(PrintWriter(log.getWriter(LogLevel.Error))) } @@ -49,7 +49,7 @@ fun main(args: Array<String>) { TODO("Remove output jars here") } } finally { - log.i("$COMMAND_NAME finished") + log.i("$executableName finished") log.flush() } diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt index 9f5d524517d0..9ff798a4b5cb 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt @@ -268,7 +268,7 @@ class HostStubGenOptions( } if (!ret.outStubJar.isSet && !ret.outImplJar.isSet) { log.w("Neither --out-stub-jar nor --out-impl-jar is set." + - " $COMMAND_NAME will not generate jar files.") + " $executableName will not generate jar files.") } if (ret.enableNonStubMethodCallDetection.get) { diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt index 937e56c2cbb5..aa63d8d9f870 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt @@ -15,6 +15,11 @@ */ package com.android.hoststubgen +/** + * Name of this executable. Set it in the main method. + */ +var executableName = "[command name not set]" + /** * A regex that maches whitespate. */ diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt index 0579c2bb0525..83e122feeeb2 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt @@ -34,6 +34,9 @@ val CLASS_INITIALIZER_NAME = "<clinit>" /** Descriptor of the class initializer method. */ val CLASS_INITIALIZER_DESC = "()V" +/** Name of constructors. */ +val CTOR_NAME = "<init>" + /** * Find any of [anyAnnotations] from the list of visible / invisible annotations. */ @@ -135,10 +138,10 @@ fun writeByteCodeToPushArguments( // Note, long and double will consume two local variable spaces, so the extra `i++`. when (type) { Type.VOID_TYPE -> throw HostStubGenInternalException("VOID_TYPE not expected") - Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE + Type.BOOLEAN_TYPE, Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE -> writer.visitVarInsn(Opcodes.ILOAD, i) - Type.LONG_TYPE -> writer.visitVarInsn(Opcodes.LLOAD, i++) Type.FLOAT_TYPE -> writer.visitVarInsn(Opcodes.FLOAD, i) + Type.LONG_TYPE -> writer.visitVarInsn(Opcodes.LLOAD, i++) Type.DOUBLE_TYPE -> writer.visitVarInsn(Opcodes.DLOAD, i++) else -> writer.visitVarInsn(Opcodes.ALOAD, i) } @@ -154,10 +157,10 @@ fun writeByteCodeToReturn(methodDescriptor: String, writer: MethodVisitor) { // See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions when (type) { Type.VOID_TYPE -> writer.visitInsn(Opcodes.RETURN) - Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE + Type.BOOLEAN_TYPE, Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE -> writer.visitInsn(Opcodes.IRETURN) - Type.LONG_TYPE -> writer.visitInsn(Opcodes.LRETURN) Type.FLOAT_TYPE -> writer.visitInsn(Opcodes.FRETURN) + Type.LONG_TYPE -> writer.visitInsn(Opcodes.LRETURN) Type.DOUBLE_TYPE -> writer.visitInsn(Opcodes.DRETURN) else -> writer.visitInsn(Opcodes.ARETURN) } diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt index bc34ef0dc8a7..92906a75b93a 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt @@ -16,13 +16,18 @@ package com.android.hoststubgen.asm import com.android.hoststubgen.ClassParseException +import com.android.hoststubgen.InvalidJarFileException +import com.android.hoststubgen.log +import org.objectweb.asm.ClassReader import org.objectweb.asm.tree.AnnotationNode import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.FieldNode import org.objectweb.asm.tree.MethodNode import org.objectweb.asm.tree.TypeAnnotationNode +import java.io.BufferedInputStream import java.io.PrintWriter import java.util.Arrays +import java.util.zip.ZipFile /** * Stores all classes loaded from a jar file, in a form of [ClassNode] @@ -62,8 +67,8 @@ class ClassNodes { /** Find a field, which may not exist. */ fun findField( - className: String, - fieldName: String, + className: String, + fieldName: String, ): FieldNode? { return findClass(className)?.fields?.firstOrNull { it.name == fieldName }?.let { fn -> return fn @@ -72,14 +77,14 @@ class ClassNodes { /** Find a method, which may not exist. */ fun findMethod( - className: String, - methodName: String, - descriptor: String, + className: String, + methodName: String, + descriptor: String, ): MethodNode? { return findClass(className)?.methods - ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn -> - return mn - } + ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn -> + return mn + } } /** @return true if a class has a class initializer. */ @@ -106,26 +111,33 @@ class ClassNodes { private fun dumpClass(pw: PrintWriter, cn: ClassNode) { pw.printf("Class: %s [access: %x]\n", cn.name, cn.access) - dumpAnnotations(pw, " ", - cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations, - cn.visibleAnnotations, cn.invisibleAnnotations, - ) + dumpAnnotations( + pw, " ", + cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations, + cn.visibleAnnotations, cn.invisibleAnnotations, + ) for (f in cn.fields ?: emptyList()) { - pw.printf(" Field: %s [sig: %s] [desc: %s] [access: %x]\n", - f.name, f.signature, f.desc, f.access) - dumpAnnotations(pw, " ", - f.visibleTypeAnnotations, f.invisibleTypeAnnotations, - f.visibleAnnotations, f.invisibleAnnotations, - ) + pw.printf( + " Field: %s [sig: %s] [desc: %s] [access: %x]\n", + f.name, f.signature, f.desc, f.access + ) + dumpAnnotations( + pw, " ", + f.visibleTypeAnnotations, f.invisibleTypeAnnotations, + f.visibleAnnotations, f.invisibleAnnotations, + ) } for (m in cn.methods ?: emptyList()) { - pw.printf(" Method: %s [sig: %s] [desc: %s] [access: %x]\n", - m.name, m.signature, m.desc, m.access) - dumpAnnotations(pw, " ", - m.visibleTypeAnnotations, m.invisibleTypeAnnotations, - m.visibleAnnotations, m.invisibleAnnotations, - ) + pw.printf( + " Method: %s [sig: %s] [desc: %s] [access: %x]\n", + m.name, m.signature, m.desc, m.access + ) + dumpAnnotations( + pw, " ", + m.visibleTypeAnnotations, m.invisibleTypeAnnotations, + m.visibleAnnotations, m.invisibleAnnotations, + ) } } @@ -136,7 +148,7 @@ class ClassNodes { invisibleTypeAnnotations: List<TypeAnnotationNode>?, visibleAnnotations: List<AnnotationNode>?, invisibleAnnotations: List<AnnotationNode>?, - ) { + ) { for (an in visibleTypeAnnotations ?: emptyList()) { pw.printf("%sTypeAnnotation(vis): %s\n", prefix, an.desc) } @@ -166,4 +178,55 @@ class ClassNodes { } } } + + companion object { + /** + * Load all the classes, without code. + */ + fun loadClassStructures(inJar: String): ClassNodes { + log.i("Reading class structure from $inJar ...") + val start = System.currentTimeMillis() + + val allClasses = ClassNodes() + + log.withIndent { + ZipFile(inJar).use { inZip -> + val inEntries = inZip.entries() + + while (inEntries.hasMoreElements()) { + val entry = inEntries.nextElement() + + BufferedInputStream(inZip.getInputStream(entry)).use { bis -> + if (entry.name.endsWith(".class")) { + val cr = ClassReader(bis) + val cn = ClassNode() + cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG + or ClassReader.SKIP_FRAMES) + if (!allClasses.addClass(cn)) { + log.w("Duplicate class found: ${cn.name}") + } + } else if (entry.name.endsWith(".dex")) { + // Seems like it's an ART jar file. We can't process it. + // It's a fatal error. + throw InvalidJarFileException( + "$inJar is not a desktop jar file. It contains a *.dex file.") + } else { + // Unknown file type. Skip. + while (bis.available() > 0) { + bis.skip((1024 * 1024).toLong()) + } + } + } + } + } + } + if (allClasses.size == 0) { + log.w("$inJar contains no *.class files.") + } + + val end = System.currentTimeMillis() + log.i("Done reading class structure in %.1f second(s).", (end - start) / 1000.0) + return allClasses + } + } } \ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt index 78b13fd36f06..5a26fc69d473 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt @@ -19,14 +19,14 @@ import com.android.hoststubgen.HostStubGenErrors import com.android.hoststubgen.HostStubGenInternalException import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME -import com.android.hoststubgen.asm.isAnonymousInnerClass -import com.android.hoststubgen.log import com.android.hoststubgen.asm.ClassNodes import com.android.hoststubgen.asm.isAnnotation +import com.android.hoststubgen.asm.isAnonymousInnerClass import com.android.hoststubgen.asm.isAutoGeneratedEnumMember import com.android.hoststubgen.asm.isEnum import com.android.hoststubgen.asm.isSynthetic import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate +import com.android.hoststubgen.log import org.objectweb.asm.tree.ClassNode /** diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt index f70a17d9b7cd..fa8fe6cd384f 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt @@ -1833,7 +1833,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 10, attributes: 2 + interfaces: 0, fields: 1, methods: 11, attributes: 2 int value; descriptor: I flags: (0x0000) @@ -1938,6 +1938,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V x: athrow LineNumberTable: + + public static native byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE } SourceFile: "TinyFrameworkNative.java" RuntimeInvisibleAnnotations: @@ -1955,7 +1959,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 4, attributes: 2 + interfaces: 0, fields: 0, methods: 5, attributes: 2 public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host(); descriptor: ()V flags: (0x0001) ACC_PUBLIC @@ -2013,6 +2017,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host Start Length Slot Name Signature 0 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; 0 7 1 arg I + + public static byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=2, args_size=2 + x: iload_0 + x: iload_1 + x: iadd + x: i2b + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 arg1 B + 0 5 1 arg2 B } SourceFile: "TinyFrameworkNative_host.java" RuntimeInvisibleAnnotations: diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt index 37de857b9780..c605f767f527 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt @@ -1554,7 +1554,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 9, attributes: 3 + interfaces: 0, fields: 1, methods: 10, attributes: 3 int value; descriptor: I flags: (0x0000) @@ -1686,6 +1686,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public static native byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } SourceFile: "TinyFrameworkNative.java" RuntimeVisibleAnnotations: diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt index c9c607c58c68..11d5939b7917 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt @@ -2236,7 +2236,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 10, attributes: 3 + interfaces: 0, fields: 1, methods: 11, attributes: 3 int value; descriptor: I flags: (0x0000) @@ -2435,6 +2435,23 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public static byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=2, args_size=2 + x: iload_0 + x: iload_1 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } SourceFile: "TinyFrameworkNative.java" RuntimeVisibleAnnotations: @@ -2457,7 +2474,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 4, attributes: 3 + interfaces: 0, fields: 0, methods: 5, attributes: 3 public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host(); descriptor: ()V flags: (0x0001) ACC_PUBLIC @@ -2551,6 +2568,31 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public static byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String nativeBytePlus + x: ldc #x // String (BB)B + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iload_0 + x: iload_1 + x: iadd + x: i2b + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 arg1 B + 15 5 1 arg2 B + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } SourceFile: "TinyFrameworkNative_host.java" RuntimeVisibleAnnotations: diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt index 37de857b9780..c605f767f527 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt @@ -1554,7 +1554,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 9, attributes: 3 + interfaces: 0, fields: 1, methods: 10, attributes: 3 int value; descriptor: I flags: (0x0000) @@ -1686,6 +1686,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public static native byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } SourceFile: "TinyFrameworkNative.java" RuntimeVisibleAnnotations: diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt index a57907d9398b..088bc80e11c5 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt @@ -2743,7 +2743,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 11, attributes: 3 + interfaces: 0, fields: 1, methods: 12, attributes: 3 int value; descriptor: I flags: (0x0000) @@ -3002,6 +3002,28 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public static byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + x: ldc #x // String nativeBytePlus + x: ldc #x // String (BB)B + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: iload_0 + x: iload_1 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } SourceFile: "TinyFrameworkNative.java" RuntimeVisibleAnnotations: @@ -3024,7 +3046,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 5, attributes: 3 + interfaces: 0, fields: 0, methods: 6, attributes: 3 private static {}; descriptor: ()V flags: (0x000a) ACC_PRIVATE, ACC_STATIC @@ -3148,6 +3170,36 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public static byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String nativeBytePlus + x: ldc #x // String (BB)B + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String nativeBytePlus + x: ldc #x // String (BB)B + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iload_0 + x: iload_1 + x: iadd + x: i2b + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 26 5 0 arg1 B + 26 5 1 arg2 B + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } SourceFile: "TinyFrameworkNative_host.java" RuntimeVisibleAnnotations: diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java index 5a5e22db59e5..09ee183a2dcc 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java @@ -52,4 +52,6 @@ public class TinyFrameworkNative { public static void nativeStillNotSupported_should_be_like_this() { throw new RuntimeException(); } + + public static native byte nativeBytePlus(byte arg1, byte arg2); } diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java index 749ebaa378e3..b23c21602967 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java @@ -34,4 +34,8 @@ public class TinyFrameworkNative_host { public static int nativeNonStaticAddToValue(TinyFrameworkNative source, int arg) { return source.value + arg; } + + public static byte nativeBytePlus(byte arg1, byte arg2) { + return (byte) (arg1 + arg2); + } } diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java index ba17c75132f2..762180dcf74b 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java @@ -153,6 +153,16 @@ public class TinyFrameworkClassTest { assertThat(TinyFrameworkNative.nativeAddTwo(3)).isEqualTo(5); } + @Test + public void testNativeSubstitutionLong() { + assertThat(TinyFrameworkNative.nativeLongPlus(1L, 2L)).isEqualTo(3L); + } + + @Test + public void testNativeSubstitutionByte() { + assertThat(TinyFrameworkNative.nativeBytePlus((byte) 3, (byte) 4)).isEqualTo(7); + } + @Test public void testNativeSubstitutionClass_nonStatic() { TinyFrameworkNative instance = new TinyFrameworkNative(); @@ -160,7 +170,6 @@ public class TinyFrameworkClassTest { assertThat(instance.nativeNonStaticAddToValue(3)).isEqualTo(8); } - @Test public void testSubstituteNativeWithThrow() throws Exception { // We can't use TinyFrameworkNative.nativeStillNotSupported() directly in this class, -- GitLab