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.classloader;
014    
015    import org.jikesrvm.VM;
016    import org.jikesrvm.compilers.common.BootImageCompiler;
017    import org.jikesrvm.compilers.common.CompiledMethod;
018    import org.jikesrvm.compilers.common.RuntimeCompiler;
019    import org.jikesrvm.runtime.DynamicLibrary;
020    import org.jikesrvm.runtime.Entrypoints;
021    import org.vmmagic.pragma.Pure;
022    import org.vmmagic.unboxed.Address;
023    import org.vmmagic.unboxed.Offset;
024    
025    /**
026     * A native method of a java class.
027     */
028    public final class NativeMethod extends RVMMethod {
029    
030      /**
031       * the IP of the native procedure
032       */
033      private Address nativeIP;
034    
035      /**
036       * the TOC of the native procedure.
037       * Only used if VM.BuildForPowerOpenABI.
038       * TODO: Consider making a PowerOpen subclass of NativeMethod
039       *       and pushing this field down to it.  For now, just bloat up
040       *       all native methods by 1 slot.
041       */
042      private Address nativeTOC;
043    
044      /**
045       * Construct native method information
046       *
047       * @param declaringClass the RVMClass object of the class that
048       *                       declared this method.
049       * @param memRef the canonical memberReference for this member.
050       * @param modifiers modifiers associated with this member.
051       * @param exceptionTypes exceptions thrown by this method.
052       * @param signature generic type of this method.
053       * @param annotations array of runtime visible annotations
054       * @param parameterAnnotations array of runtime visible parameter annotations
055       * @param annotationDefault value for this annotation that appears
056       */
057      NativeMethod(TypeReference declaringClass, MemberReference memRef, short modifiers,
058                      TypeReference[] exceptionTypes, Atom signature, RVMAnnotation[] annotations,
059                      RVMAnnotation[][] parameterAnnotations, Object annotationDefault) {
060        super(declaringClass,
061              memRef,
062              modifiers,
063              exceptionTypes,
064              signature,
065              annotations,
066              parameterAnnotations,
067              annotationDefault);
068      }
069    
070      @Override
071      protected synchronized CompiledMethod genCode() {
072        if (isSysCall()) {
073          // SysCalls are just place holder methods, the compiler
074          // generates a call to the first argument - the address of a C
075          // function
076          Entrypoints.sysCallMethod.compile();
077          return Entrypoints.sysCallMethod.getCurrentCompiledMethod();
078        } else if (!resolveNativeMethod()) {
079          // if fail to resolve native, get code to throw unsatifiedLinkError
080          Entrypoints.unimplementedNativeMethodMethod.compile();
081          return Entrypoints.unimplementedNativeMethodMethod.getCurrentCompiledMethod();
082        } else {
083          if (VM.writingBootImage) {
084            return BootImageCompiler.compile(this);
085          } else {
086            return RuntimeCompiler.compile(this);
087          }
088        }
089      }
090    
091      /**
092       * Get the native IP for this method
093       */
094      public Address getNativeIP() {
095        return nativeIP;
096      }
097    
098      /**
099       * get the native TOC for this method
100       */
101      public Address getNativeTOC() {
102        if (VM.BuildForPowerOpenABI) {
103          return nativeTOC;
104        } else {
105          return Address.zero();
106        }
107      }
108    
109      /**
110       * replace a character in a string with a string
111       */
112      @Pure
113      private String replaceCharWithString(String originalString, char targetChar, String replaceString) {
114        int first = originalString.indexOf(targetChar);
115        int next = originalString.indexOf(targetChar, first + 1);
116        if (first != -1) {
117          StringBuilder returnString  = new StringBuilder(originalString.substring(0, first));
118          returnString.append(replaceString);
119          while (next != -1) {
120            returnString.append(originalString.substring(first + 1, next));
121            returnString.append(replaceString);
122            first = next;
123            next = originalString.indexOf(targetChar, next + 1);
124          }
125          returnString.append(originalString.substring(first + 1));
126          return returnString.toString();
127        } else {
128          return originalString;
129        }
130      }
131    
132      /**
133       * Compute the mangled name of the native routine: Java_Class_Method_Sig
134       */
135      @Pure
136      private String getMangledName(boolean sig) {
137        String mangledClassName, mangledMethodName;
138        String className = getDeclaringClass().toString();
139        String methodName = getName().toString();
140    
141        // Mangled Class name
142        // Special case: underscore in class name
143        mangledClassName = replaceCharWithString(className, '_', "_1");
144        // Special case: dollar in class name
145        mangledClassName = replaceCharWithString(mangledClassName, '$', "_00024");
146    
147        // Mangled Method name
148        // Special case: underscore in method name
149        //   class._underscore  -> class__1underscore
150        //   class.with_underscore  -> class_with_1underscore
151        mangledMethodName = replaceCharWithString(methodName, '_', "_1");
152    
153        if (sig) {
154          String sigName = getDescriptor().toString();
155          sigName = sigName.substring(sigName.indexOf('(') + 1, sigName.indexOf(')'));
156          sigName = replaceCharWithString(sigName, '[', "_3");
157          sigName = replaceCharWithString(sigName, ';', "_2");
158          sigName = sigName.replace('/', '_');
159          mangledMethodName += "__" + sigName;
160        }
161    
162        String mangledName = "Java_" + mangledClassName + "_" + mangledMethodName;
163        mangledName = mangledName.replace('.', '_');
164        // VM.sysWrite("getMangledName:  " + mangledName + " \n");
165    
166        return mangledName;
167      }
168    
169      private boolean resolveNativeMethod() {
170        if (!nativeIP.isZero()) {
171          // method has already been resolved via registerNative.
172          return true;
173        }
174    
175        final String nativeProcedureName = getMangledName(false);
176        final String nativeProcedureNameWithSignature = getMangledName(true);
177    
178        Address symbolAddress = DynamicLibrary.resolveSymbol(nativeProcedureNameWithSignature);
179        if (symbolAddress.isZero()) {
180          symbolAddress = DynamicLibrary.resolveSymbol(nativeProcedureName);
181        }
182    
183        if (symbolAddress.isZero()) {
184          // native procedure not found in library
185          return false;
186        } else {
187          if (VM.BuildForPowerOpenABI) {
188            nativeIP = symbolAddress.loadAddress();
189            nativeTOC = symbolAddress.loadAddress(Offset.fromIntSignExtend(BYTES_IN_ADDRESS));
190          } else {
191            nativeIP = symbolAddress;
192          }
193          return true;
194        }
195      }
196    
197      /**
198       * Registers a native method
199       * @param symbolAddress address of native function that implements the method
200       */
201      public synchronized void registerNativeSymbol(Address symbolAddress) {
202        if (VM.BuildForPowerOpenABI) {
203          nativeIP = symbolAddress.loadAddress();
204          nativeTOC = symbolAddress.loadAddress(Offset.fromIntSignExtend(BYTES_IN_ADDRESS));
205        } else {
206          nativeIP = symbolAddress;
207        }
208        replaceCompiledMethod(null);
209      }
210    
211      /**
212       * Unregisters a native method
213       */
214      public synchronized void unregisterNativeSymbol() {
215        if (VM.BuildForPowerOpenABI) {
216          nativeIP = Address.zero();
217          nativeTOC = Address.zero();
218        } else {
219          nativeIP = Address.zero();
220        }
221        replaceCompiledMethod(null);
222      }
223    }