From: <cr...@us...> - 2009-10-15 14:50:34
|
Revision: 5683 http://jnode.svn.sourceforge.net/jnode/?rev=5683&view=rev Author: crawley Date: 2009-10-15 14:50:26 +0000 (Thu, 15 Oct 2009) Log Message: ----------- Fixes to get the JNode bootimage build working with Jikes compiler. The most important one changes the way that JNode native methods are handled. Previously, the compiler would simply use the bytecodes from the replacement method as the actual method, but this crashed because Jikes used the wrong constant pool. Now, we generate bytecodes for the actual method on the fly. Modified Paths: -------------- branches/jikesRVM/core/descriptors/org.classpath.core.xml branches/jikesRVM/core/src/core/com/ibm/JikesRVM/classloader/VM_Method.java branches/jikesRVM/core/src/core/org/jnode/vm/JvmType.java branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/ClassDecoder.java branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/VmCP.java Added Paths: ----------- branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/NativeStubGenerator.java Modified: branches/jikesRVM/core/descriptors/org.classpath.core.xml =================================================================== --- branches/jikesRVM/core/descriptors/org.classpath.core.xml 2009-10-06 14:18:58 UTC (rev 5682) +++ branches/jikesRVM/core/descriptors/org.classpath.core.xml 2009-10-15 14:50:26 UTC (rev 5683) @@ -43,12 +43,13 @@ <export name="sun.security.acl.GroupImpl"/> <export name="sun.security.action.*"/> <export name="sun.security.jca.*"/> - <export name="sun.security.pkcs.ParsingException"/> + <export name="sun.security.pkcs.*"/> <export name="sun.security.provider.SecureRandom"/> <export name="sun.security.provider.PolicyFile"/> <export name="sun.security.provider.PolicyParser"/> + <export name="sun.security.timestamp.TimestampToken"/> <export name="sun.security.util.*"/> - <export name="sun.security.x509.X509CertImpl"/> + <export name="sun.security.x509.*"/> <export name="sun.misc.Service"/> <export name="sun.misc.ServiceConfigurationError"/> Modified: branches/jikesRVM/core/src/core/com/ibm/JikesRVM/classloader/VM_Method.java =================================================================== --- branches/jikesRVM/core/src/core/com/ibm/JikesRVM/classloader/VM_Method.java 2009-10-06 14:18:58 UTC (rev 5682) +++ branches/jikesRVM/core/src/core/com/ibm/JikesRVM/classloader/VM_Method.java 2009-10-15 14:50:26 UTC (rev 5683) @@ -127,7 +127,7 @@ int n = method.getBytecode().getExceptionHandlers().size(); if (n!=0) eMap = new VM_ExceptionHandlerMap(method.getBytecode().getExceptionHandlers(), dc, n); VmLineNumberMap jnodeLineMap = method.getBytecode().getLineNrs(); - int [] lineMap = new int[jnodeLineMap.getLength()]; + int [] lineMap = new int[jnodeLineMap == null ? 0 : jnodeLineMap.getLength()]; for (int i = 0; i < lineMap.length; i++) { int startPC = jnodeLineMap.getStartPCAt(i); int lineNumber = jnodeLineMap.getLineNrAt(i); Modified: branches/jikesRVM/core/src/core/org/jnode/vm/JvmType.java =================================================================== --- branches/jikesRVM/core/src/core/org/jnode/vm/JvmType.java 2009-10-06 14:18:58 UTC (rev 5682) +++ branches/jikesRVM/core/src/core/org/jnode/vm/JvmType.java 2009-10-15 14:50:26 UTC (rev 5683) @@ -259,7 +259,68 @@ return types; } + + /** + * Gets the argument signatures for a method signature. + * + * @param signature + * @return the argument type signatures as an array of strings + */ + public static String[] getArgumentSignatures(String signature) { + final int len = signature.length(); + final String[] types = new String[getArgumentCount(signature)]; + int cnt = 0; + int start; + for (int i = 1; i < len; i++) { + final String t; + switch (signature.charAt(i)) { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + case 'F': + case 'J': + case 'D': + t = Character.toString(signature.charAt(i)); + break; + case 'L': + start = i; + while (signature.charAt(i) != ';') { + i++; + } + t = signature.substring(start, i + 1); + break; + case '[': + start = i; + while (signature.charAt(i) == '[') { + i++; + } + if (signature.charAt(i) == 'L') { + while (signature.charAt(i) != ';') { + i++; + } + } + t = signature.substring(start, i + 1); + break; + case ')': + // the end + i = len; + t = null; + break; + default: + throw new IllegalArgumentException("Unknown type" + + signature.substring(i)); + } + if (t != null) { + types[cnt++] = t; + } + } + return types; + } + + /** * Gets the return type of a method signature. * * @param signature @@ -276,6 +337,17 @@ } /** + * Gets the return type signature of a method signature. + * + * @param signature + * @return the return type signature + */ + public static String getReturnSignature(String signature) { + final int endIdx = signature.indexOf(')'); + return signature.substring(endIdx + 1); + } + + /** * Test if the given internal type value a floating point type. * * @param type the type value Modified: branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/ClassDecoder.java =================================================================== --- branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/ClassDecoder.java 2009-10-06 14:18:58 UTC (rev 5682) +++ branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/ClassDecoder.java 2009-10-15 14:50:26 UTC (rev 5683) @@ -54,6 +54,16 @@ * @author Ewout Prangsma (ep...@us...) */ public final class ClassDecoder { + + // This constant controls how we deal with native method replacement. The first + // step is to find a suitable replacement method in the "Native<ClassName>" class. + // If that works, we then need to arrange that the replacement is used. The old + // way to do this was to simply attach the bytecodes for the replacement method + // to the native method and mark it as non-native. That doesn't work with Jikes + // (because of issues with using the right constant pool.) So the alternative is + // to generate bytecodes to turn the native method into a stub method that calls + // the replacement method. This is controlled by the following constant. + private static boolean GENERATE_STUB_METHODS = true; // ------------------------------------------ // VM ClassLoader Code @@ -497,7 +507,7 @@ nativeType = cl.loadClass(nativeClassName, false); } catch (ClassNotFoundException ex) { if (verbose) { - BootLog.error("Native class replacement (" + nativeClassName + BootLog.error("Native replacement class (" + nativeClassName + ") not found"); } return null; @@ -508,20 +518,26 @@ signature = "(" + Signature.toSignature(method.getDeclaringClass()) + signature.substring(1); } - final VmMethod nativeMethod = nativeType.getNativeMethodReplacement(method.getName(), signature); + final VmMethod replacementMethod = + nativeType.getNativeMethodReplacement(method.getName(), signature); - if (nativeMethod == null) { + if (replacementMethod == null) { if (verbose) { - BootLog.error("Native method replacement (" + method - + ") not found"); + BootLog.error("Native method replacement (" + nativeClassName + + "." + method + " with signature '" + signature + "') not found"); } return null; } - if (!nativeMethod.isStatic()) { + if (!replacementMethod.isStatic()) { throw new ClassFormatError( - "Native method replacement must be static"); + "Native method replacement must be static (" + nativeClassName + + "." + method + " with signature '" + signature + "')"); } - return nativeMethod.getBytecode(); + if (GENERATE_STUB_METHODS) { + return NativeStubGenerator.generate(method, replacementMethod); + } else { + return replacementMethod.getBytecode(); + } } /** Added: branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/NativeStubGenerator.java =================================================================== --- branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/NativeStubGenerator.java (rev 0) +++ branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/NativeStubGenerator.java 2009-10-15 14:50:26 UTC (rev 5683) @@ -0,0 +1,194 @@ +/* + * $Id: ClassDecoder.java 5682 2009-10-06 14:18:58Z crawley $ + * + * Copyright (C) 2003-2009 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 org.jnode.vm.JvmType; +import org.jnode.vm.bytecode.BytecodeWriter; + +/** + * This class is used to generate the bytecodes for native code replacement stubs. + * Only 32bit vms are supported for now, since we are using JvmType.getCategory(). + * + * @author cr...@jn... + */ +public class NativeStubGenerator { + + /** + * Create the bytecodes for a stub replacement for the native method. This + * will add entries to the method's constant pool as required. + * + * @param nativeMethod the native method to be replaced + * @param replacementMethod the (non-native) Java method that will do the work + * @return the bytecodes for the stub method to replace the native method. + */ + public static VmByteCode generate(VmMethod nativeMethod, VmMethod replacementMethod) { + VmCP cp = nativeMethod.getDeclaringClass().getCP(); + int classIndex = findOrAddConstClass( + cp, replacementMethod.getDeclaringClass().getName()); + int methodIndex = findOrAddConstMethod( + cp, classIndex, replacementMethod.getName(), replacementMethod.signature); + BytecodeWriter bw = new BytecodeWriter(1024); + // (We cannot use method.getNoArguments, method.getArgumentType because they + // depend on org.jnode.vm.classmgr.Signature, which in turn assumes that + // the primitive type classes have been initialized. Instead, we use the + // JvmType class to unpick the method signature.) + String signature = nativeMethod.getSignature(); + int slotOffset = nativeMethod.isStatic() ? 0 : JvmType.getCategory(JvmType.REFERENCE); + int slot = slotOffset; + int[] argTypes = JvmType.getArgumentTypes(signature); + // Generate a stack load instruction for each argument + if (!nativeMethod.isStatic()) { + genArgLoad(bw, JvmType.REFERENCE, 0); + } + for (int i = 0; i < argTypes.length; i++) { + genArgLoad(bw, argTypes[i], slot); + slot += JvmType.getCategory(argTypes[i]); + } + // Generate the invoke instruction to call the replacement method + bw.invokestatic(methodIndex); + // Generate the return instruction depending on the return type + int returnType = JvmType.getReturnType(signature); + int returnTypeSize = JvmType.getCategory(returnType); + switch (returnType) { + case JvmType.BOOLEAN: + case JvmType.BYTE: + case JvmType.CHAR: + case JvmType.SHORT: + case JvmType.INT: + bw.ireturn(); + break; + case JvmType.LONG: + bw.lreturn(); + break; + case JvmType.FLOAT: + bw.freturn(); + break; + case JvmType.DOUBLE: + bw.dreturn(); + break; + case JvmType.REFERENCE: + bw.areturn(); + break; + default: + bw.return_(); + } + int endPos = bw.getLength(); + // Prepare a locals table with an entry for each argument. + int localOffset; + VmLocalVariable[] locals; + if (nativeMethod.isStatic()) { + localOffset = 0; + locals = new VmLocalVariable[argTypes.length]; + } else { + localOffset = 1; + locals = new VmLocalVariable[argTypes.length + localOffset]; + locals[0] = new VmLocalVariable( + (char) 0, (char) endPos, (char) 0, (char) 0, (char) 0); + } + slot = slotOffset; + String[] argSignatures = JvmType.getArgumentSignatures(signature); + for (int i = 0; i < argTypes.length; i++) { + int nameIndex = findOrAddUTF8(cp, "arg" + i); + int descriptorIndex = findOrAddUTF8(cp, argSignatures[i]); + locals[i + localOffset] = new VmLocalVariable( + (char) 0, (char) endPos, (char) nameIndex, (char) descriptorIndex, (char) slot); + slot += JvmType.getCategory(argTypes[i]); + } + // Put it all together + return new VmByteCode(nativeMethod, bw.toByteBuffer(), locals.length, + Math.max(slot, returnTypeSize), + new VmInterpretedExceptionHandler[0], + new VmLineNumberMap(new char[]{0, 1}), + new VmLocalVariableTable(locals)); + } + + private static void genArgLoad(BytecodeWriter bw, int type, int slot) { + switch (type) { + case JvmType.BOOLEAN: + case JvmType.BYTE: + case JvmType.CHAR: + case JvmType.SHORT: + case JvmType.INT: + bw.iload(slot); + break; + case JvmType.LONG: + bw.lload(slot); + break; + case JvmType.FLOAT: + bw.fload(slot); + break; + case JvmType.DOUBLE: + bw.dload(slot); + break; + case JvmType.REFERENCE: + bw.aload(slot); + break; + default: + throw new AssertionError("Unexpected arg type (" + type + ")"); + } + } + + private static int findOrAddConstMethod(VmCP cp, int classIndex, String name, + String signature) { + VmConstClass classRef = cp.getConstClass(classIndex); + for (int i = 0; i < cp.getLength(); i++) { + Object obj = cp.getAny(i); + if (obj instanceof VmConstMethodRef) { + VmConstMethodRef ref = (VmConstMethodRef) obj; + if (ref.getClassName().equals(classRef.getClassName()) && + ref.getName().equals(name) && ref.getSignature().equals(signature)) { + return i; + } + } + } + VmConstMethodRef ref = new VmConstMethodRef(classRef, name, signature); + int index = cp.getUnusedIndex(); + cp.setConstMethodRef(index, ref); + return index; + } + + private static int findOrAddConstClass(VmCP cp, String name) { + for (int i = 0; i < cp.getLength(); i++) { + Object obj = cp.getAny(i); + if (obj instanceof VmConstClass) { + VmConstClass ref = (VmConstClass) obj; + if (ref.getClassName().equals(name)) { + return i; + } + } + } + VmConstClass ref = new VmConstClass(name); + int index = cp.getUnusedIndex(); + cp.setConstClass(index, ref); + return index; + } + + private static int findOrAddUTF8(VmCP cp, String name) { + for (int i = 0; i < cp.getLength(); i++) { + if (name.equals(cp.getAny(i))) { + return i; + } + } + int index = cp.getUnusedIndex(); + cp.setUTF8(index, name); + return index; + } +} Modified: branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/VmCP.java =================================================================== --- branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/VmCP.java 2009-10-06 14:18:58 UTC (rev 5682) +++ branches/jikesRVM/core/src/core/org/jnode/vm/classmgr/VmCP.java 2009-10-15 14:50:26 UTC (rev 5683) @@ -305,7 +305,7 @@ * @param index The index where to read * @param data The Object to write */ - private void set(int index, Object data) { + public void set(int index, Object data) { if (data == null) { throw new NullPointerException( "Cannot set a null data"); @@ -316,6 +316,15 @@ final void reset(int index) { cp[index] = null; } + + public int getUnusedIndex() { + if (used == cp.length) { + Object[] newCP = new Object[cp.length + 1]; + System.arraycopy(cp, 0, newCP, 0, cp.length); + cp = newCP; + } + return used++; + } public VmType getParentType() { return parentType; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |