/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.vm.classmgr; import java.lang.reflect.Member; import org.jnode.annotation.MagicPermission; import org.jnode.annotation.PrivilegedActionPragma; import org.jnode.vm.InternString; import org.jnode.vm.LoadCompileService; import org.jnode.vm.VmAddress; import org.jnode.vm.facade.VmUtils; import org.jnode.vm.isolate.VmIsolateLocal; import org.vmmagic.unboxed.Address; import sun.reflect.ReflectionFactory; @MagicPermission public abstract class VmMethod extends VmMember implements VmSharedStaticsEntry { /** * Address of native code of this method */ private VmAddress nativeCode; /** * #Slots taken by arguments of this method (including this pointer) */ private final short argSlotCount; /** * Resolved types for each argument of this method */ private VmType[] paramTypes; /** * Resolved return type of this method */ private VmType returnType; /** * java.lang.reflect.Method for this method */ private VmIsolateLocal<Member> javaMemberHolder; /** * The bytecode (if any) */ private VmByteCode bytecode; /** * The compiled code (if any) */ private VmCompiledCode compiledCode; /** * The exceptions we can throw */ private VmExceptions exceptions; /** * The selector of this method name&type */ private int selector; /** * Optimization level of native code */ private short nativeCodeOptLevel = -1; /** * The index in the statics table */ private final int staticsIndex; /** * The mangled name of this method */ private String mangledName; /** * Flags of variour pragma's set to this method */ private char pragmaFlags; private Object annotationDefault; private byte[] rawAnnotationDefault; private byte[] rawParameterAnnotations; /** * Constructor for VmMethod. * * @param name * @param signature * @param modifiers * @param declaringClass */ protected VmMethod(String name, String signature, int modifiers, VmType<?> declaringClass) { super( name, signature, modifiers | (returnsObject(signature) ? Modifier.ACC_OBJECTREF : 0), declaringClass); this.argSlotCount = (short) (Signature.getArgSlotCount(declaringClass .getLoader().getArchitecture().getTypeSizeInfo(), signature) + (isStatic() ? 0 : 1)); final VmClassLoader cl = declaringClass.getLoader(); if (isStatic()) { this.selector = 0; } else { this.selector = cl.getSelectorMap().get(name, signature); } this.staticsIndex = cl.getSharedStatics().allocMethodCode(); } private static final boolean returnsObject(String signature) { final char firstReturnSignatureChar = signature.charAt(signature .indexOf(')') + 1); return (firstReturnSignatureChar == '[' || firstReturnSignatureChar == 'L'); } /** * Initialize this instance, copy the given method. * * @param src The method that is copied. */ protected VmMethod(VmMethod src) { super(src.name, src.signature, src.getModifiers(), src.declaringClass); this.argSlotCount = src.argSlotCount; this.selector = src.selector; this.staticsIndex = src.staticsIndex; } /** * Get the currently used byte-code information for this method. This * bytecode may have been optimized. This may return null if this is a * native or abstract method. * * @return The current bytecode */ public final VmByteCode getBytecode() { return bytecode; } //todo security review /** * Sets the bytecode information of this method. * * @param bc */ public final void setBytecode(VmByteCode bc) { this.bytecode = bc; bc.lock(); } /** * Get the number of bytes in the byte-codes for this method. * * @return Length of bytecode */ public final int getBytecodeSize() { return (bytecode == null) ? 0 : bytecode.getLength(); } /** * Gets myself as java.lang.reflect.Method or java.lang.reflect.Constructor, * depending on isConstructor(). * * @return Method */ @PrivilegedActionPragma public final Member asMember() { if (javaMemberHolder == null) { javaMemberHolder = new VmIsolateLocal<Member>(); } Member javaMember = javaMemberHolder.get(); if (javaMember == null) { //parameter types int arg_count = getNoArguments(); Class[] args = new Class[arg_count]; for (int i = 0; i < arg_count; i++) { args[i] = getArgumentType(i).asClass(); } //checked exceptions final VmExceptions exceptions = getExceptions(); int ce_count = exceptions.getLength(); final Class[] ces = new Class[ce_count]; for (int i = 0; i < ce_count; i++) { VmConstClass vmConstClass = exceptions.getException(i); if (!vmConstClass.isResolved()) { vmConstClass.doResolve(getDeclaringClass().getLoader()); } ces[i] = vmConstClass.getResolvedVmClass().asClass(); } //slot VmType decl_type = getDeclaringClass(); int slot = -1; for (int i = 0; i < decl_type.getNoDeclaredMethods(); i++) { if (this == decl_type.getDeclaredMethod(i)) { slot = i; break; } } if (isConstructor()) { if (slot == -1) { throw new ClassFormatError("Invalid constructor"); } javaMember = ReflectionFactory.getReflectionFactory().newConstructor(getDeclaringClass().asClass(), args, ces, getModifiers(), slot, getSignature(), getRawAnnotations(), getRawParameterAnnotations()); } else { if (slot == -1) { throw new ClassFormatError("Invalid method"); } javaMember = ReflectionFactory.getReflectionFactory().newMethod(getDeclaringClass().asClass(), getName(), args, getReturnType().asClass(), ces, getModifiers(), slot, getSignature(), getRawAnnotations(), getRawParameterAnnotations(), getRawAnnotationDefault()); } javaMemberHolder.set(javaMember); } return javaMember; } /** * Convert myself into a String representation * * @return String */ public final String toString() { return getMangledName(); } /** * Convert myself into a String representation * * @return The mangled name */ public final String getMangledName() { if (mangledName == null) { mangledName = InternString.internString(declaringClass.getMangledName() + Mangler.mangle('#' + getName() + '.' + getSignature())); } return mangledName; } /** * Gets the full name of this method consisting of * its declaring class, its name and its signature. * * @return the full name */ public final String getFullName() { return declaringClass.getName() + '#' + getName() + '!' + getSignature(); } //todo security review public final void resetOptLevel() { nativeCodeOptLevel = 0; } /** * Compile this method with n optimization level 1 higher then the current * optimization level. */ public final void recompile() { final int optLevel = nativeCodeOptLevel + 1; if (!declaringClass.isPrepared()) { throw new IllegalStateException( "Declaring class must have been prepared"); } LoadCompileService.compile(this, optLevel, false); } /** * Recompile a method declaring in the type given by its statics table index * and the index of the method within the type. * * @param typeStaticsIndex * @param methodIndex */ static final void recompileMethod(int typeStaticsIndex, int methodIndex) { final VmType<?> type = VmUtils.getVm().getSharedStatics().getTypeEntry( typeStaticsIndex); type.initialize(); final VmMethod method = type.getDeclaredMethod(methodIndex); method.recompile(); } public final boolean isAbstract() { return Modifier.isAbstract(getModifiers()); } public final boolean isNative() { return Modifier.isNative(getModifiers()); } public final boolean isSpecial() { return Modifier.isSpecial(getModifiers()); } public final boolean isSynchronized() { return Modifier.isSynchronized(getModifiers()); } public final boolean isConstructor() { return Modifier.isConstructor(getModifiers()); } public final boolean isInitializer() { return Modifier.isInitializer(getModifiers()); } /* * public final boolean isCompiled() { return * Modifier.isCompiled(getModifiers()); */ /** * Resolve the type of this method. */ protected final void resolve() { resolveTypes(); } private final void resolveTypes() { if (paramTypes == null) { try { Signature sig = new Signature(getSignature(), declaringClass .getLoader()); returnType = sig.getReturnType(); int count = sig.getParamCount(); final VmType[] types = new VmType[count]; for (int i = 0; i < count; i++) { types[i] = sig.getParamType(i); } this.paramTypes = types; } catch (ClassNotFoundException ex) { throw (Error) new NoClassDefFoundError("In method " + toString()).initCause(ex); } } } public final int getNoArguments() { resolveTypes(); return paramTypes.length; } public final VmType<?> getArgumentType(int index) { resolveTypes(); return paramTypes[index]; } /** * Does the given array of types match my argument types? * * @param argTypes * @return boolean */ protected final boolean matchArgumentTypes(VmType[] argTypes) { resolveTypes(); int argTypesLength = (argTypes == null) ? 0 : argTypes.length; if (paramTypes.length != argTypesLength) { return false; } for (int i = 0; i < argTypesLength; i++) { if (argTypes[i] != paramTypes[i]) { return false; } } return true; } /** * @return VmClass */ public final VmType<?> getReturnType() { resolveTypes(); return returnType; } /** * Does this method return void? * * @return boolean */ public final boolean isReturnVoid() { return (signature.charAt(signature.length() - 1) == 'V'); } /** * Does this method return long or double? * * @return boolean */ public final boolean isReturnWide() { return ((this.getModifiers() & Modifier.ACC_WIDE) != 0); } /** * Does this method return an object reference. * * @return boolean */ public final boolean isReturnObject() { return ((this.getModifiers() & Modifier.ACC_OBJECTREF) != 0); } /** * Gets the exceptions this method has declared to throw * * @return The exceptions this method has declared to throw, never null. */ public final VmExceptions getExceptions() { if (exceptions == null) { exceptions = new VmExceptions(); } return exceptions; } /** * Sets the exceptions this method has declared to throw * * @param exceptions * @throws ClassFormatError */ final void setExceptions(VmExceptions exceptions) throws ClassFormatError { if (this.exceptions == null) { this.exceptions = exceptions; this.pragmaFlags |= exceptions.getPragmaFlags(); } else { throw new ClassFormatError( "Cannot have more then 1 Exceptions attribute"); } } /** * Add the given pragma flags to my flags. */ final void addPragmaFlags(int flags) { this.pragmaFlags |= flags; // KernelSpace implies uninterruptible if ((flags & MethodPragmaFlags.KERNELSPACE) != 0) { this.pragmaFlags |= MethodPragmaFlags.UNINTERRUPTIBLE; } } /** * Gets the compiled code information of this method (if any) * * @return The compiled code, or null if no compiled code has been set. */ public final VmCompiledCode getDefaultCompiledCode() { return compiledCode; } /** * Gets the compiled code for a given magic value (if any) * * @return The compiled code, or null if no compiled code with the given * magic has been set. */ public final VmCompiledCode getCompiledCode(int magic) { final VmCompiledCode c = compiledCode; if (c != null) { return c.lookup(magic); } else { return null; } } /** * Install the generated code. * * @param code * @param optLevel The optimization level of the generated code. */ public final void addCompiledCode(VmCompiledCode code, int optLevel) { if ((this.nativeCode == null) || (optLevel > nativeCodeOptLevel)) { synchronized (this) { code.setNext(this.compiledCode); this.compiledCode = code; this.nativeCode = code.getNativeCode(); VmUtils.getVm().getSharedStatics().setMethodCode( getSharedStaticsIndex(), code.getNativeCode()); this.nativeCodeOptLevel = (short) optLevel; } } } /** * Gets the global unique selector if this method name&type. * * @return The selector */ public final int getSelector() { return selector; } /** * Gets the number of stack slots used by the arguments of this method. This * number included the slot for "this" on non-static fields. * * @return int */ public final int getArgSlotCount() { return this.argSlotCount; } /** * Is this method uninterruptible. * * @return {@code true} if the pragma flag is set, otherwise {@code false}. */ public final boolean isUninterruptible() { return ((pragmaFlags & MethodPragmaFlags.UNINTERRUPTIBLE) != 0); } /** * Is the checkpermission pragma set for this method. * * @return {@code true} if the pragma flag is set, otherwise {@code false}. */ public final boolean hasCheckPermissionPragma() { return ((pragmaFlags & MethodPragmaFlags.CHECKPERMISSION) != 0); } /** * Is the doprivileged pragma set for this method. * * @return {@code true} if the pragma flag is set, otherwise {@code false}. */ public final boolean hasDoPrivilegedPragma() { return ((pragmaFlags & MethodPragmaFlags.DOPRIVILEGED) != 0); } /** * Is the inline pragma set for this method. * * @return {@code true} if the pragma flag is set, otherwise {@code false}. */ public final boolean hasInlinePragma() { return ((pragmaFlags & MethodPragmaFlags.INLINE) != 0); } /** * Is the loadstatics pragma set for this method. * * @return {@code true} if the pragma flag is set, otherwise {@code false}. */ public final boolean hasLoadStaticsPragma() { return ((pragmaFlags & MethodPragmaFlags.LOADSTATICS) != 0); } /** * Is the noinline pragma set for this method. * * @return {@code true} if the pragma flag is set, otherwise {@code false}. */ public final boolean hasNoInlinePragma() { return ((pragmaFlags & MethodPragmaFlags.NOINLINE) != 0); } /** * Is the noreadbarrier pragma set for this method. * * @return {@code true} if the pragma flag is set, otherwise {@code false}. */ public final boolean hasNoReadBarrierPragma() { return ((pragmaFlags & MethodPragmaFlags.NOREADBARRIER) != 0); } /** * Is the nowritebarrier pragma set for this method. * * @return {@code true} if the pragma flag is set, otherwise {@code false}. */ public final boolean hasNoWriteBarrierPragma() { return ((pragmaFlags & MethodPragmaFlags.NOWRITEBARRIER) != 0); } /** * Is the privilegedaction pragma set for this method. * * @return {@code true} if the pragma flag is set, otherwise {@code false}. */ public final boolean hasPrivilegedActionPragma() { return ((pragmaFlags & MethodPragmaFlags.PRIVILEGEDACTION) != 0); } /** * Is the KernelSpace pragma set for this method. * * @return {@code true} if the pragma flag is set, otherwise {@code false}. */ public final boolean hasKernelSpacePragma() { return ((pragmaFlags & MethodPragmaFlags.KERNELSPACE) != 0); } /** * Mark this method as uninterruptable. */ final void setUninterruptible() { pragmaFlags |= MethodPragmaFlags.UNINTERRUPTIBLE; } /** * Gets the optimization level of the native code. A value of -1 means not * compiled yet. * * @return Returns the nativeCodeOptLevel. */ public final int getNativeCodeOptLevel() { return this.nativeCodeOptLevel; } /** * Gets the index of this field in the shared statics table. * * @return Returns the staticsIndex. */ public final int getSharedStaticsIndex() { return this.staticsIndex; } public boolean hasNativeCode() { if (nativeCode == null) { return false; } else { return true; } } public void testNativeCode() { if (declaringClass.isInterface()) { return; } if (nativeCode == null) { System.err.println("nativeCode == null in " + this); } else { final int ptr = Address.fromAddress(nativeCode).toInt(); if ((ptr < 0) || (Math.abs(ptr) < 4096)) { System.err.println("nativeCode has low address " + ptr + " in " + this); } } } /** * @see org.jnode.vm.objects.VmSystemObject#verifyBeforeEmit() */ public void verifyBeforeEmit() { super.verifyBeforeEmit(); if (nativeCode == null) { if (!declaringClass.isInterface()) { throw new RuntimeException("nativeCode of " + this + " is null; declaringclass compiled? " + getDeclaringClass().isCompiled()); } } } public Object getAnnotationDefault() { return annotationDefault; } public void setAnnotationDefault(Object annotationDefault) { this.annotationDefault = annotationDefault; } public byte[] getRawAnnotationDefault() { return rawAnnotationDefault; } public void setRawAnnotationDefault(byte[] rawAnnotationDefault) { this.rawAnnotationDefault = rawAnnotationDefault; } public byte[] getRawParameterAnnotations() { return rawParameterAnnotations; } public void setRawParameterAnnotations(byte[] rawParameterAnnotations) { this.rawParameterAnnotations = rawParameterAnnotations; } }