/** * Copyright 2006-2017 the original author or authors. * * 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 org.objenesis.instantiator.basic; /* * Copyright 2003,2004 The Apache Software Foundation * * 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. */ import org.objenesis.ObjenesisException; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.ProtectionDomain; /** * Helper class for ProxyObjectInstantiator. We can see the details of a class specification * here * * @author Henri Tremblay */ public final class ClassDefinitionUtils { public static final byte OPS_aload_0 = 42; public static final byte OPS_invokespecial = -73; // has two bytes parameters public static final byte OPS_return = -79; public static final byte OPS_new = -69; public static final byte OPS_dup = 89; public static final byte OPS_areturn = -80; public static final int CONSTANT_Utf8 = 1; public static final int CONSTANT_Integer = 3; public static final int CONSTANT_Float = 4; public static final int CONSTANT_Long = 5; public static final int CONSTANT_Double = 6; public static final int CONSTANT_Class = 7; public static final int CONSTANT_String = 8; public static final int CONSTANT_Fieldref = 9; public static final int CONSTANT_Methodref = 10; public static final int CONSTANT_InterfaceMethodref = 11; public static final int CONSTANT_NameAndType = 12; public static final int CONSTANT_MethodHandle = 15; public static final int CONSTANT_MethodType = 16; public static final int CONSTANT_InvokeDynamic = 18; public static final int ACC_PUBLIC = 0x0001; // Declared public; may be accessed from outside its package. public static final int ACC_FINAL = 0x0010; // Declared final; no subclasses allowed. public static final int ACC_SUPER = 0x0020; // Treat superclass methods specially when invoked by the invokespecial instruction. public static final int ACC_INTERFACE = 0x0200; // Is an interface, not a class. public static final int ACC_ABSTRACT = 0x0400; // Declared abstract; must not be instantiated. public static final int ACC_SYNTHETIC = 0x1000; // Declared synthetic; not present in the source code. public static final int ACC_ANNOTATION = 0x2000; // Declared as an annotation type. public static final int ACC_ENUM = 0x4000; // Declared as an enum type. public static final byte[] MAGIC = { (byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe }; public static final byte[] VERSION = { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x31 }; // minor_version, major_version (Java 5) private ClassDefinitionUtils() { } private static Method DEFINE_CLASS; private static final ProtectionDomain PROTECTION_DOMAIN; static { PROTECTION_DOMAIN = AccessController.doPrivileged(new PrivilegedAction() { public ProtectionDomain run() { return ClassDefinitionUtils.class.getProtectionDomain(); } }); AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { Class loader = Class.forName("java.lang.ClassLoader"); // JVM crash w/o this DEFINE_CLASS = loader.getDeclaredMethod("defineClass", new Class[]{ String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class }); DEFINE_CLASS.setAccessible(true); } catch (ClassNotFoundException e) { throw new ObjenesisException(e); } catch (NoSuchMethodException e) { throw new ObjenesisException(e); } return null; } }); } /** * Define a class in the provided class loader from the array of bytes. Inspired by cglib * ReflectUtils.defineClass * * @param type of the class returned * @param className class name in the format org.objenesis.MyClass * @param b bytes representing the class * @param loader the class loader where the class will be loaded * @return the newly loaded class * @throws Exception whenever something goes wrong */ @SuppressWarnings("unchecked") public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception { Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), PROTECTION_DOMAIN }; Class c = (Class) DEFINE_CLASS.invoke(loader, args); // Force static initializers to run. Class.forName(className, true, loader); return c; } /** * Read the bytes of a class from the classpath * * @param className full class name including the package * @return the bytes representing the class * @throws IllegalArgumentException if the class is longer than 2500 bytes * @throws IOException if we fail to read the class */ public static byte[] readClass(String className) throws IOException { // convert to a resource className = classNameToResource(className); byte[] b = new byte[2500]; // I'm assuming that I'm reading class that are not too big int length; InputStream in = ClassDefinitionUtils.class.getClassLoader().getResourceAsStream(className); try { length = in.read(b); } finally { in.close(); } if(length >= 2500) { throw new IllegalArgumentException("The class is longer that 2500 bytes which is currently unsupported"); } byte[] copy = new byte[length]; System.arraycopy(b, 0, copy, 0, length); return copy; } /** * Write all class bytes to a file. * * @param fileName file where the bytes will be written * @param bytes bytes representing the class * @throws IOException if we fail to write the class */ public static void writeClass(String fileName, byte[] bytes) throws IOException { BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileName)); try { out.write(bytes); } finally { out.close(); } } /** * Will convert a class name to its name in the class definition format (e.g {@code org.objenesis.EmptyClass} * becomes {@code org/objenesis/EmptyClass}) * * @param className full class name including the package * @return the internal name */ public static String classNameToInternalClassName(String className) { return className.replace('.', '/'); } /** * Will convert a class name to its class loader resource name (e.g {@code org.objenesis.EmptyClass} * becomes {@code org/objenesis/EmptyClass.class}) * * @param className full class name including the package * @return the resource name */ public static String classNameToResource(String className) { return classNameToInternalClassName(className) + ".class"; } /** * Check if this class already exists in the class loader and return it if it does * * @param type of the class returned * @param classLoader Class loader where to search the class * @param className Class name with full path * @return the class if it already exists or null */ @SuppressWarnings("unchecked") public static Class getExistingClass(ClassLoader classLoader, String className) { try { return (Class) Class.forName(className, true, classLoader); } catch (ClassNotFoundException e) { return null; } } }