/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.runtime; import org.jikesrvm.VM; import org.jikesrvm.classloader.Atom; import org.jikesrvm.classloader.BootstrapClassLoader; import org.jikesrvm.classloader.RVMClass; import org.jikesrvm.classloader.RVMField; import org.jikesrvm.classloader.RVMMember; import org.jikesrvm.classloader.RVMMethod; import org.jikesrvm.classloader.MethodReference; import org.jikesrvm.classloader.NormalMethod; import org.jikesrvm.classloader.TypeReference; import org.vmmagic.pragma.Entrypoint; /** * Helper class for retrieving entrypoints. Entrypoints are fields and * methods of the virtual machine that are needed by compiler-generated * machine code or C runtime code. */ public class EntrypointHelper { /** * Get description of virtual machine component (field or method). * <p> * Note: This is method is intended for use only by VM classes that need * to address their own fields and methods in the runtime virtual machine * image. It should not be used for general purpose class loading. * @param classDescriptor class descriptor - something like "Lorg/jikesrvm/RuntimeEntrypoints;" * @param memberName member name - something like "invokestatic" * @param memberDescriptor member descriptor - something like "()V" * @return corresponding RVMMember object */ private static RVMMember getMember(String classDescriptor, String memberName, String memberDescriptor) { Atom clsDescriptor = Atom.findOrCreateAsciiAtom(classDescriptor); Atom memName = Atom.findOrCreateAsciiAtom(memberName); Atom memDescriptor = Atom.findOrCreateAsciiAtom(memberDescriptor); try { TypeReference tRef = TypeReference.findOrCreate(BootstrapClassLoader.getBootstrapClassLoader(), clsDescriptor); RVMClass cls = (RVMClass) tRef.resolve(); cls.resolve(); RVMMember member; if ((member = cls.findDeclaredField(memName, memDescriptor)) != null) { verifyThatFieldIsNotFinal((RVMField) member); verifyPresenceOfEntrypointAnnotation(member); return member; } if ((member = cls.findDeclaredMethod(memName, memDescriptor)) != null) { verifyPresenceOfEntrypointAnnotation(member); return member; } } catch (Exception e) { e.printStackTrace(); } // The usual causes for getMember() to fail are: // 1. you mispelled the class name, member name, or member signature // 2. the class containing the specified member didn't get compiled // VM.sysWriteln("Entrypoints.getMember: can't resolve class=" + classDescriptor + " member=" + memberName + " desc=" + memberDescriptor); if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); return null; } private static void verifyThatFieldIsNotFinal(RVMField field) { if (field.isFinal() && field.isAnnotationPresent(Entrypoint.class) && !field.getAnnotation(Entrypoint.class).fieldMayBeFinal()) { String msg = "ERROR: Field " + field + " is marked with @Entrypoint annotation and is final." + "This is forbidden because the Java compiler views a final field " + "as immutable which it likely won't be if it's " + "explicitly accessed by code. If it's indeed final, use " + "@Entrypoint(fieldMayBeFinal = true) to mark the field."; throw new Error(msg); } } private static void verifyPresenceOfEntrypointAnnotation(RVMMember member) { if (VM.VerifyAssertions && !(member.isAnnotationPresent(Entrypoint.class))) { // For certain methods, it's clear that they're accessed by the // compiler, so an annotation is not required: boolean annotationRequired = true; if (member instanceof RVMMethod) { RVMMethod m = (RVMMethod) member; RVMClass declClass = m.getDeclaringClass(); if (declClass.getTypeRef().isMagicType() || declClass.getTypeRef().isUnboxedType() || // don't impose constraints on class library methods, // it's the VM's job to handle those correctly declClass.getPackageName().startsWith("java.lang")) { annotationRequired = false; } } // Don't require annotations on fields for the class library. if (member instanceof RVMField) { RVMField field = (RVMField) member; RVMClass declClass = field.getDeclaringClass(); if (declClass.getPackageName().startsWith("java.lang")) { annotationRequired = false; } } if (annotationRequired) { String msg = "WARNING: MISSING @Entrypoint ANNOTATION: " + member + " is missing an @Entrypoint annotation!"; throw new Error(msg); } } } public static NormalMethod getMethod(String klass, String member, String descriptor, final boolean runtimeServiceMethod) { NormalMethod m = (NormalMethod) getMember(klass, member, descriptor); m.setRuntimeServiceMethod(runtimeServiceMethod); return m; } public static NormalMethod getMethod(String klass, String member, String descriptor) { return getMethod(klass, member, descriptor, true); } private static String makeDescriptor(Class<?>... argTypes) { Class<?> lastClass = null; StringBuilder result = new StringBuilder("("); for (Class<?> c: argTypes) { if (lastClass != null) { result.append(TypeReference.findOrCreate(lastClass).getName().toString()); } lastClass = c; } result.append(")").append(TypeReference.findOrCreate(lastClass).getName().toString()); return result.toString(); } public static RVMMethod getMethod(Class<?> klass, Atom member, Class<?>... argTypes) { if (!VM.runningVM) { // avoid compiling this code into the boot image try { TypeReference tRef = TypeReference.findOrCreate(klass); RVMClass cls = tRef.resolve().asClass(); cls.resolve(); Atom descriptor = Atom.findOrCreateAsciiAtom(makeDescriptor(argTypes)); RVMMethod method = cls.findDeclaredMethod(member, descriptor); if (method != null) { verifyPresenceOfEntrypointAnnotation(method); return method; } } catch (Throwable t) { throw new Error("Entrypoints.getMethod: can't resolve class=" + klass + " member=" + member + " desc=" + makeDescriptor(argTypes), t); } } throw new Error("Entrypoints.getMethod: can't resolve class=" + klass + " member=" + member + " desc=" + makeDescriptor(argTypes)); } public static MethodReference getMethodReference(Class<?> klass, Atom member, Class<?>... argTypes) { if (!VM.runningVM) { // avoid compiling this code into the boot image TypeReference tRef = TypeReference.findOrCreate(klass); if (tRef.resolve().isClassType()) { return getMethod(klass, member, argTypes).getMemberRef().asMethodReference(); } else { // handle method references to unboxed types Atom descriptor = Atom.findOrCreateAsciiAtom(makeDescriptor(argTypes)); return MethodReference.findOrCreate(tRef, member, descriptor); } } throw new Error("Entrypoints.getMethod: can't resolve class=" + klass + " member=" + member + " desc=" + makeDescriptor(argTypes)); } public static RVMField getField(String klass, String member, String descriptor) { return (RVMField) getMember(klass, member, descriptor); } /** * Get description of virtual machine field. * @param klass class containing field * @param member member name - something like "invokestatic" * @param type of field * @return corresponding RVMField */ public static RVMField getField(Class<?> klass, String member, Class<?> type) { if (!VM.runningVM) { // avoid compiling this code into the boot image try { TypeReference klassTRef = TypeReference.findOrCreate(klass); RVMClass cls = klassTRef.resolve().asClass(); cls.resolve(); Atom memName = Atom.findOrCreateAsciiAtom(member); Atom typeName = TypeReference.findOrCreate(type).getName(); RVMField field = cls.findDeclaredField(memName, typeName); if (field != null) { verifyPresenceOfEntrypointAnnotation(field); verifyThatFieldIsNotFinal(field); return field; } } catch (Throwable t) { throw new Error("Entrypoints.getField: can't resolve class=" + klass + " member=" + member + " desc=" + type, t); } } throw new Error("Entrypoints.getField: can't resolve class=" + klass + " member=" + member + " desc=" + type); } /** * Get description of virtual machine field. * @param klass class containing field * @param member member name - something like "invokestatic" * @param type of field * @return corresponding RVMField */ static RVMField getField(String klass, String member, Class<?> type) { if (!VM.runningVM) { // avoid compiling this code into the boot image try { TypeReference tRef = TypeReference.findOrCreate(klass); RVMClass cls = tRef.resolve().asClass(); cls.resolve(); Atom memName = Atom.findOrCreateAsciiAtom(member); Atom typeName = TypeReference.findOrCreate(type).getName(); RVMField field = cls.findDeclaredField(memName, typeName); if (field != null) { verifyPresenceOfEntrypointAnnotation(field); verifyThatFieldIsNotFinal(field); return field; } } catch (Throwable t) { throw new Error("Entrypoints.getField: can't resolve class=" + klass + " member=" + member + " desc=" + type, t); } } throw new Error("Entrypoints.getField: can't resolve class=" + klass + " member=" + member + " desc=" + type); } /** * Get description of virtual machine method. * @param klass class containing method * @param member member name - something like "invokestatic" * @param descriptor member descriptor - something like "()V" * @return corresponding RVMMethod */ public static NormalMethod getMethod(Class<?> klass, String member, String descriptor) { if (!VM.runningVM) { // avoid compiling this code into the boot image try { TypeReference klassTRef = TypeReference.findOrCreate(klass); RVMClass cls = klassTRef.resolve().asClass(); cls.resolve(); Atom memName = Atom.findOrCreateAsciiAtom(member); Atom memDescriptor = Atom.findOrCreateAsciiAtom(descriptor); NormalMethod m = (NormalMethod)cls.findDeclaredMethod(memName, memDescriptor); if (m != null) { verifyPresenceOfEntrypointAnnotation(m); m.setRuntimeServiceMethod(true); return m; } } catch (Throwable t) { throw new Error("Entrypoints.getField: can't resolve class=" + klass + " member=" + member + " desc=" + descriptor, t); } } throw new Error("Entrypoints.getMethod: can't resolve class=" + klass + " method=" + member + " desc=" + descriptor); } }