diff --git a/proxy/proxy-impl/pom.xml b/proxy/proxy-impl/pom.xml index 96331085d3..1ef31611d9 100644 --- a/proxy/proxy-impl/pom.xml +++ b/proxy/proxy-impl/pom.xml @@ -172,9 +172,9 @@ - java9To25 + java9To27 - (8,26) + (8,28) @@ -256,9 +256,9 @@ - java25 + java25To27 - 25 + (24,28) diff --git a/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyUtils.java b/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyUtils.java index 258d9a33ab..f8be7b4d5c 100644 --- a/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyUtils.java +++ b/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyUtils.java @@ -24,116 +24,34 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ProxyUtils -{ - private static Logger LOGGER = LoggerFactory.getLogger(ProxyUtils.class); - public static final int JAVA_CLASS_VERSION = new BigDecimal(System.getProperty("java.class.version")).intValue(); - private static int weavingJavaVersion = -1; // initialise an invalid number +/** + * Utility class for proxy generation. + */ +public class ProxyUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(ProxyUtils.class); + /** + * The Java class version of the current JVM. + */ + public static final int JAVA_CLASS_VERSION = new BigDecimal(System.getProperty("java.class.version")).intValue(); + private static int weavingJavaVersion = -1; // initialise an invalid number + + /** + * Get the class file version to be used for weaving. + * + * @return the class file version, e.g. 69 for Java 25 + */ + public static int getWeavingJavaVersion() { + if (weavingJavaVersion == -1) { + weavingJavaVersion = verifyJavaClassVersion(JAVA_CLASS_VERSION); + } + return weavingJavaVersion; + } - /** - * Get the java version to be woven at. - * @return - */ - public static int getWeavingJavaVersion() { - if (weavingJavaVersion == -1 ) { - //In order to avoid an inconsistent stack error the version of the woven byte code needs to match - //the level of byte codes in the original class - switch(JAVA_CLASS_VERSION) { - case Opcodes.V25: - LOGGER.debug("Weaving to Java 25"); - weavingJavaVersion = Opcodes.V25; - break; - case Opcodes.V24: - LOGGER.debug("Weaving to Java 24"); - weavingJavaVersion = Opcodes.V24; - break; - case Opcodes.V23: - LOGGER.debug("Weaving to Java 23"); - weavingJavaVersion = Opcodes.V23; - break; - case Opcodes.V22: - LOGGER.debug("Weaving to Java 22"); - weavingJavaVersion = Opcodes.V22; - break; - case Opcodes.V21: - LOGGER.debug("Weaving to Java 21"); - weavingJavaVersion = Opcodes.V21; - break; - case Opcodes.V20: - LOGGER.debug("Weaving to Java 20"); - weavingJavaVersion = Opcodes.V20; - break; - case Opcodes.V19: - LOGGER.debug("Weaving to Java 19"); - weavingJavaVersion = Opcodes.V19; - break; - case Opcodes.V18: - LOGGER.debug("Weaving to Java 18"); - weavingJavaVersion = Opcodes.V18; - break; - case Opcodes.V17: - LOGGER.debug("Weaving to Java 17"); - weavingJavaVersion = Opcodes.V17; - break; - case Opcodes.V16: - LOGGER.debug("Weaving to Java 16"); - weavingJavaVersion = Opcodes.V16; - break; - case Opcodes.V15: - LOGGER.debug("Weaving to Java 15"); - weavingJavaVersion = Opcodes.V15; - break; - case Opcodes.V14: - LOGGER.debug("Weaving to Java 14"); - weavingJavaVersion = Opcodes.V14; - break; - case Opcodes.V13: - LOGGER.debug("Weaving to Java 13"); - weavingJavaVersion = Opcodes.V13; - break; - case Opcodes.V12: - LOGGER.debug("Weaving to Java 12"); - weavingJavaVersion = Opcodes.V12; - break; - case Opcodes.V11: - LOGGER.debug("Weaving to Java 11"); - weavingJavaVersion = Opcodes.V11; - break; - case Opcodes.V10: - LOGGER.debug("Weaving to Java 10"); - weavingJavaVersion = Opcodes.V10; - break; - case Opcodes.V9: - LOGGER.debug("Weaving to Java 9"); - weavingJavaVersion = Opcodes.V9; - break; - case Opcodes.V1_8: - LOGGER.debug("Weaving to Java 8"); - weavingJavaVersion = Opcodes.V1_8; - break; - case Opcodes.V1_7: - LOGGER.debug("Weaving to Java 7"); - weavingJavaVersion = Opcodes.V1_7; - break; - case Opcodes.V1_6: - LOGGER.debug("Weaving to Java 6"); - weavingJavaVersion = Opcodes.V1_6; - break; - case Opcodes.V1_5: - LOGGER.debug("Weaving to Java 5"); - weavingJavaVersion = Opcodes.V1_5; - break; - default: - if (JAVA_CLASS_VERSION > Opcodes.V25) { - // newer JVMs load older class files, so generate proxies at the highest level we know - LOGGER.debug("Weaving to Java 25 on newer Java class version {}", JAVA_CLASS_VERSION); - weavingJavaVersion = Opcodes.V25; - break; - } - //aries should work with Java 5 or above - throw new IllegalArgumentException("Invalid Java version " + JAVA_CLASS_VERSION); - } - } - return weavingJavaVersion; - } + static int verifyJavaClassVersion(int javaClassVersion) { + if (javaClassVersion < Opcodes.V1_8 || javaClassVersion == Opcodes.V1_1) { + throw new IllegalArgumentException("Unsupported Java class version: " + javaClassVersion); + } + LOGGER.debug("Weaving to Java {}", javaClassVersion - Opcodes.V1_8 + 8); + return javaClassVersion; + } } diff --git a/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/common/AbstractWovenProxyAdapter.java b/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/common/AbstractWovenProxyAdapter.java index 5b680da97d..15a6b9e1c6 100644 --- a/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/common/AbstractWovenProxyAdapter.java +++ b/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/common/AbstractWovenProxyAdapter.java @@ -196,9 +196,6 @@ public abstract class AbstractWovenProxyAdapter extends ClassVisitor implements protected boolean currentMethodDeclaringTypeIsInterface; - - public static final boolean IS_AT_LEAST_JAVA_6 = JAVA_CLASS_VERSION >= Opcodes.V1_6; - /** * Create a new adapter for the supplied class * @@ -240,7 +237,7 @@ public final void visit(int version, int access, String name, String signature, Arrays.asList(interfaces).contains(Type.getInternalName(Serializable.class)) || checkInterfacesForSerializability(interfaces); - if (!!!WovenProxy.class.isAssignableFrom(superClass)) { + if (!WovenProxy.class.isAssignableFrom(superClass)) { // We have found a type we need to add WovenProxy information to @@ -298,7 +295,7 @@ private void cannotLoadSuperClassException(String superName, UnableToProxyExcept * initialization references the subclass being woven. Odd, but seen * in the wild! */ - private final boolean superHasNoArgsConstructor(String superName, String name) { + private boolean superHasNoArgsConstructor(String superName, String name) { ConstructorFinder cf = new ConstructorFinder(); @@ -347,8 +344,8 @@ public final MethodVisitor visitMethod(int access, String name, String desc, // Only weave "real" instance methods. Not constructors, initializers or // compiler generated ones. if ((access & (ACC_STATIC | ACC_PRIVATE | ACC_SYNTHETIC - | ACC_NATIVE | ACC_BRIDGE)) == 0 && !!!name.equals("") && - !!!name.equals("")) { + | ACC_NATIVE | ACC_BRIDGE)) == 0 && !name.equals("") && + !name.equals("")) { // found a method we should weave @@ -449,7 +446,7 @@ protected abstract MethodVisitor getWeavingMethodVisitor(int access, String name /** * Write the methods we need for wovenProxies on the highest supertype */ - private final void writeFinalWovenProxyMethods() { + private void writeFinalWovenProxyMethods() { // add private fields for the Callable dispatcher // and InvocationListener. These aren't static because we can have // multiple instances of the same proxy class. These should not be @@ -519,7 +516,7 @@ private final void writeFinalWovenProxyMethods() { * overridden on each class, we also write a constructor for this method to * use if we don't have one. */ - private final void writeCreateNewProxyInstanceAndConstructor() { + private void writeCreateNewProxyInstanceAndConstructor() { GeneratorAdapter methodAdapter = getMethodGenerator(ACC_PUBLIC, new Method( "org_apache_aries_proxy_weaving_WovenProxy_createNewProxyInstance", WOVEN_PROXY_IFACE_TYPE, DISPATCHER_LISTENER_METHOD_ARGS)); @@ -557,7 +554,7 @@ private final void writeCreateNewProxyInstanceAndConstructor() { methodAdapter.invokeConstructor(typeBeingWoven, NO_ARGS_CONSTRUCTOR); else throw new RuntimeException(new UnableToProxyException(typeBeingWoven.getClassName(), - String.format("The class %s and its superclass %s do not have no-args constructors and cannot be woven.", + format("The class %s and its superclass %s do not have no-args constructors and cannot be woven.", typeBeingWoven.getClassName(), superType.getClassName()))); } methodAdapter.loadThis(); @@ -594,7 +591,7 @@ private final void writeCreateNewProxyInstanceAndConstructor() { * Create fields and an initialiser for {@link java.lang.reflect.Method} * objects in our class */ - private final void writeStaticInitMethod() { + private void writeStaticInitMethod() { // we create a static field for each method we encounter with a *unique* // random name // since each method needs to be stored individually @@ -704,7 +701,7 @@ public static void readClass(Class c, ClassVisitor adapter) throws IOExceptio * @param fieldName * @param fieldDescriptor */ - private final void generateField(String fieldName, String fieldDescriptor) { + private void generateField(String fieldName, String fieldDescriptor) { FieldVisitor fv = cv.visitField(ACC_PROTECTED | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_FINAL, fieldName, fieldDescriptor, null, null); for (String s : annotationTypeDescriptors) @@ -719,7 +716,7 @@ private final void generateField(String fieldName, String fieldDescriptor) { * @param methodSignature * @return */ - private final GeneratorAdapter getMethodGenerator(int access, Method method) { + private GeneratorAdapter getMethodGenerator(int access, Method method) { access = access | ACC_SYNTHETIC; GeneratorAdapter ga = new GeneratorAdapter(access, method, null, null, cv); for (String s : annotationTypeDescriptors) diff --git a/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/weaving/WovenProxyGenerator.java b/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/weaving/WovenProxyGenerator.java index d23e2d7720..899c06d6ec 100644 --- a/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/weaving/WovenProxyGenerator.java +++ b/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/weaving/WovenProxyGenerator.java @@ -22,7 +22,6 @@ import static org.objectweb.asm.Opcodes.ACC_ENUM; import static org.objectweb.asm.Opcodes.ACC_INTERFACE; -import org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter; import org.apache.aries.proxy.impl.common.OSGiFriendlyClassVisitor; import org.apache.aries.proxy.impl.common.OSGiFriendlyClassWriter; import org.objectweb.asm.ClassReader; @@ -34,24 +33,20 @@ */ public final class WovenProxyGenerator { - public static final byte[] getWovenProxy(byte[] original, ClassLoader loader){ + public static byte[] getWovenProxy(byte[] original, ClassLoader loader){ ClassReader cReader = new ClassReader(original); //Don't weave interfaces, enums or annotations if((cReader.getAccess() & (ACC_INTERFACE | ACC_ANNOTATION | ACC_ENUM)) != 0) return null; - //If we are Java 1.6 + compiled then we need to compute stack frames, otherwise - //maxs are fine (and faster) - int computeVal = AbstractWovenProxyAdapter.IS_AT_LEAST_JAVA_6 ? - ClassWriter.COMPUTE_FRAMES : ClassWriter.COMPUTE_MAXS; + int computeVal = ClassWriter.COMPUTE_FRAMES; ClassWriter cWriter = new OSGiFriendlyClassWriter(cReader, computeVal, loader); ClassVisitor cv = new OSGiFriendlyClassVisitor(cWriter, computeVal ); //Wrap our outer layer to add the original SerialVersionUID if it was previously being defaulted ClassVisitor weavingAdapter = new SyntheticSerialVerUIDAdder( new WovenProxyAdapter(cv, cReader.getClassName(), loader)); - // If we are Java 1.6 + then we need to skip frames as they will be recomputed - cReader.accept(weavingAdapter, AbstractWovenProxyAdapter.IS_AT_LEAST_JAVA_6 ? ClassReader.SKIP_FRAMES : 0); + cReader.accept(weavingAdapter, ClassReader.SKIP_FRAMES); return cWriter.toByteArray(); } diff --git a/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/impl/ProxyUtilsTest.java b/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/impl/ProxyUtilsTest.java new file mode 100644 index 0000000000..dcce628345 --- /dev/null +++ b/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/impl/ProxyUtilsTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.aries.proxy.impl; + +import org.objectweb.asm.Opcodes; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +public class ProxyUtilsTest { + @Test + public void testVerifyJavaClassVersion8AndAbove() { + assertEquals(Opcodes.V1_8, ProxyUtils.verifyJavaClassVersion(52)); + assertEquals(Opcodes.V9, ProxyUtils.verifyJavaClassVersion(53)); + assertEquals(Opcodes.V10, ProxyUtils.verifyJavaClassVersion(54)); + assertEquals(Opcodes.V11, ProxyUtils.verifyJavaClassVersion(55)); + assertEquals(Opcodes.V12, ProxyUtils.verifyJavaClassVersion(56)); + assertEquals(Opcodes.V13, ProxyUtils.verifyJavaClassVersion(57)); + assertEquals(Opcodes.V14, ProxyUtils.verifyJavaClassVersion(58)); + assertEquals(Opcodes.V15, ProxyUtils.verifyJavaClassVersion(59)); + assertEquals(Opcodes.V16, ProxyUtils.verifyJavaClassVersion(60)); + assertEquals(Opcodes.V17, ProxyUtils.verifyJavaClassVersion(61)); + assertEquals(Opcodes.V18, ProxyUtils.verifyJavaClassVersion(62)); + assertEquals(Opcodes.V19, ProxyUtils.verifyJavaClassVersion(63)); + assertEquals(Opcodes.V20, ProxyUtils.verifyJavaClassVersion(64)); + assertEquals(Opcodes.V21, ProxyUtils.verifyJavaClassVersion(65)); + assertEquals(Opcodes.V22, ProxyUtils.verifyJavaClassVersion(66)); + assertEquals(Opcodes.V23, ProxyUtils.verifyJavaClassVersion(67)); + assertEquals(Opcodes.V24, ProxyUtils.verifyJavaClassVersion(68)); + assertEquals(Opcodes.V25, ProxyUtils.verifyJavaClassVersion(69)); + assertEquals(Opcodes.V26, ProxyUtils.verifyJavaClassVersion(70)); + assertEquals(Opcodes.V27, ProxyUtils.verifyJavaClassVersion(71)); + assertEquals(72, ProxyUtils.verifyJavaClassVersion(72)); // supporting future versions of Java we don't know about yet + } + + @Test + public void testVerifyJavaClassVersionBelow8() { + int V1_0 = 45; // there is no Opcodes.V1_0 constant in ASM + for (int i = V1_0; i < Opcodes.V1_8; i++) { + int javaClassVersion = i; + assertThrows(IllegalArgumentException.class, () -> ProxyUtils.verifyJavaClassVersion(javaClassVersion)); + } + // special case for V1_1 which is 196653 + assertThrows(IllegalArgumentException.class, () -> ProxyUtils.verifyJavaClassVersion(Opcodes.V1_1)); + } +} diff --git a/proxy/proxy-itests/pom.xml b/proxy/proxy-itests/pom.xml index c70e9fe7c8..d67674ff89 100644 --- a/proxy/proxy-itests/pom.xml +++ b/proxy/proxy-itests/pom.xml @@ -194,7 +194,7 @@ java9To25 - (8,26) + (8,28)