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.jni;
014
015import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS;
016import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS;
017
018import org.jikesrvm.VM;
019import org.jikesrvm.classloader.RVMMethod;
020import org.jikesrvm.compilers.common.CompiledMethods;
021import org.jikesrvm.mm.mminterface.MemoryManager;
022import org.jikesrvm.runtime.BootRecord;
023import org.jikesrvm.runtime.Magic;
024import org.jikesrvm.runtime.RuntimeEntrypoints;
025import org.jikesrvm.scheduler.RVMThread;
026import org.vmmagic.pragma.Entrypoint;
027import org.vmmagic.pragma.Inline;
028import org.vmmagic.pragma.NoInline;
029import org.vmmagic.pragma.NonMoving;
030import org.vmmagic.pragma.NonMovingAllocation;
031import org.vmmagic.pragma.Uninterruptible;
032import org.vmmagic.pragma.Unpreemptible;
033import org.vmmagic.pragma.Untraced;
034import org.vmmagic.unboxed.Address;
035import org.vmmagic.unboxed.AddressArray;
036import org.vmmagic.unboxed.ObjectReference;
037import org.vmmagic.unboxed.Offset;
038
039/**
040 * A JNIEnvironment is created for each Java thread.
041 */
042@NonMoving
043public final class JNIEnvironment {
044
045  /**
046   * initial size for JNI refs, later grow as needed
047   */
048  protected static final int JNIREFS_ARRAY_LENGTH = 100;
049
050  /**
051   * sometimes we put stuff onto the jnirefs array bypassing the code
052   * that makes sure that it does not overflow (evil assembly code in the
053   * jni stubs that would be painful to fix).  So, we keep some space
054   * between the max value in JNIRefsMax and the actual size of the
055   * array.  How much is governed by this field.
056   */
057  protected static final int JNIREFS_FUDGE_LENGTH = 50;
058
059  /**
060   * This is the shared JNI function table used by native code
061   * to invoke methods in @link{JNIFunctions}.
062   */
063  @Untraced // because bootloader code must be able to access it
064  public static FunctionTable JNIFunctions;
065
066  /**
067   * For the 64-bit PowerPC ELF ABI we need a linkage triple instead of just
068   * a function pointer.
069   * This is an array of such triples that matches JNIFunctions.
070   */
071  public static LinkageTripletTable linkageTriplets;
072
073  /**
074   * This is the pointer to the shared JNIFunction table.
075   * When we invoke a native method, we adjust the pointer we
076   * pass to the native code such that this field is at offset 0.
077   * In other words, we turn a JNIEnvironment into a JNIEnv*
078   * by handing the native code an interior pointer to
079   * this object that points directly to this field.
080   */
081  @SuppressWarnings({"unused", "UnusedDeclaration"})
082  // used by native code
083  @Entrypoint
084  private final Address externalJNIFunctions =
085      VM.BuildForPower64ELF_ABI ? Magic.objectAsAddress(linkageTriplets) : Magic.objectAsAddress(JNIFunctions);
086
087  /**
088   * For saving processor register on entry to native,
089   * to be restored on JNI call from native
090   */
091  @Entrypoint
092  @Untraced
093  protected RVMThread savedTRreg;
094
095  /**
096   * For saving JTOC register on entry to native,
097   * to be restored on JNI call from native (only used on PowerPC)
098   */
099  @Entrypoint
100  @Untraced
101  private final Address savedJTOC = VM.BuildForPowerPC ? Magic.getTocPointer() : Address.zero();
102
103  /**
104   * When native code doesn't maintain a base pointer we can't chain
105   * through the base pointers when walking the stack. This field
106   * holds the basePointer on entry to the native code in such a case,
107   * and is pushed onto the stack if we re-enter Java code (e.g. to
108   * handle a JNI function). This field is currently only used on IA32.
109   */
110  @Entrypoint
111  private Address basePointerOnEntryToNative = Address.fromIntSignExtend(0xF00BAAA1);
112
113  /**
114   * When transitioning between Java and C and back, we may want to stop a thread
115   * returning into Java and executing mutator code when a GC is in progress.
116   * When in C code, the C code may never return. In these situations we need a
117   * frame pointer at which to begin scanning the stack. This field holds this
118   * value. NB. these fields don't chain together on the stack as we walk through
119   * native frames by knowing their return addresses are outside of our heaps
120   */
121  @Entrypoint
122  private  Address JNITopJavaFP;
123
124  /**
125   * Currently pending exception (null if none)
126   */
127  private Throwable pendingException;
128  private int hasPendingException;
129
130  /**
131   * true if the bottom stack frame is native,
132   * such as thread for CreateJVM or AttachCurrentThread
133   */
134  private final  boolean alwaysHasNativeFrame;
135
136  /**
137   * references passed to native code
138   */
139  @Entrypoint
140  @Untraced
141  public AddressArray JNIRefs;
142  private AddressArray JNIRefsShadow;
143
144  /**
145   * Offset of current top ref in JNIRefs array
146   */
147  @Entrypoint
148  public int JNIRefsTop;
149
150  /**
151   * Offset of end (last entry) of JNIRefs array
152   */
153  @Entrypoint
154  protected int JNIRefsMax;
155
156  /**
157   * Previous frame boundary in JNIRefs array.
158   * NB unused on IA32
159   */
160  @Entrypoint
161  public int JNIRefsSavedFP;
162
163  /**
164   * Initialize a thread specific JNI environment.
165   */
166  @NonMovingAllocation
167  public JNIEnvironment() {
168    JNIRefs = JNIRefsShadow = AddressArray.create(JNIREFS_ARRAY_LENGTH + JNIREFS_FUDGE_LENGTH);
169    JNIRefsTop = 0;
170    JNIRefsSavedFP = 0;
171    JNIRefsMax = (JNIREFS_ARRAY_LENGTH - 1) << LOG_BYTES_IN_ADDRESS;
172    alwaysHasNativeFrame = false;
173  }
174
175  /*
176   * accessor methods
177   */
178  @Uninterruptible
179  public boolean hasNativeStackFrame() {
180    return alwaysHasNativeFrame || JNIRefsTop != 0;
181  }
182
183  @Uninterruptible
184  public Address topJavaFP() {
185    return JNITopJavaFP;
186  }
187
188  @Uninterruptible
189  public AddressArray refsArray() {
190    return JNIRefs;
191  }
192
193  @Uninterruptible
194  public int refsTop() {
195    return JNIRefsTop;
196  }
197
198  @Uninterruptible
199  public int savedRefsFP() {
200    return JNIRefsSavedFP;
201  }
202
203  /**
204   * Check push of reference can succeed
205   * @param ref object to be pushed
206   * @param canGrow can the JNI reference array be grown?
207   */
208  @Uninterruptible("May be called from uninterruptible code")
209  @NoInline
210  private void checkPush(Object ref, boolean canGrow) {
211    final boolean debug = true;
212    if (VM.VerifyAssertions) {
213      VM._assert(MemoryManager.validRef(ObjectReference.fromObject(ref)));
214    }
215    if (JNIRefsTop < 0) {
216      if (debug) {
217        VM.sysWriteln("JNIRefsTop=", JNIRefsTop);
218        VM.sysWriteln("JNIRefs.length=", JNIRefs.length());
219      }
220      VM.sysFail("unchecked push to negative offset!");
221    }
222    if ((JNIRefsTop >> LOG_BYTES_IN_ADDRESS) >= JNIRefs.length()) {
223      if (debug) {
224        VM.sysWriteln("JNIRefsTop=", JNIRefsTop);
225        VM.sysWriteln("JNIRefs.length=", JNIRefs.length());
226      }
227      VM.sysFail("unchecked pushes exceeded fudge length!");
228    }
229    if (!canGrow) {
230      if ((JNIRefsTop + BYTES_IN_ADDRESS) >= JNIRefsMax) {
231        if (debug) {
232          VM.sysWriteln("JNIRefsTop=", JNIRefsTop);
233          VM.sysWriteln("JNIRefsMax=", JNIRefsMax);
234        }
235        VM.sysFail("unchecked push can't grow JNI refs!");
236      }
237    }
238  }
239
240  /**
241   * Push a reference onto thread local JNIRefs stack.
242   * To be used by JNI functions when returning a reference
243   * back to JNI native C code.
244   * @param ref the object to put on stack
245   * @return offset of entry in JNIRefs stack
246   */
247  public int pushJNIRef(Object ref) {
248    if (ref == null) {
249      return 0;
250    } else {
251      if (VM.VerifyAssertions) checkPush(ref, true);
252      JNIRefsTop += BYTES_IN_ADDRESS;
253      if (JNIRefsTop >= JNIRefsMax) {
254        JNIRefsMax *= 2;
255        replaceJNIRefs(AddressArray.create((JNIRefsMax >> LOG_BYTES_IN_ADDRESS) + JNIREFS_FUDGE_LENGTH));
256      }
257      JNIRefs.set(JNIRefsTop >> LOG_BYTES_IN_ADDRESS, Magic.objectAsAddress(ref));
258      return JNIRefsTop;
259    }
260  }
261
262  /**
263   * Atomically copies and installs a new JNIRefArray.
264   *
265   * @param newrefs the new JNIRefArray
266   */
267  @Uninterruptible
268  private void replaceJNIRefs(AddressArray newrefs) {
269    for (int i = 0; i < JNIRefs.length(); i++) {
270      newrefs.set(i, JNIRefs.get(i));
271    }
272    JNIRefs = JNIRefsShadow = newrefs;
273  }
274
275  /**
276   * Push a JNI ref, used on entry to JNI
277   * NB only used for Intel
278   * @param ref reference to place on stack or value of saved frame pointer
279   * @param isRef false if the reference isn't a frame pointer
280   * @return new offset of current top in JNIRefs array or 0 if
281   *  the reference is zero and a framepointer
282   */
283  @Uninterruptible("Encoding arguments on stack that won't be seen by GC")
284  @Inline
285  private int uninterruptiblePushJNIRef(Address ref, boolean isRef) {
286    if (isRef && ref.isZero()) {
287      return 0;
288    } else {
289      if (VM.VerifyAssertions) checkPush(isRef ? Magic.addressAsObject(ref) : null, false);
290      // we count all slots so that releasing them is straight forward
291      JNIRefsTop += BYTES_IN_ADDRESS;
292      // ensure null is always seen as slot zero
293      JNIRefs.set(JNIRefsTop >> LOG_BYTES_IN_ADDRESS, ref);
294      return JNIRefsTop;
295    }
296  }
297
298  /**
299   * Save data and perform necessary conversions for entry into JNI.
300   * NB only used for Intel.
301   *
302   * @param encodedReferenceOffsets
303   *          bit mask marking which elements on the stack hold objects that need
304   *          encoding as JNI ref identifiers
305   */
306  @Uninterruptible("Objects on the stack won't be recognized by GC, therefore don't allow GC")
307  @Entrypoint
308  public void entryToJNI(int encodedReferenceOffsets) {
309    // Save processor
310    savedTRreg = Magic.getThreadRegister();
311
312    // Save frame pointer of calling routine, once so that native stack frames
313    // are skipped and once for use by GC
314    Address callersFP = Magic.getCallerFramePointer(Magic.getFramePointer());
315    basePointerOnEntryToNative = callersFP; // NB old value saved on call stack
316    JNITopJavaFP = callersFP;
317
318    if (VM.traceJNI) {
319      RVMMethod m =
320        CompiledMethods.getCompiledMethod(
321          Magic.getCompiledMethodID(callersFP)).getMethod();
322      VM.sysWrite("calling JNI from ");
323      VM.sysWrite(m.getDeclaringClass().getDescriptor());
324      VM.sysWrite(" ");
325      VM.sysWrite(m.getName());
326      VM.sysWrite(m.getDescriptor());
327      VM.sysWriteln();
328    }
329
330    // Save current JNI ref stack pointer
331    if (JNIRefsTop > 0) {
332      uninterruptiblePushJNIRef(Address.fromIntSignExtend(JNIRefsSavedFP), false);
333      JNIRefsSavedFP = JNIRefsTop;
334    }
335
336    // Convert arguments on stack from objects to JNI references
337    Address fp = Magic.getFramePointer();
338    Offset argOffset = Offset.fromIntSignExtend(5 * BYTES_IN_ADDRESS);
339    fp.store(uninterruptiblePushJNIRef(fp.loadAddress(argOffset),true), argOffset);
340    while (encodedReferenceOffsets != 0) {
341      argOffset = argOffset.plus(BYTES_IN_ADDRESS);
342      if ((encodedReferenceOffsets & 1) != 0) {
343        fp.store(uninterruptiblePushJNIRef(fp.loadAddress(argOffset), true), argOffset);
344      }
345      encodedReferenceOffsets >>>= 1;
346    }
347    // Transition processor from IN_JAVA to IN_JNI
348    RVMThread.enterJNIFromCallIntoNative();
349  }
350
351  /**
352   * Restore data, throw pending exceptions or convert return value for exit
353   * from JNI. NB only used for Intel.
354   *
355   * @param offset
356   *          offset into JNI reference tables of result
357   * @return Object encoded by offset or null if offset is 0
358   */
359  @Unpreemptible("Don't allow preemption when we're not in a sane state. " +
360  "Code can throw exceptions so not uninterruptible.")
361  @Entrypoint
362  public Object exitFromJNI(int offset) {
363    // Transition processor from IN_JNI to IN_JAVA
364    RVMThread.leaveJNIFromCallIntoNative();
365
366    // Restore JNI ref top and saved frame pointer
367    JNIRefsTop = 0;
368    if (JNIRefsSavedFP > 0) {
369      JNIRefsTop = JNIRefsSavedFP - BYTES_IN_ADDRESS;
370      JNIRefsSavedFP = JNIRefs.get(JNIRefsSavedFP >> LOG_BYTES_IN_ADDRESS).toInt();
371    }
372
373    // Throw and clear any pending exceptions
374    if (pendingException != null) {
375      throwPendingException();
376    }
377
378    // Lookup result
379    Object result;
380    if (offset == 0) {
381      result = null;
382    } else if (offset < 0) {
383      result = JNIGlobalRefTable.ref(offset);
384    } else {
385      result = Magic.addressAsObject(JNIRefs.get(offset >> LOG_BYTES_IN_ADDRESS));
386    }
387    return result;
388  }
389
390  /**
391   * Get a reference from the JNIRefs stack.
392   * @param offset in JNIRefs stack
393   * @return reference at that offset
394   */
395  public Object getJNIRef(int offset) {
396    if (offset == 0) {
397      return null;
398    } else if (offset < 0) {
399      return JNIGlobalRefTable.ref(offset);
400    } else {
401      if (offset > JNIRefsTop) {
402        VM.sysWrite("JNI ERROR: getJNIRef for illegal offset > TOP, ");
403        VM.sysWrite(offset);
404        VM.sysWrite("(top is ");
405        VM.sysWrite(JNIRefsTop);
406        VM.sysWrite(")\n");
407        if (VM.VerifyAssertions) {
408          VM.sysFail("getJNIRef called with illegal offset > TOP (see above)");
409        } else {
410          RVMThread.dumpStack();
411        }
412        return null;
413      }
414      return Magic.addressAsObject(JNIRefs.get(offset >> LOG_BYTES_IN_ADDRESS));
415    }
416  }
417
418  /**
419   * Remove a reference from the JNIRefs stack.
420   * @param offset in JNIRefs stack
421   */
422  public void deleteJNIRef(int offset) {
423    if (offset > JNIRefsTop) {
424      VM.sysWrite("JNI ERROR: getJNIRef for illegal offset > TOP, ");
425      VM.sysWrite(offset);
426      VM.sysWrite("(top is ");
427      VM.sysWrite(JNIRefsTop);
428      VM.sysWrite(")\n");
429    }
430
431    JNIRefs.set(offset >> LOG_BYTES_IN_ADDRESS, Address.zero());
432
433    if (offset == JNIRefsTop) JNIRefsTop -= BYTES_IN_ADDRESS;
434  }
435
436  /**
437   * Dump the JNIRefs stack to the sysWrite stream
438   */
439  @Uninterruptible
440  public void dumpJniRefsStack() {
441    int jniRefOffset = JNIRefsTop;
442    VM.sysWrite("\n* * dump of JNIEnvironment JniRefs Stack * *\n");
443    VM.sysWrite("* JNIRefs = ");
444    VM.sysWrite(Magic.objectAsAddress(JNIRefs));
445    VM.sysWrite(" * JNIRefsTop = ");
446    VM.sysWrite(JNIRefsTop);
447    VM.sysWrite(" * JNIRefsSavedFP = ");
448    VM.sysWrite(JNIRefsSavedFP);
449    VM.sysWrite(".\n*\n");
450    while (jniRefOffset >= 0) {
451      VM.sysWrite(jniRefOffset);
452      VM.sysWrite(" ");
453      VM.sysWrite(Magic.objectAsAddress(JNIRefs).plus(jniRefOffset));
454      VM.sysWrite(" ");
455      MemoryManager.dumpRef(JNIRefs.get(jniRefOffset >> LOG_BYTES_IN_ADDRESS).toObjectReference());
456      jniRefOffset -= BYTES_IN_ADDRESS;
457    }
458    VM.sysWrite("\n* * end of dump * *\n");
459  }
460
461  /**
462   * Record an exception as pending so that it will be delivered on the return
463   * to the Java caller;  clear the exception by recording null
464   * @param e  An exception or error
465   */
466  public void recordException(Throwable e) {
467    // don't overwrite the first exception except to clear it
468    if (pendingException == null || e == null) {
469      pendingException = e;
470      hasPendingException = (e != null) ? 1 : 0;
471    }
472  }
473
474  /**
475   * Return and clear the (known to be non-null) pending exception.
476   */
477  @Entrypoint
478  @Unpreemptible
479  public static void throwPendingException() {
480    JNIEnvironment me = RVMThread.getCurrentThread().getJNIEnv();
481    if (VM.VerifyAssertions) VM._assert(me.pendingException != null);
482    Throwable pe = me.pendingException;
483    me.pendingException = null;
484    me.hasPendingException = 0;
485    RuntimeEntrypoints.athrow(pe);
486  }
487
488  /**
489   * @return the pending exception
490   */
491  public Throwable getException() {
492    return pendingException;
493  }
494
495  /**
496   * Initialize the array of JNI functions.
497   * This function is called during bootimage writing.
498   *
499   * @param functions the function table to initialize
500   */
501  public static void initFunctionTable(FunctionTable functions) {
502    JNIFunctions = functions;
503    BootRecord.the_boot_record.JNIFunctions = functions;
504    if (VM.BuildForPower64ELF_ABI) {
505      // Allocate the linkage triplets in the bootimage too (so they won't move)
506      linkageTriplets = LinkageTripletTable.allocate(functions.length());
507      for (int i = 0; i < functions.length(); i++) {
508        linkageTriplets.set(i, AddressArray.create(3));
509      }
510    }
511  }
512
513  /**
514   * Initialization required during VM booting; only does something if
515   * we are on a platform that needs linkage triplets.
516   */
517  public static void boot() {
518    if (VM.BuildForPower64ELF_ABI) {
519      // fill in the TOC and IP entries for each linkage triplet
520      for (int i = 0; i < JNIFunctions.length(); i++) {
521        AddressArray triplet = linkageTriplets.get(i);
522        triplet.set(1, Magic.getTocPointer());
523        triplet.set(0, Magic.objectAsAddress(JNIFunctions.get(i)));
524      }
525    }
526  }
527}