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