/** * 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.EMap; 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(); } // 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 ); } @BIF public static ESmall map_size(EObject map) { EMap self = map.testMap(); if (self == null) throw ERT.badmap(map); return ERT.box(self.map_size()); } @BIF(type=Type.GUARD, name="map_size") public static ESmall map_size_g(EObject map) { EMap self = map.testMap(); if (self == null) return null; return ERT.box(self.map_size()); } }