diff --git a/.classpath b/.classpath index a45a21e2..413f84bb 100644 --- a/.classpath +++ b/.classpath @@ -6,11 +6,9 @@ - - - - - + + + diff --git a/erl/test1.beam b/erl/test1.beam index 58f21758..136fd99e 100644 Binary files a/erl/test1.beam and b/erl/test1.beam differ diff --git a/src/erjang/ENumber.java b/src/erjang/ENumber.java index 6f6ba026..27dba1cf 100644 --- a/src/erjang/ENumber.java +++ b/src/erjang/ENumber.java @@ -99,4 +99,21 @@ public ENumber add(int i2) { throw new NotImplemented(); } + /** + * @param v2 + * @return + */ + public ENumber divide(EObject v2) { + // TODO Auto-generated method stub + return null; + } + + /** + * @param d2 + * @return + */ + public ENumber divide(double d2) { + throw new NotImplemented(); + } + } diff --git a/src/erjang/beam/analysis/BasicBlock.java b/src/erjang/beam/analysis/BasicBlock.java index 05d02bee..23ed507f 100644 --- a/src/erjang/beam/analysis/BasicBlock.java +++ b/src/erjang/beam/analysis/BasicBlock.java @@ -1,10 +1,28 @@ -package org.erlang.beam; +/** + * 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.beam.analysis; import java.util.Comparator; import java.util.Set; import java.util.TreeSet; -import org.erlang.ETuple2; +import erjang.ETuple2; class BasicBlock { @@ -13,16 +31,17 @@ class BasicBlock { TreeSet use = new TreeSet(); TreeSet kill = new TreeSet(); - + TreeSet in = new TreeSet(); TreeSet out = new TreeSet(); - + Set succ = new TreeSet( new Comparator() { @Override public int compare(BasicBlock o1, BasicBlock o2) { - if (o1==o2) return 0; - + if (o1 == o2) + return 0; + int loff = o1.label - o2.label; if (loff != 0) { return loff; @@ -41,18 +60,32 @@ public void succ(BasicBlock bb) { succ.add(bb); } - public void use_x(int reg) { use.add(KEY_X | reg); } - public void use_y(TypeMap map, int reg) { use.add(KEY_X | map.get_ypos(reg)); } - public void use_fr(int reg) { use.add(KEY_X | reg); } + public void use_x(int reg) { + use.add(KEY_X | reg); + } + + public void use_y(TypeMap map, int reg) { + use.add(KEY_X | map.get_ypos(reg)); + } + + public void use_fr(int reg) { + use.add(KEY_X | reg); + } + + public void kill_x(int reg) { + kill.add(KEY_X | reg); + } + + public void kill_y(TypeMap map, int reg) { + kill.add(KEY_X | map.get_ypos(reg)); + } - public void kill_x(int reg) { kill.add(KEY_X | reg); } - public void kill_y(TypeMap map, int reg) { kill.add(KEY_X | map.get_ypos(reg)); } - public void kill_fr(int reg) { kill.add(KEY_X | reg); } + public void kill_fr(int reg) { + kill.add(KEY_X | reg); + } static final int KEY_X = 0 << 16; static final int KEY_Y = 1 << 16; static final int KEY_FR = 2 << 16; - -} - +} diff --git a/src/erjang/beam/analysis/BeamTypeAnalysis.java b/src/erjang/beam/analysis/BeamTypeAnalysis.java index 70465ac7..85a22f33 100644 --- a/src/erjang/beam/analysis/BeamTypeAnalysis.java +++ b/src/erjang/beam/analysis/BeamTypeAnalysis.java @@ -1,8 +1,27 @@ -package org.erlang.beam; +/** + * 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.beam.analysis; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -10,39 +29,67 @@ import java.util.TreeMap; import java.util.TreeSet; -import org.erlang.EAtom; -import org.erlang.ECons; -import org.erlang.EDouble; -import org.erlang.EFun; -import org.erlang.EInteger; -import org.erlang.EList; -import org.erlang.ENil; -import org.erlang.EObject; -import org.erlang.EPID; -import org.erlang.EPort; -import org.erlang.ESeq; -import org.erlang.ETerm; -import org.erlang.ETuple; -import org.erlang.ETuple2; -import org.erlang.beam.BeamFile.FunctionVisitor; -import org.erlang.beam.BeamFile.LabeledBlockVisitor; -import org.erlang.beam.BeamFile.ModuleVisitor; import org.objectweb.asm.Type; - -public class BeamTypeAnalysis extends ModuleVisitor { +import org.objectweb.asm.commons.Method; + +import erjang.EAtom; +import erjang.EBinMatchState; +import erjang.EBinary; +import erjang.ECons; +import erjang.EDouble; +import erjang.EFun; +import erjang.EInteger; +import erjang.EList; +import erjang.ENil; +import erjang.ENumber; +import erjang.EObject; +import erjang.EPID; +import erjang.EPort; +import erjang.ESeq; +import erjang.ETerm; +import erjang.ETuple; +import erjang.ETuple2; +import erjang.beam.Arg; +import erjang.beam.BIF; +import erjang.beam.BIFUtil; +import erjang.beam.BeamCodeBlock; +import erjang.beam.BeamFunction; +import erjang.beam.BeamInstruction; +import erjang.beam.BeamOpcode; +import erjang.beam.BlockVisitor; +import erjang.beam.BlockVisitor2; +import erjang.beam.ExtFunc; +import erjang.beam.FunctionAdapter; +import erjang.beam.FunctionVisitor; +import erjang.beam.FunctionVisitor2; +import erjang.beam.ModuleAdapter; +import erjang.beam.ModuleVisitor; +import erjang.beam.Arg.Kind; + +public class BeamTypeAnalysis extends ModuleAdapter { + + /** + * + */ + public BeamTypeAnalysis(ModuleVisitor mv) { + super(mv); + } static final Type EINTEGER_TYPE = Type.getType(EInteger.class); + static final Type ENUMBER_TYPE = Type.getType(ENumber.class); static final Type EOBJECT_TYPE = Type.getType(EObject.class); static final Type EDOUBLE_TYPE = Type.getType(EDouble.class); static final Type ENIL_TYPE = Type.getType(ENil.class); static final Type EATOM_TYPE = Type.getType(EAtom.class); static final Type ETUPLE_TYPE = Type.getType(ETuple.class); + static final Type EBINARY_TYPE = Type.getType(EBinary.class); static final Type ECONS_TYPE = Type.getType(ECons.class); static final Type ESEQ_TYPE = Type.getType(ESeq.class); static final Type ELIST_TYPE = Type.getType(EList.class); 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 EMATCHSTATE_TYPE = Type.getType(EBinMatchState.class); static final ETerm X_ATOM = EAtom.intern("x"); static final ETerm Y_ATOM = EAtom.intern("y"); @@ -54,16 +101,24 @@ public class BeamTypeAnalysis extends ModuleVisitor { static final ETerm LITERAL_ATOM = EAtom.intern("literal"); static final ETerm NOFAIL_ATOM = EAtom.intern("nofail"); static final ETerm F_ATOM = EAtom.intern("f"); + static final ETerm FIELD_FLAGS_ATOM = EAtom.intern("field_flags"); + static final ETerm EXTFUNC_ATOM = EAtom.intern("extfunc"); private static final ETuple X0_REG = ETuple.make(new ETerm[] { X_ATOM, new EInteger(0) }); + private EAtom moduleName; + + private List functions = new ArrayList(); @Override - FunctionVisitor visitFunction(EAtom name, int arity, int startLabel) { - return new FV(name, arity, startLabel); + public FunctionVisitor visitFunction(EAtom name, int arity, int startLabel) { + FV f = new FV(super.visitFunction(name, arity, startLabel), name, + arity, startLabel); + functions.add(f); + return f; } - class FV extends FunctionVisitor { + class FV extends FunctionAdapter implements BeamFunction { BasicBlock makeBasicBlock(int label, int index) { assert ((label & 0xffff) == label); @@ -86,7 +141,9 @@ void live_analysis() { boolean change = false; + int iter = 0; do { + iter += 1; for (int n = all.length - 1; n >= 0; n--) { BasicBlock bb = bbs.get(all[n]); @@ -113,6 +170,9 @@ void live_analysis() { } } while (change); + + // System.err.println("live analysis for " + name + "/" + arity + // + " completed in " + iter + " iterations."); } public TypeMap getTypeMap(int i) { @@ -140,7 +200,16 @@ public int compare(LabeledBlock o1, LabeledBlock o2) { } }); - public FV(EAtom name, int arity, int startLabel) { + public int max_stack; + + public int max_xreg; + + public int max_freg; + + private boolean is_tail_recursive; + + public FV(FunctionVisitor fv, EAtom name, int arity, int startLabel) { + super(fv); this.name = name; this.arity = arity; this.startLabel = startLabel; @@ -166,13 +235,16 @@ public void visitEnd() { throw t; } + // woo! + live_analysis(); + SortedSet labels = new TreeSet(); labels.addAll(lbs.keySet()); boolean has_unreachable_code = false; for (int i : labels) { lb = lbs.get(i); - if (lb.isUnreached()) { + if (lb.isDeadCode()) { if (BeamOpcode.get(lb.insns.get(0).nth(1).asAtom()) == BeamOpcode.func_info) { // ignore this } else { @@ -184,6 +256,27 @@ public void visitEnd() { if (has_unreachable_code) { this.dump(); } + + function_visit_end(); + + } + + private void function_visit_end() { + + if (fv instanceof FunctionVisitor2) { + ((FunctionVisitor2) fv).visitMaxs(this.max_xreg, + this.max_stack, this.max_freg, this.is_tail_recursive); + } + + for (LabeledBlock block : this.lbs.values()) { + + BlockVisitor vis = super.fv + .visitLabeledBlock(block.block_label); + + block.accept(vis); + } + + super.fv.visitEnd(); } private void dump() { @@ -197,7 +290,7 @@ private void dump() { } @Override - public LabeledBlockVisitor visitLabeledBlock(int label) { + public BlockVisitor visitLabeledBlock(int label) { return get_lb(label, true); } @@ -225,7 +318,7 @@ private LabeledBlock get_lb(int label, boolean create) { } } - class LabeledBlock extends LabeledBlockVisitor { + class LabeledBlock implements BlockVisitor, BeamCodeBlock { private final int block_label; TypeMap initial; @@ -237,7 +330,258 @@ public LabeledBlock(int label) { initial = null; } - public boolean isUnreached() { + /** + * @param vis + */ + public void accept(BlockVisitor vis) { + + if (!isDeadCode()) { + + if (vis instanceof BlockVisitor2) { + accept_2((BlockVisitor2) vis); + } else { + accept_1(vis); + } + + } + + vis.visitEnd(); + } + + private void accept_1(BlockVisitor vis) { + for (int i = 0; i < insns.size(); i++) { + ETuple insn = insns.get(i); + BeamOpcode opcode = BeamOpcode.get(insn.nth(1).asAtom()); + vis.visitInsn(opcode, insn); + } + } + + private void accept_2(BlockVisitor2 vis) { + for (int insn_idx = 0; insn_idx < insns.size(); insn_idx++) { + ETuple insn = insns.get(insn_idx); + BeamOpcode opcode = BeamOpcode.get(insn.nth(1).asAtom()); + + switch (opcode) { + case func_info: + vis.visitInsn(opcode, (Arg) new ExtFunc(insn.nth(2) + .asAtom(), insn.nth(3).asAtom(), insn.nth(4) + .asInt())); + break; + + case fconv: + case fmove: + case move: { + System.err.println(insn); + Arg arg1 = decode_arg(insn_idx, insn.nth(2)); + Arg arg2 = decode_arg(insn_idx, insn.nth(3)); + + if (arg2.kind != Kind.F) { + if (arg1.kind == Kind.F) { + arg2 = new Arg(arg2, EDOUBLE_TYPE); + } else { + arg2 = new Arg(arg2, arg1.type); + } + } else { + // arg2.kind == F + } + + vis.visitInsn(opcode, arg1, arg2); + break; + } + + case arithfbif: + { + System.err.println("gen: "+insn); + EAtom name = insn.nth(2).asAtom(); + int failLabel = decode_labelref(insn.nth(3)); + ESeq parms = insn.nth(4).asSeq(); + Arg[] in = decode_args(insn_idx, parms.toArray()); + Arg out = decode_out_arg(insn_idx, insn.nth(5)); + + BIF bif = BIFUtil.getMethod(name.getName(), parmTypes( + this.map[insn_idx], parms), failLabel != 0); + + + vis.visitInsn(opcode, failLabel, in, out, bif); + break; + } + + case gc_bif: + case bif: + { + System.err.println("gen: "+insn); + EAtom name = insn.nth(2).asAtom(); + int failLabel = decode_labelref(insn.nth(3)); + ESeq parms = insn.nth(5).asSeq(); + Arg[] in = decode_args(insn_idx, parms.toArray()); + Arg out = decode_out_arg(insn_idx, insn.nth(6)); + + BIF bif = BIFUtil.getMethod(name.getName(), parmTypes( + this.map[insn_idx], parms), failLabel != 0); + + + vis.visitInsn(opcode, failLabel, in, out, bif); + break; + } + + case test: + accept_2_test(vis, insn, insn_idx); + break; + + case K_return: + vis.visitInsn(opcode); + break; + + case test_heap: + break; + + case fclearerror: + case fcheckerror: + break; + + case call_ext_only: + { + boolean is_tail = true; + boolean is_external = true; + int arg_count = insn.nth(2).asInt(); + Arg[] args = new Arg[arg_count]; + for (int i = 0; i < arg_count; i++) { + args[i] = new Arg(Kind.X, i, this.map[insn_idx].getx(i)); + } + ETuple ft = insn.nth(3).asTuple(); + if (ft.nth(1) != EXTFUNC_ATOM) throw new Error(); + ExtFunc fun = new ExtFunc(ft.nth(2).asAtom(), ft.nth(3).asAtom(), ft.nth(4).asInt()); + vis.visitCall(fun, args, is_tail, is_external); + } + break; + + default: + throw new Error("unhandled insn: " + insn); + } + + } + } + + private void accept_2_test(BlockVisitor2 vis, ETuple insn, + int insn_idx) { + + int failLabel = decode_labelref(insn.nth(3)); + + Arg[] args = decode_args(insn_idx, insn.nth(4).asSeq() + .toArray()); + + BeamOpcode test = BeamOpcode.get(insn.nth(2).asAtom()); + switch (test) { + case is_list: + vis.visitTest(test, failLabel, args[0], ECONS_TYPE); + break; + + case is_nonempty_list: + vis.visitTest(test, failLabel, args[0], ESEQ_TYPE); + break; + + case is_nil: + vis.visitTest(test, failLabel, args[0], ENIL_TYPE); + break; + + case is_eq: + vis.visitTest(test, failLabel, args, (Arg)null, Type.VOID_TYPE); + break; + + default: + throw new Error("unhandled test: " + insn + " at index " + + insn_idx); + } + + } + + /** + * @param insn_idx + * @param array + * @return + */ + private Arg[] decode_args(int insn_idx, EObject[] input) { + Arg[] output = new Arg[input.length]; + for (int i = 0; i < input.length; i++) { + output[i] = decode_arg(insn_idx, input[i]); + } + return output; + } + + /** + * @param insnIdx + * @param eObject + * @return + */ + private Arg decode_arg(int insn_idx, EObject src) { + TypeMap current = this.map[insn_idx]; + + if (src instanceof ETuple2) { + ETuple2 tup = (ETuple2) src; + if (tup.elem1 == X_ATOM) { + int xreg = tup.elem2.asInt(); + return new Arg(Arg.Kind.X, xreg, current.getx(xreg)); + } else if (tup.elem1 == Y_ATOM) { + int yreg = tup.elem2.asInt(); + return new Arg(Arg.Kind.Y, current.get_ypos(yreg), + current.gety(yreg)); + } else if (tup.elem1 == FR_ATOM) { + int freg = tup.elem2.asInt(); + return new Arg(Arg.Kind.F, freg, Type.DOUBLE_TYPE); + } else if (tup.elem1 == ATOM_ATOM) { + return new Arg(tup.elem2); + } else if (tup.elem1 == LITERAL_ATOM) { + return new Arg(tup.elem2); + } else if (tup.elem1 == INTEGER_ATOM) { + if (tup.elem2.asInteger() != null) { + return new Arg(tup.elem2, Type.INT_TYPE); + } else { + return new Arg(tup.elem2); + } + } else if (tup.elem1 == FLOAT_ATOM) { + return new Arg(tup.elem2, Type.DOUBLE_TYPE); + } + + } else if (src == NIL_ATOM) { + return new Arg(src); + + } + + return null; + + } + + private Arg decode_out_arg(int insn_idx, EObject src) { + TypeMap current = this.map[insn_idx]; + + if (src instanceof ETuple2) { + ETuple2 tup = (ETuple2) src; + if (tup.elem1 == X_ATOM) { + int xreg = tup.elem2.asInt(); + return new Arg(Arg.Kind.X, xreg); + } else if (tup.elem1 == Y_ATOM) { + int yreg = tup.elem2.asInt(); + return new Arg(Arg.Kind.Y, current.get_ypos(yreg)); + } else if (tup.elem1 == FR_ATOM) { + int freg = tup.elem2.asInt(); + return new Arg(Arg.Kind.F, freg); + } + } + + throw new Error(); + + } + + /** + * @param nth + * @return + */ + private int decode_labelref(EObject f_tup) { + assert (f_tup.asTuple().nth(1) == F_ATOM); + return f_tup.asTuple().nth(2).asInt(); + } + + public boolean isDeadCode() { return initial == null; } @@ -267,11 +611,14 @@ public void analyze0() { map = new TypeMap[insns.size()]; next_insn: for (int insn_idx = 0; insn_idx < insns.size(); insn_idx++) { - + + update_max_regs(current); + if (is_term(last_opcode)) { - throw new Error("how did we get here then...? "+this.block_label+":"+insn_idx); + throw new Error("how did we get here then...? " + + this.block_label + ":" + insn_idx); } - + map[insn_idx] = current; ETuple insn = insns.get(insn_idx); BeamOpcode code = BeamOpcode.get(insn.nth(1).asAtom()); @@ -285,21 +632,23 @@ public void analyze0() { case move: { EObject src = insn.nth(2); EObject dst = insn.nth(3); - + Type srcType = getType(current, src); + boolean boxed = false; if (sizeof(current, src) > sizeof(current, dst)) { System.err.println(insn); - if (getType(current, src) == Type.INT_TYPE) { - current = setType(current, dst, EINTEGER_TYPE); - } else if (getType(current, src) == Type.DOUBLE_TYPE) { + if (getType(current, src).equals(Type.DOUBLE_TYPE)) { current = setType(current, dst, EDOUBLE_TYPE); + boxed = true; } else { throw new Error("why?" + insn); } } - current = setType(current, (ETuple2) dst, srcType); + if (!boxed) { + current = setType(current, (ETuple2) dst, srcType); + } continue next_insn; } @@ -340,8 +689,7 @@ public void analyze0() { // System.err.println(insn); current = branch(current, insn.nth(3), insn_idx); - - + EAtom name = insn.nth(2).asAtom(); ESeq parms = insn.nth(5).asSeq(); @@ -431,7 +779,7 @@ public void analyze0() { } case put_list: { - + Type head_type = getType(current, insn.nth(2)); Type tail_type = getType(current, insn.nth(3)); @@ -458,6 +806,34 @@ public void analyze0() { continue next_insn; } + case K_try: + current = setType(current, insn.nth(2), EOBJECT_TYPE); + current = branch(current, insn.nth(3), insn_idx); + continue next_insn; + + case try_case_end: + case try_end: + // no exception happened + continue next_insn; + + case try_case: + getType(current, insn.nth(2)); + current = current.setx(0, EATOM_TYPE); // reason + current = current.setx(1, EOBJECT_TYPE); // value + current = current.setx(2, EOBJECT_TYPE); // trace + continue next_insn; + + case raise: + boolean is_guard = false; + if (insn.nth(2).asTuple().nth(2).asInt() != 0) { + is_guard = true; + } + + checkArgs(current, insn.nth(3), insn); + current = setType(current, insn.nth(4), EOBJECT_TYPE); + + continue next_insn; + case K_catch: current = branch(current, insn.nth(3), insn_idx); continue next_insn; @@ -477,14 +853,13 @@ public void analyze0() { continue next_insn; } - case remove_message: + case remove_message: // assume this insn overrides X0 // current = current.setx(0, EOBJECT_TYPE); continue next_insn; case loop_rec_end: - case timeout: - { + case timeout: { // System.err.println(insn); continue next_insn; } @@ -575,14 +950,13 @@ public void analyze0() { case apply: case call: - case call_ext: - { + case call_ext: { int argCount = insn.nth(2).asInt(); current.touchx(0, argCount); current = current.setx(0, EOBJECT_TYPE); continue next_insn; } - + // all these exit case K_return: getType(current, X0_REG); @@ -592,10 +966,11 @@ public void analyze0() { case call_only: case call_ext_last: case call_ext_only: + is_tail_recursive = true; int argCount = insn.nth(2).asInt(); current.touchx(0, argCount); continue next_insn; - + case func_info: continue next_insn; @@ -604,12 +979,18 @@ public void analyze0() { case case_end: continue next_insn; + case bs_context_to_binary: { + Type ctx = getType(current, insn.nth(2)); + current = current.setx(0, EBINARY_TYPE); + continue next_insn; + } + default: throw new Error("unhandled: " + insn + "::" + current); } } - - + + update_max_regs(current); if (is_term(last_opcode) == false) { LabeledBlock lbv = get_lb(this.block_label + 1, false); @@ -624,6 +1005,12 @@ public void analyze0() { } } + private void update_max_regs(TypeMap current) { + max_stack = Math.max(max_stack, current.stacksize); + max_xreg = Math.max(max_xreg, current.max_xreg()); + max_freg = Math.max(max_freg, current.max_freg()); + } + boolean is_term(BeamOpcode code) { switch (code) { case K_return: @@ -635,11 +1022,11 @@ boolean is_term(BeamOpcode code) { case call_ext_last: case call_ext_only: case func_info: - + case wait: case select_tuple_arity: case select_val: - + case jump: return true; default: @@ -648,16 +1035,19 @@ boolean is_term(BeamOpcode code) { } private int sizeof(TypeMap current, EObject cell) { - + ETuple at; - if ((at=cell.asTuple()) != null) { + if ((at = cell.asTuple()) != null) { if (at.arity() == 2) { - if (at.nth(1) == X_ATOM) return 32; - if (at.nth(1) == Y_ATOM) return 32; - if (at.nth(1) == FR_ATOM) return 64; + if (at.nth(1) == X_ATOM) + return 32; + if (at.nth(1) == Y_ATOM) + return 32; + if (at.nth(1) == FR_ATOM) + return 64; } } - + Type t = getType(current, cell); if (t == Type.DOUBLE_TYPE) { return 64; @@ -666,7 +1056,8 @@ private int sizeof(TypeMap current, EObject cell) { } } - private Type getBifResult(String name, Type[] parmTypes, boolean is_guard) { + private Type getBifResult(String name, Type[] parmTypes, + boolean is_guard) { return BIFUtil.getBifResult(name, parmTypes, is_guard); } @@ -701,8 +1092,6 @@ private TypeMap analyze_test(TypeMap current, ETuple insn, current = branch(current, insn.nth(3), insn_idx); - checkArgs(current, insn.nth(4), insn); - EObject[] args = insn.nth(4).asSeq().toArray(); EObject arg1 = args[0]; EObject arg2 = (args.length > 1) ? args[1] : null; @@ -710,51 +1099,66 @@ private TypeMap analyze_test(TypeMap current, ETuple insn, BeamOpcode test = BeamOpcode.get(insn.nth(2).asAtom()); switch (test) { case is_nil: { + checkArgs(current, insn.nth(4), insn); return setType(current, arg1.asTuple(), ENIL_TYPE); } case is_list: case is_nonempty_list: { + checkArgs(current, insn.nth(4), insn); return setType(current, arg1.asTuple(), ECONS_TYPE); } + case is_binary: { + checkArgs(current, insn.nth(4), insn); + return setType(current, arg1.asTuple(), EBINARY_TYPE); + } case is_tuple: { + checkArgs(current, insn.nth(4), insn); return setType(current, arg1.asTuple(), ETUPLE_TYPE); } case test_arity: { + checkArgs(current, insn.nth(4), insn); return setType(current, arg1.asTuple(), getTupleType(arg2 .asInt())); } case is_boolean: case is_atom: { + checkArgs(current, insn.nth(4), insn); return setType(current, arg1.asTuple(), EATOM_TYPE); } case is_integer: { + checkArgs(current, insn.nth(4), insn); return setType(current, arg1, EINTEGER_TYPE); } case is_pid: { + checkArgs(current, insn.nth(4), insn); return setType(current, arg1, EPID_TYPE); } case is_port: { + checkArgs(current, insn.nth(4), insn); return setType(current, arg1, EPORT_TYPE); } case is_float: { + checkArgs(current, insn.nth(4), insn); return setType(current, arg1, EDOUBLE_TYPE); } case is_ge: case is_ne: case is_ne_exact: + checkArgs(current, insn.nth(4), insn); return current; case is_eq: case is_eq_exact: { + checkArgs(current, insn.nth(4), insn); Type t1 = getType(current, arg1); Type t2 = getType(current, arg2); @@ -770,6 +1174,59 @@ private TypeMap analyze_test(TypeMap current, ETuple insn, } case is_lt: + checkArgs(current, insn.nth(4), insn); + return current; + + case bs_start_match2: + check(current, args[0]); + current = setType(current, args[3], EMATCHSTATE_TYPE); + return current; + + case bs_get_integer2: { + if (!EMATCHSTATE_TYPE.equals(getType(current, args[0]))) { + throw new Error("matching without a state"); + } + + ETuple tup; + if ((tup = args[3].asTuple()) != null) { + if (tup.nth(1) == INTEGER_ATOM + && tup.nth(2).asInt() <= 32) { + current = setType(current, args[5], Type.INT_TYPE); + return current; + } + } + + current = setType(current, args[5], ENUMBER_TYPE); + return current; + } + + case bs_get_binary2: { + if (!EMATCHSTATE_TYPE.equals(getType(current, args[0]))) { + throw new Error("matching without a state"); + } + + current = setType(current, args[5], EBINARY_TYPE); + return current; + } + + case bs_get_float2: { + if (!EMATCHSTATE_TYPE.equals(getType(current, args[0]))) { + throw new Error("matching without a state"); + } + + current = setType(current, args[5], Type.DOUBLE_TYPE); + return current; + } + + // these bit string matchers don't modify registers + + case bs_test_tail2: + case bs_test_unit: + case bs_skip_bits2: + + if (!EMATCHSTATE_TYPE.equals(getType(current, args[0]))) { + throw new Error("matching without a state"); + } return current; @@ -779,6 +1236,12 @@ private TypeMap analyze_test(TypeMap current, ETuple insn, } + private void check(TypeMap current, EObject src) { + if (getType(current, src) == null) { + throw new Error("argument has no type"); + } + } + private TypeMap branch(TypeMap current, EObject nth, int idx) { if (nth != NOFAIL_ATOM) { @@ -879,6 +1342,8 @@ private Type getType(TypeMap current, EObject src) { } } else if (tup.elem1 == FLOAT_ATOM) { return Type.DOUBLE_TYPE; + } else if (tup.elem1 == FIELD_FLAGS_ATOM) { + return Type.INT_TYPE; } } else if (src == NIL_ATOM) { @@ -891,8 +1356,120 @@ private Type getType(TypeMap current, EObject src) { throw new Error("unknown " + src); } + @Override + public BeamInstruction[] getInstructions() { + + BeamInstruction[] res = new BeamInstruction[insns.size()]; + for (int i = 0; i < insns.size(); i++) { + res[i] = new BInsn(insns.get(i), this.map[i]); + } + + return res; + } + + @Override + public int getLabel() { + return this.block_label; + } + + class BInsn implements BeamInstruction { + + private final ETuple insn; + private final TypeMap current; + + public BInsn(ETuple insn, TypeMap current) { + this.insn = insn; + this.current = current; + } + + @Override + public BeamOpcode opcode() { + return BeamOpcode.get(insn.nth(1).asAtom()); + } + + @Override + public String toString() { + return insn.toString(); + } + } + } + + @Override + public int getArity() { + return arity; } + @Override + public String getName() { + return name.getName(); + } + + @Override + public String getModuleName() { + return moduleName.getName(); + } + + @Override + public boolean isExported() { + String externalName = getName() + "/" + getArity(); + return exports.contains(externalName); + } + + @Override + public int getFregCount() { + return max_freg; + } + + @Override + public int getXregCount() { + return max_xreg; + } + + @Override + public int getYregCount() { + return max_stack; + } + + @Override + public BeamCodeBlock[] getCodeBlocks() { + + BeamCodeBlock[] blocks = this.lbs.values().toArray( + new BeamCodeBlock[0]); + + return blocks; + } + + @Override + public int getEntryLabel() { + return startLabel; + } + + } + + public void visitModule(EAtom name) { + this.moduleName = name; + super.visitModule(name); + } + + Set exports = new HashSet(); + + /** list of {Fun,Arity,Entry} */ + public void visitExport(EAtom fun, int arity, int entry) { + exports.add(fun.getName() + "/" + arity); + super.visitExport(fun, arity, entry); + } + + /** list of {Atom,Value} */ + public void visitAttribute(EAtom att, EObject value) { + super.visitAttribute(att, value); + } + + public String getModuleName() { + return this.moduleName.getName(); + } + + public BeamFunction[] functions() { + return functions.toArray(new BeamFunction[functions.size()]); } } diff --git a/src/erjang/beam/analysis/TypeMap.java b/src/erjang/beam/analysis/TypeMap.java index b4e4fef5..22d8c0d8 100644 --- a/src/erjang/beam/analysis/TypeMap.java +++ b/src/erjang/beam/analysis/TypeMap.java @@ -1,18 +1,33 @@ /** - * - */ -package org.erlang.beam; + * 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.beam.analysis; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Set; import java.util.TreeSet; -import org.erlang.EObject; -import org.erlang.ETuple; -import org.erlang.ETuple2; import org.objectweb.asm.Type; +import erjang.EObject; +import erjang.ETuple; +import erjang.ETuple2; + class TypeMap { private static Type TYPE_EOBJECT = Type.getType(EObject.class); private static Type TYPE_ETUPLE = Type.getType(ETuple.class); @@ -20,15 +35,15 @@ class TypeMap { private static Type[] NO_TYPES = new Type[0]; private Type[] xregs, yregs, fregs; - private int stacksize; // number of y-regs + final int stacksize; // number of y-regs final BasicBlock bb; - public TypeMap(BasicBlock bb) { xregs = NO_TYPES; yregs = NO_TYPES; fregs = NO_TYPES; + stacksize = 0; this.bb = bb; } @@ -103,7 +118,8 @@ private String shortName(Type type) { } } - private TypeMap(Type[] xregs, Type[] yregs, Type[] fregs, int stacksize, BasicBlock bb) { + private TypeMap(Type[] xregs, Type[] yregs, Type[] fregs, int stacksize, + BasicBlock bb) { super(); this.xregs = xregs; this.yregs = yregs; @@ -124,6 +140,9 @@ public boolean equals(Object obj) { } private static boolean eqy(TypeMap me, TypeMap other) { + if (me.stacksize != other.stacksize) + return false; + int stacksize = Math.min(me.stacksize, other.stacksize); for (int i = 0; i < stacksize; i++) { if (!eq(me.gety(i), other.gety(i))) @@ -158,11 +177,20 @@ public TypeMap mergeFrom(TypeMap other) { Type[] new_y = yregs; int new_stacksize = stacksize; if (!eqy(this, other)) { - new_stacksize = Math.min(stacksize, other.stacksize); - new_y = new Type[new_stacksize]; - for (int i = 0; i < new_stacksize; i++) { - new_y[new_stacksize - i - 1] = merge(this.gety(i), other - .gety(i)); + + if (stacksize != other.stacksize) { + // yank stack! + new_stacksize = 0; + new_y = NO_TYPES; + } else { + + // the smaller stack-size wins + new_stacksize = Math.min(stacksize, other.stacksize); + new_y = new Type[new_stacksize]; + for (int i = 0; i < new_stacksize; i++) { + new_y[new_stacksize - i - 1] = merge(this.gety(i), other + .gety(i)); + } } } @@ -212,7 +240,7 @@ public TypeMap setx(int reg, Type t) { if (eq(getx(reg), t)) return this; - + Type[] new_xregs; if (xregs.length <= reg) { new_xregs = grow(xregs, reg); @@ -332,5 +360,22 @@ public void touchx(int from, int to) { } } + public int max_xreg() { + int max = 0; + for (int i = 0; i < xregs.length; i++) { + if (xregs[i] != null) + max = i; + } + return max + 1; + } + + public int max_freg() { + int max = 0; + for (int i = 0; i < fregs.length; i++) { + if (fregs[i] != null) + max = i; + } + return max + 1; + } } \ No newline at end of file diff --git a/src/erjang/jbeam/README b/src/erjang/jbeam/README index 30cdc7e9..e6f6cacc 100644 --- a/src/erjang/jbeam/README +++ b/src/erjang/jbeam/README @@ -1 +1,6 @@ -This directory contains an earlier version +This directory contains an earlier version, which could actually compile to .class files; +but it was badly broken. + +I just keep it around because I intend to grab some snippets of code. + + diff --git a/src/erjang/jbeam/ops/AllocateZero.java b/src/erjang/jbeam/ops/AllocateZero.java index 1a08e95f..06d717d6 100644 --- a/src/erjang/jbeam/ops/AllocateZero.java +++ b/src/erjang/jbeam/ops/AllocateZero.java @@ -1,4 +1,4 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; public class AllocateZero extends Insn { diff --git a/src/erjang/jbeam/ops/AttributesDecl.java b/src/erjang/jbeam/ops/AttributesDecl.java index 63b86851..25755074 100644 --- a/src/erjang/jbeam/ops/AttributesDecl.java +++ b/src/erjang/jbeam/ops/AttributesDecl.java @@ -1,6 +1,6 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.ECons; +import erjang.ECons; public class AttributesDecl { diff --git a/src/erjang/jbeam/ops/BasicBlock.java b/src/erjang/jbeam/ops/BasicBlock.java index 805afb3c..ca3e141e 100644 --- a/src/erjang/jbeam/ops/BasicBlock.java +++ b/src/erjang/jbeam/ops/BasicBlock.java @@ -1,4 +1,4 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; import java.util.ArrayList; import java.util.List; diff --git a/src/erjang/jbeam/ops/Call.java b/src/erjang/jbeam/ops/Call.java index a651f3a8..e49cf219 100644 --- a/src/erjang/jbeam/ops/Call.java +++ b/src/erjang/jbeam/ops/Call.java @@ -1,9 +1,10 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.EAtom; import com.sun.xml.internal.ws.org.objectweb.asm.Opcodes; +import erjang.EAtom; + public class Call extends Insn { private final int nargs; diff --git a/src/erjang/jbeam/ops/CallExtOnly.java b/src/erjang/jbeam/ops/CallExtOnly.java index 338b3f4a..10ffe780 100644 --- a/src/erjang/jbeam/ops/CallExtOnly.java +++ b/src/erjang/jbeam/ops/CallExtOnly.java @@ -1,9 +1,10 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.EFun; -import org.erlang.ETerm; import org.objectweb.asm.Type; +import erjang.EFun; +import erjang.ETerm; + public class CallExtOnly extends Insn { private static final Type EFUN_TYPE = Type.getType(EFun.class); diff --git a/src/erjang/jbeam/ops/CodeAdapter.java b/src/erjang/jbeam/ops/CodeAdapter.java index e9c71022..2ac24a82 100644 --- a/src/erjang/jbeam/ops/CodeAdapter.java +++ b/src/erjang/jbeam/ops/CodeAdapter.java @@ -1,12 +1,13 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.EObject; -import org.erlang.ETerm; -import org.erlang.jbeam.BEAMFile; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; +import erjang.EObject; +import erjang.ETerm; +import erjang.jbeam.BEAMFile; + public class CodeAdapter extends MethodAdapter { private final BEAMFile beam; diff --git a/src/erjang/jbeam/ops/CompilationInfo.java b/src/erjang/jbeam/ops/CompilationInfo.java index 5c69c09c..834e7c06 100644 --- a/src/erjang/jbeam/ops/CompilationInfo.java +++ b/src/erjang/jbeam/ops/CompilationInfo.java @@ -1,6 +1,6 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.ECons; +import erjang.ECons; public class CompilationInfo extends Stmt { diff --git a/src/erjang/jbeam/ops/Deallocate.java b/src/erjang/jbeam/ops/Deallocate.java index 2d2e7ae1..cc553a3b 100644 --- a/src/erjang/jbeam/ops/Deallocate.java +++ b/src/erjang/jbeam/ops/Deallocate.java @@ -1,7 +1,7 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.jbeam.ops.FunctionAdapter; -import org.erlang.jbeam.ops.Insn; +import erjang.jbeam.ops.FunctionAdapter; +import erjang.jbeam.ops.Insn; public class Deallocate extends Insn { diff --git a/src/erjang/jbeam/ops/ExportsDecl.java b/src/erjang/jbeam/ops/ExportsDecl.java index 7af969a3..44a60abb 100644 --- a/src/erjang/jbeam/ops/ExportsDecl.java +++ b/src/erjang/jbeam/ops/ExportsDecl.java @@ -1,6 +1,6 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.ECons; +import erjang.ECons; public class ExportsDecl { diff --git a/src/erjang/jbeam/ops/Expr.java b/src/erjang/jbeam/ops/Expr.java index 9f084bad..b8381f77 100644 --- a/src/erjang/jbeam/ops/Expr.java +++ b/src/erjang/jbeam/ops/Expr.java @@ -1,4 +1,4 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; diff --git a/src/erjang/jbeam/ops/ExternalFunction.java b/src/erjang/jbeam/ops/ExternalFunction.java index 140d807a..b1ad0faf 100644 --- a/src/erjang/jbeam/ops/ExternalFunction.java +++ b/src/erjang/jbeam/ops/ExternalFunction.java @@ -1,9 +1,9 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.EAtom; -import org.erlang.EInteger; -import org.erlang.ETerm; -import org.erlang.ETuple; +import erjang.EAtom; +import erjang.EInteger; +import erjang.ETerm; +import erjang.ETuple; public class ExternalFunction { diff --git a/src/erjang/jbeam/ops/FileDecl.java b/src/erjang/jbeam/ops/FileDecl.java index e4802314..12af6c1d 100644 --- a/src/erjang/jbeam/ops/FileDecl.java +++ b/src/erjang/jbeam/ops/FileDecl.java @@ -1,6 +1,6 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.EString; +import erjang.EString; public class FileDecl extends Stmt { diff --git a/src/erjang/jbeam/ops/FuncInfo.java b/src/erjang/jbeam/ops/FuncInfo.java index dfe0ee3e..dfafb04d 100644 --- a/src/erjang/jbeam/ops/FuncInfo.java +++ b/src/erjang/jbeam/ops/FuncInfo.java @@ -1,10 +1,11 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.ERT; -import org.erlang.EAtom; import org.objectweb.asm.Type; import org.objectweb.asm.commons.Method; +import erjang.EAtom; +import erjang.ERT; + public class FuncInfo extends Insn { diff --git a/src/erjang/jbeam/ops/FunctionAdapter.java b/src/erjang/jbeam/ops/FunctionAdapter.java index a41e55ed..0b76b1a6 100644 --- a/src/erjang/jbeam/ops/FunctionAdapter.java +++ b/src/erjang/jbeam/ops/FunctionAdapter.java @@ -1,18 +1,19 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; import java.util.HashMap; import java.util.Map; -import org.erlang.EAtom; -import org.erlang.ETerm; -import org.erlang.Tail; -import org.erlang.jbeam.BEAMFile; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; +import erjang.EAtom; +import erjang.ETerm; +import erjang.Tail; +import erjang.jbeam.BEAMFile; + public class FunctionAdapter extends CodeAdapter { private static final Type TAIL_TYPE = Type.getType(Tail.class); diff --git a/src/erjang/jbeam/ops/FunctionDecl.java b/src/erjang/jbeam/ops/FunctionDecl.java index 6e2d176d..99e6ef40 100644 --- a/src/erjang/jbeam/ops/FunctionDecl.java +++ b/src/erjang/jbeam/ops/FunctionDecl.java @@ -1,12 +1,8 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; import java.util.ArrayList; import java.util.List; -import org.erlang.EAtom; -import org.erlang.ETerm; -import org.erlang.Tail; -import org.erlang.jbeam.BEAMFile; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodAdapter; @@ -14,6 +10,11 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; +import erjang.EAtom; +import erjang.ETerm; +import erjang.Tail; +import erjang.jbeam.BEAMFile; + public class FunctionDecl extends Stmt implements Opcodes { private static final Type TAIL_TYPE = Type.getType(Tail.class); diff --git a/src/erjang/jbeam/ops/GCBif.java b/src/erjang/jbeam/ops/GCBif.java index 62894a88..3a616cbc 100644 --- a/src/erjang/jbeam/ops/GCBif.java +++ b/src/erjang/jbeam/ops/GCBif.java @@ -1,16 +1,17 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.EBIF; -import org.erlang.EAtom; -import org.erlang.ETerm; import org.objectweb.asm.Label; import org.objectweb.asm.Type; +import erjang.EAtom; +import erjang.ETerm; +import erjang.modules.ErlangModule; + public class GCBif extends Insn { private static final Type EXCEPTION_TYPE = Type.getType(Exception.class); - private static final Type BIF_TYPE = Type.getType(EBIF.class); + private static final Type BIF_TYPE = Type.getType(ErlangModule.class); private static final Type ETERM_TYPE = Type.getType(ETerm.class); diff --git a/src/erjang/jbeam/ops/Insn.java b/src/erjang/jbeam/ops/Insn.java index 268400f1..019d0833 100644 --- a/src/erjang/jbeam/ops/Insn.java +++ b/src/erjang/jbeam/ops/Insn.java @@ -1,9 +1,10 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.ETerm; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; +import erjang.ETerm; + public abstract class Insn extends Stmt implements Opcodes { Type emit_push(FunctionAdapter ma, Object value) { diff --git a/src/erjang/jbeam/ops/ModuleDecl.java b/src/erjang/jbeam/ops/ModuleDecl.java index 528d5771..8192e776 100644 --- a/src/erjang/jbeam/ops/ModuleDecl.java +++ b/src/erjang/jbeam/ops/ModuleDecl.java @@ -1,6 +1,6 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.EAtom; +import erjang.EAtom; public class ModuleDecl extends Stmt { diff --git a/src/erjang/jbeam/ops/Move.java b/src/erjang/jbeam/ops/Move.java index 9255fa53..2083d702 100644 --- a/src/erjang/jbeam/ops/Move.java +++ b/src/erjang/jbeam/ops/Move.java @@ -1,4 +1,4 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; public class Move extends Insn { diff --git a/src/erjang/jbeam/ops/PutInsn.java b/src/erjang/jbeam/ops/PutInsn.java index 9fcb3ff3..7594e399 100644 --- a/src/erjang/jbeam/ops/PutInsn.java +++ b/src/erjang/jbeam/ops/PutInsn.java @@ -1,4 +1,4 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; import org.objectweb.asm.Opcodes; diff --git a/src/erjang/jbeam/ops/PutToupleInsn.java b/src/erjang/jbeam/ops/PutToupleInsn.java index e057607e..766de346 100644 --- a/src/erjang/jbeam/ops/PutToupleInsn.java +++ b/src/erjang/jbeam/ops/PutToupleInsn.java @@ -1,4 +1,4 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; import org.objectweb.asm.Opcodes; diff --git a/src/erjang/jbeam/ops/Register.java b/src/erjang/jbeam/ops/Register.java index 4f55b932..da420c22 100644 --- a/src/erjang/jbeam/ops/Register.java +++ b/src/erjang/jbeam/ops/Register.java @@ -1,4 +1,4 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; import org.objectweb.asm.Type; diff --git a/src/erjang/jbeam/ops/RegisterX.java b/src/erjang/jbeam/ops/RegisterX.java index e41cc644..fbb6288d 100644 --- a/src/erjang/jbeam/ops/RegisterX.java +++ b/src/erjang/jbeam/ops/RegisterX.java @@ -1,4 +1,4 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; public class RegisterX extends Register { diff --git a/src/erjang/jbeam/ops/RegisterY.java b/src/erjang/jbeam/ops/RegisterY.java index 658cc743..83a1b7f4 100644 --- a/src/erjang/jbeam/ops/RegisterY.java +++ b/src/erjang/jbeam/ops/RegisterY.java @@ -1,4 +1,4 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; public class RegisterY extends Register { diff --git a/src/erjang/jbeam/ops/Return.java b/src/erjang/jbeam/ops/Return.java index 5669143c..0586e633 100644 --- a/src/erjang/jbeam/ops/Return.java +++ b/src/erjang/jbeam/ops/Return.java @@ -1,4 +1,4 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; public class Return extends Insn { diff --git a/src/erjang/jbeam/ops/Stmt.java b/src/erjang/jbeam/ops/Stmt.java index a0925e8a..0becd899 100644 --- a/src/erjang/jbeam/ops/Stmt.java +++ b/src/erjang/jbeam/ops/Stmt.java @@ -1,4 +1,4 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; public class Stmt { diff --git a/src/erjang/jbeam/ops/Test.java b/src/erjang/jbeam/ops/Test.java index f576e708..933710bd 100644 --- a/src/erjang/jbeam/ops/Test.java +++ b/src/erjang/jbeam/ops/Test.java @@ -1,17 +1,18 @@ -package org.erlang.jbeam.ops; +package erjang.jbeam.ops; -import org.erlang.EBIF; -import org.erlang.EAtom; import org.objectweb.asm.Label; import org.objectweb.asm.Type; +import erjang.EAtom; +import erjang.modules.ErlangModule; + public class Test extends Insn { private final EAtom how; private final Label failto; private final Object[] args; - private static final Type BIF_TYPE = Type.getType(EBIF.class); + private static final Type BIF_TYPE = Type.getType(ErlangModule.class); public Test(EAtom how, Label failto, Object[] args) { this.how = how; diff --git a/src/erjang/modules/ErlangModule.java b/src/erjang/modules/ErlangModule.java index a2efe65b..a3bdccec 100644 --- a/src/erjang/modules/ErlangModule.java +++ b/src/erjang/modules/ErlangModule.java @@ -1,15 +1,40 @@ -package org.erlang; - -import org.erlang.bif.Type; - -public class EBIF { - - @bif +package erjang.modules; + +import java.math.BigInteger; + +import erjang.BIF; +import erjang.EAtom; +import erjang.EBig; +import erjang.ECons; +import erjang.EDouble; +import erjang.EInteger; +import erjang.ENode; +import erjang.ENumber; +import erjang.EObject; +import erjang.EPID; +import erjang.ERT; +import erjang.ESeq; +import erjang.ETuple; +import erjang.ETuple3; +import erjang.ErlFun; +import erjang.Module; +import erjang.BIF.Type; + +@Module("erlang") +public class ErlangModule { + + static final EAtom ATOM_TRUE = EAtom.intern("true"); + static final EAtom ATOM_FALSE = EAtom.intern("false"); + + static { + } + + @BIF @ErlFun(export=true) static public EPID self() { return null; } - @bif + @BIF @ErlFun(export=true) static public EObject element(EInteger idx, EObject obj) { ETuple tup; if ((tup = obj.asTuple()) != null && tup.arity() >= idx.value) { @@ -18,7 +43,7 @@ static public EObject element(EInteger idx, EObject obj) { throw ERT.badarg("erlang", "element", idx, obj); } - @bif + @BIF static public EObject element(EInteger idx, ETuple tup) { if (tup.arity() >= idx.value) { return tup.nth(idx.value); @@ -26,7 +51,7 @@ static public EObject element(EInteger idx, ETuple tup) { throw ERT.badarg("erlang", "element", idx, tup); } - @bif + @BIF static public EObject element(int idx, ETuple tup) { if (tup.arity() >= idx) { return tup.nth(idx); @@ -34,7 +59,7 @@ static public EObject element(int idx, ETuple tup) { throw ERT.badarg("erlang", "element", idx, tup); } - @bif + @BIF static public EObject element(int idx, EObject obj) { ETuple tup; if ((tup = obj.asTuple()) != null && tup.arity() >= idx) { @@ -43,12 +68,12 @@ static public EObject element(int idx, EObject obj) { throw ERT.badarg("erlang", "element", idx, obj); } - @bif + @BIF static public EObject hd(ECons cell) { return cell.head(); } - @bif + @BIF static public EObject hd(EObject cell) { ECons cons; if ((cons = cell.asCons()) != null) { @@ -57,7 +82,7 @@ static public EObject hd(EObject cell) { throw ERT.badarg("erlang", "hd", cell); } - @bif + @BIF static public int length(EObject list) { ESeq seq; if ((seq = list.asSeq()) != null) { @@ -66,12 +91,12 @@ static public int length(EObject list) { throw ERT.badarg("erlang", "length", list); } - @bif + @BIF static public int length(ESeq list) { return list.length(); } - @bif(name="length", type=Type.GUARD) + @BIF(name="length", type=Type.GUARD) static public EInteger length$guard(EObject list) { ESeq seq; if ((seq = list.asSeq()) != null) { @@ -80,31 +105,31 @@ static public int length(ESeq list) { return null; } - @bif + @BIF @ErlFun(export=true) static public ENode node() { return null; } - @bif + @BIF @ErlFun(export=true) static public ENode node(EObject name) { return null; } // process dict - @bif + @BIF @ErlFun(export=true) static public ECons get() { return null; } - @bif + @BIF @ErlFun(export=true) static public EObject get(EObject key) { return null; } // floats - @bif(type=Type.ARITHBIF) + @BIF(type=Type.ARITHBIF) static public double fdiv(double v1, double v2) { if (v2 == 0.0) throw ERT.badarith("erlang", "/", v1, v2); @@ -112,34 +137,58 @@ static public double fdiv(double v1, double v2) { return v1 / v2; } - @bif(type=Type.ARITHBIF) + @BIF(type=Type.ARITHBIF) static public double fsub(double v1, double v2) { return v1 - v2; } - @bif(type=Type.ARITHBIF) + @BIF(type=Type.ARITHBIF) static public double fadd(double v1, double v2) { return v1 + v2; } - @bif(type=Type.ARITHBIF) + @BIF(type=Type.ARITHBIF) static public double fmul(double v1, double v2) { return v1 * v2; } // arithmetic - @bif(name="-") - static public ENumber $minus$(EObject v1, int v2) { + @BIF(name="div") + static public ENumber div(EObject v1, int v2) { + ENumber n1; + if ((n1 = v1.asNumber()) != null) { + return n1.div(v2); + } + throw ERT.badarg(); + } + + @BIF(name="div") + static public ENumber div(ENumber n1, int v2) { + return n1.div(v2); + } + + @BIF(name="-") + static public ENumber minus(EObject v1, int v2) { ENumber n1; if ((n1 = v1.asNumber()) != null) { return n1.minus(v2); } - throw ERT.badarg((Throwable) null, "erlang", "-", v1, v2); + throw ERT.badarg("erlang", "-", v1, v2); + } + + @BIF(name="-") + static public ENumber minus(int v1, int v2) { + long res = (long)v1 - (long)v2; + int intres = (int) res; + if (res == intres) + return new EInteger(intres); + else + return new EBig(BigInteger.valueOf(res)); } - @bif(name="-") - static public ENumber $minus$(EObject v1, EObject v2) { + @BIF(name="-") @ErlFun(name="-", export=true) + static public ENumber minus(EObject v1, EObject v2) { ENumber n1; if ((n1 = v1.asNumber()) != null) { ENumber n2; @@ -150,7 +199,7 @@ static public double fmul(double v1, double v2) { throw ERT.badarg((Throwable) null, "erlang", "-", v1, v2); } - @bif(name="-", type=Type.GUARD) + @BIF(name="-", type=Type.GUARD) static public ENumber subtract$guard(EObject v1, EObject v2) { ENumber n1; if ((n1 = v1.asNumber()) != null) { @@ -162,7 +211,49 @@ static public double fmul(double v1, double v2) { return null; } - @bif(name = "+") + @BIF(name="/", type=Type.GUARD) + static public ENumber divide$guard(EObject v1, EObject v2) { + ENumber n1; + if ((n1 = v1.asNumber()) != null) { + ENumber n2; + if ((n2 = v2.asNumber()) != null) { + if (n2.intValue() == 0) return null; + return n1.divide(v2); + } + } + return null; + } + + @BIF(name="/", type=Type.GUARD) + static public ENumber divide$guard(EObject v1, double d2) { + ENumber n1; + if (d2 != 0.0 && (n1 = v1.asNumber()) != null) { + return n1.divide(d2); + } + return null; + } + + + + @BIF(name = "+", type=Type.GUARD) + static public ENumber plus$guard(EObject v1, EObject v2) { + ENumber n1; + if ((n1 = v1.asNumber()) != null) { + ENumber n2; + if ((n2 = v2.asNumber()) != null) { + return n1.add(n2); + } + } + return null; + } + + + @BIF(name="+") + static public ENumber plus(int v1, int v2) { + return ENumber.valueFor((long)v1 + (long)v2); + } + + @BIF(name = "+") static public ENumber plus(EObject v1, EObject v2) { ENumber n1; if ((n1 = v1.asNumber()) != null) { @@ -174,8 +265,17 @@ static public ENumber plus(EObject v1, EObject v2) { throw ERT.badarg((Throwable) null, "erlang", "+", v1, v2); } - @bif(name = "*") - static public ENumber $multiply$(EObject v1, EObject v2) { + @BIF(name = "+") + static public ENumber plus(EObject v1, int i2) { + ENumber n1; + if ((n1 = v1.asNumber()) != null) { + return n1.add(i2); + } + throw ERT.badarg(v1, i2); + } + + @BIF(name = "*") + static public ENumber multiply(EObject v1, EObject v2) { ENumber n1; if ((n1 = v1.asNumber()) != null) { ENumber n2; @@ -186,7 +286,12 @@ static public ENumber plus(EObject v1, EObject v2) { throw ERT.badarg((Throwable) null, "erlang", "+", v1, v2); } - @bif(name = "trunc") + @BIF(name = "*") + static public ENumber multiply(int v1, int v2) { + return ENumber.valueFor((long)v1 * (long)v2); + } + + @BIF(name = "trunc") @ErlFun(export=true) static public ENumber trunc(EObject v1) { ENumber n1; if ((n1 = v1.asNumber()) != null) { @@ -195,17 +300,27 @@ static public ENumber trunc(EObject v1) { throw ERT.badarg((Throwable) null, "erlang", "trunc", v1); } - @bif(name = "trunc") + @BIF(name = "trunc") static public double trunc(double d) { return Math.floor(d); } - @bif(name="round") + @BIF(name = "trunc") + static public double trunc(EDouble d1) { + return Math.floor(d1.value); + } + + @BIF(name="round") static public double round(double d) { return Math.round(d); } - @bif(name = "rem") + @BIF(name="round") + static public double round(EDouble d) { + return Math.round(d.value); + } + + @BIF @ErlFun(export=true) static public ENumber rem(EObject v1, EObject v2) { ENumber n1; if ((n1 = v1.asNumber()) != null) { @@ -217,7 +332,7 @@ static public ENumber rem(EObject v1, EObject v2) { throw ERT.badarg("erlang", "rem", v1, v2); } - @bif(name="trunc", type=Type.GUARD) + @BIF(name="rem", type=Type.GUARD) static public ENumber rem$guard(EObject v1, EObject v2) { ENumber n1; if ((n1 = v1.asNumber()) != null) { @@ -229,7 +344,7 @@ static public ENumber rem(EObject v1, EObject v2) { return null; } - @bif(name = "rem") + @BIF(name = "rem") static public ENumber rem(EObject v1, int v2) { ENumber n1; if ((n1 = v1.asNumber()) != null) { @@ -238,7 +353,7 @@ static public ENumber rem(EObject v1, int v2) { throw ERT.badarg("erlang", "rem", v1, v2); } - @bif(name="abs", type=Type.GUARD) + @BIF(name="abs", type=Type.GUARD) static public ENumber abs$guard(EObject v1) { ENumber num; if ((num = v1.asNumber()) != null) { @@ -247,7 +362,7 @@ static public ENumber rem(EObject v1, int v2) { return null; } - @bif(name = "abs") + @BIF(name = "abs") @ErlFun(export=true) static public ENumber abs(EObject v1) { ENumber num; if ((num = v1.asNumber()) != null) { @@ -256,12 +371,12 @@ static public ENumber abs(EObject v1) { throw ERT.badarg("erlang", "abs", v1); } - @bif(name = "abs") + @BIF(name = "abs") static public ENumber abs(ENumber v1) { return v1.asb(); } - @bif(name = "now") + @BIF(name = "now") @ErlFun(export=true) static public ETuple3 now() { long now = System.currentTimeMillis(); @@ -278,4 +393,12 @@ static public ETuple3 now() { return res; } + // tests + + @BIF(name="is_eq",type=Type.GUARD) + public static final EAtom is_eq$g(EObject a1, EObject a2) { return a1.equals(a2) ? ATOM_TRUE : null; } + + @BIF + public static final EAtom is_eq(EObject a1, EObject a2) { return a1.equals(a2) ? ATOM_TRUE : ATOM_FALSE; } + }