| /* |
| * Copyright 2019 The Bazel Authors. All rights reserved. |
| * |
| * 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.google.devtools.build.android.desugar.nest; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import com.google.devtools.build.android.desugar.testing.junit.AsmNode; |
| import com.google.devtools.build.android.desugar.testing.junit.DesugarRule; |
| import com.google.devtools.build.android.desugar.testing.junit.DesugarRunner; |
| import com.google.devtools.build.android.desugar.testing.junit.DynamicClassLiteral; |
| import com.google.devtools.build.android.desugar.testing.junit.JdkSuppress; |
| import com.google.devtools.build.android.desugar.testing.junit.JdkVersion; |
| import com.google.devtools.build.android.desugar.testing.junit.RuntimeMethodHandle; |
| import com.google.devtools.build.android.desugar.testing.junit.RuntimeMethodHandle.MemberUseContext; |
| import java.lang.invoke.MethodHandle; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.reflect.Method; |
| import java.nio.file.Paths; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.stream.Collectors; |
| import javax.inject.Inject; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.objectweb.asm.tree.ClassNode; |
| |
| /** Tests for accessing private methods from another class within a nest. */ |
| @RunWith(DesugarRunner.class) |
| @JdkSuppress(minJdkVersion = JdkVersion.V11) |
| public final class NestDesugaringMethodAccessTest { |
| |
| @Rule |
| public final DesugarRule desugarRule = |
| DesugarRule.builder(this, MethodHandles.lookup()) |
| .addSourceInputs(Paths.get(System.getProperty("input_srcs"))) |
| .addJavacOptions("-source 11", "-target 11") |
| .setWorkingJavaPackage( |
| "com.google.devtools.build.android.desugar.nest.testsrc.simpleunit.method") |
| .addCommandOptions("desugar_nest_based_private_access", "true") |
| .build(); |
| |
| @Inject |
| @DynamicClassLiteral(value = "MethodNest$MethodOwnerMate") |
| private Class<?> mate; |
| |
| @Inject |
| @DynamicClassLiteral("MethodNest$SubMate") |
| private Class<?> subClassMate; |
| |
| @Inject |
| @DynamicClassLiteral("MethodNest") |
| private Class<?> invoker; |
| |
| private Object mateInstance; |
| private Object invokerInstance; |
| |
| @Before |
| public void loadClassesInNest() throws Exception { |
| mateInstance = mate.getConstructor().newInstance(); |
| invokerInstance = invoker.getDeclaredConstructor().newInstance(); |
| } |
| |
| @Test |
| |
| public void inputClassFileMajorVersions( |
| @AsmNode(className = "MethodNest", round = 0) ClassNode beforeDesugarClassNode, |
| @AsmNode(className = "MethodNest", round = 1) ClassNode afterDesugarClassNode) { |
| assertThat(beforeDesugarClassNode.version).isEqualTo(JdkVersion.V11); |
| assertThat(afterDesugarClassNode.version).isEqualTo(JdkVersion.V1_7); |
| } |
| |
| @Test |
| public void methodBridgeGeneration() throws Exception { |
| List<String> bridgeMethodNames = |
| Arrays.stream(mate.getDeclaredMethods()) |
| .map(Method::getName) |
| .filter(name -> !name.startsWith("$jacoco")) |
| .collect(Collectors.toList()); |
| assertThat(bridgeMethodNames) |
| .containsExactly( |
| "staticMethod", |
| "instanceMethod", |
| "privateStaticMethod", |
| "privateInstanceMethod", |
| "inClassBoundStaticMethod", |
| "inClassBoundInstanceMethod", |
| "privateStaticMethod$bridge", |
| "privateInstanceMethod$bridge"); |
| } |
| |
| @Test |
| public void invokePrivateStaticMethod_staticInitializer( |
| @RuntimeMethodHandle( |
| className = "MethodNest", |
| memberName = "populatedFromInvokePrivateStaticMethod", |
| usage = MemberUseContext.FIELD_GETTER) |
| MethodHandle populatedFromInvokePrivateStaticMethod) |
| throws Throwable { |
| long result = (long) populatedFromInvokePrivateStaticMethod.invoke(); |
| assertThat(result).isEqualTo(385L); // 128L + 256 + 1 |
| } |
| |
| @Test |
| public void invokePrivateInstanceMethod_instanceInitializer( |
| @RuntimeMethodHandle( |
| className = "MethodNest", |
| memberName = "populatedFromInvokePrivateInstanceMethod", |
| usage = MemberUseContext.FIELD_GETTER) |
| MethodHandle populatedFromInvokePrivateInstanceMethod) |
| throws Throwable { |
| long result = (long) populatedFromInvokePrivateInstanceMethod.invoke(invokerInstance); |
| assertThat(result).isEqualTo(768L); // 128L + 256 + 1 |
| } |
| |
| @Test |
| public void invokePrivateStaticMethod( |
| @RuntimeMethodHandle(className = "MethodNest", memberName = "invokePrivateStaticMethod") |
| MethodHandle invokePrivateStaticMethod) |
| throws Throwable { |
| long result = (long) invokePrivateStaticMethod.invokeExact((long) 1L, (int) 2); |
| assertThat(result).isEqualTo(1L + 2); |
| } |
| |
| @Test |
| public void invokePrivateInstanceMethod( |
| @RuntimeMethodHandle(className = "MethodNest", memberName = "invokePrivateInstanceMethod") |
| MethodHandle invokePrivateInstanceMethod) |
| throws Throwable { |
| long result = (long) invokePrivateInstanceMethod.invoke(mateInstance, 2L, 3); |
| assertThat(result).isEqualTo(2L + 3); |
| } |
| |
| @Test |
| public void invokeStaticMethod( |
| @RuntimeMethodHandle(className = "MethodNest", memberName = "invokeStaticMethod") |
| MethodHandle invokeStaticMethod) |
| throws Throwable { |
| long x = 3L; |
| int y = 4; |
| |
| long result = (long) invokeStaticMethod.invoke(x, y); |
| assertThat(result).isEqualTo(x + y); |
| } |
| |
| @Test |
| public void invokeInstanceMethod( |
| @RuntimeMethodHandle(className = "MethodNest", memberName = "invokeInstanceMethod") |
| MethodHandle invokeInstanceMethod) |
| throws Throwable { |
| long x = 4L; |
| int y = 5; |
| |
| long result = (long) invokeInstanceMethod.invoke(mateInstance, x, y); |
| assertThat(result).isEqualTo(x + y); |
| } |
| |
| @Test |
| public void invokeSuperAccessPrivateInstanceMethod( |
| @RuntimeMethodHandle( |
| className = "MethodNest", |
| memberName = "invokeSuperAccessPrivateInstanceMethod") |
| MethodHandle invokeSuperAccessPrivateInstanceMethod) |
| throws Throwable { |
| assertThat( |
| invokeSuperAccessPrivateInstanceMethod.invoke( |
| subClassMate.getConstructor().newInstance(), 7L, 8)) |
| .isEqualTo(16L); // 15 + 1 |
| } |
| |
| @Test |
| public void invokeCastAccessPrivateInstanceMethod( |
| @RuntimeMethodHandle( |
| className = "MethodNest", |
| memberName = "invokeCastAccessPrivateInstanceMethod") |
| MethodHandle invokeCastAccessPrivateInstanceMethod) |
| throws Throwable { |
| long result = |
| (long) |
| invokeCastAccessPrivateInstanceMethod.invoke( |
| subClassMate.getConstructor().newInstance(), 9L, 10); |
| assertThat(result).isEqualTo(21L); // 19 + 2 |
| } |
| } |