/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.jikesrvm.jni;

import java.lang.ref.WeakReference;
import org.jikesrvm.VM;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.mm.mminterface.MemoryManager;
import org.vmmagic.pragma.Entrypoint;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.ObjectReference;
import org.vmmagic.unboxed.AddressArray;
import org.vmmagic.unboxed.Address;

/**
 * Weak Global References are global references (negative numbers), with the
 * 2^30 bit UNset.  Mask in the 2^30 bit to get the real index into the table.
 */
public class JNIGlobalRefTable {

  @Entrypoint
  public static AddressArray JNIGlobalRefs = AddressArray.create(100);
  private static int free = 1;

  static int newGlobalRef(Object referent) {
    if (VM.VerifyAssertions) VM._assert(MemoryManager.validRef(ObjectReference.fromObject(referent)));

    if (free >= JNIGlobalRefs.length()) {
      AddressArray newGlobalRefs = AddressArray.create(JNIGlobalRefs.length() * 2);
      copyAndReplaceGlobalRefs(newGlobalRefs);
    }

    JNIGlobalRefs.set(free, Magic.objectAsAddress(referent));
    return -free++;
  }

  @Uninterruptible
  private static void copyAndReplaceGlobalRefs(AddressArray newGlobalRefs) {
    for(int i=0; i < JNIGlobalRefs.length(); i++) {
      newGlobalRefs.set(i, JNIGlobalRefs.get(i));
    }
    JNIGlobalRefs = newGlobalRefs;
  }

  /* Weak references are returned with the STRONG_REF_BIT bit UNset.  */
  public static final int STRONG_REF_BIT = 1 << 30;

  static int newWeakRef(Object referent) {
    int gref = newGlobalRef(new WeakReference<Object>(referent));
    return gref & ~STRONG_REF_BIT;
  }

  static void deleteGlobalRef(int index) {
    if (VM.VerifyAssertions) VM._assert(!isWeakRef(index));
    JNIGlobalRefs.set(-index, Address.zero());
  }

  static void deleteWeakRef(int index) {
    if (VM.VerifyAssertions) VM._assert(isWeakRef(index));
    int gref = index | STRONG_REF_BIT;
    deleteGlobalRef(gref);
  }

  static Object globalRef(int index) {
    if (VM.VerifyAssertions) VM._assert(!isWeakRef(index));

    return Magic.addressAsObject(JNIGlobalRefs.get(-index));
  }

  static Object weakRef(int index) {
    if (VM.VerifyAssertions) VM._assert(isWeakRef(index));
    @SuppressWarnings("unchecked") // yes, we're being bad.
    WeakReference<Object> ref = (WeakReference<Object>) globalRef(index | STRONG_REF_BIT);
    return ref.get();
  }

  static Object ref(int index) {
    if (isWeakRef(index)) {
      return weakRef(index);
    } else {
      return globalRef(index);
    }
  }

  static boolean isWeakRef(int index) {
    return (index & STRONG_REF_BIT) == 0;
  }
}
