Package erjang.m.erlang

Source Code of erjang.m.erlang.ErlBif

/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2009 by Trifork
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

package erjang.m.erlang;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.CRC32;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicStampedReference;

import kilim.Pausable;
import kilim.Task;
import erjang.BIF;
import erjang.EAtom;
import erjang.EBinary;
import erjang.EBitString;
import erjang.ECons;
import erjang.EDouble;
import erjang.EFun;
import erjang.EIOListVisitor;
import erjang.EInteger;
import erjang.EModule;
import erjang.EModuleLoader;
import erjang.EModuleManager;
import erjang.ENode;
import erjang.ENumber;
import erjang.EObject;
import erjang.EPID;
import erjang.EPeer;
import erjang.EPort;
import erjang.EProc;
import erjang.ERT;
import erjang.ESeq;
import erjang.ESmall;
import erjang.EString;
import erjang.ETuple;
import erjang.ETuple2;
import erjang.ETuple3;
import erjang.ErlangError;
import erjang.ErlangExit;
import erjang.ErlangException;
import erjang.ErlangRaise;
import erjang.ErlangThrow;
import erjang.FunID;
import erjang.Module;
import erjang.NIF;
import erjang.NotImplemented;
import erjang.BIF.Type;
import erjang.beam.BIFUtil;
import erjang.beam.BuiltInFunction;
import erjang.driver.IO;
import erjang.m.ets.EMatchSpec;
import erjang.m.java.JavaObject;

/** bifs for the module erlang */
@Module("erlang")
public class ErlBif {

    /**
   *
   */
  private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC");
  private static Logger log = Logger.getLogger("erlang");
  private static EAtom am_wall_clock = EAtom.intern("wall_clock");
  private static EAtom am_reductions = EAtom.intern("reductions");
  private static EAtom am_exact_reductions = EAtom.intern("exact_reductions");
  private static EAtom am_garbage_collection = EAtom.intern("garbage_collection");
  private static EAtom am_runtime = EAtom.intern("runtime");
  private static EAtom am_nif_error = EAtom.intern("nif_error");
  private static final EAtom am_run_queue = EAtom.intern("run_queue");
  private static EAtom am_load_failed = EAtom.intern("load_failed");
  private static EAtom am_on_load = EAtom.intern("on_load");
  private static Field CRC32_crc;
 
  @BIF
  static EObject apply(EProc proc, EObject fun, EObject args) throws Pausable {
    ESeq a = args.testSeq();
    if (a==null) throw ERT.badarg(fun,args);
   
    EObject res;
    EFun f = fun.testFunction();
    if (f != null) {
      res = apply_last(proc, f, a);
    } else {

      ETuple t = fun.testTuple();
      if (t == null) {
        throw ERT.badfun(fun);
      }
 
      ETuple2 t2 = ETuple2.cast(t);
      if (t2 == null) {
        throw ERT.badarg(fun,args);
      }
     
      EAtom mn = t2.elem1.testAtom();
      EAtom fn = t2.elem2.testAtom();
     
      FunID funspec;
      f = EModuleManager.resolve(funspec=new FunID(mn,fn,a.length()));
     
      if (f == null) {
        throw ERT.undef(funspec, a.toArray());
      }
     
      res = apply_last(proc, f, a);
    }
   
    while (res == EProc.TAIL_MARKER) {
      res = proc.tail.go(proc);
    }
   
    return res;
  }
 
  @BIF
  public static EBinary list_to_binary(EObject val) {
    EString es;
    if ((es = val.testString()) != null) {
      return es.asBitString();
    }

    ECons cons = val.testCons();
    if (cons == null) throw ERT.badarg(val);
    List<ByteBuffer> out = new ArrayList<ByteBuffer>();
    if (!cons.collectIOList(out)) {
      throw ERT.badarg(val);
    }

    if (out.size() == 1) {
      ByteBuffer b = out.get(0);
      return EBinary.make(b);
    }
   
    int length = 0;
    for (int i = 0; i < out.size(); i++) {
      length += out.get(i).remaining();
    }

    byte[] all = new byte[length];
    int pos = 0;
    for (int i = 0; i < out.size(); i++) {
      ByteBuffer bb = out.get(i);
      int len = bb.remaining();
      bb.get(all, pos, len);
      pos += len;
    }
   
    assert(pos == length);
   
    return new EBinary(all);
  }

  @BIF
  static ENumber crc32(EObject io_list) {
    CRC32 crc = new CRC32();
    EIOListVisitor.update(io_list, crc);
    return ERT.box(crc.getValue());
  }
 
  static {
    try {
      CRC32_crc = CRC32.class.getDeclaredField("crc");
      CRC32_crc.setAccessible(true);
    } catch (NoSuchFieldException | SecurityException e) {
      throw new RuntimeException(e);
    }
  }
 
  @BIF
  static ENumber crc32(EObject num, EObject io_list) {
    EInteger init;
    long val;
    if (((init=num.testInteger()) == null)
        || (val = init.longValue()) != (val & 0xffffffffL)) {
      throw ERT.badarg(num, io_list);
    }
   
    CRC32 crc = new CRC32();
    try {
      CRC32_crc.setInt(crc, (int)val);
    } catch (IllegalArgumentException | IllegalAccessException e) {
      throw new RuntimeException(e);
    }
    EIOListVisitor.update(io_list, crc);
    return ERT.box(crc.getValue());
  }
 
  @BIF
  static ESmall iolist_size(EObject val) {

    EBinary bin;
    if ((bin=val.testBinary()) != null) {
      return new ESmall(bin.byteSize());
    }
   
    EString str;
    if ((str=val.testString()) != null) {
      return new ESmall(str.length());
    }
   
   
    ECons seq;
    if ((seq = val.testCons()) == null) {
      throw ERT.badarg(val);
    }
   
    ArrayList<ByteBuffer> al = new ArrayList<ByteBuffer>();
    seq.collectIOList(al);

    int size = 0;
    for (int i = 0; i < al.size(); i++) {
      size += al.get(i).remaining();
    }
   
    return new ESmall(size);
  }

  @BIF
  public static EObject apply(EProc proc, EObject one, EObject two, EObject three) throws Pausable {
    EAtom mod = one.testAtom();
    ETuple t = one.testTuple();
    JavaObject jo = one.testJavaObject();
   
    EAtom fun = two.testAtom();
    ESeq  args = three.testSeq();
   
    if ((mod==null && t == null && jo == null)||fun==null||args==null) throw ERT.badarg(one, two, three);
   
    EFun f = ERT.resolve_fun(one, fun, args.length());
   
    EObject res = apply_last(proc, f, args);
   
    while (res == EProc.TAIL_MARKER) {
      res = proc.tail.go(proc);
    }

    return res;
  }

  public static EObject apply_last(EProc proc, EFun fun, ESeq args)
      throws Pausable {
    ESeq rargs = args.reverse();
    int len = args.length();
    switch(len) {   
    case 11:
      proc.arg10 = rargs.head(); rargs = rargs.tail();
    case 10:
      proc.arg9 = rargs.head(); rargs = rargs.tail();
    case 9:
      proc.arg8 = rargs.head(); rargs = rargs.tail();
    case 8:
      proc.arg7 = rargs.head(); rargs = rargs.tail();
    case 7:
      proc.arg6 = rargs.head(); rargs = rargs.tail();
    case 6:
      proc.arg5 = rargs.head(); rargs = rargs.tail();
    case 5:
      proc.arg4 = rargs.head(); rargs = rargs.tail();
    case 4:
      proc.arg3 = rargs.head(); rargs = rargs.tail();
    case 3:
      proc.arg2 = rargs.head(); rargs = rargs.tail();
    case 2:
      proc.arg1 = rargs.head(); rargs = rargs.tail();
    case 1:
      proc.arg0 = rargs.head();
    case 0:
      break;
     
    default:
      return fun.apply(proc, args);
    }
   
    proc.tail = fun;
    return EProc.TAIL_MARKER;
  }
 
  @BIF
 
  static public EPID self(EProc proc) {
    if (proc == null) {
      log.severe("Houston, we have a problem.");
    }
    return proc.self_handle();
  }

    private static final AtomicStampedReference<ETuple> cachedDate = new AtomicStampedReference(null, 0);
  static final int MILLIS_PER_MINUTE = 60 * 1000;

