/**
* 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;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import erjang.codegen.EFunCG;
import kilim.Mailbox;
import kilim.Pausable;
import kilim.Task;
import erjang.driver.Drivers;
import erjang.driver.EAsync;
import erjang.driver.EDriver;
import erjang.driver.EDriverTask;
import erjang.driver.efile.Posix;
import erjang.m.java.JavaObject;
import erjang.m.re.ECompiledRE;
@Module(value = "erlang")
public class ERT {
public static Logger log = Logger.getLogger("erjang");
static Logger ipclog = Logger.getLogger("erjang.ipc");
public static EAtom am_badsig = EAtom.intern("badsig");
public static EAtom am_file = EAtom.intern("file");
public static EAtom am_line = EAtom.intern("line");
public static EObject raise(EObject trace, EObject value) throws ErlangException {
// log.warning("raise "+trace);
if (trace instanceof ErlangException.ExceptionAsObject) {
ErlangException etrace = ((ErlangException.ExceptionAsObject) trace).getException();
EAtom clazz = etrace.getExClass();
ESeq traz = etrace.getLazyTrace();
throw new ErlangRaise(clazz, value, traz);
} else if (trace==am_exit || trace==am_error || trace==am_throw) {
log.warning("Pre-R10-1 exception style is not supported.");
}
ESeq trz;
if ((trz = trace.testSeq()) != null) {
throw new ErlangRaise(am_error, value, trz);
}
Throwable error = new Throwable("bad argument to raise2 :: "+ trace.getClass().getName());
log.log(Level.WARNING, "bad argument to raise2: ("+value+", "+trace+")", error);
throw ERT.badarg(trace, value);
}
public static final EAtom am_badarg = EAtom.intern("badarg");
public static final EAtom am_notsup = EAtom.intern("notsup");
public static final EAtom AM_BADMATCH = EAtom.intern("badmatch");
public static final EAtom AM_BADARITH = EAtom.intern("badarith");
public static final EAtom am_module = EAtom.intern("module");
public static ECons cons(EObject h, EObject t) {
return t.cons(h);
}
public static ErlangError badarg() {
throw new ErlangError(am_badarg);
}
public static ErlangError notsup() {
throw new ErlangError(am_notsup);
}
public static ErlangError badarg(EObject... args) {
throw new ErlangError(am_badarg, args);
}
/** Utility method throws <code>erlang:error('badarg', [o1, o2])</code>. */
public static ErlangError badarg(EObject o1, EObject o2) throws ErlangError {
throw new ErlangError(am_badarg, NIL.cons(o2).cons(o1));
}
public static ErlangError badarg(EObject o1) throws ErlangError {
throw new ErlangError(am_badarg, NIL.cons(o1));
}
public static ErlangError badarith(EObject... args) {
throw new ErlangError(AM_BADARITH, args);
}
public static ErlangError badarith(EObject o1, EObject o2) throws ErlangError {
throw new ErlangError(AM_BADARITH, NIL.cons(o2).cons(o1));
}
public static ErlangError badarith(int o1, EObject o2) {
throw new ErlangError(AM_BADARITH, NIL.cons(o2).cons(o1));
}
public static ErlangError badarith(EObject o1, int o2) {
throw new ErlangError(AM_BADARITH, NIL.cons(o2).cons(o1));
}
public static ErlangError badarith(double o1, EObject o2) {
throw new ErlangError(AM_BADARITH, NIL.cons(o2).cons(o1));
}
public static ErlangError badarith(EObject o1, double o2) {
throw new ErlangError(AM_BADARITH, NIL.cons(o2).cons(o1));
}
public static ErlangError badarith(BigInteger o1, EObject o2) {
throw new ErlangError(AM_BADARITH, NIL.cons(o2).cons(o1));
}
public static ErlangError badfun(EObject o) {
throw new ErlangError(new ETuple2(am_badfun, o));
}
public static final EAtom TRUE = EAtom.intern("true");
public static final EAtom FALSE = EAtom.intern("false");
public static boolean eq(EObject o1, EObject o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
public static boolean eq(EAtom o1, EAtom o2) {
return o1 == o2;
}
public static EAtom as_atom_or_null(EObject o) {
return o == null ? null : o.testAtom();
}
public static ECons as_nonempty_list_or_null(EObject o) {
return o == null ? null : o.testNonEmptyList();
}
public static ENil as_nil_or_null(EObject o) {
return o == null ? ERT.NIL : o.testNil();
}
public static EDouble as_float_or_null(EObject o) {
return o == null ? null : o.testFloat();
}
/**
* @param s
* @return
*/
public static EPID loopkup_pid(ESeq name) {
throw new NotImplemented();
}
// "definer" holds a reference to ClassLoader#defineClass
static private final Method definer;
static {
try {
definer = ClassLoader.class.getDeclaredMethod("defineClass",
new Class[] { String.class, byte[].class, int.class,
int.class });
definer.setAccessible(true);
} catch (Exception e) {
throw new ErlangError(e);
}
}
/**
* @param classLoader
* @param name
* @param data
* @param i
* @param length
* @return
*/
@SuppressWarnings("unchecked")
public static <T> Class<? extends T> defineClass(ClassLoader classLoader,
String name, byte[] data) {
/*
* Class<? extends ETuple> res = (Class<? extends ETuple>)
* loader2.define(name, data);
*/
Class<? extends T> res;
try {
res = (Class<? extends T>) definer.invoke(classLoader, name.replace('/', '.'), data, 0,
data.length);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (!name.equals(res.getName())) {
throw new Error();
}
return res;
}
public static ESmall box(int v) {
return ESmall.make(v);
}
/**
* Boxes a <code>long</code> value to an EInteger (EBig or ESmall)
*
* @param longValue
* @return
*/
public static EInteger box2(long longVal) {
// very simple: see if the longValue can be converted
// to an int and back again without loosing it's value
int intVal = (int) longVal;
if (longVal == (long) intVal) {
return ESmall.make(intVal);
} else {
return new EBig(longVal);
}
}
public static EInteger box(long longVal) {
int intVal = (int)longVal;
if (intVal == longVal) {
return ESmall.make(intVal);
} else {
return new EBig(longVal);
}
}
/**
* @param doubleVal
* @return
*/
public static EDouble box(double doubleVal) {
return new EDouble(doubleVal);
}
static BigInteger INT_MIN_AS_BIG = BigInteger.valueOf(Integer.MIN_VALUE);
static BigInteger INT_MAX_AS_BIG = BigInteger.valueOf(Integer.MAX_VALUE);
private static final ENode localNode = new ENode();
/**
* @param add
* @return
*/
public static EInteger box(BigInteger res) {
if (res.compareTo(INT_MIN_AS_BIG) < 0)
return new EBig(res);
if (res.compareTo(INT_MAX_AS_BIG) > 0)
return new EBig(res);
return ESmall.make(res.intValue());
}
public static EInteger box_parse(String str) {
return box(new BigInteger(str));
}
/**
* @param b
* @return
*/
public static EAtom box(boolean bool) {
return bool ? TRUE : FALSE;
}
/**
* @param b
* @return
*/
public static EAtom guard(boolean bool) {
return bool ? TRUE : FALSE;
}
public static final ENil NIL = new ENil();
public static final EAtom am_EXIT = EAtom.intern("EXIT");
public static final EAtom IGNORED = EAtom.intern("ignored");
private static final EAtom am_badmatch = EAtom.intern("badmatch");
private static final EAtom am_case_clause = EAtom.intern("case_clause");
public static final EAtom am_undefined = EAtom.intern("undefined");
public static final EObject am_receive_clause = EAtom
.intern("receive_clause");
public static final EObject AM_NOT_IMPLEMENTED = EAtom
.intern("not_implemented");
public static final EAtom AM_TIMEOUT = EAtom.intern("timeout");
public static final EAtom am_try_case_clause = EAtom
.intern("try_case_clause");
public static final EAtom am_if_clause = EAtom.intern("if_clause");
public static final EBinary EMPTY_BINARY = new EBinary(new byte[0]);
public static final ByteBuffer[] EMPTY_BYTEBUFFER_ARR = new ByteBuffer[0];
public static final ByteBuffer EMPTY_BYTEBUFFER = ByteBuffer.allocate(0);
public static final EAtom am_infinity = EAtom.intern("infinity");
public static final EAtom am_noproc = EAtom.intern("noproc");
public static final EAtom am_error = EAtom.intern("error");
public static final EAtom am_exit = EAtom.intern("exit");
public static final EAtom am_throw = EAtom.intern("throw");
public static final EAtom am_badfile = EAtom.intern("badfile");
public static final EAtom am_value = EAtom.intern("value");
public static final EAtom am_timeout = EAtom.intern("timeout");
public static final EAtom am_function_clause = EAtom
.intern("function_clause");
public static final EAtom am_ok = EAtom.intern("ok");
public static final EAtom am_noconnect = EAtom.intern("noconnect");
public static final EAtom am_latin1 = EAtom.intern("latin1");
public static final EAtom am_utf8 = EAtom.intern("utf8");
public static final EAtom am_unicode = EAtom.intern("unicode");
private static final EAtom am_init = EAtom.intern("init");
private static final EAtom am_stop = EAtom.intern("stop");
protected static final EAtom am_new = EAtom.intern("new");
public static EBitStringBuilder bs_init(int size, int flags) {
if (size<0) throw ERT.badarg();
return new EBitStringBuilder(size, flags);
}
public static EBitStringBuilder bs_initBits(int size, int flags) {
if (size<0) throw ERT.badarg();
return new EBitStringBuilder(size/8, size%8, flags);
}
/**
* @param e
* @return
*/
public static String describe_exception(Throwable e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.print("java trace: ");
e.printStackTrace(pw);
pw.close();
return sw.toString();
}
public static void test_fun(EObject orig, EFun fun) {
if (fun == null) {
if ((orig.testFunction()) != null) {
throw new ErlangError(new ETuple2(am_badarity, new ETuple2(orig, NIL)));
} else {
throw badfun(orig);
}
}
}
static EInteger max_send_time = ERT.box(4294967295L);
static ESmall zero = ERT.box(0);
public static final boolean gt(EInteger v1, EInteger v2) {
return v1.erlangCompareTo(v2) > 0;
}
public static final boolean lt(EInteger v1, EInteger v2) {
return v1.erlangCompareTo(v2) < 0;
}
/*
public static final <T> boolean le(Comparable<T> v1, T v2) {
return v1.erlangCompareTo(v2) <= 0;
}
*/
@BIF
public static EObject cancel_timer(EObject ref)
{
// check arguments
ERef timer_ref = ref.testReference();
if (timer_ref == null) throw ERT.badarg();
long time_left = ETimerTask.cancel(timer_ref);
if (time_left > 0) {
return ERT.box(time_left);
} else {
return ERT.FALSE;
}
}
@BIF
public static EObject read_timer(EObject ref)
{
// check arguments
ERef timer_ref = ref.testReference();
if (timer_ref == null) throw ERT.badarg();
long time_left = ETimerTask.read_timer(timer_ref);
if (time_left > 0) {
return ERT.box(time_left);
} else {
return ERT.FALSE;
}
}
@BIF
public static EObject send_after(final EProc proc, EObject time, final EObject rcv, final EObject msg)
{
// check arguments
EInteger when = time.testInteger();
final EInternalPID rcv_pid = rcv.testInternalPID();
EAtom rcv_atom = rcv.testAtom();
if (when == null
|| gt(when, max_send_time)
|| lt(when, zero)
|| (rcv_pid == null && rcv_atom == null)) {
throw ERT.badarg(time, rcv, msg);
}
ETimerTask send_task = new ETimerTask(rcv_pid) {
@Override
public void on_timeout() throws Pausable {
EHandle p;
if ((p = rcv.testHandle()) != null) {
p.send(proc.self_handle(), msg);
return;
}
p = register.get(rcv);
if (p != null) {
p.send(proc.self_handle(), msg);
}
}
};
send_task.schedule(when.longValue());
return send_task.ref;
}
@BIF
public static EObject start_timer(EObject time, final EObject rcv, final EObject msg)
{
// check arguments
EInteger when = time.testInteger();
final EInternalPID rcv_pid = rcv.testInternalPID();
EAtom rcv_atom = rcv.testAtom();
if (when == null
|| gt(when, max_send_time)
|| lt(when, zero)
|| (rcv_pid == null && rcv_atom == null)) {
throw ERT.badarg(time, rcv, msg);
}
ETimerTask send_task = new ETimerTask(rcv_pid) {
@Override
public void on_timeout() throws Pausable {
ETuple3 timeout_msg = new ETuple3();
timeout_msg.elem1 = am_timeout;
timeout_msg.elem2 = this.ref;
timeout_msg.elem3 = msg;
EHandle p;
if ((p = rcv.testHandle()) != null) {
p.sendb(timeout_msg);
return;
}
p = register.get(rcv);
if (p != null) {
p.sendb(timeout_msg);
}
}
};
send_task.schedule(when.longValue());
return send_task.ref;
}
@Import(module="erlang", fun="dsend", arity=2)
static EFun erlang__dsend__2;
@Import(module="erlang", fun="dsend", arity=3)
static EFun erlang__dsend__3;
/**
* @param owner
* @param make
* @throws Pausable
*/
@BIF(name = "!")
public static EObject send(EProc proc, EObject pid, EObject msg) throws Pausable {
// TODO handle ports also?
proc.check_exit();
// log.log(Level.FINER, "ignored options to send: " + options);
EHandle p;
EAtom reg_name;
if ((p = pid.testHandle()) != null) {
send_to_handle(proc, p, msg);
} else if ((reg_name = pid.testAtom()) != null) {
send_to_locally_registered(proc, reg_name, msg);
} else {
ETuple t;
EAtom node_name;
if ((t = pid.testTuple()) != null && t.arity()==2 &&
(reg_name = t.elm(1).testAtom()) != null &&
(node_name = t.elm(2).testAtom()) != null)
{
send_to_remote(proc, t, node_name, reg_name, msg, null);
} else { // PID was of a bad type.
ipclog.info("trying to send message to "+pid+" failed.");
throw badarg(pid, msg);
}
}
// Arguments were of valid types; return the message:
return msg;
}
@BIF
public static EObject send(EProc proc, final EObject pid, final EObject msg, EObject options) throws Pausable {
// TODO handle ports also?
proc.check_exit();
// log.log(Level.FINER, "ignored options to send: " + options);
EHandle handle;
EAtom reg_name;
if ((handle = pid.testHandle()) != null) {
send_to_handle(proc, handle, msg);
return am_ok;
} else if ((reg_name = pid.testAtom()) != null) {
boolean ok = send_to_locally_registered(proc, reg_name, msg);
if (ok) return am_ok;
else throw badarg(pid, msg);
} else {
ETuple t;
EAtom node_name;
if ((t = pid.testTuple()) != null && t.arity()==2 &&
(reg_name = t.elm(1).testAtom()) != null &&
(node_name = t.elm(2).testAtom()) != null)
{
return send_to_remote(proc, t, node_name, reg_name, msg, options);
} else { // PID was of a bad type.
ipclog.info("trying to send message to "+pid+" failed.");
throw badarg(pid, msg);
}
}
}
private static EObject send_to_remote(EProc proc, ETuple dest, EAtom node_name, EAtom reg_name, EObject msg, EObject options) throws Pausable {
// INVARIANT: t == ETuple.make(node_name, reg_name)
if (node_name == getLocalNode().node) { // We're talking to ourselves
send_to_locally_registered(proc, reg_name, msg);
return am_ok; // Even if the process does not exist.
//TODO: Return 'noconnect' if options contain noconnect?...
} else { // We're talking to another node
if (ipclog.isLoggable(Level.FINE)) {
ipclog.fine("sending msg "+dest+" ! "+msg);
}
EAbstractNode node = EPeer.get(node_name);
if (node == null) {
EObject[] args = (options!=null
? new EObject[] { dest, msg, options }
: new EObject[] { dest, msg, ERT.NIL });
return erlang__dsend__3.invoke(proc, args);
} else {
node.dsig_reg_send(proc.self_handle(), reg_name, msg);
return am_ok;
}
}
}
private static boolean send_to_locally_registered(EProc proc, EAtom name, EObject msg) throws Pausable {
EHandle handle;
if ((handle = register.get(name)) != null) {
send_to_handle(proc, handle, msg);
return true;
} else return false;
}
private static void send_to_handle(EProc proc, EHandle handle, EObject msg) throws Pausable {
int penalty = handle.send(proc.self_handle(), msg);
proc.bump_reductions(penalty);
}
/**
* @return
*/
public static ENode getLocalNode() {
return localNode;
}
static EAtom am_undef = EAtom.intern("undef");
/**
* @param fun
* @return
*/
public static ErlangException undef(FunID fun, EObject... args) {
throw new ErlangError(am_undef, args);
}
public static EFun resolve_fun(EObject mod, EObject fun, int arity) {
EAtom f = fun.testAtom();
if (f == null) {
throw ERT.badarg(mod, fun, ESmall.make(arity));
}
JavaObject jo;
if ((jo = mod.testJavaObject()) != null) {
return jo.resolve_fun(f, arity);
}
EAtom m = mod.testAtom();
if (m == null)
{
final ETuple tup;
EAtom pmod;
if ((tup = mod.testTuple()) != null
&& tup.arity() > 0
&& (pmod=tup.elm(1).testAtom()) != null) {
final EFun pfun = EModuleManager.resolve(new FunID(pmod, f, arity+1));
return EFunCG.get_fun_with_handler(pmod.getName(), f.getName(), arity, new EFunHandler() {
@Override
public EObject invoke(EProc proc, EObject[] args) throws Pausable {
EObject[] real_args = new EObject[args.length + 1];
System.arraycopy(args, 0, real_args, 0, args.length);
real_args[args.length] = tup;
return pfun.invoke(proc, real_args);
}
}, ERT.class.getClassLoader());
}
throw ERT.badarg(mod, fun, ESmall.make(arity));
}
EFun efun = EModuleManager.resolve(new FunID(m, f, arity));
return efun;
}
public static EObject apply_last(EProc proc, EObject mod, EObject fun, EObject args) throws Pausable {
ESeq seq = args.testSeq();
if (seq == null) { throw ERT.badarg(mod, fun, args); }
return apply_list_last(proc, mod, fun, seq, seq.length());
}
public static EObject apply_list_last(EProc proc, EObject mod, EObject fun,
ESeq seq, int len) throws Pausable {
EAtom f = fun.testAtom();
ESeq a = seq.testSeq();
if (f == null || a == null)
throw ERT.badarg(mod, fun, seq);
EFun found = resolve_fun(mod, fun, len);
if (len > 9) {
// TODO: make it real tail recursion in stead
return found.invoke(proc, a.toArray());
}
proc.tail = found;
a = a.reverse();
switch (len) {
default:
throw new NotImplemented();
case 9:
proc.arg8 = a.head();
a = a.tail();
case 8:
proc.arg7 = a.head();
a = a.tail();
case 7:
proc.arg6 = a.head();
a = a.tail();
case 6:
proc.arg5 = a.head();
a = a.tail();
case 5:
proc.arg4 = a.head();
a = a.tail();
case 4:
proc.arg3 = a.head();
a = a.tail();
case 3:
proc.arg2 = a.head();
a = a.tail();
case 2:
proc.arg1 = a.head();
a = a.tail();
case 1:
proc.arg0 = a.head();
a = a.tail();
case 0:
}
return EProc.TAIL_MARKER;
}
static Map<EAtom, EHandle> register = new ConcurrentHashMap<EAtom, EHandle>();
/**
* @param aname
* @param handle
*/
public static void register(EAtom aname, EHandle handle) {
register.put(aname, handle);
handle.setName(aname);
}
public static boolean unregister(EAtom aname) {
EHandle val = register.remove(aname);
if (val != null) {
val.setName(ERT.am_undefined);
return true;
} else {
return false;
}
}
/**
* @param regname
* @return
*/
public static EObject whereis(EObject regname) {
EObject result = register.get(regname);
if (result == null) {
// System.out.println(regname + " => " + am_undefined);
return am_undefined;
} else {
// System.out.println(regname + " => " + result);
return result;
}
}
public static EObject badmatch(EObject val) {
throw new ErlangError(new ETuple2(am_badmatch, val));
}
public static void paranoiaCheck(final EObject e, String details) {
if (e == null) throw new Error("Bif returned null: "+details);
}
public static EObject decode_exception2(final ErlangException e) {
return e.getCatchValue();
}
public static ETuple3 decode_exception3(final ErlangException e) {
return e.getTryValue();
}
public static EObject case_end(EObject val) {
throw new ErlangError(ETuple.make(am_case_clause, val));
}
public static EObject if_end() {
throw new ErlangError(am_if_clause);
}
public static EObject try_case_end(EObject val) {
throw new ErlangError(ETuple.make(am_try_case_clause, val));
}
static kilim.Scheduler scheduler = new kilim.Scheduler(threadPoolSize());
static kilim.Scheduler async_scheduler = new kilim.Scheduler(asyncThreadPoolSize());
public static EAtom am_io = EAtom.intern("io");
public static EAtom am_attributes = EAtom.intern("attributes");
public static EAtom am_compile = EAtom.intern("compile");
public static EAtom am_exports = EAtom.intern("exports");
public static EAtom am_badfun = EAtom.intern("badfun");
public static EAtom am_badarity = EAtom.intern("badarity");
public static EAtom am_name = EAtom.intern("name");
public static EAtom am_arity = EAtom.intern("arity");
public static EAtom am_env = EAtom.intern("env");
public static EAtom am_index = EAtom.intern("index");
public static EAtom am_new_index = EAtom.intern("new_index");
public static EAtom am_new_uniq = EAtom.intern("new_uniq");
public static EAtom am_uniq = EAtom.intern("uniq");
public static EAtom am_pid = EAtom.intern("pid");
public static EAtom am_type = EAtom.intern("type");
public static EAtom am_local = EAtom.intern("local");
public static EAtom am_external = EAtom.intern("external");
public static EAtom am_DOWN = EAtom.intern("DOWN");
public static EAtom am_killed = EAtom.intern("killed");
public static void run(Task task) {
task.setScheduler(scheduler);
task.start();
}
public static void run_async(Task task) {
task.setScheduler(async_scheduler);
task.start();
}
/*
* Skeleton for receive statement:
*
* L1: <-------------------+
* <-----------+ |
* | |
* loop_rec L2 ------+---+ |
* ... | | |
* remove_message | | |
* jump L3 | | |
* ... | | |
* loop_rec_end L1 --+ | |
* L2: <---------------+ |
* wait L1 -----------------+ or wait_timeout
* timeout
*
* L3: Code after receive...
*
*/
/** peek mbox at current index (proc.midx), which is 0 upon entry to the loop. */
public static EObject loop_rec(EProc proc) {
int idx = proc.midx;
proc.in_receive = true;
EObject msg = proc.mbox.peek(idx);
if (ipclog.isLoggable(Level.FINE)) ipclog.fine("WAIT| entered loop #"+idx+" message="+msg);
return msg;
}
/** remove current message, and reset message index */
public static void remove_message(EProc proc) {
proc.mbox.remove(proc.midx);
proc.midx = 0;
proc.timeout_start = 0L;
proc.in_receive = false;
}
/** message did not match incoming, goto next message (will be followed by goto top-of-loop)*/
public static void loop_rec_end(EProc proc) {
proc.midx += 1;
}
/** wait for howlong, for one more message to be available */
public static boolean wait_timeout(EProc proc, EObject howlong)
throws Pausable {
try {
proc.check_exit();
if (ipclog.isLoggable(Level.FINE)) ipclog.fine("WAIT| "+proc+" waits for messages for "+howlong+" ms");
if (howlong == am_infinity) {
proc.mbox.untilHasMessages(proc.midx+1);
proc.check_exit();
if (ipclog.isLoggable(Level.FINE)) ipclog.fine("WAIT| "+proc+" wakes up on message");
return true;
} else {
long now = System.currentTimeMillis();
if (proc.midx == 0 || proc.timeout_start == 0L) {
proc.timeout_start = now;
}
EInteger ei;
if ((ei = howlong.testInteger()) == null)
throw new ErlangError(EAtom.intern("timeout_value"));
long end = proc.timeout_start + ei.longValue();
long left = end - now;
if (left < 0) {
return false;
}
if (!proc.in_receive) {
Task.sleep(left);
return false;
} else {
if (ipclog.isLoggable(Level.FINE)) ipclog.fine("WAIT| "+proc+" waiting for "+left+"ms for msg #"+(proc.midx+1));
boolean res = proc.mbox
.untilHasMessages(proc.midx + 1, left);
proc.check_exit();
if (ipclog.isLoggable(Level.FINE)) ipclog.fine("WAIT| "+proc+" wakes up "+(res?"on message" : "after timeout"));
return res;
}
}
} finally {
proc.in_receive = false;
}
}
/** wait forever, for one more message to be available */
public static void wait(EProc proc) throws Pausable {
try {
int idx = proc.midx + 1;
if (ipclog.isLoggable(Level.FINE)) ipclog.fine("WAIT| "+proc+" waits for "+idx+" messages");
proc.mbox.untilHasMessages(idx);
if (ipclog.isLoggable(Level.FINE)) ipclog.fine("WAIT| "+proc+" wakes up after timeout; now has "+(idx));
} finally {
proc.in_receive = false;
}
}
/** message reception timed out, reset message index */
public static void timeout(EProc proc) {
if (ipclog.isLoggable(Level.FINE)) ipclog.fine("WAIT| "+proc+" timed out");
proc.midx = 0;
proc.timeout_start = 0L;
proc.in_receive = false;
}
public static int unboxToInt(EInteger i) {
return i.intValue();
}
public static int unboxToInt(EObject i) {
ESmall ii;
if ((ii = i.testSmall()) == null)
throw ERT.badarg(i);
return ii.value;
}
public static int unboxToInt(ENumber i) {
return i.intValue();
}
public static double unboxToDouble(EInteger i) {
return i.doubleValue();
}
public static double unboxToDouble(ENumber i) {
return i.doubleValue();
}
public static double unboxToDouble(EObject i) {
ENumber num;
if ((num = i.testNumber()) == null)
throw ERT.badarg(i);
return num.doubleValue();
}
public static double unboxToDouble(int i) {
return i;
}
public static Boolean asBoolean(EObject obj) {
if (obj == ERT.TRUE) return true;
if (obj == ERT.FALSE) return false;
return null;
}
public static void check_exit(EProc p) {
p.check_exit();
}
public static EObject func_info(EAtom mod, EAtom fun, int arity) {
throw new ErlangError(am_function_clause);
}
public static EObject func_info(EAtom mod, EAtom fun, ESeq args) {
throw new ErlangError(am_function_clause, args);
}
public static ERef make_ref() {
return getLocalNode().createRef();
}
/**
* @param command
* @return
*/
public static EDriver find_driver(EString command) {
String name = command.stringValue();
int idx = name.indexOf(' ');
if (idx != -1) {
name = name.substring(0, idx);
}
return Drivers.getDriver(name);
}
/**
* @param job
*/
public static void run_async(final EAsync job, final EDriverTask dt) {
run_async(new Task() {
@Override
public void execute() throws Pausable, Exception {
job.async();
dt.async_done(job);
}
});
}
/**
* @return
*/
public static int threadPoolSize() {
String threads = ErjangConfig.getString("erjang.beam.option.S");
if (threads != null)
return Integer.parseInt(threads);
else
return Math.max(1, Runtime.getRuntime().availableProcessors()/2);
}
public static int asyncThreadPoolSize() {
String threads = ErjangConfig.getString("erjang.beam.option.A");
if (threads != null)
return Integer.parseInt(threads);
else
return 10;
}
public static int processLimit() {
String threads = ErjangConfig.getString("erjang.beam.option.P");
if (threads != null)
return Integer.parseInt(threads);
else
return 500000;
}
public static ESeq registered() {
ESeq res = ERT.NIL;
for (EAtom reg : register.keySet()) {
res = res.cons(reg);
}
return res;
}
/** Shuts down currently running OTP */
public static void shutdown() {
EObject init_proc = whereis(am_init);
ETuple tup = ETuple.make(am_stop, am_stop);
EPID init_pid;
if ((init_pid = init_proc.testPID()) != null) {
init_pid.sendb(tup);
}
}
/**
* Shutdown Kilim schedulers.
*/
/*package*/ static void shutdownSchedulers() {
scheduler.shutdown();
async_scheduler.shutdown();
}
static protected volatile InputStream in = System.in;
static protected volatile PrintStream out = System.out;
static protected volatile PrintStream err = System.err;
public static void set_stdio(InputStream in,
PrintStream out, PrintStream err) {
// avoid null
ERT.in = (in != null ? in : new NullInputStream());
ERT.out = (out != null ? out : new PrintStream(new NullOutputStream()));
ERT.err = (err != null ? err : new PrintStream(new NullOutputStream()));
}
public static void set_stdio(InputStream in,
OutputStream out, OutputStream err) {
// avoid null
ERT.in = (in != null ? in : new NullInputStream());
ERT.out = (out != null ? new PrintStream(out) : new PrintStream(new NullOutputStream()));
ERT.err = (err != null ? new PrintStream(err) : new PrintStream(new NullOutputStream()));
}
public static void setInputStream(InputStream in) {
ERT.in = (in != null ? in : new NullInputStream());
}
public static InputStream getInputStream() {
return in;
}
public static void setOutputStream(PrintStream out) {
ERT.out = (out != null ? out : new PrintStream(new NullOutputStream()));
}
public static void setOutputStream(OutputStream out) {
ERT.out = (out != null ? new PrintStream(out) : new PrintStream(new NullOutputStream()));
}
public static PrintStream getOutputStream() {
return out;
}
public static void setErrorStream(PrintStream err) {
ERT.err = (err != null ? err : new PrintStream(new NullOutputStream()));
}
public static void setErrorStream(OutputStream err) {
ERT.err = (err != null ? new PrintStream(err) : new PrintStream(new NullOutputStream()));
}
public static PrintStream getErrorStream() {
return err;
}
public static File newFile(String file_name) {
File file = new File(file_name);
if (file.isAbsolute())
return file;
return new File(Posix.getCWD(), file_name);
}
public static void debug(String text) {
log.fine(text);
}
public static void debug(boolean condition, String text) {
if (condition) {
debug(text);
}
}
static class NullInputStream extends InputStream {
@Override
public int read() throws IOException {
return -1;
}
}
static class NullOutputStream extends OutputStream {
@Override
public void write(int arg0) throws IOException {
// ignore
}
@Override
public void write(byte[] b) throws IOException {
// ignore
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
// ignore
}
}
/**
* contains versions, paths and other information.
* use {@link ERT#setRuntimeInfo(RuntimeInfo)} to set this field.
*/
public static RuntimeInfo runtime_info = null;
public static void setRuntimeInfo(RuntimeInfo info) {
// TODO perform synchronization? We usually set this
// field only once from Main before ERT is started
runtime_info = info;
if (runtime_info != null) {
System.setProperty("erjang.path", runtime_info.erl_bootstrap_ebindir);
}
}
public static EObject compile_elixir_regex(EObject patt, EObject opts) {
byte[] ops = opts.testBinary().getByteArray();
ECons l = ECompiledRE.decode_options(ops);
EObject tup = erjang.m.re.Native.compile(patt, l);
return tup.testTuple().elm(2);
}
public static void print_all_stack_traces() {
System.err.println("== Trace ==");
Mailbox<EObject> mbox = new Mailbox<>( EProc.process_count() );
ESeq all = EProc.processes();
for(EObject o : all) {
EInternalPID pid = (EInternalPID) o;
pid.task().printStackTrace( mbox );
}
while (true) {
EObject o = mbox.getb(1000);
if (o == null)
break;
ETuple2 tup = ETuple2.cast(o);
EHandle handle = tup.elm(1).testHandle();
ESeq stack = tup.elm(2).testSeq();
System.err.println("\n == "+handle+" : "+handle.name);
for (EObject elm : stack) {
ETuple4 tup4 = ETuple4.cast(elm);
if (tup4 != null) {
ESeq args = tup4.elem3.testSeq();
ESmall arity = tup4.elem3.testSmall();
if (arity == null && args != null) {
arity = ERT.box(args.length());
}
StringBuffer file_line = new StringBuffer();
ESeq info = tup4.elem4.testSeq();
if (info != null) {
String file = "?";
int line = -1;
for (EObject inf : info) {
ETuple2 t;
if ((t=ETuple2.cast(inf)) != null) {
ESmall n;
EString f;
if (t.elem1 == ErlangException.am_line && ((n = t.elem2.testSmall()) != null)) {
line = n.value;
} else
if (t.elem1 == ErlangException.am_file && ((f = t.elem2.testString()) != null)) {
file = f.stringValue();
}
}
}
if ( line != -1) {
file_line.append('(').append(file).append(':').append(line).append(')');
}
}
String module = tup4.elem1.toString();
String mfa;
System.err.print( mfa = make_width(20, module) + ":" + tup4.elem2 + "/" + arity );
System.err.println( " " + make_width(65-mfa.length(), file_line.toString()));
} else {
System.err.println(elm);
}
}
}
}
private static String make_width(int n, String s) {
if (s.length() > n) return s;
StringBuilder sb = new StringBuilder();
while ((sb.length()+s.length()) < n) {
sb.append(' ');
}
sb.append(s);
return sb.toString();
}
/*
*
async_scheduler.schedule(new Task() {
@Override
public void execute() throws Pausable, Exception {
try {
execute0();
} catch (Throwable e) {
e.printStackTrace();
}
}
public void execute0() throws Pausable, Exception {
System.err.println("== Trace ==");
Mailbox<EObject> mbox = new Mailbox<>( EProc.process_count() );
ESeq all = EProc.processes();
for(EObject o : all) {
EInternalPID pid = (EInternalPID) o;
pid.task().printStackTrace( mbox );
}
while (mbox.untilHasMessages(1, 1000L)) {
ETuple2 tup = (ETuple2) mbox.get();
EHandle handle = tup.elm(1).testHandle();
ESeq stack = tup.elm(2).testSeq();
System.err.println(" == "+handle+" : "+handle.name);
for (EObject o : stack) {
System.err.println(o);
}
}
}
}); */
}