/* * 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 java.lang; import static org.jikesrvm.runtime.SysCall.sysCall; import org.jikesrvm.VM; import org.jikesrvm.architecture.StackFrameLayout; import org.jikesrvm.classloader.RVMArray; import org.jikesrvm.classloader.Atom; import org.jikesrvm.classloader.RVMClass; import org.jikesrvm.classloader.RVMField; import org.jikesrvm.classloader.RVMMember; import org.jikesrvm.mm.mminterface.MemoryManager; import org.jikesrvm.runtime.RuntimeEntrypoints; import org.jikesrvm.runtime.Entrypoints; import org.jikesrvm.scheduler.Synchronization; import org.vmmagic.pragma.Entrypoint; import org.vmmagic.pragma.NoInline; import org.vmmagic.unboxed.Offset; /** * Common utilities for Jikes RVM implementations of the java.lang API */ final class VMCommonLibrarySupport { /* ---- Non-inlined Exception Throwing Methods --- */ /** * Method just to throw an illegal access exception without being inlined */ @NoInline private static void throwNewIllegalAccessException(RVMMember member, RVMClass accessingClass) throws IllegalAccessException { throw new IllegalAccessException("Access to " + member + " is denied to " + accessingClass); } /* ---- General Reflection Support ---- */ /** * Check to see if a method declared by the accessingClass * should be allowed to access the argument RVMMember. * Assumption: member is not public. This trivial case should * be approved by the caller without needing to call this method. */ static void checkAccess(RVMMember member, RVMClass accessingClass) throws IllegalAccessException { RVMClass declaringClass = member.getDeclaringClass(); if (member.isPrivate()) { // access from the declaringClass is allowed if (accessingClass == declaringClass) return; } else if (member.isProtected()) { // access within the package is allowed. if (declaringClass.getClassLoader() == accessingClass.getClassLoader() && declaringClass.getPackageName().equals(accessingClass.getPackageName())) return; // access by subclasses is allowed. for (RVMClass cls = accessingClass; cls != null; cls = cls.getSuperClass()) { if (accessingClass == declaringClass) return; } } else { // default: access within package is allowed if (declaringClass.getClassLoader() == accessingClass.getClassLoader() && declaringClass.getPackageName().equals(accessingClass.getPackageName())) return; } throwNewIllegalAccessException(member, accessingClass); } /* ---- Runtime Support ---- */ /** * Class responsible for acquiring and call the gc method. If a call has * taken place the gc method will just return. */ private static final class GCLock { @SuppressWarnings("unused") // Accessed from EntryPoints @Entrypoint private int gcLock; private final Offset gcLockOffset = Entrypoints.gcLockField.getOffset(); GCLock() {} void gc() { if (Synchronization.testAndSet(this, gcLockOffset, 1)) { MemoryManager.gc(); Synchronization.fetchAndStore(this, gcLockOffset, 0); } } } private static final GCLock gcLockSingleton = new GCLock(); /** * Request GC */ static void gc() { gcLockSingleton.gc(); } /** * Copy src array to dst array from location srcPos for length len to dstPos * * @param src array * @param srcPos position within source array * @param dst array * @param dstPos position within destination array * @param len amount of elements to copy */ @Entrypoint static void arraycopy(Object src, int srcPos, Object dst, int dstPos, int len) { if (src == null || dst == null) { RuntimeEntrypoints.raiseNullPointerException(); } else if ((src instanceof char[]) && (dst instanceof char[])) { RVMArray.arraycopy((char[])src, srcPos, (char[])dst, dstPos, len); } else if ((src instanceof Object[]) && (dst instanceof Object[])) { RVMArray.arraycopy((Object[])src, srcPos, (Object[])dst, dstPos, len); } else if ((src instanceof byte[]) && (dst instanceof byte[])) { RVMArray.arraycopy((byte[])src, srcPos, (byte[])dst, dstPos, len); } else if ((src instanceof boolean[]) && (dst instanceof boolean[])) { RVMArray.arraycopy((boolean[])src, srcPos, (boolean[])dst, dstPos, len); } else if ((src instanceof short[]) && (dst instanceof short[])) { RVMArray.arraycopy((short[])src, srcPos, (short[])dst, dstPos, len); } else if ((src instanceof int[]) && (dst instanceof int[])) { RVMArray.arraycopy((int[])src, srcPos, (int[])dst, dstPos, len); } else if ((src instanceof long[]) && (dst instanceof long[])) { RVMArray.arraycopy((long[])src, srcPos, (long[])dst, dstPos, len); } else if ((src instanceof float[]) && (dst instanceof float[])) { RVMArray.arraycopy((float[])src, srcPos, (float[])dst, dstPos, len); } else if ((src instanceof double[]) && (dst instanceof double[])) { RVMArray.arraycopy((double[])src, srcPos, (double[])dst, dstPos, len); } else { RuntimeEntrypoints.raiseArrayStoreException(); } } /** * Set the value of a static final stream field of the System class * @param fieldName name of field to set * @param stream value */ static void setSystemStreamField(String fieldName, Object stream) { try { RVMField field = ((RVMClass)JikesRVMSupport.getTypeForClass(System.class)) .findDeclaredField(Atom.findOrCreateUnicodeAtom(fieldName)); field.setObjectValueUnchecked(null, stream); } catch (Exception e) { throw new Error("Error setting stream field " + fieldName + " of java.lang.System", e); } } /** * Apply library prefixes and suffixes as necessary to libname to produce a * full file name. For example, on linux "rvm" would become "librvm.so". * * @param libname name of library without any prefix or suffix * @return complete name of library */ static String mapLibraryName(String libname) { String libSuffix; if (VM.BuildForLinux || VM.BuildForSolaris) { libSuffix = ".so"; } else if (VM.BuildForOsx) { libSuffix = ".jnilib"; } else { libSuffix = ".a"; } return "lib" + libname + libSuffix; } /** * Get the value of an environment variable. */ static String getenv(String envarName) { byte[] buf = new byte[128]; // Modest amount of space for starters. byte[] nameBytes = envarName.getBytes(); // sysCall is uninterruptible so passing buf is safe int len = sysCall.sysGetenv(nameBytes, buf, buf.length); if (len < 0) // not set. return null; if (len > buf.length) { buf = new byte[len]; sysCall.sysGetenv(nameBytes, buf, len); } return new String(buf, 0, len); } /** * Converts from {@link java.lang.Thread} stack size to Jikes * RVM internal stack size. * <p> * Note that Jikes RVM currently restricts the stack size to be an integer. * This is consistent with the API specification of the java.lang.Thread * constructors. The spec says that the VM is free to treat the stack size * from java.lang.Thread as a suggestion. * * @param stacksize the stack size coming from java.lang.Thread. * 0 means that the VM should use the values that should use the values * that it would use if stack size weren't specified. * @return the actual stack size to use */ public static int stackSizeFromAPIToJikesRVM(long stacksize) { if (stacksize > Integer.MAX_VALUE) { return Integer.MAX_VALUE; } else if (stacksize == 0) { // According to the JavaDoc of the java.lang.Thread constructors, // a stack size of 0 means that the VM should use the values that // it would use if stack size weren't specified. return StackFrameLayout.getNormalStackSize(); } else if (stacksize < 0) { if (VM.VerifyAssertions) { String failingStackSize = "Received invalid stack size " + stacksize + " from java.lang.Thread!"; VM._assert(VM.NOT_REACHED, failingStackSize); } return StackFrameLayout.getNormalStackSize(); } return (int) stacksize; } }