  @BIF
  static public ETuple date() {
    // Calculating the date using GregorianCalendar is rather slow, so we cache.
    // As stamp, we use 'midnight at the end of the day', in minutes since Epoch.
      long millis = System.currentTimeMillis();
      int curMinutes = (int)(millis / MILLIS_PER_MINUTE);
    int[] cacheValidUntilMinutes = new int[1];
    ETuple cachedResult = cachedDate.get(cacheValidUntilMinutes);
    if (curMinutes < cacheValidUntilMinutes[0&& cachedResult != null) { // Still valid
      return cachedResult;
    }

    // Cache is invalid.
    // Calculate the current date:
    GregorianCalendar cal = new GregorianCalendar();
    int year = cal.get(Calendar.YEAR);
    int month = cal.get(Calendar.MONTH)+1;
    int day = cal.get(Calendar.DAY_OF_MONTH);

    ETuple result = ETuple.make(ERT.box(year), ERT.box(month), ERT.box(day));

    // Calculate valid-until, i.e. next midnight:
    cal.add(Calendar.DAY_OF_YEAR, 1);
    cal.set(Calendar.HOUR, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    int newValidUntilMinutes =  (int)(cal.getTime().getTime() / MILLIS_PER_MINUTE);
    // Update cache (or try to):
    cachedDate.weakCompareAndSet(cachedResult, result,
                   cacheValidUntilMinutes[0], newValidUntilMinutes);

    return result;
  }

  @BIF
  static public ETuple time() {
    GregorianCalendar cal = new GregorianCalendar();
    int hour   = cal.get(Calendar.HOUR_OF_DAY);
    int minute = cal.get(Calendar.MINUTE);
    int second = cal.get(Calendar.SECOND);
   
    return ETuple.make(ERT.box(hour), ERT.box(minute), ERT.box(second));
  }

  @BIF
  static public EString integer_to_list(EObject obj) {
    EInteger num;
    if ((num = obj.testInteger()) != null) {
      return new EString(num.toString());
    }
    throw ERT.badarg(obj);
  }

  @BIF
 
  static public EAtom list_to_atom(EObject obj) {
    EString es;
    if ((es = obj.testString()) != null) {
      return EAtom.intern(es.stringValue());
    }
    throw ERT.badarg(obj);
  }

  @BIF
 
  static public EPID list_to_pid(EObject obj) {
    ECons list;
    if ((list = obj.testCons()) != null) {
      ESeq s = EString.make(list);
      return ERT.loopkup_pid(s);
    }
    throw ERT.badarg(obj);
  }

  @BIF
 
  static public EString pid_to_list(EObject obj) {
    EPID pid;
    if ((pid = obj.testPID()) != null) {
      return pid.getName();
    }
    throw ERT.badarg(obj);
  }

  @BIF
 
  static public EString port_to_list(EObject obj) {
    EPort port;
    if ((port = obj.testPort()) != null) {
      return port.getName();
    }
    throw ERT.badarg(obj);
  }

  @BIF 
  static public EBinary float_to_binary(EObject obj) {
    EDouble value;
    if ((value = obj.testFloat()) != null) {
      return new EBinary(value.format(ERT.NIL).getBytes(IO.ISO_LATIN_1));
    }
    throw ERT.badarg(obj);
  }

  @BIF 
  static public EBinary float_to_binary(EObject obj, EObject options) {
    EDouble value;
    if ((value = obj.testFloat()) != null) {
      return new EBinary(value.format(options).getBytes(IO.ISO_LATIN_1));
    }
    throw ERT.badarg(obj, options);
  }

  @BIF 
  static public EString float_to_list(EObject obj) {
    EDouble value;
    if ((value = obj.testFloat()) != null) {
      return new EString(value.format(ERT.NIL));
    }
    throw ERT.badarg(obj);
  }

  @BIF
  static public EString float_to_list(EObject obj, EObject options) {
    EDouble value;
    if ((value = obj.testFloat()) != null) {
      return new EString(value.format(options));
    }
    throw ERT.badarg(obj, options);
  }

  @BIF
  public static EDouble binary_to_float(EObject obj) {
    EBinary bin = obj.testBinary();
    if (bin != null) {
      try {
        double d = Double.parseDouble( new String(bin.getByteArray(), IO.ISO_LATIN_1) );
        return new EDouble(d);
      } catch (NumberFormatException e) {
        // ignore //
      }
    }
    throw ERT.badarg(obj);
  }
 
  @BIF
 
  static public EObject error(EObject reason) {
    throw new ErlangError(reason);
  }

  @BIF
 
  static public EObject error(EObject reason, EObject args) {
    ESeq aseq = args.testSeq();
    if (aseq == null) throw ERT.badarg(reason, args);
    throw new ErlangError(reason, aseq.toArray());
  }

  @BIF(name="throw")
 
  static public EObject throw_ex(EObject reason) {
    throw new ErlangThrow(reason);
  }

  @BIF
 
  static public ESeq get_module_info(EObject mod) {
    // TODO: get all the attributes from the beam code
    ESeq res = ERT.NIL;
    res = res.cons(new ETuple2(ERT.am_compile,
           get_module_info(mod, ERT.am_compile)));

    res = res.cons(new ETuple2(ERT.am_attributes,
           get_module_info(mod, ERT.am_attributes)));

    res = res.cons(new ETuple2(ERT.am_exports,
           get_module_info(mod, ERT.am_exports)));

    return res;
  }

  @BIF
 
  static public EObject get_module_info(EObject mod, EObject key) {
    EAtom m = mod.testAtom();
    EAtom k = key.testAtom();
   
    if (m == null || k == null) {
      throw ERT.badarg(mod, key);
    }

    if (k == ERT.am_attributes) {
      return EModuleManager.get_attributes(m);
    }
   
    if (k == ERT.am_compile) {
      return EModuleManager.get_compile(m);
    }
   
    if (k == ERT.am_exports) {
      return EModuleManager.get_exports(m);
    }
   
    if (k == ERT.am_module) {
      return mod;
    }

    return ERT.am_undefined;
  }

  public static final String[] PRE_LOADED_MODULES = new String[] {
    "erlang",
    "erl_prim_loader",
    "init",
    "otp_ring0",
    "prim_file",
    "prim_inet",
    "prim_zip",
    "zlib"
  };
 
  @BIF
  public static ESeq pre_loaded() {
    ESeq res = ERT.NIL;
    for (int i = 0; i < PRE_LOADED_MODULES.length; i++) {
      res = res.cons(EAtom.intern(PRE_LOADED_MODULES[i]));
    }
    return res;
  }

  @BIF
  public static EObject garbage_collect() {
    System.gc();
    return ERT.TRUE;
  }
 
  @BIF
  public static EObject garbage_collect(EObject pid) {
    System.gc();
    return ERT.TRUE;
  }
 
  @BIF
  static public ETuple setelement(EObject a1, EObject a2, EObject term) {
    ETuple t = a2.testTuple();
    ESmall i = a1.testSmall();
    if (t == null || i == null) throw ERT.badarg(a1,a2,term);
    return t.setelement(i.value, term);
  }

  /**
   * Optimized version which does not test the types of arguments.
   * Used in case the type-analysis can infer the exact argument types
   * (which is the case for BEAM code generated for record-update).
   * */
  @BIF
  static public ETuple setelement(int index, ETuple a2, EObject term) {
    return a2.setelement(index, term);
  }

  @BIF
  static public ETuple setelement(ESmall index, ETuple a2, EObject term) {
    return a2.setelement(index.value, term);
  }

  @BIF(type = Type.GUARD, name = "element")
  static public EObject element$g(EObject idx, EObject tup) {
    ESmall i = idx.testSmall();
    ETuple t = tup.testTuple();
    if (i == null || t == null) {
      return null;
    }
    if (i.value > t.arity())
      return null;

    return t.elm(i.value);
  }

  @BIF
  static public EObject element(EObject idx, EObject tup) {
    ESmall i = idx.testSmall();
    ETuple t = tup.testTuple();
    if (i == null || t == null || i.value < 1 || i.value > t.arity()) {
      throw ERT.badarg(idx, tup);
    }

    return t.elm(i.asInt());
  }

  /*
  @BIF
 
  static public EObject element(ESmall idx, EObject obj) {
    ETuple tup;
    if ((tup = obj.testTuple()) != null && tup.arity() >= idx.value) {
      return tup.elm(idx.value);
    }
    throw ERT.badarg(idx, obj);
  }

  @BIF
  static public EObject element(ESmall idx, ETuple tup) {
    if (tup.arity() >= idx.value) {
      return tup.elm(idx.value);
    }
    throw ERT.badarg(idx, tup);
  }
  */

  @BIF
  static public EObject element(int idx, ETuple tup) {
    if (tup.arity() >= idx) {
      return tup.elm(idx);
    }
    throw ERT.badarg(new ESmall(idx), tup);
  }

  @BIF
  static public EObject element(ESmall sidx, ETuple tup) {
    int idx = sidx.value;
    if (tup.arity() >= idx) {
      return tup.elm(idx);
    }
    throw ERT.badarg(sidx, tup);
  }

  @BIF
  static public EObject element(int idx, EObject obj) {
    ETuple tup;
    if ((tup = obj.testTuple()) != null && tup.arity() >= idx) {
      return tup.elm(idx);
    }
    throw ERT.badarg(new ESmall(idx), obj);
  }

  /*
   * TODO: figure out why sofs.erl compiles to this with EInteger 1st arg
  @BIF
  static public EObject element(ESmall sidx, EObject obj) {
    int idx = sidx.value;
    ETuple tup;
    if ((tup = obj.testTuple()) != null && tup.arity() >= idx) {
      return tup.elm(idx);
    }
    throw ERT.badarg(new ESmall(idx), obj);
  }
*/
 
  @BIF
  static public EObject hd(ECons cell) {
    return cell.head();
  }

  @BIF(type = Type.GUARD, name = "hd")
  static public EObject hd$p(ECons cell) {
    return cell.head();
  }

  @BIF
  static public EObject hd(EObject cell) {
    ECons cons;
    if ((cons = cell.testNonEmptyList()) != null) {
      return cons.head();
    }
    throw ERT.badarg(cell);
  }

  @BIF
  static public EObject tl(EObject cell) {
    ECons cons;
    if ((cons = cell.testNonEmptyList()) != null) {
      return cons.tail();
    }
    throw ERT.badarg(cell);
  }

  @BIF(type = Type.GUARD, name = "hd")
  static public EObject hd$p(EObject cell) {
    ECons cons;
    if ((cons = cell.testNonEmptyList()) != null) {
      return cons.head();
    }
    return null;
  }

  @BIF(type = Type.GUARD, name = "tl")
  static public EObject tl$p(EObject cell) {
    ECons cons;
    if ((cons = cell.testNonEmptyList()) != null) {
      return cons.tail();
    }
    return null;
  }

  @BIF
  static public EInteger length(EObject list) {
    ESeq seq;
    if ((seq = list.testSeq()) != null) {
      return ERT.box(seq.length());
    }
    throw ERT.badarg(list);
  }

//  @BIF
//  static public int length(ESeq list) {
//    return list.length();
//  }

  @BIF(name = "length", type = Type.GUARD)
  static public ESmall length$p(EObject list) {
    ESeq seq;
    if ((seq = list.testSeq()) != null) {
      return ERT.box(seq.length());
    }
    return null;
  }

  @BIF
  static public EObject whereis(EProc proc, EObject regname) {
    return ERT.whereis(regname);
  }

  static final long wall_clock0 = System.currentTimeMillis();
  private static final EAtom am_total = EAtom.intern("total");
 
  // TODO: figure out if this needs to be stored in the current process
  static long last_wall_clock = wall_clock0;
  static long last_reductions = 0;
  static long last_exact_reductions = 0;
  static long last_runtime = 0;

  @BIF
  static public EObject statistics(EProc proc, EObject spec) {
   
    if (spec == am_wall_clock) {
      long now = System.currentTimeMillis();
      long since_last = now-last_wall_clock;
      long since_epoch = now-wall_clock0;
      last_wall_clock = now;
      return ETuple.make(ERT.box(since_epoch), ERT.box(since_last));

    } else if (spec == am_reductions) {
      long current_reds = proc.get_reductions();
      long since_last = current_reds - last_reductions;
      last_reductions = current_reds;
     
      return new ETuple2(ERT.box(current_reds),ERT.box(since_last));     

    } else if (spec == am_exact_reductions) {
     
      long current_reds = 0L;
      for (EProc p : EProc.all_tasks.values()) {
        current_reds += p.get_reductions();
      }
      long since_last = current_reds - last_exact_reductions;
      last_exact_reductions = current_reds;
     
      return new ETuple2(ERT.box(current_reds),ERT.box(since_last));     

    } else if (spec == am_runtime) {
     
      ThreadMXBean b = ManagementFactory.getThreadMXBean();
      long[] ts = b.getAllThreadIds();
      long current_runtime = 0;
      for (int i = 0; i < ts.length; i++) {
        long thread_time = b.getThreadCpuTime(ts[i]);
        if (thread_time != -1) { // it died in the mean time...
          current_runtime += thread_time;
        }
      }
      long since_last = (current_runtime - last_runtime) / 1000000;
      last_runtime = current_runtime;
     
      return new ETuple2(ERT.box(current_runtime / 1000000),ERT.box(since_last))
     
    } else if (spec == am_garbage_collection) {
     
      List<GarbageCollectorMXBean> b = ManagementFactory.getGarbageCollectorMXBeans();

      long num_gcs = 0;
      long time_gcs = 0;

      for (GarbageCollectorMXBean bb : b) {
        num_gcs += bb.getCollectionCount();
        time_gcs += bb.getCollectionTime();
      }
     
      return ETuple.make(ERT.box(num_gcs), ERT.box(time_gcs), ERT.box(0));
     
    } else if (spec == am_run_queue) {
      return ERT.box(0);
    }
   
    throw new NotImplemented("erlang:statistics("+spec+")");
  }

  @BIF
  static public EObject put(EProc proc, EObject key, EObject value) {
    return proc.put(key, value);
  }

  @BIF
  static public EString name(EObject a1, EObject a2) {
    throw new NotImplemented();
  }


  // process dict

  @BIF
 
  static public ECons get(EProc proc) {
    return proc.get();
  }

  @BIF
 
  static public EObject get(EProc proc, EObject key) {
    return proc.get(key);
  }

  // floats
  /* These BIFs operate on floating point registers, which are not boxed. */

  @BIF(type = Type.ARITHBIF)
  static public double fdiv(double v1, double v2) {
    test_zero(v1, v2);
    return v1 / v2;
  }

  private static void test_zero(double v1, double v2) {
    if (v2 == 0.0)
      throw new ErlangError(ERT.AM_BADARITH, ERT.NIL.cons(v2).cons(v1));
  }

  @BIF(type = Type.ARITHBIF)
  static public double fsub(double v1, double v2) {
    return v1 - v2;
  }

  @BIF(type = Type.ARITHBIF)
  static public double fadd(double v1, double v2) {
    return v1 + v2;
  }

  @BIF(type = Type.ARITHBIF)
  static public double fmul(double v1, double v2) {
    return v1 * v2;
  }

  @BIF(type=Type.ARITHBIF)
  public static double fnegate(double val) { return -val; }


  // arithmetic

  @BIF(name = "+")
  static public ENumber not_neg(EObject v1) {
    ENumber n1;
    if ((n1 = v1.testNumber()) != null) {
      return n1;
    }
    throw ERT.badarg(v1);
  }
 
  @BIF(name = "-")
  static public ENumber neg(EObject v1) {
    ENumber n1;
    if ((n1 = v1.testNumber()) != null) {
      return n1.negate();
    }
    throw ERT.badarg(v1);
  }

  @BIF(name = "-", type=Type.GUARD)
  static public ENumber neg$g(EObject v1) {
    ENumber n1;
    if ((n1 = v1.testNumber()) != null) {
      return n1.negate();
    }
    return null;
  }

  @BIF
  static public EInteger div(EObject o1, EObject o2) {
    return o1.idiv(o2);
  }

  @BIF(name = "div")
  static public EInteger div(EObject v1, int v2) {
    return v1.idiv(v2);
  }

  @BIF(name = "div")
  static public EInteger div(ENumber n1, int v2) {
    return n1.idiv(v2);
  }

  @BIF(name = "div", type = Type.GUARD)
  static public EInteger div$g(EObject v1, EObject v2) {
    ENumber n1, n2;
    if ((n1 = v1.testInteger()) != null &&
        (n2 = v2.testInteger()) != null)
    {
      if (n2.erlangEquals(ESmall.ZERO)) return null;
      return n1.idiv(n2);
    }
    return null;
  }


  @BIF(name = "-")
  static public ENumber minus(EObject v1, int v2) {
    return v1.subtract(v2);
  }

  @BIF(name = "-")
  static public ENumber minus(int v1, int v2) {
    return ERT.box((long) v1 - (long) v2);
  }

  @BIF(name = "-")
  static public ENumber minus(EObject v1, EObject v2) {
    return v1.subtract(v2, false);
  }

  @BIF(name = "-", type = Type.GUARD)
  static public ENumber subtract$p(EObject v1, EObject v2) {
    return v1.subtract(v2, true);
  }

  @BIF(name = "/", type = Type.GUARD)
  static public ENumber divide$p(EObject v1, EObject v2) {
    ENumber n1;
    if ((n1 = v1.testNumber()) != null) {
      ENumber n2;
      if ((n2 = v2.testNumber()) != null) {
        if (n2.doubleValue() == 0.0)
          return null;
        return n1.divide(v2);
      }
    }
    return null;
  }

  @BIF(name = "/", type = Type.GUARD)
  static public ENumber divide$p(EObject v1, double d2) {
    ENumber n1;
    if (d2 != 0.0 && (n1 = v1.testNumber()) != null) {
      return n1.divide(d2);
    }
    return null;
  }

  @BIF(name = "/")
  static public ENumber divide(EObject v1, EObject v2) {
    return v1.divide(v2);
  }

  @BIF(name = "+", type = Type.GUARD)
  static public ENumber plus$p(EObject v1, EObject v2) {
    return v1.add(v2, true);
  }

  @BIF(name = "+", type = Type.GUARD)
  static public ENumber plus$p(EObject v1, ESmall s2) {
    return v1.add(s2.value, true);
  }

  @BIF(name = "+")
  static public ENumber plus(int v1, int v2) {
    return ERT.box((long) v1 + (long) v2);
  }

  @BIF(name = "+")
  static public ENumber plus(EObject v1, EObject v2) {
    return v1.add(v2, false);
  }

  @BIF(name = "+")
  static public ENumber plus(EObject v1, int i2) {
    return v1.add(i2, false);
  }

  @BIF(name = "+")
  static public ENumber plus(EObject v1, ESmall i2) {
    return v1.add(i2.value, false);
  }

  @BIF(name = "-")
  static public ENumber minus(EObject v1, ESmall i2) {
    return v1.subtract(i2.value);
  }

  @BIF(name = "-", type = Type.GUARD)
  static public ENumber minus$g(EObject v1, ESmall i2) {
    ENumber n1;
    if ((n1 = v1.testNumber()) != null) {
      return n1.subtract(i2.value);
    }
    return null;
  }

  @BIF(name = "*", type=Type.GUARD)
  static public ENumber multiply$g(EObject v1, EObject v2) {
    ENumber n1;
    if ((n1 = v1.testNumber()) != null) {
      ENumber n2;
      if ((n2 = v2.testNumber()) != null) {
        return n1.multiply(n2);
      }
    }
    return null;
  }

  @BIF(name = "*")
  static public ENumber multiply(int v1, int v2) {
    return ERT.box((long) v1 * (long) v2);
  }


  @BIF(name = "*")
  static public ENumber multiply(EObject v1, EObject v2) {
    return v1.multiply(v2);
  }

  @BIF
  static public EInteger trunc(EObject v1) {
    EInteger i1;
    if ((i1 = v1.testInteger()) != null) {
      return i1;
    }
    EDouble n1;
    if ((n1 = v1.testFloat()) != null) {
      return trunc(n1.value);
    }
    throw ERT.badarg(v1);
  }

  @BIF
  static public EInteger trunc(double d) {
    //TODO: use (int)d or (long)d if magnitude is small
    return ERT.box(new BigDecimal(d).toBigInteger());
  }

  @BIF
  static public EInteger trunc(EDouble d1) {
    //TODO: use (int)d.value or (long)d.value if magnitude is small
    return ERT.box(new BigDecimal(d1.value).toBigInteger());
  }

  @BIF(name = "round")
  static public EInteger round(double d) {
    return ERT.box(Math.round(d));
  }

  @BIF(name = "round")
  static public EInteger round(EDouble d) {
    return ERT.box(Math.round(d.value));
  }

  @BIF(name = "round")
  static public EInteger round(EObject o) {
    EDouble d;
    if ((d = o.testFloat()) != null)
      return ERT.box(Math.round(d.value));

    EInteger i;
    if ((i = o.testInteger()) != null)
      return i;
   
    throw ERT.badarg(o);
  }

  @BIF(name = "round", type = Type.GUARD)
  static public EInteger round$g(double d) {
    return ERT.box(Math.round(d));
  }

  @BIF(name = "round", type = Type.GUARD)
  static public EInteger round$g(EDouble d) {
    return ERT.box(Math.round(d.value));
  }

  @BIF(name = "round", type = Type.GUARD)
  static public EInteger round$g(EObject o) {
    EDouble d = o.testFloat();
    if (d == null) return null;
    return ERT.box(Math.round(d.value));
  }

  @BIF(name = "float")
  static public double float$n(int v) {
    return (double)v;
  }
  @BIF(name = "float")
  static public double float$n(ENumber v) {
    return v.doubleValue();
  }
  @BIF(name = "float")
  static public EDouble float$n(EObject v) {
    ENumber n = v.testNumber();
    if (n==null) throw ERT.badarg(v);
    return ERT.box(n.doubleValue());
  }

  @BIF(name = "float", type=Type.GUARD)
  static public EDouble float$g(EObject v) {
    return v.testFloat();
  }

  @BIF
  static public ENumber rem(EObject v1, EObject v2) {
    return v1.irem(v2);
  }

  @BIF(name = "rem", type = Type.GUARD)
  static public EInteger rem$p(EObject v1, EObject v2) {
    if (v2.erlangEquals(ESmall.ZERO) || v1.testInteger()==null || v2.testInteger()==null) {
      return null;
    } else {
      return v1.irem(v2);
    }
  }

  @BIF(name = "rem")
  static public EInteger rem(EObject v1, int v2) {
    return v1.irem(v2);
  }

  @BIF(name = "abs", type = Type.GUARD)
  static public ENumber abs$p(EObject v1) {
    ENumber num;
    if ((num = v1.testNumber()) != null) {
      return abs(num);
    }
    return null;
  }

  @BIF(name = "abs")
  static public ENumber abs(EObject v1) {
    ENumber num;
    if ((num = v1.testNumber()) != null) {
      return abs(num);
    }
    throw ERT.badarg(v1);
  }

  @BIF(name = "abs")
  static public ENumber abs(ENumber v1) {
    return v1.abs();
  }

  @BIF(name = "now")
  static public ETuple3 now() {
    long now = now_unique_micros();
    int micros = (int)(now % 1000000); now /= 1000000;
    int secs   = (int)(now % 1000000); now /= 1000000;
    int megas  = (int)now;

    ETuple3 res = new ETuple3();

    res.elem1 = ERT.box(megas);
    res.elem2 = ERT.box(secs);
    res.elem3 = ERT.box(micros);

    return res;
  }

  final static AtomicLong latest_now = new AtomicLong();
  final static long micros_from_epoch_to_nanotime =
    System.currentTimeMillis() * 1000 - System.nanoTime() / 1000;

  public static long now_raw_micros() {
    return System.nanoTime() / 1000 + micros_from_epoch_to_nanotime;
  }

  static long now_unique_micros() {
    /* now() must fulfill:
     * - Any return value approximates the current time.
     * - The return values are strictly increasing (and thus unique).
     * We ensure the latter by (a) always increasing latest_now,
     * (b) always returning what we set it to.
     */
    long micros = now_raw_micros();
    long prev;
    while ((prev = latest_now.get()) < micros) {
      if (latest_now.compareAndSet(prev,micros)) {
        return micros;
      }
    }
    return latest_now.incrementAndGet();
  }


  // tests

  @BIF(name = "==", type = Type.GUARD)
  public static final EAtom is_eq$p(EObject a1, EObject a2) {
    return ERT.guard(a1.erlangEquals(a2));
  }

  @BIF(name = "=/=", type = Type.GUARD)
  public static final EAtom is_ne_exact$g2(EObject a1, EObject a2) {
    return ERT.guard(!a1.equalsExactly(a2));
  }

  @BIF(name = "=/=", type = Type.GUARD)
  public static final EAtom is_ne_exact$g(EObject a1, EAtom a2) {
    return ERT.guard(a1 != a2);
  }

  @BIF(name = "=/=")
  public static final EAtom is_ne_exact(EObject a1, EAtom a2) {
    return ERT.box(a1 != a2);
  }

  @BIF(name = "=/=")
  public static final EAtom is_ne_exact(EObject a1, EObject a2) {
    return ERT.box(!a1.equalsExactly(a2));
  }

  // what are these supposed to do? Get the size of a tuple_

  @BIF
  public static final ESmall size(EObject o) {
    ETuple t;
    if ((t = o.testTuple()) == null)
    {
      EBinary b;
      if ((b = o.testBinary()) == null)
        throw ERT.badarg(o);
     
      return ERT.box(b.byteSize());
    }
    return ERT.box(t.arity());
  }

  @BIF
  public static final ESmall size(ETuple t) {
    return ERT.box(t.arity());
  }

  @BIF(type = Type.GUARD, name = "size")
  public static final ESmall size$g(EObject o) {
    ETuple t;
    if ((t = o.testTuple()) != null)
      return ERT.box(t.arity());

    EBinary b;
    if ((b = o.testBinary()) != null)
      return ERT.box(b.byteSize());
   
    return null;
  }

  @BIF(type = Type.GUARD, name = "size")
  public static final ESmall size$g(ETuple t) {
    return ERT.box(t.arity());
  }

  @BIF(type = Type.GUARD, name = "size")
  public static final ESmall size$g(EBinary b) {
    return ERT.box(b.byteSize());
  }

  @BIF(name = "=:=", type = Type.GUARD)
  public static final EAtom eqxp(EObject a1, EAtom a2) {
    return ERT.guard(a1 == a2);
  }

  @BIF(name = "=:=", type = Type.GUARD)
  public static final EAtom eqxp(EObject a1, ESmall s2) {
    ESmall s1;
    if ((s1 = a1.testSmall()) != null) {
      return ERT.guard(s1.value == s2.value);
    }

    return ERT.guard(s2.equalsExactly(a1));
  }

  @BIF(name = "=:=", type = Type.GUARD)
  public static final EAtom eqxp(EObject a1, EObject a2) {
    return ERT.guard(a1.equalsExactly(a2));
  }

  @BIF(name = "==", type = Type.GUARD)
  public static final EAtom is_eq_op$g(EObject a1, EObject a2) {
    return ERT.guard(a1.erlangEquals(a2));
  }

  @BIF(name = "==")
  public static final EAtom is_eq_op(EObject a1, EObject a2) {
    return a1.erlangEquals(a2) ? ERT.TRUE : ERT.FALSE;
  }

  @BIF(name = "=/=", type = Type.GUARD)
  public static final EAtom is_ne_exact$g(EObject a1, EObject a2) {
    return ERT.guard( !a1.equalsExactly(a2) );
  }

  @BIF(name = ">=", type = Type.GUARD)
  public static final EAtom is_ge$g2(EObject a1, EObject a2) {
    return ERT.guard( a1.erlangCompareTo(a2) >= 0 );
  }

  @BIF(name = ">", type = Type.GUARD)
  public static final EAtom is_gt$g(EObject a1, EObject a2) {
    return ERT.guard( a1.erlangCompareTo(a2) > 0 );
  }

  @BIF(name = "is_ge", type = Type.GUARD)
  public static final EAtom is_ge$g(EObject a1, EObject a2) {
    return ERT.guard(a1.erlangCompareTo(a2) >= 0);
  }

  @BIF(name = ">")
  public static EAtom gt(EObject v1, EObject v2) {
    return ERT.box(v1.erlangCompareTo(v2) > 0);
  }
  @BIF(name = ">")
  public static EAtom gt(EObject v1, ESmall v2) {
    return ERT.box(v1.erlangCompareTo(v2) > 0);
  }
  @BIF(name = ">")
  public static EAtom gt(ESmall v1, EObject v2) {
    return ERT.box(v1.erlangCompareTo(v2) > 0);
  }

  @BIF(name = "/=")
  public static final EAtom is_ne(EObject a1, EObject a2) {
    boolean eq = a1.erlangEquals(a2);
    return ERT.box(!eq);
  }

  @BIF(name = "/=", type = Type.GUARD)
  public static final EAtom is_ne$g(EObject a1, EObject a2) {
    boolean eq = a1.erlangEquals(a2);
    return ERT.guard(!eq);
  }

  @BIF(name = "<", type = Type.GUARD)
  public static final EAtom is_lt$g(EObject a1, EObject a2) {
    return ERT.guard(a1.erlangCompareTo(a2) < 0);
  }

  @BIF(name = "=<")
  public static final EAtom is_le(EObject a1, EObject a2) {
    return ERT.box(a1.erlangCompareTo(a2) <= 0);
  }

  @BIF(name = "<")
  public static final EAtom is_lt(EObject a1, ESmall a2) {
    return ERT.box( a2.erlangCompareTo(a1) > 0 );
  }

  @BIF(name = "<")
  public static final EAtom is_lt(ESmall a1, EObject a2) {
    return ERT.box( a1.erlangCompareTo(a2) < 0 );
  }

  @BIF(name = "=<", type = Type.GUARD)
  public static final EAtom is_le$g(EObject a1, EObject a2) {
    return ERT.guard(a1.erlangCompareTo(a2) <= 0);
  }

  @BIF(name = "<")
  public static final EAtom is_lt(EObject a1, EObject a2) {
    return ERT.box(a1.erlangCompareTo(a2) < 0);
  }

  @BIF(name = ">=")
  public static final EAtom is_ge(EObject a1, EObject a2) {
    return ERT.box(a1.erlangCompareTo(a2) >= 0);
  }

  @BIF
  public static final EAtom is_eq(EObject a1, EObject a2) {
    return ERT.box(a1.erlangEquals(a2));
  }

  @BIF(name = "=:=")
  public static final EAtom is_eq_exact(EObject a1, EObject a2) {
    return ERT.box(a1.equalsExactly(a2));
  }

  @BIF(name = "++")
  public static EObject append(EObject l1, EObject l2) {
   
    ESeq ll1 = l1.testSeq();
    if (ll1 == null) throw ERT.badarg(l1, l2);
   
    return l2.prepend(ll1);
  }

        @BIF
        public static EAtom is_record(EObject term, EObject tag) {
            return ERT.box( is_record$g(term, tag) == ERT.TRUE );
  }

        @BIF
        public static EAtom is_record(EObject term, EObject tag, EObject size) {
            return ERT.box( is_record$g(term, tag, size) == ERT.TRUE );
  }

    @BIF(type=Type.GUARD, name="is_record")
            public static EAtom is_record$g(EObject term, EObject tag) {
                EAtom atag = tag.testAtom();
                ETuple tup = term.testTuple();
                boolean ok = ( (atag != null) && (tup != null)
                                && (tup.arity() > 0)
                                && (tup.elm(1) == atag) );
                if (ok) return ERT.TRUE;
                else return null;
  }

    @BIF(type=Type.GUARD, name="is_record")
            public static EAtom is_record$g(EObject term, EObject tag, EObject size) {
                EAtom atag = tag.testAtom();
                ETuple tup = term.testTuple();
                ESmall siz = size.testSmall();
                boolean ok = ( (atag != null) && (tup != null) && (siz != null)
                                && (tup.arity() == siz.value)
                                && (tup.elm(1) == atag) );
                if (ok) return ERT.TRUE;
                else return null;
  }

  @BIF
  public static EAtom is_list(EObject o) {
    return ERT.box(o.testCons() != null || o.testNil() != null);
  }

  @BIF
  public static EAtom is_nil(EObject o) {
    return ERT.box(o.testNil() != null);
  }

  @BIF
  public static EString atom_to_list(EObject atom) {
    EAtom am = atom.testAtom();
    if (am == null)
      throw ERT.badarg(atom);
    return new EString(am.getName());
  }

  @BIF
  public static EObject process_flag(EProc proc, EObject a1, EObject a2) {
    return proc.process_flag(a1.testAtom(), a2);
  }

  @BIF
  public static ESeq nodes() {
    return EPeer.getRemoteNodes();
//    return ERT.getRemoteNodes();
  }

  @BIF(name = "is_atom", type = Type.GUARD)
  public static EAtom is_atom$p(EObject obj) {
    return ERT.guard(obj.testAtom() != null);
  }

  @BIF
  public static EAtom is_atom(EObject obj) {
    return (obj.testAtom() != null) ? ERT.TRUE : ERT.FALSE;
  }

  @BIF(name = "is_list", type = Type.GUARD)
  public static EAtom is_list$p(EObject obj) {
    return ERT.guard(obj.testCons() != null);
  }

  @BIF(name = "is_tuple", type = Type.GUARD)
  public static EAtom is_tuple$p(EObject obj) {
    return ERT.guard(obj.testTuple() != null);
  }

  @BIF
  public static EAtom is_tuple(EObject obj) {
    return ERT.box(obj.testTuple() != null);
  }

  @BIF(name = "is_binary", type = Type.GUARD)
  public static EAtom is_binary$p(EObject obj) {
    return ERT.guard(obj.testBinary() != null);
  }

  @BIF
  public static EAtom is_binary(EObject obj) {
    return ERT.box(obj.testBinary() != null);
  }

  @BIF
  public static EAtom is_bitstring(EObject obj) {
    return ERT.box(obj.testBitString() != null);
  }

  @BIF(name = "is_bitstring", type = Type.GUARD)
  public static EAtom is_bitstring$g(EObject obj) {
    return ERT.guard(obj.testBitString() != null);
  }

  @BIF
  public static EAtom is_boolean(EObject obj) {
    return ERT.box(obj==ERT.TRUE || obj==ERT.FALSE);
  }

  @BIF(type=Type.GUARD, name="is_boolean")
  public static EAtom is_boolean$g(EObject obj) {
    return ERT.guard(obj==ERT.TRUE || obj==ERT.FALSE);
  }

  @BIF(name = "is_integer", type = Type.GUARD)
  public static EAtom is_integer$p(EObject obj) {
    return ERT.guard(obj.testInteger() != null);
  }

  @BIF(name = "is_float", type = Type.GUARD)
  public static EAtom is_float$g(EObject obj) {
    return ERT.guard(obj.testFloat() != null);
  }

  @BIF(name = "is_number", type = Type.GUARD)
  public static EAtom is_number$g(EObject obj) {
    return ERT.guard(obj.testNumber() != null);
  }

  @BIF
  public static EAtom is_function(EObject obj) {
    return ERT.box(obj.testFunction() != null);
  }
 
  @BIF
  public static EAtom is_function(EObject obj, ESmall num) {
    return ERT.box(obj.testFunction2(num.value) != null);
  }
 
  @BIF(name="is_function", type=Type.GUARD)
  public static EAtom is_function_guard(EObject obj) {
    return ERT.guard(obj.testFunction() != null);
  }
 
  @BIF
  public static EAtom is_reference(EObject obj) {
    return ERT.box(obj.testReference() != null);
  }
 
  @BIF
  public static EAtom is_pid(EObject obj) {
    return ERT.box(obj.testPID() != null);
  }
 
  @BIF(name="is_pid", type=Type.GUARD)
  public static EAtom is_pid_guard(EObject obj) {
    return ERT.guard(obj.testPID() != null);
  }

  @BIF
  public static EAtom is_port(EObject obj) {
    return ERT.box(obj.testPort() != null);
  }
 
  @BIF(name="is_port", type=Type.GUARD)
  public static EAtom is_port$g(EObject obj) {
    return ERT.guard(obj.testPort() != null);
  }
 
  @BIF
  public static EObject load_nif(EObject path, EObject info)
  {
    EString str = path.testString();
    if (str == null) { throw ERT.badarg(path, info); }
   
    NIF nif = NIF.load(str.stringValue() + ".so", info);
    if (nif == null) {
      return new ETuple2(ERT.am_error,
            new ETuple2(am_load_failed, ERT.am_undefined)
          );
    } else {
      return ERT.am_ok;
    }
  }
 
  @BIF
  public static ESeq loaded() {
    return EModuleManager.loaded_modules();
  }
 
  @BIF
  public static EAtom delete_module(EObject m) {
    EAtom mod = m.testAtom();
    if (mod == null) throw ERT.badarg(m);
    return EModuleManager.delete_module(mod);
  }
 
  @BIF
  public static EObject call_on_load_function(EProc proc, EObject mod) throws Pausable {
    EAtom name;
    if ((name=mod.testAtom()) == null) {
      throw ERT.badarg(mod);
    }
   
    return EModuleLoader.call_on_load_function(proc, name);
  }
 
  @BIF
  public static EObject finish_after_on_load(EObject mod, EObject keep)
  {
    if (keep != ERT.TRUE) {
      System.err.println("NIF unloading not implemented...");
    }
    return ERT.am_ok;
  }
 
  @BIF 
  public static ETuple2 load_module(EObject mod, EObject bin) {
    EAtom name = mod.testAtom();
    EBinary binary = bin.testBinary();
    return load_module(name, binary);
  }

  @BIF
  public static ETuple2 load_module(EAtom mod, EBinary bin) {
    if (mod == null || bin == null)
      throw ERT.badarg(mod, bin);

    try {
      EModule module = EModuleLoader.load_module(mod.getName(), bin);
      if (module.has_on_load()) {
        return new ETuple2(ERT.am_error, am_on_load);
      } else {
        return new ETuple2(ERT.am_module, mod);
      }
    } catch (ErlangException e) {
      log.log(Level.FINE, "cannot load module", e);
      return new ETuple2(ERT.am_error, e.reason());
    } catch (ThreadDeath e) {
      throw e;
    } catch (Throwable e) {
      ErlangError ee = new ErlangError(ERT.am_badfile, e, mod, bin);
      ETuple2 result = new ETuple2(ERT.am_error, ee.reason());
     
      log.log(Level.SEVERE, "cannot load module "+mod, e);
     
      return result;
    }
   
  }
 
  @BIF
  public static ETuple make_tuple(EObject arity, EObject initial) {
    ESmall sm = arity.testSmall();
    if (sm == null || sm.value < 0) throw ERT.badarg(arity, initial);
    ETuple et = ETuple.make(sm.value);
    for (int i = 1; i <= sm.value; i++) {
      et.set(i, initial);
    }
    return et;
  }

  @BIF
  public static EAtom is_integer(EObject o) {
    return ERT.box(o.testInteger() != null);
  }

  @BIF
  public static EAtom is_float(EObject o) {
    return ERT.box(o.testFloat() != null);
  }

  @BIF
  public static EAtom is_number(EObject o) {
    return ERT.box(o.testNumber() != null);
  }

  @BIF
  public static ESmall tuple_size(ETuple tup) {
    return ERT.box(tup.arity());
  }

  @BIF
  public static ESmall tuple_size(EObject tup) {
    ETuple t;
    if ((t = tup.testTuple()) == null)
      throw ERT.badarg(tup);
    return ERT.box(t.arity());
  }

  @BIF(type = Type.GUARD, name = "tuple_size")
  public static ESmall tuple_size_guard(EObject tup) {
    ETuple t;
    if ((t = tup.testTuple()) == null)
      return null;
    return ERT.box(t.arity());
  }

  @BIF
  public static ESmall byte_size(EObject o) {
    EBitString bin = o.testBitString();
    if (bin == null)
      throw ERT.badarg(o);
    return ERT.box(bin.totalByteSize());
  }

 
  @BIF(type=Type.GUARD, name="byte_size")
  public static ESmall byte_size_guard(EObject o) {
    EBitString bin = o.testBitString();
    if (bin == null)
      return null;
    return ERT.box(bin.totalByteSize());
  }

  @BIF
  public static EInteger bit_size(EObject o) {
    EBitString bin = o.testBitString();
    if (bin == null)
      throw ERT.badarg(o);
    return ERT.box(bin.bitSize());
  }


  @BIF(type=Type.GUARD, name="bit_size")
  public static EInteger bit_size_guard(EObject o) {
    EBitString bin = o.testBitString();
    if (bin == null)
      return null;
    return ERT.box(bin.bitSize());
  }

  @BIF
  public static EAtom or(EObject o1, EObject o2) {
    Boolean b1 = ERT.asBoolean(o1);
    Boolean b2 = ERT.asBoolean(o2);
    if (b1==null || b2==null) throw ERT.badarg(o1,o2);
    return ERT.box(b1.booleanValue() || b2.booleanValue());
  }

  @BIF
  public static EAtom and(EObject o1, EObject o2) {
    Boolean b1 = ERT.asBoolean(o1);
    Boolean b2 = ERT.asBoolean(o2);
    if (b1==null || b2==null) throw ERT.badarg(o1,o2);
    return ERT.box(b1.booleanValue() && b2.booleanValue());
  }

  @BIF(type = Type.GUARD, name = "or")
  public static EAtom or$g(EObject o1, EObject o2) {
    if (o1==ERT.TRUE) {
      if (o2==ERT.TRUE || o2==ERT.FALSE) return ERT.TRUE;
    } else if (o2==ERT.TRUE) {
      if (o1==ERT.FALSE) return ERT.TRUE;
    } else if (o1 == ERT.FALSE && o2 == ERT.FALSE) {
      return ERT.FALSE;
    }
    return null;
  }

  @BIF(type = Type.GUARD, name = "and")
  public static EAtom and$g(EObject o1, EObject o2) {
    return ERT.guard(o1 == ERT.TRUE && o2 == ERT.TRUE);
  }

  @BIF
  public static EAtom not(EObject o1) {
    if  (o1.testBoolean() == null) throw ERT.badarg(o1);
    return ERT.box(o1 == ERT.FALSE);
  }

  @BIF
  public static EInteger bnot(EObject o) {
    return o.bnot();
  }

  @BIF
  public static EInteger bor(EObject o1, EObject o2) {
    return o1.bor(o2);
  }

  @BIF
  public static EInteger bxor(EObject o1, EObject o2) {
    return o1.bxor(o2);
  }

  @BIF
  public static EInteger band(EObject o1, EObject o2) {
    return o1.band(o2);
  }

  @BIF
  public static EInteger band(EObject o1, ESmall o2) {
    return o2.band(o1);
  }

  @BIF
  public static EInteger bsl(EObject o1, EObject o2) {
    return o1.bsl(o2);
  }

  @BIF
  public static EInteger bsr(EObject o1, EObject o2) {
    return o1.bsr(o2);
  }

  @BIF(type = Type.GUARD, name = "not")
  public static EAtom not$g(EObject o1) {
    if  (o1.testBoolean() == null) return null;
    return ERT.box(o1 == ERT.FALSE);
  }

  @BIF
  public static EAtom xor(EObject o1, EObject o2) {
    EAtom a1, a2;
    if ((a1 = o1.testBoolean()) == null ||
        (a2 = o2.testBoolean()) == null)
      throw ERT.badarg(o1, o2);
    boolean b1 = a1 == ERT.TRUE,
            b2 = a2 == ERT.TRUE;
    return ERT.box(b1 ^ b2);
  }

  @BIF(type = Type.GUARD, name = "bnot")
  public static EInteger bnot$g(EObject o) {
    EInteger i;
    if ((i = o.testInteger()) == null)
      return null;
    return i.bnot();
  }

  @BIF(type = Type.GUARD, name = "bor")
  public static EInteger bor$g(EObject o1, EObject o2) {
    EInteger i1;
    EInteger i2;
    if ((i1 = o1.testInteger()) == null || (i2 = o2.testInteger()) == null)
      return null;
    return i1.bor(i2);
  }

  @BIF(type = Type.GUARD, name = "bxor")
  public static EInteger bxor$g(EObject o1, EObject o2) {
    EInteger i1;
    EInteger i2;
    if ((i1 = o1.testInteger()) == null || (i2 = o2.testInteger()) == null)
      return null;
    return i1.bxor(i2);
  }

  @BIF(type = Type.GUARD, name = "band")
  public static EInteger band$g(EObject o1, EObject o2) {
    EInteger i1;
    EInteger i2;
    if ((i1 = o1.testInteger()) == null || (i2 = o2.testInteger()) == null)
      return null;
    return i1.band(i2);
  }

  @BIF(type = Type.GUARD, name = "bsl")
  public static EInteger bsl$g(EObject o1, EObject o2) {
    EInteger i1;
    EInteger i2;
    if ((i1 = o1.testInteger()) == null || (i2 = o2.testInteger()) == null)
      return null;
    return i1.bsl(i2);
  }

  @BIF(type = Type.GUARD, name = "bsr")
  public static EInteger bsr$g(EObject o1, EObject o2) {
    EInteger i1;
    EInteger i2;
    if ((i1 = o1.testInteger()) == null || (i2 = o2.testInteger()) == null)
      return null;
    return i1.bsr(i2);
  }

  @BIF
  public static EObject yield() throws Pausable {
    Task.yield();
    Task.getCurrentTask().checkKill();
    return ERT.TRUE;
  }
 
  @BIF
  public static EObject bump_reductions(EProc self, EObject howmuch) throws Pausable {
    // yield?
    Task.yield();
    Task.getCurrentTask().checkKill();
    return ERT.TRUE;
  }
 
  @BIF
  public static ETuple2 localtime()
  {
    Calendar c = GregorianCalendar.getInstance();
   
    ETuple3 date = new ETuple3();
    date.set(1, ERT.box(c.get(Calendar.YEAR)));
    date.set(2, ERT.box(c.get(Calendar.MONTH)-Calendar.JANUARY+1));
    date.set(3, ERT.box(c.get(Calendar.DAY_OF_MONTH)));
   
    ETuple3 time = new ETuple3();
    time.set(1, ERT.box(c.get(Calendar.HOUR_OF_DAY)));
    time.set(2, ERT.box(c.get(Calendar.MINUTE)));
    time.set(3, ERT.box(c.get(Calendar.SECOND)));
   
    return new ETuple2(date, time);
  }
 

  @BIF
  public static ETuple2 universaltime()
  {
    Calendar c = GregorianCalendar.getInstance(UTC_TIME_ZONE);
   
    ETuple3 date = new ETuple3();
    date.set(1, ERT.box(c.get(Calendar.YEAR)));
    date.set(2, ERT.box(c.get(Calendar.MONTH)-Calendar.JANUARY+1));
    date.set(3, ERT.box(c.get(Calendar.DAY_OF_MONTH)));
   
    ETuple3 time = new ETuple3();
    time.set(1, ERT.box(c.get(Calendar.HOUR_OF_DAY)));
    time.set(2, ERT.box(c.get(Calendar.MINUTE)));
    time.set(3, ERT.box(c.get(Calendar.SECOND)));
   
    return new ETuple2(date, time);
  }
 
  @BIF
  static public EObject localtime_to_universaltime(EObject a1)
  {
    return localtime_to_universaltime(a1, ERT.am_undefined);
  }
 
  @BIF
  static public EObject localtime_to_universaltime(EObject a1, EObject a2)
  {
    ETuple2 dt;
    if ((dt=ETuple2.cast(a1)) != null) {
      ETuple3 date;
      ETuple3 time;
      ESmall year;
      ESmall month;
      ESmall day;
      ESmall hour;
      ESmall minute;
      ESmall sec;
      if (   (date=ETuple3.cast( dt.elem1 )) != null
        && (year = date.elem1.testSmall()) != null
        && (month = date.elem2.testSmall()) != null
        && (day = date.elem3.testSmall()) != null
       
        && (time=ETuple3.cast( dt.elem2 )) != null
        && (hour = time.elem1.testSmall()) != null
        && (minute = time.elem2.testSmall()) != null
        && (sec = time.elem3.testSmall()) != null
       
      ) {

        Calendar in_date = GregorianCalendar.getInstance();
        in_date.set(Calendar.YEAR, year.value);
        in_date.set(Calendar.MONTH, month.value-1+Calendar.JANUARY);
        in_date.set(Calendar.DAY_OF_MONTH, day.value);
       
        in_date.set(Calendar.HOUR_OF_DAY, hour.value);
        in_date.set(Calendar.MINUTE, minute.value);
        in_date.set(Calendar.SECOND, sec.value);
       
        Calendar out_date = GregorianCalendar.getInstance(UTC_TIME_ZONE);
        out_date.setTimeInMillis(in_date.getTimeInMillis());
       
        ETuple3 date2 = new ETuple3();
        date2.set(1, ERT.box(out_date.get(Calendar.YEAR)));
        date2.set(2, ERT.box(out_date.get(Calendar.MONTH)-Calendar.JANUARY+1));
        date2.set(3, ERT.box(out_date.get(Calendar.DAY_OF_MONTH)));
       
        ETuple3 time2 = new ETuple3();
        time2.set(1, ERT.box(out_date.get(Calendar.HOUR_OF_DAY)));
        time2.set(2, ERT.box(out_date.get(Calendar.MINUTE)));
        time2.set(3, ERT.box(out_date.get(Calendar.SECOND)));
       
        return new ETuple2(date2, time2);   
      }
    }

    throw ERT.badarg(a1, a2);
  }
 
  @BIF
  static public EObject universaltime_to_localtime(EObject a1)
  {
    ETuple2 dt;
    if ((dt=ETuple2.cast(a1)) != null) {
      ETuple3 date;
      ETuple3 time;
      ESmall year;
      ESmall month;
      ESmall day;
      ESmall hour;
      ESmall minute;
      ESmall sec;
      if (   (date=ETuple3.cast( dt.elem1 )) != null
        && (year = date.elem1.testSmall()) != null
        && (month = date.elem2.testSmall()) != null
        && (day = date.elem3.testSmall()) != null
       
        && (time=ETuple3.cast( dt.elem2 )) != null
        && (hour = time.elem1.testSmall()) != null
        && (minute = time.elem2.testSmall()) != null
        && (sec = time.elem3.testSmall()) != null
       
      ) {

        Calendar in_date = GregorianCalendar.getInstance(UTC_TIME_ZONE);
        in_date.set(Calendar.YEAR, year.value);
        in_date.set(Calendar.MONTH, month.value-1+Calendar.JANUARY);
        in_date.set(Calendar.DAY_OF_MONTH, day.value);
       
        in_date.set(Calendar.HOUR_OF_DAY, hour.value);
        in_date.set(Calendar.MINUTE, minute.value);
        in_date.set(Calendar.SECOND, sec.value);
       
        Calendar out_date = GregorianCalendar.getInstance();
        out_date.setTimeInMillis(in_date.getTimeInMillis());
       
        ETuple3 date2 = new ETuple3();
        date2.set(1, ERT.box(out_date.get(Calendar.YEAR)));
        date2.set(2, ERT.box(out_date.get(Calendar.MONTH)-Calendar.JANUARY+1));
        date2.set(3, ERT.box(out_date.get(Calendar.DAY_OF_MONTH)));
       
        ETuple3 time2 = new ETuple3();
        time2.set(1, ERT.box(out_date.get(Calendar.HOUR_OF_DAY)));
        time2.set(2, ERT.box(out_date.get(Calendar.MINUTE)));
        time2.set(3, ERT.box(out_date.get(Calendar.SECOND)));
       
        return new ETuple2(date2, time2);   
      }
    }

    throw ERT.badarg(a1);
  }
 
  @BIF
  static public EObject posixtime_to_universaltime(EObject a1)
  {
    EInteger num = a1.testInteger();
    if (num == null) throw ERT.badarg(a1);
   
    Calendar out_date = GregorianCalendar.getInstance(UTC_TIME_ZONE);
    out_date.setTimeInMillis(num.longValue() * 1000L);
   
    ETuple3 date2 = new ETuple3();
    date2.set(1, ERT.box(out_date.get(Calendar.YEAR)));
    date2.set(2, ERT.box(out_date.get(Calendar.MONTH)-Calendar.JANUARY+1));
    date2.set(3, ERT.box(out_date.get(Calendar.DAY_OF_MONTH)));
   
    ETuple3 time2 = new ETuple3();
    time2.set(1, ERT.box(out_date.get(Calendar.HOUR_OF_DAY)));
    time2.set(2, ERT.box(out_date.get(Calendar.MINUTE)));
    time2.set(3, ERT.box(out_date.get(Calendar.SECOND)));
   
    return new ETuple2(date2, time2);   
  }
 
  @BIF
  static public EObject universaltime_to_posixtime(EObject a1)
  {
    ETuple2 dt;
    if ((dt=ETuple2.cast(a1)) != null) {
      ETuple3 date;
      ETuple3 time;
      ESmall year;
      ESmall month;
      ESmall day;
      ESmall hour;
      ESmall minute;
      ESmall sec;
      if (   (date=ETuple3.cast( dt.elem1 )) != null
        && (year = date.elem1.testSmall()) != null
        && (month = date.elem2.testSmall()) != null
        && (day = date.elem3.testSmall()) != null
       
        && (time=ETuple3.cast( dt.elem2 )) != null
        && (hour = time.elem1.testSmall()) != null
        && (minute = time.elem2.testSmall()) != null
        && (sec = time.elem3.testSmall()) != null
       
      ) {

        Calendar in_date = GregorianCalendar.getInstance(UTC_TIME_ZONE);
        in_date.set(Calendar.YEAR, year.value);
        in_date.set(Calendar.MONTH, month.value-1+Calendar.JANUARY);
        in_date.set(Calendar.DAY_OF_MONTH, day.value);
       
        in_date.set(Calendar.HOUR_OF_DAY, hour.value);
        in_date.set(Calendar.MINUTE, minute.value);
        in_date.set(Calendar.SECOND, sec.value);
       
        return ERT.box(in_date.getTimeInMillis() / 1000L);
      }
    }

    throw ERT.badarg(a1);
  }
 
  @BIF
  static public EObject system_flag(EObject flag_arg, EObject value)
  {
    throw new NotImplemented();
  }
 
 
  static EObject sysmon_pid = ERT.am_undefined;
 
  @BIF
  static public EObject system_monitor(EObject pid, EObject opts) {
    EPID spid = pid.testPID();
    if(spid == null) throw ERT.badarg(pid, opts);
    sysmon_pid = pid;
    return system_monitor();
  }
 
  @BIF
  static public EObject system_monitor() {
    if (log.isLoggable(Level.FINE)) log.fine("system_monitor setting ignored");
    return new ETuple2(sysmon_pid, ERT.NIL);
  }
 
  @BIF
  static public EObject memory(EObject type) {
    Runtime runtime = Runtime.getRuntime();
    if (type == am_total) {
      return ERT.box(runtime.totalMemory()-runtime.freeMemory());
    } else {
      throw ERT.notsup();
    }
  }

  @BIF
  public static EObject md5_init(EProc self)
  {
    MessageDigest md;
     try {
       md = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
      throw new NotImplemented();
    }
   
    return new JavaObject(self, md);
  }
 
  @BIF
  public static EObject md5_update(EProc self, EObject context, EObject iolist_arg)
  {
    List<ByteBuffer> buf = new ArrayList<ByteBuffer>();
    if (!iolist_arg.collectIOList(buf)) {
      throw ERT.badarg(context, iolist_arg);
    }
   
    JavaObject jo;
    if ((jo = context.testJavaObject()) != null && (jo.realObject() instanceof MessageDigest)) {
      MessageDigest md = (MessageDigest) jo.realObject();
     
      for (int i = 0; i < buf.size(); i++) {
        md.update(buf.get(i));
     
     
      return new JavaObject(self, md);
    }
   
    throw ERT.badarg(context, iolist_arg);
  }
 
 
  @BIF
  public static EObject md5_final(EProc self, EObject context)
  {
    JavaObject jo;
    if ((jo = context.testJavaObject()) != null && (jo.realObject() instanceof MessageDigest)) {
      MessageDigest md = (MessageDigest) jo.realObject();
     
      byte[] res = md.digest();
      return EBinary.make(res, 0, res.length, 0);
    }
   
    throw ERT.badarg(context);
  }
 
 
 
  @BIF
  public static EObject md5(EObject iolist_arg)
  {
   
    List<ByteBuffer> buf = new ArrayList<ByteBuffer>();
    if (!iolist_arg.collectIOList(buf)) {
      throw ERT.badarg(iolist_arg);
    }
   
    MessageDigest md;
     try {
       md = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
      throw new NotImplemented();
    }
   
    for (int i = 0; i < buf.size(); i++) {
      md.update(buf.get(i));
    }
   
    byte[] res = md.digest();
    return EBinary.make(res, 0, res.length, 0);
  }
 
  @BIF
  public static EObject raise(EObject kind, EObject value, EObject trace)
      throws ErlangException {

    EAtom clazz = kind.testAtom();
    ESeq traz = trace.testSeq();

    if (traz == null) {
//      System.err.println("bad argument to raise3: ("+kind+", "+value+", "+trace+")");
      return ERT.am_badarg;
    }

    if (clazz==ERT.am_exit || clazz==ERT.am_error || clazz==ERT.am_throw)
      throw new ErlangRaise(clazz, value, traz);
   
//    System.err.println("bad argument to raise4: ("+kind+", "+value+", "+trace+")");
    return ERT.am_badarg;
  }
 
  @BIF
  public static ESeq registered() {
    return ERT.registered();
  }
 
  @BIF
  public static EAtom breakpoint() {
    return ERT.am_ok;
  }

        @BIF
        public static EObject dt_spread_tag(EObject bool) {
            return new ETuple2(ERT.box(0), ERT.NIL);
        }

        @BIF
        public static EObject dt_append_vm_tag_data(EObject val) {
            return val;
        }

        @BIF
        public static EObject dt_prepend_vm_tag_data(EObject o) {
            return o;
        }

        @BIF
        public static EObject dt_restore_tag(EObject val) {
            return val;
        }

    @BIF
    public static EAtom check_old_code(EObject obj) {
        EAtom module = obj.testAtom();
        if (module == null)
            throw ERT.badarg(obj);

        boolean result = EModuleManager.module_loaded(module);
        return ERT.box(result);
    }

    @BIF
    public static EObject nif_error(EObject err) {

        ErlangError exit = new ErlangError(err);

        log.log(Level.INFO, "missing nif "+err, exit);

        throw exit;
    }
   
    @BIF
    public static EObject binary_part(EObject bin, EObject start, EObject length) {
      EObject result = binary_part_guard(bin, start, length);
      if (result == null) {
        throw ERT.badarg(bin, start, length);       
      }
      return result;
    }
   
    @BIF(name="binary_part", type=Type.GUARD)
    public static EObject binary_part_guard(EObject bin, EObject start) {
      EBinary bin1 = bin.testBinary();
      ESmall start1 = start.testSmall();
      if (bin1 == null || start1 == null) {
        return null;
      }
     
      return binary_part_guard(bin, start, ERT.box( bin1.byteSize() - start1.value ));
    }
   
    @BIF(name="binary_part", type=Type.GUARD)
    public static EObject binary_part_guard(EObject bin, EObject start, EObject length) {
      EBinary bin1 = bin.testBinary();
      ESmall start1 = start.testSmall();
      ESmall length1 = length.testSmall();
     
      if (bin1 == null || start1 == null || length == null) {
        return null;
      }
     
      if (start1.value < 0
          || (start1.value + length1.value) > bin1.byteSize()
        || length1.value < 0) {
        return null;
      }
 
      return bin1.substring(start1.value * 8, length1.value * 8);
    }
   

  @BIF
  static public EObject match_spec_test(EObject term, EObject matchSpec, EObject how)
  {
    EMatchSpec spec;
    ESeq seq;
    if ((matchSpec instanceof EMatchSpec)) {
      spec = (EMatchSpec) matchSpec;
    } else if ((seq=matchSpec.testSeq()) != null) {
      try {
        spec = EMatchSpec.compile(seq);
      } catch (ErlangError e) {
        if (e.reason() == ERT.am_badarg) {
          throw ERT.badarg(term, matchSpec, how);
        } else {
          throw e;
        }
      }
    } else {
      throw ERT.badarg(term, matchSpec, how);
    }
   
    EObject res = spec.match(term);
    if (res == null) {
      return ETuple.make(ERT.am_ok, ERT.FALSE, ERT.NIL, ERT.NIL);
    } else {
      return ETuple.make(ERT.am_ok, ERT.TRUE, ERT.NIL, ERT.NIL);
    }

  }
 
  @BIF
  public static EAtom is_builtin(EObject m, EObject f, EObject a) {
    EAtom mod  = m.testAtom();
    EAtom fun  = f.testAtom();
    ESmall ary = a.testSmall();
   
    if (mod == null || fun == null || ary == null)
      throw ERT.badarg(m, f, a);
   
    BuiltInFunction bif = BIFUtil.getMethod(mod, fun, ary.value, false, false);
    return ERT.box( bif != null );
  }
   
}
TOP

Related Classes of erjang.m.erlang.ErlBif

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.