From 7e133da3c90995f4817d0e1e2488994e79190079 Mon Sep 17 00:00:00 2001 From: Kresten Krab Thorup Date: Fri, 27 Nov 2009 08:38:45 +0100 Subject: [PATCH] now loading erl_ring0 --- .classpath | 1 + src/main/java/erjang/EAbstractNode.java | 226 ++++++++++++ src/main/java/erjang/EBitString.java | 15 +- src/main/java/erjang/EExecPort.java | 293 +++++++++++++++ src/main/java/erjang/EFun.java | 218 +++++++++-- src/main/java/erjang/EFunHandler.java | 27 ++ src/main/java/erjang/ELocalNode.java | 87 +++++ src/main/java/erjang/EModule.java | 338 +++++++++++++----- .../erjang/{EReference.java => ENode.java} | 17 +- src/main/java/erjang/EPID.java | 7 + src/main/java/erjang/EPeer.java | 28 ++ src/main/java/erjang/EPort.java | 20 ++ src/main/java/erjang/EProc.java | 39 +- src/main/java/erjang/ERT.java | 74 +++- src/main/java/erjang/ERef.java | 165 +++++++++ src/main/java/erjang/EString.java | 17 +- src/main/java/erjang/ErlangException.java | 28 +- src/main/java/erjang/Main.java | 13 +- .../java/erjang/beam/CompilerVisitor.java | 33 +- src/main/java/erjang/beam/EUtil.java | 2 +- .../beam/analysis/BeamTypeAnalysis.java | 4 +- src/main/java/erjang/m/erlang/ErlBif.java | 4 +- src/main/java/erjang/m/erlang/ErlPort.java | 74 ++++ src/main/java/erjang/m/erlang/ErlProc.java | 166 ++++++++- src/main/java/erjang/m/erlang/Native.java | 7 +- 25 files changed, 1734 insertions(+), 169 deletions(-) create mode 100644 src/main/java/erjang/EAbstractNode.java create mode 100644 src/main/java/erjang/EExecPort.java create mode 100644 src/main/java/erjang/EFunHandler.java create mode 100644 src/main/java/erjang/ELocalNode.java rename src/main/java/erjang/{EReference.java => ENode.java} (77%) create mode 100644 src/main/java/erjang/EPeer.java create mode 100644 src/main/java/erjang/ERef.java create mode 100644 src/main/java/erjang/m/erlang/ErlPort.java diff --git a/.classpath b/.classpath index 9336b9f1..1b0e4227 100644 --- a/.classpath +++ b/.classpath @@ -7,5 +7,6 @@ + diff --git a/src/main/java/erjang/EAbstractNode.java b/src/main/java/erjang/EAbstractNode.java new file mode 100644 index 00000000..2319ec43 --- /dev/null +++ b/src/main/java/erjang/EAbstractNode.java @@ -0,0 +1,226 @@ +/** + * 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.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; + + +/** + * + */ +public abstract class EAbstractNode { + + static String localHost = null; + EAtom node; + String host; + String alive; + EAtom cookie; + static EAtom defaultCookie = null; + + // Node types + static final int NTYPE_R6 = 110; // 'n' post-r5, all nodes + static final int NTYPE_R4_ERLANG = 109; // 'm' Only for source compatibility + static final int NTYPE_R4_HIDDEN = 104; // 'h' Only for source compatibility + + // Node capability flags + static final int dFlagPublished = 1; + static final int dFlagAtomCache = 2; + static final int dFlagExtendedReferences = 4; + static final int dFlagDistMonitor = 8; + static final int dFlagFunTags = 0x10; + static final int dFlagDistMonitorName = 0x20; // NOT USED + static final int dFlagHiddenAtomCache = 0x40; // NOT SUPPORTED + static final int dflagNewFunTags = 0x80; + static final int dFlagExtendedPidsPorts = 0x100; + static final int dFlagExportPtrTag = 0x200; // NOT SUPPORTED + static final int dFlagBitBinaries = 0x400; + static final int dFlagNewFloats = 0x800; + + int ntype = NTYPE_R6; + int proto = 0; // tcp/ip + int distHigh = 5; // Cannot talk to nodes before R6 + int distLow = 5; // Cannot talk to nodes before R6 + int creation = 0; + int flags = dFlagExtendedReferences | dFlagExtendedPidsPorts + | dFlagBitBinaries | dFlagNewFloats | dFlagFunTags + | dflagNewFunTags; + + + /* initialize hostname and default cookie */ + static { + try { + localHost = InetAddress.getLocalHost().getHostName(); + /* + * Make sure it's a short name, i.e. strip of everything after first + * '.' + */ + final int dot = localHost.indexOf("."); + if (dot != -1) { + localHost = localHost.substring(0, dot); + } + } catch (final UnknownHostException e) { + localHost = "localhost"; + } + + final String dotCookieFilename = System.getProperty("user.home") + + File.separator + ".erlang.cookie"; + BufferedReader br = null; + + try { + final File dotCookieFile = new File(dotCookieFilename); + + br = new BufferedReader(new FileReader(dotCookieFile)); + defaultCookie = EAtom.intern( br.readLine().trim() ); + } catch (final IOException e) { + defaultCookie = EAtom.intern(""); + } finally { + try { + if (br != null) { + br.close(); + } + } catch (final IOException e) { + } + } + } + + + /** + * @param node + */ + public EAbstractNode(EAtom node) { + this(node, defaultCookie); + } + + /** + * + */ + public EAbstractNode() { + this(EAtom.intern("nonode@nohost")); + } + + /** + * @param node + * @param cookie + */ + public EAbstractNode(EAtom node, EAtom cookie) { + + this.cookie = cookie; + + String name = node.getName(); + + final int i = name.indexOf('@', 0); + if (i < 0) { + alive = name; + host = localHost; + } else { + alive = name.substring(0, i); + host = name.substring(i + 1, name.length()); + } + + if (alive.length() > 0xff) { + alive = alive.substring(0, 0xff); + } + + node = EAtom.intern( alive + "@" + host ); + } + + /** + * Get the name of this node. + * + * @return the name of the node represented by this object. + */ + public EAtom node() { + return node; + } + + /** + * Get the hostname part of the nodename. Nodenames are composed of two + * parts, an alivename and a hostname, separated by '@'. This method returns + * the part of the nodename following the '@'. + * + * @return the hostname component of the nodename. + */ + public String host() { + return host; + } + + /** + * Get the alivename part of the hostname. Nodenames are composed of two + * parts, an alivename and a hostname, separated by '@'. This method returns + * the part of the nodename preceding the '@'. + * + * @return the alivename component of the nodename. + */ + public String alive() { + return alive; + } + + /** + * Get the authorization cookie used by this node. + * + * @return the authorization cookie used by this node. + */ + public EAtom cookie() { + return cookie; + } + + // package scope + int type() { + return ntype; + } + + // package scope + int distHigh() { + return distHigh; + } + + // package scope + int distLow() { + return distLow; + } + + // package scope: useless information? + int proto() { + return proto; + } + + // package scope + int creation() { + return creation; + } + + /** + * Set the authorization cookie used by this node. + * + * @return the previous authorization cookie used by this node. + */ + public EAtom setCookie(final EAtom cookie) { + final EAtom prev = this.cookie; + this.cookie = cookie; + return prev; + } + + +} diff --git a/src/main/java/erjang/EBitString.java b/src/main/java/erjang/EBitString.java index 9c4ad4ac..d772b15a 100644 --- a/src/main/java/erjang/EBitString.java +++ b/src/main/java/erjang/EBitString.java @@ -185,7 +185,7 @@ public int bitAt(int bitPos) { public int intBitsAt(int bitPos, int bitLength) { - if (bitPos + bitLength > this.bits) { + if (bitOff + bitPos + bitLength > this.bits) { throw new IllegalArgumentException( "reading beyond end of BitString"); } @@ -369,4 +369,17 @@ public String toString() { return sb.toString(); } + /** + * @return + */ + public byte[] toByteArray() { + if (!isBinary()) throw ERT.badarg(); + + byte[] result = new byte[bits/8]; + for (int i = 0; i < result.length; i++) { + result[i] = this.byteAt(i*8); + } + return result; + } + } diff --git a/src/main/java/erjang/EExecPort.java b/src/main/java/erjang/EExecPort.java new file mode 100644 index 00000000..0ec5097a --- /dev/null +++ b/src/main/java/erjang/EExecPort.java @@ -0,0 +1,293 @@ +/** + * 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.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * + */ +public class EExecPort extends EPort { + + static enum Mode { + STREAM, PACKET, LINE + }; + + private boolean send_binary_data; + private boolean is_out_only; + private boolean is_in_only; + private boolean send_eof; + private boolean hide; + private int port_out_fd; + private int port_in_fd; + private boolean send_exit_status; + private int packet; + private int line_length; + + private Mode mode = Mode.STREAM; + private EProc owner; + private Process process; + private DataOutputStream out; + private DataInputStream in; + + /** + * @param proc + * @param name + * @param portSetting + */ + public EExecPort(EProc proc, ETuple2 name, EObject portSetting) { + + this.owner = proc; + + // argument can be any list, ... turn it into a string + ECons cons; + if ((cons = name.elem2.testCons()) == null) + throw ERT.badarg(); + EString es = EString.make(cons); + + // set by options + String[] cmd = new String[] { es.stringValue() }; + this.packet = -1; // not set + this.line_length = -1; + + String cwd = System.getProperty("user.dir"); + + Map env = new HashMap(System.getenv()); + + this.send_exit_status = false; + this.port_in_fd = 1; + this.port_out_fd = 2; + + this.hide = false; + this.send_eof = false; + + this.is_in_only = false; + this.is_out_only = false; + this.send_binary_data = false; + + ECons settings = portSetting.testCons(); + if (settings == null) + throw ERT.badarg(); + + for (; settings != null && !settings.isNil(); settings = settings + .tail().testCons()) { + + EObject val = settings.head(); + ETuple tup; + if ((tup = val.testTuple()) != null) { + ETuple2 tup2; + if ((tup2 = ETuple2.cast(tup)) != null) { + + if (tup2.elem1 == am_args) { + ESeq list = tup2.elem2.testWellformedList(); + EObject[] nargs = list.toArray(); + + String[] new_cmd = new String[nargs.length + 1]; + new_cmd[0] = cmd[0]; + for (int i = 0; i < nargs.length; i++) { + new_cmd[i + 1] = EString.make(nargs[i]) + .stringValue(); + } + cmd = new_cmd; + + } else if (tup2.elem1 == am_arg0) { + String[] new_cmd = new String[2]; + new_cmd[0] = cmd[0]; + new_cmd[1] = EString.make(tup2.elem2).stringValue(); + + } else if (tup2.elem1 == am_packet) { + packet = tup2.elem2.asInt(); + mode = Mode.PACKET; + + } else if (tup2.elem1 == am_cd) { + cwd = EString.make(tup2.elem2).stringValue(); + + } else if (tup2.elem1 == am_env) { + + ESeq ee; + if ((ee = tup2.elem2.testWellformedList()) == null) { + throw ERT.badarg(); + } + + EObject[] envs = ee.toArray(); + for (int i = 0; i < envs.length; i++) { + ETuple2 e = ETuple2.cast(envs[i].testTuple()); + if (e.elem2 == ERT.FALSE) { + env.remove(EString.make(e.elem1).stringValue()); + } else { + env.put(EString.make(e.elem1).stringValue(), + EString.make(e.elem2).stringValue()); + } + } + + } else if (tup2.elem1 == am_line) { + line_length = tup2.elem2.asInt(); + mode = Mode.LINE; + + } else { + + throw ERT.badarg(); + } + + } + } else if (val == am_stream) { + mode = Mode.STREAM; + + } else if (val == am_use_stdio) { + port_in_fd = 1; + port_out_fd = 2; + + } else if (val == am_nouse_stdio) { + port_in_fd = 3; + port_out_fd = 4; + + } else if (val == am_hide) { + hide = true; + + } else if (val == am_exit_status) { + send_exit_status = true; + + } else if (val == am_eof) { + send_eof = true; + + } else if (val == am_in) { + is_in_only = true; + + } else if (val == am_out) { + is_out_only = true; + + } else if (val == am_binary) { + send_binary_data = true; + + } + } + + String[] envp = new String[env.size()]; + int pos = 0; + for (Map.Entry e : env.entrySet()) { + envp[pos++] = e.getKey() + "=" + e.getValue(); + } + + File file = new File(cmd[0]); + if (!file.exists()) { + throw new ErlangException(EAtom.intern("enoent")); + } + + if (!file.canExecute()) { + throw new ErlangException(EAtom.intern("eaccess")); + } + + try { + this.process = Runtime.getRuntime().exec(cmd, envp, new File(cwd)); + } catch (IOException e1) { + throw new ErlangException(e1); + } + + this.out = new DataOutputStream(this.process.getOutputStream()); + this.in = new DataInputStream(this.process.getInputStream()); + } + + public void send(EObject val) { + + // first, grok argument + byte[] data; + int data_off; + int data_len; + + ECons cons; + EBitString bin; + if ((cons=val.testCons()) != null) { + EString str = EString.make(cons); + data = str.data; + data_off = str.off; + data_len = data.length-data_off; + + } else if ((bin=val.testBinString()) != null) { + if (!bin.isBinary()) throw ERT.badarg(val); + + if ((bin.bitOff % 8) == 0) { + data = bin.data; + data_off = bin.bitOff/8; + data_len = bin.bits/8; + } else { + data = bin.toByteArray(); + data_off = 0; + data_len = data.length; + + } + } else { + throw ERT.badarg(val); + } + + try { + internal_send(data, data_off, data_len); + } catch (IOException e) { + owner.self().send( ETuple.make(ERT.EXIT, this, ERT.get_posix_code(e)) ); + } + + } + + public synchronized void internal_send(byte[] data, int data_off, int data_len) throws IOException { + + + + switch (mode) { + case STREAM: + this.out.write(data, data_off, data_len); + return; + + case PACKET: + switch (packet) { + case 1: + for (int off = 0; off < data_len; off += 256) { + int rest = Math.min(256, data_len - off); + out.writeByte(rest); + out.write(data, data_off+off, rest); + } + return; + + case 2: + for (int off = 0; off < data_len; off += (1 << 16)) { + int rest = Math.min((1 << 16), data_len - off); + out.writeShort(rest); + out.write(data, data_off+off, rest); + } + return; + + case 4: + out.writeInt(data_len); + out.write(data, data_off, data_len); + return; + + default: + throw new Error("should not happen"); + } + + case LINE: + throw new NotImplemented(); + + + } + } +} diff --git a/src/main/java/erjang/EFun.java b/src/main/java/erjang/EFun.java index 8f846af5..4a885f71 100644 --- a/src/main/java/erjang/EFun.java +++ b/src/main/java/erjang/EFun.java @@ -18,10 +18,15 @@ package erjang; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -29,19 +34,20 @@ import erjang.beam.CompilerVisitor; import erjang.beam.EUtil; -public abstract class EFun extends EObject { - +public abstract class EFun extends EObject implements Opcodes { + public abstract int arity(); - + public EFun testFunction2(int nargs) { - if (this.arity() == nargs) return this; + if (this.arity() == nargs) + return this; return null; } - + public EFun testFunction() { return this; } - + @Override int cmp_order() { return 3; @@ -54,9 +60,15 @@ int cmp_order() { public abstract EObject invoke(EProc proc, EObject[] args); private static final Type EFUN_TYPE = Type.getType(EFun.class); + private static final Type EFUNHANDLER_TYPE = Type + .getType(EFunHandler.class); private static final Type EOBJECT_TYPE = Type.getType(EObject.class); private static final Type EOBJECT_ARR_TYPE = Type.getType(EObject[].class); private static final Type EPROC_TYPE = Type.getType(EProc.class); + static final String GO_DESC = "(" + EPROC_TYPE.getDescriptor() + ")" + + EOBJECT_TYPE.getDescriptor(); + private static final String EPROC_NAME = EPROC_TYPE.getInternalName(); + private static final String EOBJECT_DESC = EOBJECT_TYPE.getDescriptor(); /** * @param method @@ -73,13 +85,14 @@ static EFun make(Method method) { Class[] parameterTypes = method.getParameterTypes(); int ary = parameterTypes.length; boolean proc = (ary > 0 && parameterTypes[0].equals(EProc.class)); - if (proc) ary -= 1; + if (proc) + ary -= 1; String mname = EUtil.getJavaName(EAtom.intern(method.getName()), ary); - + Class declaringClass = method.getDeclaringClass(); Type type = Type.getType(declaringClass); - byte[] data = CompilerVisitor.make_invoker(type, mname, method.getName(), ary, - proc, 0); + byte[] data = CompilerVisitor.make_invoker(type, mname, method + .getName(), ary, proc, 0); String clname = type.getClassName() + "$FN_" + mname; @@ -87,9 +100,9 @@ static EFun make(Method method) { // make sure we have it's superclass loaded get_fun_class(ary); - - Class res_class = ERT.defineClass(cl, clname.replace('/', '.'), data, 0, - data.length); + + Class res_class = ERT.defineClass(cl, clname.replace( + '/', '.'), data, 0, data.length); try { return res_class.newInstance(); @@ -104,29 +117,29 @@ static Class get_fun_class(int arity) { String self_type = EFUN_TYPE.getInternalName() + arity; try { - return (Class) Class.forName(EFUN_TYPE.getClassName() + arity, true, - EFun.class.getClassLoader()); + return (Class) Class.forName(EFUN_TYPE + .getClassName() + + arity, true, EFun.class.getClassLoader()); } catch (ClassNotFoundException ex) { // that's what we'll do here... } byte[] data = gen_fun_class_data(arity); - + ETuple.dump(self_type, data); - - return ERT.defineClass(EFun.class.getClassLoader(), self_type.replace('/', '.'), data, 0, - data.length); + + return ERT.defineClass(EFun.class.getClassLoader(), self_type.replace( + '/', '.'), data, 0, data.length); } static byte[] gen_fun_class_data(int arity) { - + String self_type = EFUN_TYPE.getInternalName() + arity; ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, self_type, null, EFUN_TYPE.getInternalName(), null); - MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, "invoke", EUtil.getSignature(arity, true), null, null); @@ -136,14 +149,13 @@ static byte[] gen_fun_class_data(int arity) { "invoke_tail", EUtil.getSignature(arity, true), null, null); mv.visitEnd(); - mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "arity", "()I", null, null); mv.visitCode(); mv.visitLdcInsn(new Integer(arity)); mv.visitInsn(Opcodes.IRETURN); mv.visitMaxs(2, 2); mv.visitEnd(); - + mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "invoke", "(" + EPROC_TYPE.getDescriptor() + EOBJECT_ARR_TYPE.getDescriptor() + ")" + EOBJECT_TYPE.getDescriptor(), null, null); @@ -165,18 +177,172 @@ static byte[] gen_fun_class_data(int arity) { mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "", "()V", null, null); mv.visitVarInsn(Opcodes.ALOAD, 0); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, EFUN_TYPE.getInternalName(), "", "()V"); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, EFUN_TYPE.getInternalName(), + "", "()V"); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(arity + 2, arity + 2); mv.visitEnd(); - - + cw.visitEnd(); byte[] data = cw.toByteArray(); return data; } + static Map> handlers = new HashMap>(); + + static EFun get_fun_with_handler(int arity, EFunHandler handler) { + Constructor h = handlers.get(arity); + + if (h == null) { + + String self_type = EFUN_TYPE.getInternalName() + "Handler" + arity; + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + String super_class_name = EFUN_TYPE.getInternalName() + arity; + cw.visit(Opcodes.V1_4, ACC_PUBLIC, self_type, null, + super_class_name, null); + + // create handler field + FieldVisitor fv = cw.visitField(ACC_PRIVATE, "handler", + EFUNHANDLER_TYPE.getDescriptor(), null, null); + fv.visitEnd(); + + // make constructor + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "(" + + EFUNHANDLER_TYPE.getDescriptor() + ")V", null, null); + mv.visitCode(); + + mv.visitVarInsn(ALOAD, 0); + mv + .visitMethodInsn(INVOKESPECIAL, super_class_name, "", + "()V"); + + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, self_type, "handler", EFUNHANDLER_TYPE + .getDescriptor()); + + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + + // make invoke_tail method + CompilerVisitor.make_invoketail_method(cw, self_type, arity, 0); + make_invoke_method(cw, self_type, arity); + make_go_method(cw, self_type, arity); + + cw.visitEnd(); + byte[] data = cw.toByteArray(); + + ETuple.dump(self_type, data); + + Class clazz = ERT.defineClass(EFun.class + .getClassLoader(), self_type.replace('/', '.'), data, 0, + data.length); + + try { + h = clazz.getConstructor(EFunHandler.class); + } catch (Exception e) { + throw new Error(e); + } + + handlers.put(arity, h); + } + + try { + return h.newInstance(handler); + } catch (Exception e) { + throw new Error(e); + } + } + + private static void make_go_method(ClassWriter cw, String self_type, + int arity) { + MethodVisitor mv; + mv = cw.visitMethod(ACC_PUBLIC, "go", GO_DESC, null, null); + mv.visitCode(); + + for (int i = 0; i < arity; i++) { + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(GETFIELD, EPROC_NAME, "arg" + i, EOBJECT_DESC); + mv.visitVarInsn(ASTORE, i + 2); + } + for (int i = 0; i < arity; i++) { + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(ACONST_NULL); + mv.visitFieldInsn(PUTFIELD, EPROC_NAME, "arg" + i, EOBJECT_DESC); + } + + // load handler + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, self_type, "handler", EFUNHANDLER_TYPE + .getDescriptor()); + + // load proc + mv.visitVarInsn(ALOAD, 1); + + // create array + mv.visitLdcInsn(new Integer(arity)); + mv.visitTypeInsn(ANEWARRAY, EOBJECT_TYPE.getInternalName()); + + for (int i = 0; i < arity; i++) { + mv.visitInsn(DUP); + mv.visitLdcInsn(new Integer(i)); + mv.visitVarInsn(ALOAD, i + 2); + mv.visitInsn(AASTORE); + } + + mv.visitMethodInsn(INVOKEINTERFACE, EFUNHANDLER_TYPE.getInternalName(), "invoke", + "(" + EPROC_TYPE.getDescriptor() + "[" + EOBJECT_DESC + ")" + EOBJECT_DESC); + mv.visitInsn(ARETURN); + mv.visitMaxs(arity + 2, arity + 2); + mv.visitEnd(); + + cw.visitEnd(); + } + + private static void make_invoke_method(ClassWriter cw, String self_type, + int arity) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "invoke", EUtil + .getSignature(arity, true), null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + for (int i = 0; i < arity; i++) { + mv.visitVarInsn(ALOAD, i + 2); + } + mv.visitMethodInsn(INVOKEVIRTUAL, self_type, "invoke_tail", EUtil + .getSignature(arity, true)); + mv.visitVarInsn(ASTORE, arity + 2); + + Label done = new Label(); + Label loop = new Label(); + mv.visitLabel(loop); + mv.visitVarInsn(ALOAD, arity + 2); + mv.visitFieldInsn(GETSTATIC, EPROC_TYPE.getInternalName(), + "TAIL_MARKER", EOBJECT_TYPE.getDescriptor()); + mv.visitJumpInsn(IF_ACMPNE, done); + + // load proc + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(GETFIELD, EPROC_TYPE.getInternalName(), "tail", + EFUN_TYPE.getDescriptor()); + mv.visitVarInsn(ALOAD, 1); + + mv.visitMethodInsn(INVOKEVIRTUAL, EFUN_TYPE.getInternalName(), "go", + GO_DESC); + mv.visitVarInsn(ASTORE, arity + 2); + + mv.visitJumpInsn(GOTO, loop); + + mv.visitLabel(done); + mv.visitVarInsn(ALOAD, arity + 2); + mv.visitInsn(ARETURN); + mv.visitMaxs(arity + 2, arity + 2); + mv.visitEnd(); + } + /** * @param mv * @param i diff --git a/src/main/java/erjang/EFunHandler.java b/src/main/java/erjang/EFunHandler.java new file mode 100644 index 00000000..1969b939 --- /dev/null +++ b/src/main/java/erjang/EFunHandler.java @@ -0,0 +1,27 @@ +/** + * 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; + +/** + * + */ +public interface EFunHandler { + EObject invoke(EProc proc, EObject[] args); +} diff --git a/src/main/java/erjang/ELocalNode.java b/src/main/java/erjang/ELocalNode.java new file mode 100644 index 00000000..2458d188 --- /dev/null +++ b/src/main/java/erjang/ELocalNode.java @@ -0,0 +1,87 @@ +/** + * 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; + + +/** + * + */ +public class ELocalNode extends EAbstractNode { + + private int serial = 0; + private int pidCount = 1; + private int portCount = 1; + private int refId[]; + + + protected ELocalNode() { + super(); + init(); + } + + /** + * Create a node with the given name and the default cookie. + */ + protected ELocalNode(final EAtom node) { + super(node); + init(); + } + + /** + * Create a node with the given name and cookie. + */ + protected ELocalNode(final EAtom node, final EAtom cookie) { + super(node, cookie); + init(); + } + + + + + private void init() { + serial = 0; + pidCount = 1; + portCount = 1; + refId = new int[3]; + refId[0] = 1; + refId[1] = 0; + refId[2] = 0; + } + + + public synchronized ERef createRef() { + final ERef r = new ERef(node, refId, creation); + + // increment ref ids (3 ints: 18 + 32 + 32 bits) + refId[0]++; + if (refId[0] > 0x3ffff) { + refId[0] = 0; + + refId[1]++; + if (refId[1] == 0) { + refId[2]++; + } + } + + return r; + + } + +} diff --git a/src/main/java/erjang/EModule.java b/src/main/java/erjang/EModule.java index 6b49f9cc..d9bfef84 100644 --- a/src/main/java/erjang/EModule.java +++ b/src/main/java/erjang/EModule.java @@ -23,8 +23,13 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.math.BigInteger; import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; import sun.tools.java.Imports; @@ -35,47 +40,181 @@ public abstract class EModule { - - static private Map modules = new HashMap(); - - private static final EAtom AM_BADARG = EAtom.intern("badarg"); + static private Map infos = new HashMap(); - public EModule() { - // TODO: handle if there is a module of this name already! - modules.put(EAtom.intern(this.module_name()), this); - } + static class FunctionInfo { + private final FUN fun; + + /** + * @param fun + */ + public FunctionInfo(FUN fun) { + this.fun = fun; + } - private Map exports = new HashMap(); - private Map imports = new HashMap(); - - public void resolve() - { - for (Map.Entry imp : imports.entrySet()) { - FUN spec = imp.getKey(); - Field field = imp.getValue(); - - EModule mod = modules.get(spec.module); - if (mod == null) { - throw new Error("cannot import "+spec+" from "+module_name()+" (no such module)"); + EModule defining_module; + EFun resolved_value; + Collection resolve_points = new ArrayList(); + + /** + * @param ref + * @throws IllegalAccessException + * @throws IllegalArgumentException + */ + synchronized boolean add_import(Field ref) throws Exception { + resolve_points.add(ref); + if (resolved_value != null) { + ref.set(null, resolved_value); + return true; + } else { + Object h = EFun.get_fun_with_handler(fun.arity, + new EFunHandler() { + public EObject invoke(EProc proc, EObject[] args) { + throw new Error("{undef, [{"+fun.module+","+fun.function+","+fun.arity+"}]}"); + } + }); + ref.set(null, h); } - - EFun fun = mod.exports.get(spec); - if (fun == null) { - throw new Error("cannot import "+spec+" from "+module_name()+" (no such function)"); + return false; + } + + /** + * @param fun2 + * @param value + */ + synchronized void add_export(EModule definer, FUN fun2, EFun value) + throws Exception { + this.resolved_value = value; + this.defining_module = definer; + + for (Field f : resolve_points) { + f.set(null, value); } - - try { - field.set(null, fun); - } catch (Exception e) { - System.out.println("type "+fun.getClass()); - System.out.println(" super "+fun.getClass().getSuperclass()); - throw new Error("cannot assign to "+field, e); + } + + /** + * @return + * + */ + public EFun resolve() { + return resolved_value; + } + + /** + * @return + */ + public boolean exported() { + return resolved_value != null; + } + } + + static class ModuleInfo { + + private final EAtom name; + private EModule module; + + /** + * @param module + */ + public ModuleInfo(EAtom module) { + this.name = module; + } + + Map binding_points = new HashMap(); + + /** + * @param fun + * @param ref + * @return + * @throws Exception + */ + public boolean add_import(FUN fun, Field ref) throws Exception { + FunctionInfo info = get_function_info(fun); + return info.add_import(ref); + } + + private synchronized FunctionInfo get_function_info(FUN fun) { + FunctionInfo info = binding_points.get(fun); + if (info == null) { + binding_points.put(fun, info = new FunctionInfo(fun)); } + return info; + } + + /** + * @param fun + * @param value + * @throws Exception + */ + public void add_export(EModule definer, FUN fun, EFun value) + throws Exception { + get_function_info(fun).add_export(definer, fun, value); + } + + /** + * @param eModule + */ + public void setModule(EModule eModule) { + this.module = eModule; + } + + /** + * @param start + * @return + */ + public EFun resolve(FUN fun) { + return get_function_info(fun).resolve(); + } + + /** + * @param fun + * @return + */ + public boolean exports(FUN fun) { + return get_function_info(fun).exported(); + } + + } + + boolean add_import(FUN fun, Field ref) throws Exception { + return get_module_info(fun.module).add_import(fun, ref); + } + + private static ModuleInfo get_module_info(EAtom module) { + ModuleInfo mi; + synchronized (infos) { + mi = infos.get(module); + if (mi == null) { + infos.put(module, mi = new ModuleInfo(module)); + } + } + return mi; + } + + void add_export(FUN fun, EFun value) throws Exception { + get_module_info(fun.module).add_export(this, fun, value); + } + + // static private Map modules = new HashMap(); + + private static final EAtom AM_BADARG = EAtom.intern("badarg"); + + public EModule() { + // TODO: handle if there is a module of this name already! + // modules.put(EAtom.intern(this.module_name()), this); + + get_module_info(EAtom.intern(module_name())).setModule(this); + + try { + read_annotations(); + } catch (Exception e) { + throw new Error(e); } } @SuppressWarnings("unchecked") - private void read_annotations() { + private void read_annotations() throws Exception { Class module = getClass(); Field[] fields = module.getDeclaredFields(); @@ -94,9 +233,11 @@ private void read_annotations() { if (imp != null) { field.setAccessible(true); FUN f; - imports.put(f = new FUN(imp), field); - System.out.println(" import " + f); + boolean resolved = add_import(f = new FUN(imp), field); + + // System.out.println(" import " + f + // + (resolved ? "resolved" : "")); continue; } @@ -115,14 +256,16 @@ private void read_annotations() { throw new Error("field " + field + " not initialized"); FUN f; - exports.put(f = new FUN(exp), value); + add_export(f = new FUN(exp), value); - System.out.println(" export " + f); + // System.out.println(" export " + f); continue; } } + process_native_annotations(this.getClass()); + String cname = module.getName(); String nname = cname.substring(0, cname.lastIndexOf('.')) + ".Native"; @@ -132,7 +275,7 @@ private void read_annotations() { natives = (Class) Class.forName(nname, true, module.getClassLoader()); } catch (ClassNotFoundException e) { - System.out.println("no native: "+nname); +// System.out.println("no native: " + nname); return; } @@ -145,67 +288,67 @@ private void read_annotations() { } for (Class nat : en.getNativeClasses()) { + process_native_annotations(nat); + } - for (Field field : nat.getDeclaredFields()) { - if (!Modifier.isStatic(field.getModifiers())) - continue; + } - Import imp = field.getAnnotation(Import.class); - if (imp != null) { - field.setAccessible(true); - FUN f; - imports.put(f = new FUN(imp), field); + private void process_native_annotations(Class nat) throws Exception { + for (Field field : nat.getDeclaredFields()) { + if (!Modifier.isStatic(field.getModifiers())) + continue; - System.out.println("N import " + f); + Import imp = field.getAnnotation(Import.class); + if (imp != null) { + field.setAccessible(true); + FUN f; + add_import(f = new FUN(imp), field); - continue; - } + // System.out.println("N import " + f); + continue; } - // for native methods - Method[] methods = nat.getDeclaredMethods(); - - next_method: - for (Method method : methods) { - - BIF efun = method.getAnnotation(BIF.class); - if (efun != null && efun.type().export()) { - String mod = module_name(); - - String name = efun.name(); - if (name.equals("__SELFNAME__")) - name = method.getName(); - - Class[] parameterTypes = method.getParameterTypes(); - int arity = parameterTypes.length; - if (arity > 0 - && parameterTypes[0] - .equals(EProc.class)) { - arity -= 1; - } - - for (int i = 0; i < parameterTypes.length; i++) { - if (i == 0 && parameterTypes[i].equals(EProc.class)) continue; - if (parameterTypes[i].equals(EObject.class)) continue; - - // we only allow EProc as zero'th and EObject as other args in exported functions - continue next_method; - } - - FUN f = new FUN(mod, name, arity); - - System.out.println("N export " + f); - - if (!exports.containsKey(f)) { - exports.put(f, EFun.make(method)); - } + } + + // for native methods + Method[] methods = nat.getDeclaredMethods(); + + next_method: for (Method method : methods) { + + BIF efun = method.getAnnotation(BIF.class); + if (efun != null && efun.type().export()) { + String mod = module_name(); + + String name = efun.name(); + if (name.equals("__SELFNAME__")) + name = method.getName(); + + Class[] parameterTypes = method.getParameterTypes(); + int arity = parameterTypes.length; + if (arity > 0 && parameterTypes[0].equals(EProc.class)) { + arity -= 1; } + for (int i = 0; i < parameterTypes.length; i++) { + if (i == 0 && parameterTypes[i].equals(EProc.class)) + continue; + if (parameterTypes[i].equals(EObject.class)) + continue; + + // we only allow EProc as zero'th and EObject as other args + // in exported functions + continue next_method; + } + + FUN f = new FUN(mod, name, arity); + + // System.out.println("N export " + f); + + add_export(f, EFun.make(method)); } } - } /** @@ -250,7 +393,7 @@ public static void load_module(EAtom mod, EBinary bin) { new Object[] { mod, bin }, e); } - mi.read_annotations(); + // mi.read_annotations(); } @@ -273,9 +416,28 @@ public static EModule load_module(EAtom mod, URL url) { new Object[] { mod }, e); } - mi.read_annotations(); + // mi.read_annotations(); return mi; } + /** + * @param start + * @return + */ + public static EFun resolve(FUN start) { + return get_module_info(start.module).resolve(start); + } + + /** + * @param m + * @param f + * @param value + * @return + */ + public static boolean function_exported(EAtom m, EAtom f, int a) { + FUN fun = new FUN(m,f,a); + return get_module_info(m).exports(fun); + } + } diff --git a/src/main/java/erjang/EReference.java b/src/main/java/erjang/ENode.java similarity index 77% rename from src/main/java/erjang/EReference.java rename to src/main/java/erjang/ENode.java index ec1aa7c9..9d551619 100644 --- a/src/main/java/erjang/EReference.java +++ b/src/main/java/erjang/ENode.java @@ -22,17 +22,14 @@ /** * */ -public class EReference extends EObject { +public class ENode extends ELocalNode { - @Override - int compare_same(EObject rhs) { - // TODO: make faster - return toString().compareTo(rhs.toString()); - } - - @Override - int cmp_order() { - return 2; + /** + * @param amNode + */ + public boolean connect(EAtom node) { + // TODO: implement + return false; } } diff --git a/src/main/java/erjang/EPID.java b/src/main/java/erjang/EPID.java index 64bc8e59..931b5f5b 100644 --- a/src/main/java/erjang/EPID.java +++ b/src/main/java/erjang/EPID.java @@ -42,6 +42,13 @@ int compare_same(EObject rhs) { // TODO: make faster return toString().compareTo(rhs.toString()); } + + /** + * @param msg + */ + public void send(EObject msg) { + throw new NotImplemented(); + } } diff --git a/src/main/java/erjang/EPeer.java b/src/main/java/erjang/EPeer.java new file mode 100644 index 00000000..0328afef --- /dev/null +++ b/src/main/java/erjang/EPeer.java @@ -0,0 +1,28 @@ +/** + * 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; + + +/** + * + */ +public class EPeer extends EAbstractNode { + +} diff --git a/src/main/java/erjang/EPort.java b/src/main/java/erjang/EPort.java index 512de23f..39803cf2 100644 --- a/src/main/java/erjang/EPort.java +++ b/src/main/java/erjang/EPort.java @@ -37,10 +37,30 @@ public EString getName() { @Override int compare_same(EObject rhs) { + // TODO: make faster return toString().compareTo(rhs.toString()); } + static EAtom am_fd = EAtom.intern("fd"); + static EAtom am_packet = EAtom.intern("packet"); + static EAtom am_stream = EAtom.intern("stream"); + static EAtom am_line = EAtom.intern("line"); + static EAtom am_hide = EAtom.intern("hide"); + static EAtom am_cd = EAtom.intern("cd"); + static EAtom am_env = EAtom.intern("env"); + static EAtom am_args = EAtom.intern("args"); + static EAtom am_arg0 = EAtom.intern("arg0"); + static EAtom am_exit_status = EAtom.intern("exit_status"); + static EAtom am_use_stdio = EAtom.intern("use_stdio"); + static EAtom am_nouse_stdio = EAtom.intern("nouse_stdio"); + static EAtom am_stderr_to_stdout = EAtom.intern("stderr_to_stdout"); + static EAtom am_in = EAtom.intern("in"); + static EAtom am_out = EAtom.intern("out"); + static EAtom am_binary = EAtom.intern("binary"); + static EAtom am_eof = EAtom.intern("eof"); + + } diff --git a/src/main/java/erjang/EProc.java b/src/main/java/erjang/EProc.java index 204e7adc..13002f49 100644 --- a/src/main/java/erjang/EProc.java +++ b/src/main/java/erjang/EProc.java @@ -24,20 +24,25 @@ + /** * An erlang process */ public class EProc { public static final EObject TAIL_MARKER = new ETailMarker(); + + private static final EAtom am_trap_exit = EAtom.intern("trap_exit"); public EFun tail; public EObject arg0, arg1, arg2, arg3, arg4, arg5, arg6; + private EPID self = new EPID(); + /** * @return */ public EPID self() { - throw new NotImplemented(); + return self; } /** * @param key @@ -46,6 +51,8 @@ public EPID self() { */ Map pdict = new HashMap(); + + private EAtom trap_exit; public EObject put(EObject key, EObject value) { EObject res = pdict.put(key, value); @@ -75,6 +82,36 @@ public EObject erase(EObject key) { if (res == null) res = ERT.NIL; return res; } + + /** + * @return + */ + public EPID group_leader() { + throw new NotImplemented(); + } + + /** + * @return + */ + public ELocalNode getLocalNode() { + return ERT.getLocalNode(); + } + + /** + * @param testAtom + * @param a2 + * @return + */ + public EObject process_flag(EAtom flag, EObject value) { + + if (flag == am_trap_exit) { + EAtom old = this.trap_exit; + trap_exit = value.testBoolean(); + return old; + } + + throw new NotImplemented(); + } } diff --git a/src/main/java/erjang/ERT.java b/src/main/java/erjang/ERT.java index 86f4df13..2b42086d 100644 --- a/src/main/java/erjang/ERT.java +++ b/src/main/java/erjang/ERT.java @@ -18,8 +18,12 @@ package erjang; +import java.io.IOException; import java.lang.reflect.Method; import java.math.BigInteger; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + public class ERT { @@ -34,7 +38,7 @@ public static EObject raise(EObject kind, EObject value, EObject trace) { public static final EAtom AM_BADARITH = EAtom.intern("badarith"); public static final EAtom AM_MODULE = EAtom.intern("module"); - public static EObject cons(EObject h, EObject t) { + public static ECons cons(EObject h, EObject t) { return t.cons(h); } @@ -199,6 +203,7 @@ public static EDouble box(double 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 ENode localNode; /** * @param add @@ -232,9 +237,76 @@ public static EAtom guard(boolean bool) { } public static final ENil NIL = new ENil(); + public static final EAtom EXIT = EAtom.intern("EXIT"); + public static final EAtom IGNORED = EAtom.intern("ignored"); + private static final EAtom am_badmatch = EAtom.intern("badmatch"); public EBitStringBuilder bs_init(int size, int flags) { return new EBitStringBuilder(size, flags); } + + /** + * @param e + * @return + */ + public static ESmall get_posix_code(IOException e) { + + // TODO Auto-generated method stub + return null; + } + + /** + * @param owner + * @param make + */ + public static void send(EObject pid, EObject msg) { + EPID p; + if ((p=pid.testPID()) == null) { + throw badarg(pid, msg); + } + + p.send(msg); + } + + /** + * @return + */ + public static ENode getLocalNode() { + return localNode; + } + + static EAtom am_undef = EAtom.intern("undef"); + + /** + * @param fun + * @return + */ + public static ErlangException undef(FUN fun, EObject... args) { + throw new ErlangException(am_undef, args); + } + + public static EObject apply$last(EProc proc, EObject arg1, EObject mod, EObject fun) + { + EAtom m = mod.testAtom(); + EAtom f = fun.testAtom(); + + if (m==null||f==null) throw ERT.badarg(mod, fun, arg1); + + return EModule.resolve(new FUN(m,f,1)).invoke(proc, NIL.cons(arg1).toArray()); + } + + static Map register = new ConcurrentHashMap(); + + /** + * @param aname + * @param eObject + */ + public static void register(EAtom aname, EObject eObject) { + register.put(aname, eObject); + } + + public static EObject badmatch(EObject val) { + throw new ErlangException(am_badmatch, val); + } } diff --git a/src/main/java/erjang/ERef.java b/src/main/java/erjang/ERef.java new file mode 100644 index 00000000..fdc8871c --- /dev/null +++ b/src/main/java/erjang/ERef.java @@ -0,0 +1,165 @@ +/** + * 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; + +/** + * + */ +public class ERef extends EObject { + + private EAtom node; + private int creation; + private int[] ids; + + /** + * @param node + * @param refId + * @param creation + */ + public ERef(EAtom node, int[] ids, int creation) { + this.node = node; + this.creation = creation & 0x03; // 2 bits + + // use at most 82 bits (18 + 32 + 32) + int len = ids.length; + this.ids = new int[3]; + this.ids[0] = 0; + this.ids[1] = 0; + this.ids[2] = 0; + + if (len > 3) { + len = 3; + } + System.arraycopy(ids, 0, this.ids, 0, len); + this.ids[0] &= 0x3ffff; // only 18 significant bits in first number + } + + @Override + int compare_same(EObject rhs) { + if (this.equals(rhs)) return 0; + + // TODO: optimize + return toString().compareTo(rhs.toString()); + } + + /** + * Determine if two refs are equal. Refs are equal if their components are + * equal. New refs and old refs are considered equal if the node, creation + * and first id numnber are equal. + * + * @param o + * the other ref to compare to. + * + * @return true if the refs are equal, false otherwise. + */ + @Override + public boolean equals(final Object o) { + if (!(o instanceof ERef)) { + return false; + } + + final ERef ref = (ERef) o; + + if (!(node.equals(ref.node()) && creation == ref.creation())) { + return false; + } + + if (isNewRef() && ref.isNewRef()) { + return ids[0] == ref.ids[0] && ids[1] == ref.ids[1] + && ids[2] == ref.ids[2]; + } + return ids[0] == ref.ids[0]; + } + + + @Override + int cmp_order() { + return 2; + } + + /** + * Get the id number from the ref. Old style refs have only one id number. + * If this is a new style ref, the first id number is returned. + * + * @return the id number from the ref. + */ + public int id() { + return ids[0]; + } + + /** + * Get the array of id numbers from the ref. If this is an old style ref, + * the array is of length 1. If this is a new style ref, the array has + * length 3. + * + * @return the array of id numbers from the ref. + */ + public int[] ids() { + return ids; + } + + /** + * Determine whether this is a new style ref. + * + * @return true if this ref is a new style ref, false otherwise. + */ + public boolean isNewRef() { + return ids.length > 1; + } + + /** + * Get the creation number from the ref. + * + * @return the creation number from the ref. + */ + public int creation() { + return creation; + } + + /** + * Get the node name from the ref. + * + * @return the node name from the ref. + */ + public EAtom node() { + return node; + } + + /** + * Get the string representation of the ref. Erlang refs are printed as + * #Ref<node.id> + * + * @return the string representation of the ref. + */ + @Override + public String toString() { + String s = "#Ref<" + node; + + for (int i = 0; i < ids.length; i++) { + s += "." + ids[i]; + } + + s += ">"; + + return s; + } + + +} diff --git a/src/main/java/erjang/EString.java b/src/main/java/erjang/EString.java index 0f3b4a61..7cf2e5e1 100644 --- a/src/main/java/erjang/EString.java +++ b/src/main/java/erjang/EString.java @@ -30,13 +30,14 @@ public class EString extends ESeq implements CharSequence { private static final Charset ISO_LATIN_1 = Charset.forName("ISO-8859-1"); - private byte[] data; - private int off; + final byte[] data; + final int off; private int hash = -1; public EString(String value) { this.hash = value.hashCode(); this.data = value.getBytes(ISO_LATIN_1); + this.off = 0; } public EString testString() @@ -270,7 +271,7 @@ public ECons testNonEmptyList() { } public ECons testCons() { - return length() == 0 ? null : this; + return this; } /* (non-Javadoc) @@ -354,4 +355,14 @@ public ECons prepend(ECons list) { public EBitString asBitString() { return new EBinary(data, off, length()); } + + /** + * @param eObject + * @return + */ + public static EString make(EObject eObject) { + ESeq str; + if ((str=eObject.testSeq()) == null) throw ERT.badarg(); + return make(str); + } } diff --git a/src/main/java/erjang/ErlangException.java b/src/main/java/erjang/ErlangException.java index c4c1fa13..24dbd3bf 100644 --- a/src/main/java/erjang/ErlangException.java +++ b/src/main/java/erjang/ErlangException.java @@ -42,11 +42,25 @@ public ErlangException(EAtom reason, Throwable cause) { } public ErlangException(EAtom reason, Object... args) { - super(reason.getName()); + super(reason.getName() + show(args)); this.reason = reason; this.trace = init_trace(); this.trace = fix_args(trace, args); } + + /** + * @param args + * @return + */ + private static String show(Object[] args) { + StringBuilder sb = new StringBuilder("["); + for (int i = 0; i < args.length; i++) { + if (i!=0) sb.append(','); + sb.append(args[i]); + } + sb.append(']'); + return sb.toString(); + } public ErlangException(EAtom reason, Throwable cause, Object... args) { super(reason.getName(), cause); @@ -114,7 +128,7 @@ ESeq init_trace() { Class[] class_trace = null; StackTraceElement[] stack_trace = this.getStackTrace(); - for (int i = stack_trace.length - 1; i <= 0; i++) { + for (int i = stack_trace.length - 1; i >= 0; i--) { StackTraceElement elm = stack_trace[i]; ETuple t = cache.get(elm); @@ -140,9 +154,11 @@ ESeq init_trace() { ETuple spec = resolve(c, m, m.getAnnotation(BIF.class), m .getAnnotation(ErlFun.class)); - cache.put(elm, spec); - - list = list.cons(spec); + + if (spec != null) { + cache.put(elm, spec); + list = list.cons(spec); + } } return list; @@ -156,6 +172,8 @@ ESeq init_trace() { */ private ETuple resolve(Class c, Method m, BIF ann1, ErlFun ann2) { + if (ann1 == null && ann2 == null) return null; + String module; if (ann2 == null) { module = get_class_module(c); diff --git a/src/main/java/erjang/Main.java b/src/main/java/erjang/Main.java index 9af45cfa..d7c05421 100644 --- a/src/main/java/erjang/Main.java +++ b/src/main/java/erjang/Main.java @@ -23,6 +23,8 @@ import erjang.beam.Compiler; import erjang.beam.EUtil; +import erjang.m.lists.bootstrap_lists; +import erjang.m.net_kernel.boot_net_kernel; /** * @@ -48,6 +50,9 @@ public static void main(String[] args) throws ClassNotFoundException, MalformedU } */ + //new bootstrap_lists(); + //new boot_net_kernel(); + EModule[] modules = new EModule[MODULES.length]; File preloaded_dir = new File(PRELOADED); @@ -64,9 +69,11 @@ public static void main(String[] args) throws ClassNotFoundException, MalformedU modules[i] = EModule.load_module(EAtom.intern(mod), path.toURI().toURL()); } - for (int i = 0; i < modules.length; i++) { - modules[i].resolve(); - } + FUN start = new FUN(EAtom.intern("otp_ring0"), EAtom.intern("start"), 2); + EProc proc = new EProc(); + EFun boot = EModule.resolve(start); + + boot.invoke(proc, new EObject[] { ERT.NIL, ERT.NIL } ); } } diff --git a/src/main/java/erjang/beam/CompilerVisitor.java b/src/main/java/erjang/beam/CompilerVisitor.java index deedad09..4189af0f 100644 --- a/src/main/java/erjang/beam/CompilerVisitor.java +++ b/src/main/java/erjang/beam/CompilerVisitor.java @@ -55,6 +55,7 @@ import erjang.EPort; import erjang.EProc; import erjang.ERT; +import erjang.ERef; import erjang.ESeq; import erjang.ESmall; import erjang.EString; @@ -271,7 +272,7 @@ private void generate_classinit() { mv.visitMethodInsn(INVOKESPECIAL, clazz, "", "()V"); mv.visitFieldInsn(PUTSTATIC, self_type.getInternalName(), field, - "L" + clazz + ";"); + "L" + funt.get(field) + ";"); } @@ -334,6 +335,7 @@ public FunctionVisitor visitFunction(EAtom name, int arity, int startLabel) { Map lambdas_xx = new TreeMap(); Map funs = new HashMap(); + Map funt = new HashMap(); class ASMFunctionAdapter implements FunctionVisitor2 { @@ -429,7 +431,7 @@ public void visitEnd() { freevars = 0; FieldVisitor fv = cv.visitField(ACC_STATIC | ACC_FINAL, mname, - "L" + full_inner_name + ";", null, null); + "L" + EFUN_NAME+arity + ";", null, null); if (isExported(fun_name, arity)) { System.err.println("export "+module_name+":"+fun_name+"/"+arity); @@ -443,6 +445,7 @@ public void visitEnd() { fv.visitEnd(); funs.put(mname, full_inner_name); + funt.put(mname, EFUN_NAME+arity); } @@ -1178,19 +1181,19 @@ private void push(Arg value, Type stack_type) { private Type push_immediate(EObject value, Type stack_type) { if (value == ERT.NIL) { - mv.visitFieldInsn(GETSTATIC, ENIL_NAME, "NIL", ENIL_TYPE + mv.visitFieldInsn(GETSTATIC, ERT_NAME, "NIL", ENIL_TYPE .getDescriptor()); return ENIL_TYPE; } if (value == ERT.TRUE) { - mv.visitFieldInsn(GETSTATIC, ERT_NAME, "ATOM_TRUE", + mv.visitFieldInsn(GETSTATIC, ERT_NAME, "TRUE", EATOM_DESC); return EATOM_TYPE; } if (value == ERT.FALSE) { - mv.visitFieldInsn(GETSTATIC, ERT_NAME, "ATOM_FALSE", + mv.visitFieldInsn(GETSTATIC, ERT_NAME, "FALSE", EATOM_DESC); return EATOM_TYPE; } @@ -1433,16 +1436,16 @@ public void visitInsn(BeamOpcode opcode, int val, Arg out) { + ")" + getTubleType(3).getDescriptor()); mv.visitInsn(DUP); - mv.visitFieldInsn(GETFIELD, ETUPLE_NAME + 3, "elm0", + mv.visitFieldInsn(GETFIELD, ETUPLE_NAME + 3, "elem1", EOBJECT_DESC); mv.visitVarInsn(ASTORE, xregs[0]); mv.visitInsn(DUP); - mv.visitFieldInsn(GETFIELD, ETUPLE_NAME + 3, "elm1", + mv.visitFieldInsn(GETFIELD, ETUPLE_NAME + 3, "elem2", EOBJECT_DESC); mv.visitVarInsn(ASTORE, xregs[1]); - mv.visitFieldInsn(GETFIELD, ETUPLE_NAME + 3, "elm2", + mv.visitFieldInsn(GETFIELD, ETUPLE_NAME + 3, "elem3", EOBJECT_DESC); mv.visitVarInsn(ASTORE, xregs[2]); @@ -1510,13 +1513,13 @@ public void visitInsn(BeamOpcode opcode, Arg val, Arg out, int pos) { push(out, out.type); push(val, EOBJECT_TYPE); mv.visitFieldInsn(PUTFIELD, out.type.getInternalName(), - "elm" + pos, EOBJECT_DESC); + "elem" + pos, EOBJECT_DESC); return; } else if (opcode == BeamOpcode.get_tuple_element) { push(val, val.type); mv.visitFieldInsn(GETFIELD, val.type.getInternalName(), - "elm" + pos, EOBJECT_DESC); + "elem" + (pos+1), EOBJECT_DESC); pop(out, EOBJECT_TYPE); return; } else if (opcode == BeamOpcode.set_tuple_element) { @@ -1900,7 +1903,7 @@ public void visitInsn(BeamOpcode opcode, Arg[] ys) { if (opcode == BeamOpcode.allocate_zero || opcode == BeamOpcode.allocate_heap_zero) { - mv.visitFieldInsn(GETSTATIC, ENIL_NAME, "NIL", ENIL_TYPE + mv.visitFieldInsn(GETSTATIC, ERT_NAME, "NIL", ENIL_TYPE .getDescriptor()); for (int i = 0; i < ys.length; i++) { if (i != (ys.length - 1)) @@ -2259,7 +2262,7 @@ public void visitCall(ExtFunc fun, Arg[] args, boolean is_tail, mv.visitMethodInsn(INVOKESTATIC, self_type .getInternalName(), EUtil.getJavaName(fun.fun, fun.no) - + (is_tail ? "$tail" : ""), EUtil.getSignature( + + (is_tail ? "$tail" : "$call"), EUtil.getSignature( args.length, true)); if (is_tail) { @@ -2310,7 +2313,7 @@ private boolean isExitFunc(ExtFunc fun) { static Method IS_PID_TEST = Method.getMethod("erjang.EPID testPID()"); static Method IS_PORT_TEST = Method.getMethod("erjang.EPort testPort()"); static Method IS_REFERENCE_TEST = Method - .getMethod("erjang.EReference testReference()"); + .getMethod(ERef.class.getName() + " testReference()"); static Method IS_FUNCTION_TEST = Method .getMethod("erjang.EFun testFunction()"); static Method IS_FUNCTION2_TEST = Method @@ -2411,7 +2414,7 @@ private static void make_invoke_method(ClassWriter cw, String outer_name, } for (int i = 0; i < freevars; i++) { mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, outer_name + "$" + mname, "fv" + i, + mv.visitFieldInsn(GETFIELD, outer_name + "$FN_" + mname, "fv" + i, EOBJECT_DESC); } mv.visitMethodInsn(INVOKESTATIC, outer_name, mname, EUtil.getSignature( @@ -2442,7 +2445,7 @@ private static void make_invoke_method(ClassWriter cw, String outer_name, mv.visitEnd(); } - private static void make_invoketail_method(ClassWriter cw, + public static void make_invoketail_method(ClassWriter cw, String full_inner, int arity, int freevars) { MethodVisitor mv; mv = cw.visitMethod(ACC_PUBLIC, "invoke_tail", EUtil.getSignature(arity diff --git a/src/main/java/erjang/beam/EUtil.java b/src/main/java/erjang/beam/EUtil.java index d4f1ba42..2c53dccc 100644 --- a/src/main/java/erjang/beam/EUtil.java +++ b/src/main/java/erjang/beam/EUtil.java @@ -164,7 +164,7 @@ public static String getJavaName(ExtFunc fun) { * @return */ public static String getFunClassName(Type self_type, ExtFunc efun) { - return self_type.getInternalName() + "$" + return self_type.getInternalName() + "$FN_" + getJavaName(efun.fun, efun.no); } diff --git a/src/main/java/erjang/beam/analysis/BeamTypeAnalysis.java b/src/main/java/erjang/beam/analysis/BeamTypeAnalysis.java index 6e2fbb61..b7735f0c 100644 --- a/src/main/java/erjang/beam/analysis/BeamTypeAnalysis.java +++ b/src/main/java/erjang/beam/analysis/BeamTypeAnalysis.java @@ -41,7 +41,7 @@ import erjang.EFun; import erjang.EInteger; import erjang.ERT; -import erjang.EReference; +import erjang.ERef; import erjang.ESmall; import erjang.EList; import erjang.ENil; @@ -95,7 +95,7 @@ public BeamTypeAnalysis(ModuleVisitor mv) { static final Type EFUN_TYPE = Type.getType(EFun.class); static final Type EPID_TYPE = Type.getType(EPID.class); static final Type EPORT_TYPE = Type.getType(EPort.class); - static final Type EREFERENCE_TYPE = Type.getType(EReference.class); + static final Type EREFERENCE_TYPE = Type.getType(ERef.class); static final Type EMATCHSTATE_TYPE = Type.getType(EBinMatchState.class); static final EObject X_ATOM = EAtom.intern("x"); diff --git a/src/main/java/erjang/m/erlang/ErlBif.java b/src/main/java/erjang/m/erlang/ErlBif.java index 6a899b66..ba6165f9 100644 --- a/src/main/java/erjang/m/erlang/ErlBif.java +++ b/src/main/java/erjang/m/erlang/ErlBif.java @@ -801,8 +801,8 @@ public static EString atom_to_list(EObject atom) { } @BIF - public static EObject process_flag(EObject a1, EObject a2) { - throw new NotImplemented(); + public static EObject process_flag(EProc proc, EObject a1, EObject a2) { + return proc.process_flag(a1.testAtom(), a2); } @BIF diff --git a/src/main/java/erjang/m/erlang/ErlPort.java b/src/main/java/erjang/m/erlang/ErlPort.java new file mode 100644 index 00000000..68d26959 --- /dev/null +++ b/src/main/java/erjang/m/erlang/ErlPort.java @@ -0,0 +1,74 @@ +/** + * 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.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import erjang.BIF; +import erjang.EAtom; +import erjang.ECons; +import erjang.EExecPort; +import erjang.EObject; +import erjang.EProc; +import erjang.ERT; +import erjang.ESeq; +import erjang.EString; +import erjang.ETuple; +import erjang.ETuple2; +import erjang.ErlangException; +import erjang.NotImplemented; + +/** + * + */ +public class ErlPort { + + static EAtom am_spawn = EAtom.intern("spawn"); + static EAtom am_spawn_driver = EAtom.intern("spawn_driver"); + static EAtom am_spawn_executable = EAtom.intern("spawn_executable"); + + @BIF + static EObject open_port(EProc proc, EObject portName, EObject portSetting) { + + + ETuple t; + if ((t = portName.testTuple()) == null) + throw ERT.badarg(); + + ETuple2 name; + if ((name = ETuple2.cast(t)) == null) + throw ERT.badarg(); + + if (name.elem1 == am_spawn) { + throw new NotImplemented(); + + } else if (name.elem1 == am_spawn_driver) { + throw new NotImplemented(); + + } else if (name.elem1 == am_spawn_executable) { + return new EExecPort(proc, name, portSetting); + } + + throw ERT.badarg(portName, portSetting); + } + +} diff --git a/src/main/java/erjang/m/erlang/ErlProc.java b/src/main/java/erjang/m/erlang/ErlProc.java index 4f961336..bdba2729 100644 --- a/src/main/java/erjang/m/erlang/ErlProc.java +++ b/src/main/java/erjang/m/erlang/ErlProc.java @@ -16,12 +16,30 @@ * limitations under the License. **/ - package erjang.m.erlang; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryManagerMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryType; +import java.lang.management.MemoryUsage; +import java.util.List; + import erjang.BIF; +import erjang.EAtom; +import erjang.ECons; +import erjang.EModule; import erjang.EObject; +import erjang.EPID; +import erjang.EPort; import erjang.EProc; +import erjang.ERT; +import erjang.ERef; +import erjang.ESmall; +import erjang.EString; +import erjang.ETuple; import erjang.NotImplemented; /** @@ -29,36 +47,170 @@ */ public class ErlProc { + private static final EAtom am_smp_support = EAtom.intern("smp_support"); + private static final EAtom am_threads = EAtom.intern("threads"); + private static final EAtom am_wordsize = EAtom.intern("wordsize"); + private static final EAtom am_thread_pool_size = EAtom + .intern("thread_pool_size"); + private static final EAtom am_os_type = EAtom.intern("os_type"); + private static final EAtom am_win32 = EAtom.intern("win32"); + private static final EAtom am_unix = EAtom.intern("unix"); + private static final EAtom am_version = EAtom.intern("version"); + private static final EAtom am_undefined = EAtom.intern("undefined"); + private static final EAtom am_heap = EAtom.intern("heap"); + private static final EAtom am_non_heap = EAtom.intern("non_heap"); + private static final EAtom am_jvm = EAtom.intern("jvm"); + private static final EAtom am_allocated_areas = EAtom.intern("allocated_areas"); + private static final EObject am_otp_version = EAtom.intern("am_otp_version"); + @BIF public static EObject display(EProc proc, EObject obj) { System.out.println(obj); return obj; } - + @BIF public static EObject erase(EProc proc, EObject key) { return proc.erase(key); } - @BIF public static EObject register(EProc proc, EObject name, EObject pid) { - throw new NotImplemented(); + EAtom aname; + if ((aname=name.testAtom()) == null) throw ERT.badarg(name, pid); + EPort pport = null; + EPID ppid; + if (((ppid=pid.testPID()) == null ) + && ((pport=pid.testPort()) == null)) + throw ERT.badarg(name, pid); + ERT.register(aname, pport==null?ppid:pport); + return ERT.TRUE; } - + @BIF public static EObject unlink(EProc proc, EObject pid) { throw new NotImplemented(); } - + @BIF public static EObject exit(EProc proc, EObject a1, EObject a2) { throw new NotImplemented(); } - + @BIF public static EObject exit(EProc proc, EObject a1) { throw new NotImplemented(); } + + @BIF + public static ERef make_ref(EProc proc) + { + return ERT.getLocalNode().createRef(); + } + @BIF + static EObject group_leader(EProc proc) { + return proc.group_leader(); + } + + static EAtom am_allocator = EAtom.intern("allocator"); + static EAtom am_heap_type = EAtom.intern("heap_type"); + static EAtom am_shared = EAtom.intern("shared"); + + @BIF + static EObject system_info(EProc proc, EObject type) { + + if (type == am_allocated_areas) { + + ECons res = ERT.NIL; + + List bean2 = ManagementFactory + .getMemoryPoolMXBeans(); + + if (bean2 == null) { + + MemoryMXBean bean = ManagementFactory.getMemoryMXBean(); + if (bean != null) { + + MemoryUsage mu = bean.getHeapMemoryUsage(); + res = res.cons(ETuple.make(am_heap, ERT.box(mu.getCommitted()), + ERT.box(mu.getUsed()))); + + mu = bean.getNonHeapMemoryUsage(); + res = res.cons(ETuple.make(am_non_heap, ERT.box(mu + .getCommitted()), ERT.box(mu.getUsed()))); + + } + + return res; + } + + for (MemoryPoolMXBean mb : bean2) { + + String name = mb.getName(); + MemoryUsage mu = mb.getUsage(); + if (mu == null) continue; + + String name2 = (mb.getType()==MemoryType.HEAP ? "heap:" : "non_heap:" ) + name; + + res = res.cons(ETuple.make(EAtom.intern(name2), ERT.box(mu + .getCommitted()), ERT.box(mu.getUsed()))); + + + } + + return res; + + } else if (type == am_allocator) { + return am_jvm; + + } else if (type == am_heap_type) { + return am_shared; + + } else if (type == am_smp_support) { + return ERT.TRUE; + + } else if (type == am_thread_pool_size) { + + // TODO: hook up to thread pool + return new ESmall(8); + + } else if (type == am_os_type) { + String os = System.getProperty("os.name"); + if (os.startsWith("Windows")) { + return ETuple.make(am_win32, new EString(os)); + } else { + return ETuple.make(am_unix, new EString(os)); + } + + } else if (type == am_threads) { + return ERT.TRUE; + + } else if (type == am_version) { + // TODO: be smarter somehow + return new EString("5.7.3"); + + } else if (type == am_otp_version) { + // TODO: be smarter somehow + return new EString("R13B"); + + } else if (type == am_wordsize) { + return new ESmall(32); + + } else { + return am_undefined; + } + + } + + @BIF + static EObject function_exported(EObject mod, EObject fun, EObject arity) { + EAtom m = mod.testAtom(); + EAtom f = fun.testAtom(); + ESmall a = arity.testSmall(); + + if (m==null||f==null||a==null) throw ERT.badarg(mod,fun,arity); + + return ERT.box( EModule.function_exported (m,f,a.value) ); + } } diff --git a/src/main/java/erjang/m/erlang/Native.java b/src/main/java/erjang/m/erlang/Native.java index 619abe9f..1f93d649 100644 --- a/src/main/java/erjang/m/erlang/Native.java +++ b/src/main/java/erjang/m/erlang/Native.java @@ -16,7 +16,6 @@ * limitations under the License. **/ - package erjang.m.erlang; import erjang.ENative; @@ -27,9 +26,9 @@ public class Native extends ENative { @Override - protected - Class[] getNativeClasses() { - return new Class[] { BinOps.class, ErlBif.class, ErlProc.class, ErlList.class, ErlConvert.class }; + protected Class[] getNativeClasses() { + return new Class[] { BinOps.class, ErlBif.class, ErlProc.class, + ErlList.class, ErlConvert.class, ErlPort.class }; } }