001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.jikesrvm.jni; 014 015 import org.jikesrvm.VM; 016 import static org.jikesrvm.SizeConstants.BYTES_IN_ADDRESS; 017 import org.jikesrvm.classloader.UTF8Convert; 018 import org.jikesrvm.runtime.Magic; 019 import org.jikesrvm.runtime.Memory; 020 import org.jikesrvm.util.StringUtilities; 021 import org.vmmagic.unboxed.Address; 022 import org.vmmagic.unboxed.Offset; 023 import org.vmmagic.unboxed.Word; 024 025 import java.nio.charset.CharacterCodingException; 026 import java.nio.charset.Charset; 027 import java.nio.charset.CharsetDecoder; 028 import java.nio.charset.CharsetEncoder; 029 import java.nio.charset.CodingErrorAction; 030 import java.nio.ByteBuffer; 031 import java.nio.CharBuffer; 032 033 /** 034 * Platform independent utility functions called from JNIFunctions 035 * (cannot be placed in JNIFunctions because methods 036 * there are specially compiled to be called from native). 037 * 038 * @see JNIFunctions 039 */ 040 public abstract class JNIGenericHelpers { 041 042 /** 043 * Compute the length of the given null-terminated string 044 * 045 * @param ptr address of string in memory 046 * @return the length of the string in bytes 047 */ 048 public static int strlen(Address ptr) { 049 int length=0; 050 // align address to size of machine 051 while (!ptr.toWord().and(Word.fromIntZeroExtend(BYTES_IN_ADDRESS - 1)).isZero()) { 052 byte bits = ptr.loadByte(Offset.fromIntZeroExtend(length)); 053 if (bits == 0) { 054 return length; 055 } 056 length++; 057 } 058 // Ascii characters are normally in the range 1 to 128, if we subtract 1 059 // from each byte and look if the top bit of the byte is set then if it is 060 // the chances are the byte's value is 0. Loop over words doing this quick 061 // test and then do byte by byte tests when we think we have the 0 062 Word onesToSubtract; 063 Word maskToTestHighBits; 064 if (VM.BuildFor32Addr) { 065 onesToSubtract = Word.fromIntZeroExtend(0x01010101); 066 maskToTestHighBits = Word.fromIntZeroExtend(0x80808080); 067 } else { 068 onesToSubtract = Word.fromLong(0x0101010101010101L); 069 maskToTestHighBits = Word.fromLong(0x8080808080808080L); 070 } 071 while (true) { 072 Word bytes = ptr.loadWord(Offset.fromIntZeroExtend(length)); 073 if(!bytes.minus(onesToSubtract).and(maskToTestHighBits).isZero()) { 074 if (VM.LittleEndian) { 075 for(int byteOff=0; byteOff < BYTES_IN_ADDRESS; byteOff++) { 076 if(bytes.and(Word.fromIntZeroExtend(0xFF)).isZero()) { 077 return length + byteOff; 078 } 079 bytes = bytes.rshl(8); 080 } 081 } else { 082 for(int byteOff=BYTES_IN_ADDRESS-1; byteOff >= 0; byteOff--) { 083 if(bytes.rshl(byteOff*8).and(Word.fromIntZeroExtend(0xFF)).isZero()) { 084 return length + (BYTES_IN_ADDRESS - 1 - byteOff); 085 } 086 } 087 } 088 } 089 length += BYTES_IN_ADDRESS; 090 } 091 } 092 /** 093 * Given an address in C that points to a null-terminated string, 094 * create a new Java byte[] with a copy of the string. 095 * 096 * @param stringAddress an address in C space for a string 097 * @return a new Java byte[] 098 */ 099 public static byte[] createByteArrayFromC(Address stringAddress) { 100 101 int length = strlen(stringAddress); 102 byte[] contents = new byte[length]; 103 Memory.memcopy(Magic.objectAsAddress(contents), stringAddress, length); 104 105 return contents; 106 } 107 108 /** 109 * Create a string from the given charset decoder and bytebuffer 110 */ 111 private static String createString(CharsetDecoder csd, ByteBuffer bbuf) throws CharacterCodingException { 112 char[] v; 113 int o; 114 int c; 115 CharBuffer cbuf = csd.decode(bbuf); 116 if(cbuf.hasArray()) { 117 v = cbuf.array(); 118 o = cbuf.position(); 119 c = cbuf.remaining(); 120 } else { 121 // Doubt this will happen. But just in case. 122 v = new char[cbuf.remaining()]; 123 cbuf.get(v); 124 o = 0; 125 c = v.length; 126 } 127 return java.lang.JikesRVMSupport.newStringWithoutCopy(v, o, c); 128 } 129 /** 130 * Given an address in C that points to a null-terminated string, 131 * create a new Java String with a copy of the string. 132 * 133 * @param stringAddress an address in C space for a string 134 * @return a new Java String 135 */ 136 public static String createStringFromC(Address stringAddress) { 137 if (VM.fullyBooted) { 138 try { 139 String encoding = System.getProperty("file.encoding"); 140 CharsetDecoder csd = Charset.forName(encoding).newDecoder(); 141 csd.onMalformedInput(CodingErrorAction.REPLACE); 142 csd.onUnmappableCharacter(CodingErrorAction.REPLACE); 143 ByteBuffer bbuf = 144 java.nio.JikesRVMSupport.newDirectByteBuffer(stringAddress, 145 strlen(stringAddress)); 146 return createString(csd, bbuf); 147 } catch(Exception ex){ 148 // Any problems fall through to default encoding 149 } 150 } 151 // Can't do real Char encoding until VM is fully booted. 152 // All Strings encountered during booting must be ascii 153 byte[] tmp = createByteArrayFromC(stringAddress); 154 return StringUtilities.asciiBytesToString(tmp); 155 } 156 /** 157 * Given an address in C that points to a null-terminated string, 158 * create a new UTF encoded Java String with a copy of the string. 159 * 160 * @param stringAddress an address in C space for a string 161 * @return a new Java String 162 */ 163 public static String createUTFStringFromC(Address stringAddress) { 164 final boolean USE_LIBRARY_CODEC = false; 165 byte[] tmp; 166 ByteBuffer bbuf; 167 if (VM.fullyBooted) { 168 try { 169 bbuf = java.nio.JikesRVMSupport.newDirectByteBuffer(stringAddress, 170 strlen(stringAddress)); 171 if (USE_LIBRARY_CODEC) { 172 CharsetDecoder csd = Charset.forName("UTF8").newDecoder(); 173 return createString(csd, bbuf); 174 } else { 175 return UTF8Convert.fromUTF8(bbuf); 176 } 177 } catch(Exception ex){ 178 // Any problems fall through to default encoding 179 } 180 } 181 // Can't do real Char encoding until VM is fully booted. 182 // All Strings encountered during booting must be ascii 183 tmp = createByteArrayFromC(stringAddress); 184 return StringUtilities.asciiBytesToString(tmp); 185 } 186 187 188 /** 189 * Convert a String into a a malloced region 190 */ 191 public static void createUTFForCFromString(String str, Address copyBuffer, int len) { 192 ByteBuffer bbuf = 193 java.nio.JikesRVMSupport.newDirectByteBuffer(copyBuffer, len); 194 195 final boolean USE_LIBRARY_CODEC = false; 196 if (USE_LIBRARY_CODEC) { 197 char[] strChars = java.lang.JikesRVMSupport.getBackingCharArray(str); 198 int strOffset = java.lang.JikesRVMSupport.getStringOffset(str); 199 int strLen = java.lang.JikesRVMSupport.getStringLength(str); 200 CharBuffer cbuf = CharBuffer.wrap(strChars, strOffset, strLen); 201 CharsetEncoder cse = Charset.forName("UTF8").newEncoder(); 202 cse.encode(cbuf, bbuf, true); 203 } else { 204 UTF8Convert.toUTF8(str, bbuf); 205 } 206 // store terminating zero 207 copyBuffer.store((byte)0, Offset.fromIntZeroExtend(len-1)); 208 } 209 210 /** 211 * A JNI helper function, to set the value pointed to by a C pointer 212 * of type (jboolean *). 213 * @param boolPtr Native pointer to a jboolean variable to be set. May be 214 * the NULL pointer, in which case we do nothing. 215 * @param val Value to set it to (usually TRUE) 216 * 217 */ 218 static void setBoolStar(Address boolPtr, boolean val) { 219 if (boolPtr.isZero()) { 220 return; 221 } 222 if (val) { 223 boolPtr.store((byte)1); 224 } else { 225 boolPtr.store((byte)0); 226 } 227 } 228 }