/** * 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; import static erjang.beam.CodeAtoms.ERLANG_ATOM; import static erjang.beam.CodeAtoms.FALSE_ATOM; import static erjang.beam.CodeAtoms.TRUE_ATOM; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.logging.Level; import erjang.codegen.EFunCG; import kilim.Pausable; import kilim.analysis.ClassInfo; import kilim.analysis.ClassWeaver; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassVisitor; 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; import org.objectweb.asm.commons.Method; import erjang.EAtom; import erjang.EBig; import erjang.EBinMatchState; import erjang.EBinary; import erjang.EBitString; import erjang.EBitStringBuilder; import erjang.ECons; import erjang.EDouble; import erjang.EFun; import erjang.EInteger; import erjang.EInternalPID; import erjang.EList; import erjang.EMap; import erjang.EModuleManager; import erjang.ENil; import erjang.ENumber; import erjang.EObject; import erjang.EOutputStream; import erjang.EPID; import erjang.EPort; import erjang.EProc; import erjang.ERT; import erjang.ERef; import erjang.ESeq; import erjang.ESmall; import erjang.EString; import erjang.ETask; import erjang.ETuple; import erjang.ETuple2; import erjang.ErlangException; import erjang.Export; import erjang.FunID; import erjang.Import; import erjang.Internal; import erjang.LocalFunID; import erjang.Module; import erjang.OnLoad; import erjang.beam.Arg.Kind; import erjang.beam.ModuleAnalyzer.FunInfo; import erjang.beam.repr.ExtFun; import erjang.beam.repr.Insn; import erjang.m.erlang.ErlConvert; /** * */ public class CompilerVisitor implements ModuleVisitor, Opcodes { public static boolean PARANOIA_MODE = false; // a select ins with up to this many cases that are all // atom values is just encoded as an if-then-else-etc. public static final int ATOM_SELECT_IF_ELSE_LIMIT = 4; EAtom am_source = EAtom.intern("source"); ESeq atts = ERT.NIL; ESeq compile_info = ERT.NIL; String source = null; private Set<String> exported = new HashSet<String>(); private final ClassVisitor cv; private EAtom module_name; private Type self_type; private static final EObject ATOM_field_flags = EAtom.intern("field_flags"); private static final EObject ATOM_start = EAtom.intern("start"); private static final EObject am_erlang = EAtom.intern("erlang"); private static final EObject am_apply = EAtom.intern("apply"); static final String[] PAUSABLE_EX = new String[] { Type.getType( Pausable.class).getInternalName() }; static final Type EBINMATCHSTATE_TYPE = Type.getType(EBinMatchState.class); static final Type EBINSTRINGBUILDER_TYPE = Type .getType(EBitStringBuilder.class); static final Type ERLANG_EXCEPTION_TYPE = Type .getType(ErlangException.class); static final Type ERT_TYPE = Type.getType(ERT.class); static final Type EINTEGER_TYPE = Type.getType(EInteger.class); static final Type ESTRING_TYPE = Type.getType(EString.class); static final Type ECOMPILEDMODULE_TYPE = Type .getType(ECompiledModule.class); /** * */ static final String ECOMPILEDMODULE_NAME = ECOMPILEDMODULE_TYPE .getInternalName(); static final Type ENUMBER_TYPE = Type.getType(ENumber.class); static final Type EOBJECT_TYPE = Type.getType(EObject.class); static final Type EMAP_TYPE = Type.getType(EMap.class); static final String EOBJECT_DESC = EOBJECT_TYPE.getDescriptor(); static final Type ETASK_TYPE = Type.getType(ETask.class); static final String ETASK_NAME = ETASK_TYPE.getInternalName(); static final Type EPROC_TYPE = Type.getType(EProc.class); static final String EPROC_NAME = EPROC_TYPE.getInternalName(); static final String EPROC_DESC = EPROC_TYPE.getDescriptor(); static final Type ESMALL_TYPE = Type.getType(ESmall.class); static final String ESMALL_NAME = ESMALL_TYPE.getInternalName(); static final Type EEXCEPTION_TYPE = Type.getType(ErlangException.class); static final String EEXCEPTION_DESC = EEXCEPTION_TYPE.getDescriptor(); /**/ static final String GO_DESC = "(" + EPROC_TYPE.getDescriptor() + ")" + EOBJECT_DESC; static final Type EDOUBLE_TYPE = Type.getType(EDouble.class); static final Type EBIG_TYPE = Type.getType(EBig.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 EBITSTRING_TYPE = Type.getType(EBitString.class); static final Type EBITSTRINGBUILDER_TYPE = Type .getType(EBitStringBuilder.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 String EFUN_NAME = EFUN_TYPE.getInternalName(); static final String EOBJECT_NAME = EOBJECT_TYPE.getInternalName(); static final String ETUPLE_NAME = ETUPLE_TYPE.getInternalName(); static final String ERT_NAME = ERT_TYPE.getInternalName(); static final String EDOUBLE_NAME = EDOUBLE_TYPE.getInternalName(); static final String EBIG_NAME = EBIG_TYPE.getInternalName(); static final String EINTEGER_NAME = EINTEGER_TYPE.getInternalName(); static final String ENIL_NAME = ENIL_TYPE.getInternalName(); static final String ESEQ_NAME = ESEQ_TYPE.getInternalName(); static final String ETUPLE_DESC = ETUPLE_TYPE.getDescriptor(); static final String EATOM_DESC = EATOM_TYPE.getDescriptor(); static final String ECONS_DESC = ECONS_TYPE.getDescriptor(); static final String ESEQ_DESC = ESEQ_TYPE.getDescriptor(); /** * */ static final String EFUN_DESCRIPTOR = EFUN_TYPE.getDescriptor(); 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 Type MODULE_ANN_TYPE = Type.getType(Module.class); static final Type IMPORT_ANN_TYPE = Type.getType(Import.class); static final Type EXPORT_ANN_TYPE = Type.getType(Export.class); static final Type INTERNAL_ANN_TYPE = Type.getType(Internal.class); static final Type ONLOAD_ANN_TYPE = Type.getType(OnLoad.class); private final ClassRepo classRepo; private boolean uses_on_load; /** * @param classRepo * */ public CompilerVisitor(ClassVisitor cv, ClassRepo classRepo) { this.cv = cv; this.classRepo = classRepo; } /* * (non-Javadoc) * * @see erjang.beam.ModuleVisitor#visitModule(erjang.EAtom) */ @Override public void visitModule(EAtom name) { // If any of the contained functions uses on_load, then we // need to do module-local calls differently, because they // may need to be replaced by a NIF call. if (funInfos != null) { for (FunInfo fi : funInfos.values()) { if (fi.call_on_load == true) { this.uses_on_load = true; } } } if (uses_on_load) { for (FunInfo fi : funInfos.values()) { fi.is_pausable = true; fi.call_is_pausable = true; } } this.module_name = name; this.self_type = Type.getType("L" + getInternalClassName() + ";"); cv.visit(V1_6, ACC_PUBLIC, self_type.getInternalName(), null, ECOMPILEDMODULE_NAME, null); add_module_annotation(cv); } private void add_module_annotation(ClassVisitor cv) { AnnotationVisitor av = cv.visitAnnotation( MODULE_ANN_TYPE.getDescriptor(), true); av.visit("value", getModuleName()); av.visitEnd(); } public String getInternalClassName() { String moduleName = getModuleName(); return Compiler.moduleClassName(moduleName); } /** * @return */ private String getModuleName() { return module_name.getName(); } Map<EObject, String> constants = new HashMap<EObject, String>(); Map<BitSet, String> bitsets = new HashMap<BitSet, String>(); /* * (non-Javadoc) * * @see erjang.beam.ModuleVisitor#visitAttribute(erjang.EAtom, * erjang.EObject) */ @Override public void visitAttribute(EAtom att, EObject value) { atts = atts.cons(ETuple2.make(att, value)); } @Override public void visitCompile(EAtom att, EObject value) { EString string; if (att == am_source && (string = value.testString()) != null) { source = string.stringValue(); } compile_info = compile_info.cons(ETuple2.make(att, value)); } String source() { if (source == null) { return module_name.getName() + ".erl"; } else { int idx = source.lastIndexOf('/'); if (idx == -1) { return source; } else { return source.substring(idx + 1); } } } /* * (non-Javadoc) * * @see erjang.beam.ModuleVisitor#visitEnd() */ @Override public void visitEnd() { cv.visitSource(source(), null); // wow, this is where we generate <clinit> for (Map.Entry<String, ExtFun> ent : imported.entrySet()) { String field_name = ent.getKey(); ExtFun f = ent.getValue(); FieldVisitor fv = cv.visitField(ACC_STATIC, ent.getKey(), "L" + EFUN_NAME + f.arity + ";", null, null); EFunCG.ensure(f.arity); AnnotationVisitor av = fv.visitAnnotation( IMPORT_ANN_TYPE.getDescriptor(), true); av.visit("module", f.mod.getName()); av.visit("fun", f.fun.getName()); av.visit("arity", f.arity); av.visitEnd(); fv.visitEnd(); } generate_on_load(); generate_classinit(); cv.visitEnd(); } private void generate_on_load() { for (FunInfo fi : funInfos.values()) { if (fi.call_on_load) { MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "on_load", "(Lerjang/EProc;)Lerjang/EObject;", null, PAUSABLE_EX); mv.visitCode(); String mname = EUtil.getJavaName(fi.name.function, fi.name.arity); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKESTATIC, self_type.getInternalName(), mname, EUtil.getSignature(0, true)); mv.visitInsn(ARETURN); mv.visitMaxs(1, 3); mv.visitEnd(); mv = cv.visitMethod(ACC_PUBLIC, "has_on_load", "()Z", null, null); mv.visitCode(); mv.visitInsn(ICONST_1); mv.visitInsn(IRETURN); mv.visitMaxs(1, 1); mv.visitEnd(); return; } } return; } Type getConstantType(EObject term) { Type type = Type.getType(term.getClass()); if (type.equals(ESTRING_TYPE)) return type; if (type.equals(ENIL_TYPE)) return type; if (term.testSeq() != null) { type = ESEQ_TYPE; } return type; } /** * */ private void generate_classinit() { MethodVisitor mv = cv.visitMethod(ACC_STATIC | ACC_PRIVATE, "<clinit>", "()V", null, null); mv.visitCode(); for (Map.Entry<String, String> ent : funs.entrySet()) { String field = ent.getKey(); String clazz = ent.getValue(); mv.visitTypeInsn(NEW, clazz); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, clazz, "<init>", "()V"); mv.visitFieldInsn(PUTSTATIC, self_type.getInternalName(), field, "L" + funt.get(field) + ";"); } for (Map.Entry<BitSet,String> ent : bitsets.entrySet()) { BitSet bs = ent.getKey(); String name = ent.getValue(); FieldVisitor fv = cv.visitField(ACC_STATIC, name, Type.getDescriptor(BitSet.class), null, null); fv.visitEnd(); byte[] bytes = bs.toByteArray(); mv.visitLdcInsn(new String(bytes, StandardCharsets.ISO_8859_1)); mv.visitLdcInsn("ISO-8859-1"); mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(String.class), "getBytes", "(Ljava/lang/String;)[B"); mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(BitSet.class), "valueOf", "([B)" + Type.getDescriptor(BitSet.class)); mv.visitFieldInsn(PUTSTATIC, self_type.getInternalName(), name, Type.getDescriptor(BitSet.class)); } cv.visitField(ACC_STATIC, "attributes", ESEQ_DESC, null, null).visitEnd(); constants.put(atts.reverse(), "attributes"); cv.visitField(ACC_STATIC, "compile", ESEQ_DESC, null, null).visitEnd(); constants.put(compile_info.reverse(), "compile"); if (this.module_md5 != null) { cv.visitField(ACC_STATIC, "module_md5", EBINARY_TYPE.getDescriptor(), null, null).visitEnd(); constants.put(module_md5, "module_md5"); } for (Map.Entry<EObject, String> ent : constants.entrySet()) { EObject term = ent.getKey(); Type type = getConstantType(term); ETuple tup; if (((tup=term.testTuple()) != null || term.testCons() != null) && term != ERT.NIL && !type.equals(ESTRING_TYPE) ) { EBinary bin = ErlConvert.term_to_binary(term, EList.make(ErlConvert.am_compressed)); bin.emit_const(mv); mv.visitMethodInsn(INVOKESTATIC, Type.getType(ErlConvert.class).getInternalName(), "binary_to_term", EUtil.getSignature(1, false)); mv.visitTypeInsn(CHECKCAST, type.getInternalName()); } else { term.emit_const(mv); } mv.visitFieldInsn(Opcodes.PUTSTATIC, self_type.getInternalName(), ent.getValue(), type .getDescriptor()); } mv.visitInsn(RETURN); mv.visitMaxs(200, 10); mv.visitEnd(); // make the method module_name mv = cv.visitMethod(ACC_PROTECTED, "module_name", "()Ljava/lang/String;", null, null); mv.visitCode(); mv.visitLdcInsn(this.module_name.getName()); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); // make the method attributes mv = cv.visitMethod(ACC_PROTECTED, "attributes", "()" + ESEQ_TYPE.getDescriptor(), null, null); mv.visitCode(); mv.visitFieldInsn(Opcodes.GETSTATIC, self_type.getInternalName(), "attributes", ESEQ_TYPE.getDescriptor()); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); // make the method attributes mv = cv.visitMethod(ACC_PROTECTED, "compile", "()" + ESEQ_TYPE.getDescriptor(), null, null); mv.visitCode(); mv.visitFieldInsn(Opcodes.GETSTATIC, self_type.getInternalName(), "compile", ESEQ_TYPE.getDescriptor()); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); // make default constructor mv = cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, ECOMPILEDMODULE_NAME, "<init>", "()V"); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); mv = cv.visitMethod(ACC_PUBLIC, "registerImportsAndExports", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, ECOMPILEDMODULE_NAME, "registerImportsAndExports", "()V"); for (Lambda l : lambdas_xx.values()) { mv.visitTypeInsn(NEW, Type.getInternalName(LocalFunID.class)); mv.visitInsn(DUP); module_name.emit_const(mv); l.fun.emit_const(mv); push_int(mv, l.arity); push_int(mv, l.old_index); push_int(mv, l.index); push_int(mv, l.old_uniq); mv.visitFieldInsn(GETSTATIC, self_type.getInternalName(), "module_md5", EBINARY_TYPE.getDescriptor()); mv.visitMethodInsn( INVOKESPECIAL, Type.getInternalName(LocalFunID.class), "<init>", "(" + EATOM_DESC + EATOM_DESC + "IIII" + EBINARY_TYPE.getDescriptor() + ")V"); mv.visitInsn(DUP); cv.visitField(ACC_STATIC, anon_fun_name(l), Type.getDescriptor(LocalFunID.class), null, null) .visitEnd(); mv.visitFieldInsn(PUTSTATIC, self_type.getInternalName(), anon_fun_name(l), Type.getDescriptor(LocalFunID.class)); String mname = EUtil.getJavaName(l.fun, l.arity - l.freevars); String outer_name = self_type.getInternalName(); String inner_name = "FN_" + mname; String full_inner_name = outer_name + "$" + inner_name; mv.visitLdcInsn(full_inner_name.replace('/', '.')); mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Class.class), "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); mv.visitMethodInsn( INVOKESTATIC, Type.getInternalName(EModuleManager.class), "register_lambda", "(" + Type.getDescriptor(LocalFunID.class) + Type.getDescriptor(Class.class) + ")V"); } mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } public static String anon_fun_name(Lambda l) { return "lambda_" + l.index + "_" + l.old_index + "_" + l.old_uniq; } private void push_int(MethodVisitor mv, int val) { if (val == -1) { mv.visitInsn(ICONST_M1); } else if (val >= 0 && val <= 5) { mv.visitInsn(ICONST_0 + val); } else { mv.visitLdcInsn(new Integer(val)); } } /* * (non-Javadoc) * * @see erjang.beam.ModuleVisitor#visitExport(erjang.EAtom, int, int) */ @Override public void visitExport(EAtom name, int arity, int entry) { exported.add(EUtil.getJavaName(name, arity)); } boolean isExported(EAtom name, int arity) { return exported.contains(EUtil.getJavaName(name, arity)); } @Override public void declareFunction(EAtom fun, int arity, int label) { /* ignore */ } @Override public FunctionVisitor visitFunction(EAtom name, int arity, int startLabel) { return new ASMFunctionAdapter(name, arity, startLabel); } Map<FunID, Lambda> lambdas_xx = new TreeMap<FunID, Lambda>(); Map<String, String> funs = new HashMap<String, String>(); Map<String, String> funt = new HashMap<String, String>(); Set<String> non_pausable_methods = new HashSet<String>(); public EBinary module_md5; private static final String SEQ_CONS_SIG = "(" + EOBJECT_DESC + ")" + ESEQ_DESC; private static final String FUNC_INFO_SIG = "(" + EATOM_DESC + EATOM_DESC + ESEQ_DESC + ")" + EOBJECT_DESC; private static final String ERT_CONS_SIG = "(" + EOBJECT_DESC + EOBJECT_DESC + ")" + ECONS_TYPE.getDescriptor(); private static final String TEST_FUN_SIG = "(" + EOBJECT_DESC + EFUN_DESCRIPTOR + ")V"; class ASMFunctionAdapter implements FunctionVisitor2 { public class FunWithArgs { private ExtFun fun; private Arg[] args; public FunWithArgs(ExtFun fun, Arg[] args) { this.fun = fun; this.args = args; } @Override public int hashCode() { return fun.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof FunWithArgs) { FunWithArgs fwa = (FunWithArgs) obj; return fun.equals(fwa.fun) && Arrays.equals(args, fwa.args); } return false; } } private final EAtom fun_name; private final int arity; private final int startLabel; Map<Integer, Label> labels = new TreeMap<Integer, Label>(); Map<Integer, Label> ex_handler_labels = new TreeMap<Integer, Label>(); Set<Integer> label_inserted = new TreeSet<Integer>(); List<EXHandler> ex_handlers = new ArrayList<EXHandler>(); EXHandler activeExceptionHandler; BeamExceptionHandler active_beam_exh; private boolean isTailRecursive; private MethodVisitor mv; private int[] xregs; private int[] yregs; private int[] fpregs; private Label start; private Label end; private int scratch_reg; private int bit_string_builder; private int bit_string_matcher; private int bit_string_save; private FunInfo funInfo; private Collection<Integer> deadBlocks; public Map<FunWithArgs,Label> local_self_call = new HashMap<FunWithArgs,Label>(); Label getLabel(int i) { if (i <= 0) throw new Error(); Label l = labels.get(i); if (l == null) { labels.put(i, l = new Label()); } return l; } Label getExceptionHandlerLabel(BeamExceptionHandler exh) { int i = exh.getHandlerLabel(); Label l = ex_handler_labels.get(i); if (l == null) { ex_handler_labels.put(i, l = new Label()); } return l; } /** * @param name * @param arity * @param startLabel * @param isTailRecursive */ public ASMFunctionAdapter(EAtom name, int arity, int startLabel) { this.fun_name = name; this.arity = arity; this.startLabel = startLabel; } @Override public void visitMaxs(Collection<Integer> x_regs, int y_count, int fp_count, boolean isTailRecursive, Collection<Integer> dead_blocks) { FunID me = new FunID(module_name, fun_name, arity); this.funInfo = funInfos.get(me); this.isTailRecursive = isTailRecursive; this.deadBlocks = dead_blocks; Lambda lambda = get_lambda_freevars(fun_name, arity); final int freevars = lambda == null ? 0 : lambda.freevars; int real_arity = arity - freevars; String javaName = EUtil.getJavaName(fun_name, real_arity); String signature = EUtil.getSignature(arity, true); mv = cv.visitMethod(ACC_STATIC | ACC_PUBLIC, javaName, signature, null, funInfo.is_pausable ? PAUSABLE_EX : null); if (!funInfo.is_pausable) { non_pausable_methods.add(javaName); } this.start = new Label(); this.end = new Label(); mv.visitCode(); allocate_regs_to_locals(x_regs, y_count, fp_count); mv.visitLabel(start); mv.visitJumpInsn(GOTO, getLabel(startLabel)); } /* * (non-Javadoc) * * @see erjang.beam.FunctionVisitor#visitEnd() */ @Override public void visitEnd() { adjust_exception_handlers(null, false); mv.visitLabel(end); for (EXHandler h : ex_handlers) { if (!label_inserted.contains(h.handler_beam_label)) throw new InternalError("Exception handler not inserted: " + h.handler_beam_label); if (deadBlocks.contains(h.handler_beam_label)) { continue; } mv.visitTryCatchBlock(h.begin, h.end, h.target, Type.getType(ErlangException.class).getInternalName()); } mv.visitMaxs(20, scratch_reg + 3); // mark this function to be called "on load" if (funInfo.call_on_load) { AnnotationVisitor an = mv.visitAnnotation( ONLOAD_ANN_TYPE.getDescriptor(), true); an.visitEnd(); } mv.visitEnd(); int arity_plus = arity; Lambda lambda = get_lambda_freevars(fun_name, arity_plus); final int freevars = lambda == null ? 0 : lambda.freevars; int real_arity = arity_plus - freevars; String mname = EUtil.getJavaName(fun_name, real_arity); String outer_name = self_type.getInternalName(); String inner_name = "FN_" + mname; String full_inner_name = outer_name + "$" + inner_name; boolean make_fun = false; boolean is_exported = isExported(fun_name, arity); if (lambda != null) { CompilerVisitor.this.module_md5 = lambda.uniq; make_fun = true; } else { String fun_type; if (uses_on_load) { fun_type = EFUN_NAME + arity; } else { fun_type = full_inner_name; } if (!Boolean.getBoolean("erjang.inline_calls") && funInfo.is_called_locally_in_nontail_position) generate_invoke_call_self(); if (funInfo.is_called_locally_in_tail_position) generate_tail_call_self(fun_type); if (funInfo.mustHaveFun() || uses_on_load) { FieldVisitor fv = cv.visitField(ACC_STATIC | (uses_on_load ? 0 : ACC_FINAL), mname, "L" + fun_type + ";", null, null); EFunCG.ensure(arity); if (is_exported) { if (ModuleAnalyzer.log.isLoggable(Level.FINE)) ModuleAnalyzer.log.fine("export " + module_name + ":" + fun_name + "/" + arity); AnnotationVisitor an = fv.visitAnnotation( EXPORT_ANN_TYPE.getDescriptor(), true); an.visit("module", module_name.getName()); an.visit("fun", fun_name.getName()); an.visit("arity", new Integer(arity)); an.visitEnd(); } else if (uses_on_load) { AnnotationVisitor an = fv.visitAnnotation( INTERNAL_ANN_TYPE.getDescriptor(), true); an.visit("module", module_name.getName()); an.visit("fun", fun_name.getName()); an.visit("arity", new Integer(arity)); an.visitEnd(); } fv.visitEnd(); funs.put(mname, full_inner_name); funt.put(mname, fun_type); EFunCG.ensure(arity); make_fun = true; } } if (make_fun) { cv.visitInnerClass(full_inner_name, outer_name, inner_name, ACC_STATIC); byte[] data = CompilerVisitor.make_invoker( module_name.getName(), fun_name.getName(), self_type, mname, mname, arity, true, is_exported, false, lambda, EOBJECT_TYPE, funInfo.may_return_tail_marker, funInfo.is_pausable | funInfo.call_is_pausable); ClassWeaver w = new ClassWeaver(data, new Compiler.ErjangDetector( self_type.getInternalName(), non_pausable_methods)); w.weave(); if (w.getClassInfos().size() == 0) { // Class did not need // weaving try { classRepo.store(full_inner_name, data); } catch (IOException e) { e.printStackTrace(); } } else { for (ClassInfo ci : w.getClassInfos()) { try { // System.out.println("> storing "+ci.className); String iname = ci.className.replace('.', '/'); classRepo.store(iname, ci.bytes); } catch (IOException e) { e.printStackTrace(); } } } } } private void ensure_exception_handler_in_place() { adjust_exception_handlers(active_beam_exh, false); } private void adjust_exception_handlers(BeamExceptionHandler exh, boolean expectChange) { int desiredLabel = exh == null ? -1 : exh.getHandlerLabel(); int actualLabel = activeExceptionHandler == null ? -1 : activeExceptionHandler.handler_beam_label; if (expectChange) assert (actualLabel != desiredLabel); if (actualLabel != desiredLabel) { // Terminate the old handler block: if (activeExceptionHandler != null) { mv.visitLabel(activeExceptionHandler.end); activeExceptionHandler = null; } // ...and begin a new if necessary: if (exh != null) { EXHandler h = new EXHandler(); h.begin = new Label(); h.end = new Label(); h.target = getExceptionHandlerLabel(exh); h.handler_beam_label = exh.getHandlerLabel(); h.beam_exh = exh; ex_handlers.add(h); mv.visitLabel(h.begin); // mv.visitInsn(NOP); // To avoid potentially-empty // exception block; Kilim can't handle those. activeExceptionHandler = h; } } } /** * */ private void generate_invoke_call_self() { boolean pausable = funInfo.is_pausable || funInfo.call_is_pausable; String javaName = EUtil.getJavaName(fun_name, arity); String signature = EUtil.getSignature(arity, true); mv = cv.visitMethod(ACC_STATIC, javaName + "$call", signature, null, pausable ? PAUSABLE_EX : null); mv.visitCode(); if (!pausable) { non_pausable_methods.add(javaName + "$call"); } // if (isTailRecursive) { mv.visitVarInsn(ALOAD, 0); for (int i = 0; i < arity; i++) { mv.visitVarInsn(ALOAD, i + 1); } mv.visitMethodInsn(INVOKESTATIC, self_type.getInternalName(), javaName, EUtil.getSignature(arity, true)); if (funInfo.may_return_tail_marker) { mv.visitVarInsn(ASTORE, arity + 1); Label done = new Label(); Label loop = new Label(); mv.visitLabel(loop); mv.visitVarInsn(ALOAD, arity + 1); if (EProc.TAIL_MARKER == null) { mv.visitJumpInsn(IFNONNULL, done); } else { mv.visitFieldInsn(GETSTATIC, EPROC_NAME, "TAIL_MARKER", EOBJECT_DESC); mv.visitJumpInsn(IF_ACMPNE, done); } // load proc mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, EPROC_NAME, "tail", EFUN_DESCRIPTOR); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, EFUN_NAME, (pausable ? "go" : "go2"), GO_DESC); mv.visitVarInsn(ASTORE, arity + 1); mv.visitJumpInsn(GOTO, loop); mv.visitLabel(done); mv.visitVarInsn(ALOAD, arity + 1); } mv.visitInsn(ARETURN); mv.visitMaxs(arity + 2, arity + 2); mv.visitEnd(); } /** * @param full_inner_name * TODO * */ private void generate_tail_call_self(String full_inner_name) { String javaName = EUtil.getJavaName(fun_name, arity); String signature = EUtil.getSignature(arity, true); mv = cv.visitMethod(ACC_STATIC, javaName + "$tail", signature, null, null); mv.visitCode(); // if (isTailRecursive) { for (int i = 0; i < arity; i++) { mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, i + 1); mv.visitFieldInsn(PUTFIELD, EPROC_NAME, "arg" + i, EOBJECT_DESC); } mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETSTATIC, self_type.getInternalName(), javaName, "L" + full_inner_name + ";"); mv.visitFieldInsn(PUTFIELD, EPROC_NAME, "tail", EFUN_DESCRIPTOR); if (EProc.TAIL_MARKER == null) { mv.visitInsn(ACONST_NULL); } else { mv.visitFieldInsn(GETSTATIC, EPROC_NAME, "TAIL_MARKER", EOBJECT_DESC); } /* * } else { for (int i = 0; i < arity + 1; i++) { * mv.visitVarInsn(ALOAD, i); } * * mv.visitMethodInsn(INVOKESTATIC, self_type.getInternalName(), * javaName, signature); } */ mv.visitInsn(ARETURN); mv.visitMaxs(arity + 2, arity + 2); mv.visitEnd(); } /** * @param xCount * @param yCount * @param fpCount */ private void allocate_regs_to_locals(Collection<Integer> xRegs, int yCount, int fpCount) { int max_y = yCount; int max_f = fpCount; int local = 1; Integer[] xxregs = xRegs.toArray(new Integer[xRegs.size()]); if (xxregs.length > 0) { Arrays.sort(xxregs); Integer biggest_used = xxregs[xxregs.length - 1]; xregs = new int[biggest_used + 1]; for (int i = 0; i < xxregs.length; i++) { xregs[xxregs[i]] = i + local; } local += xxregs.length; } yregs = new int[max_y]; for (int i = 0; i < max_y; i++) { // mv.visitLocalVariable("Y" + i, EOBJECT_DESCRIPTOR, // null, start, end, local); yregs[i] = local; local += 1; } fpregs = new int[max_f]; for (int i = 0; i < max_f; i++) { // mv.visitLocalVariable("F" + i, // Type.DOUBLE_TYPE.getDescriptor(), null, start, end, // local); fpregs[i] = local; local += 2; } this.bit_string_builder = local++; this.bit_string_matcher = local++; this.bit_string_save = local++; this.scratch_reg = local; } /* * (non-Javadoc) * * @see erjang.beam.FunctionVisitor#visitLabeledBlock(int) */ @Override public BlockVisitor visitLabeledBlock(int label) { Label blockLabel = getLabel(label); mv.visitLabel(blockLabel); label_inserted.add(label); // mv.visitLineNumber(label & 0x7fff, blockLabel); return new ASMBlockVisitor(label); } class ASMBlockVisitor implements BlockVisitor2 { final int beam_label; public ASMBlockVisitor(int beam_label) { this.beam_label = beam_label; } @Override public void visitBegin(BeamExceptionHandler exh) { active_beam_exh = exh; if (exh == null || (exh.getHandlerLabel() != beam_label)) { adjust_exception_handlers(exh, false); mv.visitInsn(NOP); } } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitBSAdd(erjang.beam.Arg[], * erjang.beam.Arg) */ @Override public void visitBSAdd(Arg in1, Arg in2, int scale, Arg out) { push(in1, Type.INT_TYPE); push_scaled(in2, scale); mv.visitInsn(IADD); pop(out, Type.INT_TYPE); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitBS(erjang.beam.BeamOpcode, * erjang.beam.Arg) */ @Override public void visitBS(BeamOpcode opcode, Arg arg, Arg imm, int failLabel) { switch (opcode) { case bs_save2: case bs_restore2: push(arg, EOBJECT_TYPE); if (imm.value == ATOM_start) { String methName = (opcode == BeamOpcode.bs_restore2) ? "bs_restore2_start" : "bs_save2_start"; mv.visitMethodInsn(INVOKESTATIC, EBINMATCHSTATE_TYPE.getInternalName(), methName, "(" + EOBJECT_DESC + ")V"); } else { push(imm, Type.INT_TYPE); mv.visitMethodInsn(INVOKESTATIC, EBINMATCHSTATE_TYPE.getInternalName(), opcode.name(), "(" + EOBJECT_DESC + "I)V"); } return; case bs_context_to_binary: push(arg, EOBJECT_TYPE); mv.visitMethodInsn(INVOKESTATIC, EBINMATCHSTATE_TYPE.getInternalName(), "bs_context_to_binary", "(" + EOBJECT_DESC + ")" + EOBJECT_DESC); pop(arg, EOBJECT_TYPE); return; case bs_utf16_size: case bs_utf8_size: push(arg, EOBJECT_TYPE); mv.visitMethodInsn(INVOKESTATIC, EBINSTRINGBUILDER_TYPE.getInternalName(), opcode.name(), "(" + EOBJECT_DESC + ")" + ESMALL_TYPE.getDescriptor()); if (failLabel == 0) { mv.visitInsn(DUP); Label okLabel = new Label(); mv.visitJumpInsn(IFNONNULL, okLabel); push(arg, EOBJECT_TYPE); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "badarg", "(Lerjang/EObject;)Lerjang/ErlangError;"); mv.visitInsn(ATHROW); mv.visitLabel(okLabel); pop(imm, ESMALL_TYPE); } else { mv.visitInsn(DUP); pop(imm, ESMALL_TYPE); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); } return; } throw new Error("unhandled: " + opcode); } @Override public void visitInitWritable(Arg size, Arg out) { push(size, EOBJECT_TYPE); mv.visitMethodInsn(INVOKESTATIC, EBITSTRINGBUILDER_TYPE.getInternalName(), "bs_init_writable", "(" + EOBJECT_DESC + ")" + EBITSTRINGBUILDER_TYPE.getDescriptor()); mv.visitInsn(DUP); mv.visitVarInsn(ASTORE, bit_string_builder); mv.visitMethodInsn(INVOKEVIRTUAL, EBITSTRINGBUILDER_TYPE.getInternalName(), "bitstring", "()" + EBITSTRING_TYPE.getDescriptor()); pop(out, EBITSTRING_TYPE); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitInitBitString(erjang.EObject, * erjang.beam.Arg, erjang.beam.Arg) */ @Override public void visitInitBitString(Arg size, int flags, Arg out, boolean unit_is_bits) { push(size, Type.INT_TYPE); mv.visitLdcInsn(new Integer(flags)); String methodName = unit_is_bits ? "bs_initBits" : "bs_init"; mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, methodName, "(II)" + EBITSTRINGBUILDER_TYPE.getDescriptor()); mv.visitInsn(DUP); mv.visitVarInsn(ASTORE, bit_string_builder); mv.visitMethodInsn(INVOKEVIRTUAL, EBITSTRINGBUILDER_TYPE.getInternalName(), "bitstring", "()" + EBITSTRING_TYPE.getDescriptor()); pop(out, EBITSTRING_TYPE); return; } @Override public void visitBitStringAppend(BeamOpcode opcode, int label, Arg extra_size, Arg src, int unit, int flags, Arg dst) { push(src, EOBJECT_TYPE); push(extra_size, Type.INT_TYPE); push_int(unit); push_int(flags); mv.visitMethodInsn(INVOKESTATIC, EBITSTRINGBUILDER_TYPE.getInternalName(), opcode.name(), "(" + EOBJECT_DESC + "III)" + EBITSTRINGBUILDER_TYPE.getDescriptor()); mv.visitInsn(DUP); mv.visitVarInsn(ASTORE, bit_string_builder); mv.visitMethodInsn(INVOKEVIRTUAL, EBITSTRINGBUILDER_TYPE.getInternalName(), "bitstring", "()" + EBITSTRING_TYPE.getDescriptor()); pop(dst, EBITSTRING_TYPE); return; } /* * (non-Javadoc) * * @see * erjang.beam.BlockVisitor2#visitBitStringPut(erjang.beam.BeamOpcode * , erjang.EObject) */ @Override public void visitBitStringPut(BeamOpcode opcode, Arg arg, Arg size, int unit, int flags) { switch (opcode) { case bs_put_string: mv.visitVarInsn(ALOAD, bit_string_builder); push(arg, ESTRING_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, EBITSTRINGBUILDER_TYPE.getInternalName(), "put_string", "(" + ESTRING_TYPE.getDescriptor() + ")V"); return; case bs_put_integer: mv.visitVarInsn(ALOAD, bit_string_builder); push(arg, EINTEGER_TYPE); push_scaled(size, unit); push_int(flags); mv.visitMethodInsn(INVOKEVIRTUAL, EBITSTRINGBUILDER_TYPE.getInternalName(), "put_integer", "(" + EOBJECT_DESC + "II)V"); return; case bs_put_float: mv.visitVarInsn(ALOAD, bit_string_builder); push(arg, EDOUBLE_TYPE); push_scaled(size, unit); push_int(flags); mv.visitMethodInsn(INVOKEVIRTUAL, EBITSTRINGBUILDER_TYPE.getInternalName(), "put_float", "(" + EOBJECT_DESC + "II)V"); return; case bs_put_utf8: mv.visitVarInsn(ALOAD, bit_string_builder); push(arg, EINTEGER_TYPE); push_int(flags); mv.visitMethodInsn(INVOKEVIRTUAL, EBITSTRINGBUILDER_TYPE.getInternalName(), "put_utf8", "(" + EOBJECT_DESC + "I)V"); return; case bs_put_utf16: mv.visitVarInsn(ALOAD, bit_string_builder); push(arg, EINTEGER_TYPE); push_int(flags); mv.visitMethodInsn(INVOKEVIRTUAL, EBITSTRINGBUILDER_TYPE.getInternalName(), "put_utf16", "(" + EOBJECT_DESC + "I)V"); return; case bs_put_utf32: mv.visitVarInsn(ALOAD, bit_string_builder); push(arg, EINTEGER_TYPE); push_int(flags); mv.visitMethodInsn(INVOKEVIRTUAL, EBITSTRINGBUILDER_TYPE.getInternalName(), "put_utf32", "(" + EOBJECT_DESC + "I)V"); return; case bs_put_binary: mv.visitVarInsn(ALOAD, bit_string_builder); push(arg, EBITSTRING_TYPE); if (size.kind == Kind.IMMEDIATE && size.value.equals(EAtom.intern("all"))) push_int(-1); else push_scaled(size, unit); push_int(flags); mv.visitMethodInsn(INVOKEVIRTUAL, EBITSTRINGBUILDER_TYPE.getInternalName(), "put_bitstring", "(" + EOBJECT_TYPE.getDescriptor() + "II)V"); return; } throw new Error("unhandled: " + opcode); } @Override public void visitBitStringTest(BeamOpcode test, int failLabel, Arg in, int intg, Arg dst) { switch (test) { case bs_start_match2: { push(in, EOBJECT_TYPE); push_int(intg); // Slots mv.visitMethodInsn( INVOKESTATIC, EBINMATCHSTATE_TYPE.getInternalName(), test.name(), "(" + EOBJECT_DESC + "I)" + EMATCHSTATE_TYPE.getDescriptor()); mv.visitInsn(DUP); mv.visitVarInsn(ASTORE, bit_string_matcher); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); mv.visitVarInsn(ALOAD, bit_string_matcher); pop(dst, EBINMATCHSTATE_TYPE); return; } /* * {test,bs_get_utf8,{f,6},[{x,0},1, * {field_flags,[...,unsigned,big]},{x,1}]}. */ case bs_get_utf8: case bs_get_utf16: case bs_get_utf32: { push(in, EBINMATCHSTATE_TYPE); push_int(intg); // Flags mv.visitMethodInsn(INVOKEVIRTUAL, EBINMATCHSTATE_TYPE.getInternalName(), test.name(), "(I)I"); mv.visitInsn(DUP); mv.visitVarInsn(ISTORE, scratch_reg); mv.visitJumpInsn(IFLT, getLabel(failLabel)); mv.visitVarInsn(ILOAD, scratch_reg); emit_box(Type.INT_TYPE, ESMALL_TYPE); pop(dst, ESMALL_TYPE); return; } default: throw new Error("unhandled bit string test: " + test); } } @Override public void visitBitStringTest(BeamOpcode test, int failLabel, Arg in, EBitString bin) { switch (test) { case bs_match_string: { push(in, EBINMATCHSTATE_TYPE); push_immediate(bin, EBITSTRING_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, EBINMATCHSTATE_TYPE.getInternalName(), test.name(), "(" + EBITSTRING_TYPE.getDescriptor() + ")" + EBITSTRING_TYPE); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); return; } default: throw new Error("unhandled bit string test: " + test); } } @Override public void visitBitStringTest(BeamOpcode test, int failLabel, Arg in, Arg bits, int unit, int flags) { switch (test) { case bs_skip_bits2: { // {test,bs_skip_bits2, {f,39}, // [{x,1},{x,0},8,{field_flags,0}]} push(in, EBINMATCHSTATE_TYPE); push(bits, EINTEGER_TYPE); push_int(unit); // TODO: Scale here instead? push_int(flags); mv.visitMethodInsn(INVOKEVIRTUAL, EBINMATCHSTATE_TYPE.getInternalName(), test.name(), "(" + EOBJECT_DESC + "II)" + EOBJECT_DESC); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); return; } default: throw new Error("unhandled bit string test: " + test); } } @Override public void visitLine(int line) { Label here = new Label(); mv.visitLabel(here); mv.visitLineNumber(line, here); } @Override public void visitBitStringTest(BeamOpcode test, int failLabel, Arg in, Arg bits, int unit, int flags, Arg dst) { switch (test) { case bs_get_binary2: { push(in, EBINMATCHSTATE_TYPE); push(bits, EOBJECT_TYPE); // TODO: scale by unit, handling // 'all' push_int(flags); mv.visitMethodInsn(INVOKEVIRTUAL, EBINMATCHSTATE_TYPE.getInternalName(), test.name(), "(" + EOBJECT_DESC + "I)" + EBITSTRING_TYPE); mv.visitInsn(DUP); mv.visitVarInsn(ASTORE, scratch_reg); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); mv.visitVarInsn(ALOAD, scratch_reg); pop(dst, EBITSTRING_TYPE); return; } // {test,bs_get_integer2,{f,348},[{x,3},4,{integer,32},1,{field_flags,0},{x,4}]} case bs_get_integer2: { ESmall small; int int_bits; if (bits.kind == Kind.IMMEDIATE && bits.value != null && (small=bits.value.testSmall()) != null && (int_bits = (small.value * unit)) <= 32 && flags == 0) { push(in, EBINMATCHSTATE_TYPE); push_int(int_bits); mv.visitMethodInsn(INVOKEVIRTUAL, EBINMATCHSTATE_TYPE.getInternalName(), "bs_get_integer2__0", "(I)L" + ESMALL_NAME + ";"); mv.visitInsn(DUP); mv.visitVarInsn(ASTORE, scratch_reg); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); mv.visitVarInsn(ALOAD, scratch_reg); pop(dst, ESMALL_TYPE); return; } else { push(in, EBINMATCHSTATE_TYPE); push(bits, Type.INT_TYPE); push_int(unit); push_int(flags); mv.visitMethodInsn(INVOKEVIRTUAL, EBINMATCHSTATE_TYPE.getInternalName(), test.name(), "(III)" + EINTEGER_TYPE.getDescriptor()); mv.visitInsn(DUP); mv.visitVarInsn(ASTORE, scratch_reg); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); mv.visitVarInsn(ALOAD, scratch_reg); pop(dst, EOBJECT_TYPE); return; } } case bs_get_float2: { push(in, EBINMATCHSTATE_TYPE); push(bits, Type.INT_TYPE); push_int(unit); push_int(flags); mv.visitMethodInsn(INVOKEVIRTUAL, EBINMATCHSTATE_TYPE.getInternalName(), test.name(), "(III)" + EDOUBLE_TYPE.getDescriptor()); mv.visitInsn(DUP); mv.visitVarInsn(ASTORE, scratch_reg); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); mv.visitVarInsn(ALOAD, scratch_reg); pop(dst, EOBJECT_TYPE); return; } default: throw new Error("unhandled bit string test: " + test); } } @Override public void visitBitStringTest(BeamOpcode test, int failLabel, Arg in, int intg) { // case bs_test_tail2: // intg == expected bits left // case bs_test_unit: // intg == unit // case bs_skip_utfXX: // intg == flags push(in, EBINMATCHSTATE_TYPE); push_int(intg); mv.visitMethodInsn(INVOKEVIRTUAL, EBINMATCHSTATE_TYPE.getInternalName(), test.name(), "(I)Z"); mv.visitJumpInsn(IFEQ, getLabel(failLabel)); return; } @Override public void visitMapQuery(BeamOpcode opcode, int failLabel, Arg src, Arg[] keys, Arg[] dest) { if (! src.type.equals(EMAP_TYPE)) { visitTest(BeamOpcode.is_map, failLabel, src, EMAP_TYPE); src.type = EMAP_TYPE; } if (failLabel != 0) { for (int i = 0; i < keys.length; i++) { // call EMap#has_key push(src, EMAP_TYPE); push(keys[i], EOBJECT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, EMAP_TYPE.getInternalName(), "has_key", "(Lerjang/EObject;)Z"); mv.visitJumpInsn(IFEQ, getLabel(failLabel)); } } if (opcode == BeamOpcode.get_map_elements) { for (int i = 0; i < keys.length; i++) { // call EMap#get push(src, EMAP_TYPE); push(keys[i], EOBJECT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, EMAP_TYPE.getInternalName(), "get", "(Lerjang/EObject;)Lerjang/EObject;"); pop(dest[i], EOBJECT_TYPE); } } } @Override public void visitMapUpdate(BeamOpcode opcode, int failLabel, Arg src, Arg dst, Arg[] keys, Arg[] vals) { if (! src.type.equals(EMAP_TYPE)) { visitTest(BeamOpcode.is_map, failLabel, src, EMAP_TYPE); src.type = EMAP_TYPE; } boolean is_exact_no_label = (opcode == BeamOpcode.put_map_exact) && (failLabel == 0); if (opcode == BeamOpcode.put_map_exact && !is_exact_no_label) { for (int i = 0; i < keys.length; i++) { push(src, EMAP_TYPE); push(keys[i], EOBJECT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, EMAP_TYPE.getInternalName(), "has_key", "(Lerjang/EObject;)Z"); mv.visitJumpInsn(IFEQ, getLabel(failLabel)); } } push(src, EMAP_TYPE); for (int i = 0; i < keys.length; i++) { push(keys[i], EOBJECT_TYPE); push(vals[i], EOBJECT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, EMAP_TYPE.getInternalName(), is_exact_no_label ? "update" : "put", "(Lerjang/EObject;Lerjang/EObject;)Lerjang/EMap;"); } pop(dst, EMAP_TYPE); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitInsn(erjang.beam.BeamOpcode, * int, erjang.beam.Arg[], erjang.beam.Arg) */ @Override public void visitInsn(BeamOpcode opcode, int failLabel, Arg[] in, Arg ex) { if (opcode == BeamOpcode.raise) { push(in[0], EOBJECT_TYPE); push(in[1], EOBJECT_TYPE); // raise will actually throw (if successful) mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "raise", "(" + EOBJECT_DESC + EOBJECT_DESC + ")" + EOBJECT_DESC); mv.visitInsn(ARETURN); return; } throw new Error("unhandled: " + opcode); } /** * */ /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitInsn(erjang.beam.BeamOpcode, * int, erjang.beam.Arg[], erjang.beam.Arg, * java.lang.reflect.Method) */ public void visitDecrement(Arg src, Arg out) { push(src, src.type); mv.visitMethodInsn(INVOKEVIRTUAL, EOBJECT_NAME, "dec", "()" + ENUMBER_TYPE.getDescriptor()); pop(out, EOBJECT_TYPE); }; @Override public void visitIncrement(Arg src, Arg out) { push(src, src.type); mv.visitMethodInsn(INVOKEVIRTUAL, EOBJECT_NAME, "inc", "()" + ENUMBER_TYPE.getDescriptor()); pop(out, EOBJECT_TYPE); return; } @Override public void visitInsn(BeamOpcode opcode, int failLabel, Arg[] in, Arg out, BuiltInFunction bif) { ensure_exception_handler_in_place(); switch (opcode) { case bif0: case bif1: case bif2: case gc_bif1: case gc_bif2: case gc_bif3: case fnegate: case fadd: case fsub: case fmul: case fdiv: Type[] parameterTypes = bif.getArgumentTypes(); push(in, parameterTypes, bif.isVirtual()); mv.visitMethodInsn(bif.isVirtual() ? INVOKEVIRTUAL : INVOKESTATIC, bif.owner.getInternalName(), bif .getName(), bif.getDescriptor()); if (failLabel != 0) { // guard // dup result for test if (out != null) { pop(out, bif.getReturnType()); push(out, bif.getReturnType()); } if (bif.getReturnType().getSort() == Type.BOOLEAN) { mv.visitJumpInsn(IFEQ, getLabel(failLabel)); } else { if (bif.getReturnType().getSort() != Type.OBJECT) throw new Error( "guards must return object type - " + bif); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); } } else { if (PARANOIA_MODE && bif.getReturnType().getSort() == Type.OBJECT) { // Expect non-guards to return non-null. mv.visitInsn(DUP); mv.visitLdcInsn(bif.toString()); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "paranoiaCheck", "(Lerjang/EObject;Ljava/lang/String;)V"); } pop(out, bif.getReturnType()); } return; } throw new Error(); } public void visitUnreachablePoint() { // mv.visitLdcInsn("Reached unreachable point."); // mv.visitInsn(DUP); // mv.visitMethodInsn(INVOKESPECIAL, // "java/lang/RuntimeException", "<init>", // "(Ljava/lang/String;)V"); // mv.visitInsn(ATHROW); } public void visitCatchBlockStart(BeamOpcode opcode, int label, Arg out, BeamExceptionHandler exh) { switch (opcode) { case K_try: case K_catch: { active_beam_exh = exh; return; } } } public void visitCatchBlockEnd(BeamOpcode opcode, Arg out, BeamExceptionHandler exh) { active_beam_exh = exh.getParent(); adjust_exception_handlers(active_beam_exh, false); switch (opcode) { case try_end: { } break; case catch_end: { // Insert exception decoding sequence: Label after = new Label(); mv.visitJumpInsn(GOTO, after); mv.visitLabel(getExceptionHandlerLabel(exh)); // Remember the exception value: mv.visitInsn(DUP); mv.visitVarInsn(ALOAD, 0); mv.visitInsn(SWAP); mv.visitFieldInsn(PUTFIELD, EPROC_NAME, "last_exception", EEXCEPTION_DESC); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "decode_exception2", "(" + ERLANG_EXCEPTION_TYPE.getDescriptor() + ")" + EOBJECT_DESC); mv.visitVarInsn(ASTORE, xregs[0]); mv.visitLabel(after); } break; case try_case: { mv.visitLabel(getExceptionHandlerLabel(exh)); // Remember the exception value: mv.visitInsn(DUP); mv.visitVarInsn(ALOAD, 0); mv.visitInsn(SWAP); mv.visitFieldInsn(PUTFIELD, EPROC_NAME, "last_exception", EEXCEPTION_DESC); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "decode_exception3", "(" + ERLANG_EXCEPTION_TYPE.getDescriptor() + ")" + getTubleType(3).getDescriptor()); mv.visitInsn(DUP); mv.visitFieldInsn(GETFIELD, ETUPLE_NAME + 3, "elem1", EOBJECT_DESC); mv.visitVarInsn(ASTORE, xregs[0]); mv.visitInsn(DUP); mv.visitFieldInsn(GETFIELD, ETUPLE_NAME + 3, "elem2", EOBJECT_DESC); mv.visitVarInsn(ASTORE, xregs[1]); mv.visitFieldInsn(GETFIELD, ETUPLE_NAME + 3, "elem3", EOBJECT_DESC); mv.visitVarInsn(ASTORE, xregs[2]); } break; } } /** * @param out * @return */ private int var_index(Arg out) { switch (out.kind) { case X: return xregs[out.no]; case Y: return yregs[out.no]; case F: return fpregs[out.no]; } throw new Error(); } /** * @param out * @param stack_type */ private void pop(Arg out, Type stack_type) { if (out == null) { if (stack_type != null && Type.DOUBLE_TYPE.equals(stack_type)) { mv.visitInsn(POP2); } else { mv.visitInsn(POP); } return; } if (stack_type == Type.DOUBLE_TYPE && (out.kind == Kind.X || out.kind == Kind.Y)) { emit_convert( stack_type, stack_type == Type.DOUBLE_TYPE ? EDOUBLE_TYPE : (stack_type == Type.INT_TYPE ? EINTEGER_TYPE : EOBJECT_TYPE)); } if (out.kind == Kind.X || out.kind == Kind.Y) { if (out.type == Type.INT_TYPE || out.type == Type.BOOLEAN_TYPE || (out.type == null && stack_type == Type.INT_TYPE) || (out.type == null && stack_type == Type.BOOLEAN_TYPE)) { mv.visitVarInsn(ISTORE, var_index(out)); } else { mv.visitVarInsn(ASTORE, var_index(out)); } } else if (out.kind == Kind.F) { if (!stack_type.equals(Type.DOUBLE_TYPE)) { emit_convert(stack_type, Type.DOUBLE_TYPE); } mv.visitVarInsn(DSTORE, var_index(out)); } else { throw new Error(); } } /** * @param type * @param stackType */ private void emit_convert(Type from_type, Type to_type) { if (from_type.equals(to_type)) return; if (from_type.getSort() == Type.OBJECT && to_type.getSort() == Type.OBJECT) { return; } if (to_type.getSort() == Type.OBJECT) { emit_box(from_type, to_type); } else { emit_unbox(from_type, to_type); } } /** * @param fromType * @param toType */ private void emit_unbox(Type fromType, Type toType) { if (toType.equals(Type.INT_TYPE)) { if (fromType.equals(ESMALL_TYPE)) { mv.visitFieldInsn(GETFIELD, ESMALL_NAME, "value", "I"); return; } } if (toType.equals(Type.DOUBLE_TYPE)) { if (fromType.equals(EDOUBLE_TYPE)) { mv.visitFieldInsn(GETFIELD, EDOUBLE_NAME, "value", "D"); return; } } mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "unboxTo" + primTypeName(toType), "(" + fromType.getDescriptor() + ")" + toType.getDescriptor()); } /** * @param toType * @return */ private String primTypeName(Type typ) { switch (typ.getSort()) { case Type.DOUBLE: return "Double"; case Type.INT: return "Int"; case Type.BOOLEAN: return "Atom"; default: throw new Error(); } } /** * @param fromType * @param toType */ private void emit_box(Type fromType, Type toType) { if (fromType.equals(Type.INT_TYPE) && toType.getSort() == Type.OBJECT) { mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "box", "(I)" + ESMALL_TYPE.getDescriptor()); } else if (fromType.equals(Type.DOUBLE_TYPE) && toType.getSort() == Type.OBJECT) { mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "box", "(D)" + EDOUBLE_TYPE.getDescriptor()); } else if (fromType.equals(Type.BOOLEAN_TYPE) && toType.getSort() == Type.OBJECT) { mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "box", "(Z)" + EATOM_TYPE.getDescriptor()); } else { throw new Error("cannot box " + fromType + " -> " + toType); } } /** * @param in * @param parameterTypes */ private void push(Arg[] in, Type[] parameterTypes, boolean isVirtual) { int off = 0; if (isVirtual) { push(in[0], EOBJECT_TYPE); off = 1; } if (in.length == parameterTypes.length - 1 && EPROC_TYPE.equals(parameterTypes[0])) { mv.visitVarInsn(ALOAD, 0); } for (int i = 0; i < in.length - off; i++) { Arg arg = in[i + off]; Type pt = parameterTypes[i]; push(arg, pt); } } private void push_scaled(Arg value, int factor) { if (value.kind == Kind.IMMEDIATE && value.value instanceof ESmall) { ESmall sm = (ESmall) value.value; mv.visitLdcInsn(new Integer(factor * sm.intValue())); } else { push(value, Type.INT_TYPE); push_int(factor); mv.visitInsn(IMUL); } } /** * @param arg * @param class1 */ private void push(Arg value, Type stack_type) { Type t = value.type; if (value.kind == Kind.X || value.kind == Kind.Y) { if (value.type == Type.INT_TYPE || value.type == Type.BOOLEAN_TYPE) { // throw new Error("should not happen"); mv.visitVarInsn(ILOAD, var_index(value)); } else { mv.visitVarInsn(ALOAD, var_index(value)); } } else if (value.kind == Kind.F) { mv.visitVarInsn(DLOAD, var_index(value)); } else if (value.kind == Kind.IMMEDIATE) { t = push_immediate(value.value, stack_type); } else { throw new Error(); } if (t != null && !t.equals(stack_type)) { emit_convert(t, stack_type); } } /** * @param value * @param stack_type */ private Type push_immediate(EObject value, Type stack_type) { if (value == ERT.NIL) { mv.visitFieldInsn(GETSTATIC, ERT_NAME, "NIL", ENIL_TYPE.getDescriptor()); return ENIL_TYPE; } if (value == ERT.TRUE) { mv.visitFieldInsn(GETSTATIC, ERT_NAME, "TRUE", EATOM_DESC); return EATOM_TYPE; } if (value == ERT.FALSE) { mv.visitFieldInsn(GETSTATIC, ERT_NAME, "FALSE", EATOM_DESC); return EATOM_TYPE; } // Handle conversions to primitive Java types: if (stack_type.getSort() != Type.OBJECT) { if (value instanceof ESmall) { mv.visitLdcInsn(new Integer(value.asInt())); return Type.INT_TYPE; } else if (value instanceof EBig && stack_type.getSort() == Type.DOUBLE) { push_immediate(value, EBIG_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, EBIG_NAME, "doubleValue", "()D"); return Type.DOUBLE_TYPE; } else if (value instanceof EDouble) { mv.visitLdcInsn(new Double(((EDouble) value).value)); return Type.DOUBLE_TYPE; } else if (value == TRUE_ATOM) { mv.visitInsn(ICONST_1); return Type.BOOLEAN_TYPE; } else if (value == FALSE_ATOM) { mv.visitInsn(ICONST_0); return Type.BOOLEAN_TYPE; } if (value instanceof ETuple2) { ETuple2 t2 = (ETuple2) value; if (t2.elm(1) == ATOM_field_flags) { push_int(t2.elem2.asInt()); return Type.INT_TYPE; } } throw new Error("cannot convert " + value + " as " + stack_type); } String known = constants.get(value); Type type = getConstantType(value); if (known == null) { String cn = getConstantName(value, constants.size()); constants.put(value, known = cn); cv.visitField(ACC_STATIC, known, type.getDescriptor(), null, null); // System.err.println(constants); } if (known != null) { mv.visitFieldInsn(GETSTATIC, self_type.getInternalName(), known, type.getDescriptor()); return type; } throw new Error("cannot push " + value + " as " + stack_type); // return Type.getType(value.getClass()); } /** * @param value * @param size * @return */ private String getConstantName(Object value, int size) { if (value instanceof EAtom) return "atom_" + EUtil.toJavaIdentifier((EAtom) value); if (value instanceof ENumber) return EUtil.toJavaIdentifier("num_" + value.toString().replace('.', '_') .replace('-', '_')); if (value instanceof EString) return "str_" + size; if (value instanceof BitSet) return "bitset_" + size; else return "cst_" + size; } /* * (non-Javadoc) * * @see * erjang.beam.BlockVisitor2#visitReceive(erjang.beam.BeamOpcode, * int, erjang.beam.Arg) */ @Override public void visitReceive(BeamOpcode opcode, int blockLabel, Arg out) { switch (opcode) { case loop_rec: ensure_exception_handler_in_place(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "loop_rec", "(" + EPROC_TYPE.getDescriptor() + ")" + EOBJECT_DESC); mv.visitInsn(DUP); pop(out, EOBJECT_TYPE); mv.visitJumpInsn(IFNULL, getLabel(blockLabel)); return; } throw new Error(); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitInsn(erjang.beam.BeamOpcode, * erjang.beam.Arg[], erjang.beam.Arg) */ @Override public void visitInsn(BeamOpcode opcode, Arg[] in, Arg out) { switch (opcode) { case put_list: push(in[0], EOBJECT_TYPE); push(in[1], EOBJECT_TYPE); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "cons", ERT_CONS_SIG); pop(out, ECONS_TYPE); return; case call_fun: case i_call_fun_last: { ensure_exception_handler_in_place(); boolean is_tail = opcode == BeamOpcode.i_call_fun_last; int nargs = in.length - 1; push(in[nargs], EOBJECT_TYPE); mv.visitInsn(DUP); String funtype = EFUN_NAME + nargs; mv.visitMethodInsn(INVOKESTATIC, funtype, "cast", "(" + EOBJECT_DESC + ")L" + funtype + ";"); mv.visitInsn(DUP_X1); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "test_fun", TEST_FUN_SIG); mv.visitVarInsn(ALOAD, 0); // load proc for (int i = 0; i < nargs; i++) { push(in[i], EOBJECT_TYPE); } mv.visitMethodInsn(INVOKEVIRTUAL, funtype, is_tail ? "invoke_tail" : "invoke", EUtil.getSignature(nargs, true)); if (is_tail) { mv.visitInsn(ARETURN); } else { pop(out, EOBJECT_TYPE); } return; } }// switch throw new Error(); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitInsn(erjang.beam.BeamOpcode) */ @Override public void visitInsn(BeamOpcode insn) { switch (insn) { case if_end: mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "if_end", EUtil.getSignature(0, false)); mv.visitInsn(ARETURN); return; case timeout: mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "timeout", "(" + EPROC_DESC + ")V"); return; case remove_message: mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "remove_message", "(" + EPROC_TYPE.getDescriptor() + ")V"); return; } throw new Error(); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitInsn(erjang.beam.BeamOpcode, * int, erjang.beam.Arg) */ @Override public void visitMakeTuple(int arity, Arg out, Arg[] elems) { String name = out.type.getInternalName(); StringBuffer sb = new StringBuffer("("); for (int i = 0; i<arity; i++) sb.append(EOBJECT_DESC); sb.append(")"); sb.append(out.type.getDescriptor()); for (int i = 0; i < arity; i++) { push(elems[i], EOBJECT_TYPE); } mv.visitMethodInsn(INVOKESTATIC, name, "make_tuple", sb.toString()); pop(out, out.type); } @Override public void visitInsn(BeamOpcode opcode, int val, Arg out) { switch (opcode) { case put_tuple: { String name = out.type.getInternalName(); mv.visitMethodInsn(INVOKESTATIC, name, "create", "()L" + name + ";"); pop(out, out.type); } return; case wait_timeout: { mv.visitVarInsn(ALOAD, 0); push(out, EOBJECT_TYPE); mv.visitMethodInsn( INVOKESTATIC, ERT_NAME, "wait_timeout", "(" + EPROC_TYPE.getDescriptor() + EOBJECT_TYPE.getDescriptor() + ")Z"); mv.visitJumpInsn(IFNE, getLabel(val)); return; } case loop_rec_end: mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "loop_rec_end", "(" + EPROC_TYPE.getDescriptor() + ")V"); mv.visitJumpInsn(GOTO, getLabel(val)); return; case wait: { mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "wait", "(" + EPROC_TYPE.getDescriptor() + ")V"); mv.visitJumpInsn(GOTO, getLabel(val)); return; } } throw new Error("unhandled: " + opcode); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitInsn(erjang.beam.BeamOpcode, * erjang.beam.Arg, erjang.beam.Arg, int) */ @Override public void visitInsn(BeamOpcode opcode, Arg val, Arg out, int pos) { if (opcode == BeamOpcode.put) { push(out, out.type); push(val, EOBJECT_TYPE); mv.visitFieldInsn(PUTFIELD, out.type.getInternalName(), "elem" + pos, EOBJECT_DESC); return; } else if (opcode == BeamOpcode.get_tuple_element) { int known_arity = get_known_arity(val.type); if (known_arity >= pos + 1) { push(val, val.type); mv.visitFieldInsn(GETFIELD, val.type.getInternalName(), "elem" + (pos + 1), EOBJECT_DESC); } else { push(val, val.type); mv.visitTypeInsn(CHECKCAST, ETUPLE_NAME); push_int(pos + 1); mv.visitMethodInsn(INVOKEVIRTUAL, ETUPLE_NAME, "elm", "(I)" + EOBJECT_DESC); } pop(out, EOBJECT_TYPE); return; } else if (opcode == BeamOpcode.set_tuple_element) { push(out, out.type); if (get_known_arity(out.type) < 0) mv.visitTypeInsn(CHECKCAST, ETUPLE_NAME); push_int(pos + 1); push(val, val.type); mv.visitMethodInsn(INVOKEVIRTUAL, ETUPLE_NAME, "set", "(I" + EOBJECT_DESC + ")V"); return; } throw new Error(); } /** * @param type * @return -1 if non-ETuple; arity [#elements] otherwise */ private int get_known_arity(Type type) { String in = type.getInternalName(); if (in.startsWith(ETUPLE_NAME)) { int pfx_len = ETUPLE_NAME.length(); if (in.length() == pfx_len) { return 0; } try { String arity = in.substring(pfx_len); return Integer.parseInt(arity); } catch (NumberFormatException e) { return 0; } } // not a tuple return -1; } /** * @param pos */ private void push_int(int pos) { if (pos >= -1 && pos <= 5) { mv.visitInsn(ICONST_0 + pos); } else { mv.visitLdcInsn(new Integer(pos)); } } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitTest(erjang.beam.BeamOpcode, * int, erjang.beam.Arg, java.lang.reflect.Method) */ @Override public void visitInsn(BeamOpcode test, int failLabel, Arg arg1, org.objectweb.asm.commons.Method bif) { throw new Error(); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor#visitEnd() */ @Override public void visitEnd() { // skip // } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor#visitInsn(erjang.beam.BeamOpcode, * erjang.ETuple) */ @Override public void visitInsn(Insn insn) { throw new Error(); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitInsn(erjang.beam.BeamOpcode, * erjang.beam.Arg) */ @Override public void visitInsn(BeamOpcode opcode, Arg arg) { switch (opcode) { case K_return: push(arg, EOBJECT_TYPE); mv.visitInsn(ARETURN); return; case init: push_immediate(ERT.NIL, ENIL_TYPE); pop(arg, ENIL_TYPE); return; case try_case_end: push(arg, EOBJECT_TYPE); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "try_case_end", EUtil.getSignature(1, false)); mv.visitInsn(ARETURN); return; case case_end: ensure_exception_handler_in_place(); push(arg, EOBJECT_TYPE); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "case_end", EUtil.getSignature(1, false)); mv.visitInsn(ARETURN); return; case badmatch: ensure_exception_handler_in_place(); push(arg, EOBJECT_TYPE); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "badmatch", EUtil.getSignature(1, false)); mv.visitInsn(ARETURN); return; } throw new Error("unhandled " + opcode); } public void visitInsn(BeamOpcode opcode, ExtFun f) { switch (opcode) { case func_info: push_immediate(f.mod, EATOM_TYPE); push_immediate(f.fun, EATOM_TYPE); push_immediate(ERT.NIL, ENIL_TYPE); for (int i = f.arity - 1; i >= 0; i--) { push(new Arg(Kind.X, i), EOBJECT_TYPE); String base = ESEQ_NAME; if (i == f.arity - 1) { base = ENIL_NAME; } mv.visitMethodInsn(INVOKEVIRTUAL, base, "cons", SEQ_CONS_SIG); } mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "func_info", FUNC_INFO_SIG); mv.visitInsn(ARETURN); return; }// switch throw new Error("unhandled " + opcode); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitTest(erjang.beam.BeamOpcode, * int, erjang.beam.Arg) */ @Override public void visitTest(BeamOpcode test, int failLabel, Arg arg1, Type out) { if (test == BeamOpcode.is_integer && (arg1.type.equals(ESMALL_TYPE) || arg1.type.equals(EINTEGER_TYPE))) { return; } if (test == BeamOpcode.is_integer && (arg1.type.equals(Type.INT_TYPE))) { push(arg1, arg1.type); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "box", "(I)L" + ESMALL_TYPE + ";"); } Method test_bif = get_test_bif(test, arg1.type); // System.err.println(test_bif); push(arg1, arg1.type); mv.visitMethodInsn(INVOKEVIRTUAL, EOBJECT_NAME, test_bif.getName(), test_bif.getDescriptor()); Type returnType = test_bif.getReturnType(); if (failLabel != 0) { // guard if (returnType.getSort() != Type.OBJECT) throw new Error("guards must return object type: " + test_bif); // dup result for test if (arg1 != null) { mv.visitInsn(DUP); mv.visitVarInsn(ASTORE, scratch_reg); } mv.visitJumpInsn(IFNULL, getLabel(failLabel)); if (arg1 != null) { mv.visitVarInsn(ALOAD, scratch_reg); pop(arg1, returnType); } } else { pop(arg1, returnType); } } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitTest(erjang.beam.BeamOpcode, * int, erjang.beam.Arg, int, org.objectweb.asm.Type) */ @Override public void visitTest(BeamOpcode test, int failLabel, Arg arg, int arity, Type tupleType) { switch (test) { case test_arity: { Type tt = getTubleType(arity); if (tt.equals(arg.type)) { // do nothing // } else { push(arg, EOBJECT_TYPE); mv.visitMethodInsn(INVOKESTATIC, tt.getInternalName(), "cast", "(" + arg.type.getDescriptor() + ")" + tt.getDescriptor()); mv.visitInsn(DUP); mv.visitVarInsn(ASTORE, scratch_reg); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); mv.visitVarInsn(ALOAD, scratch_reg); pop(arg, getTubleType(arity)); } return; } }// switch throw new Error("unhandled " + test); } @Override public void visitTest(BeamOpcode test, int failLabel, Arg arg, Arg arity, Type funType) { switch (test) { case is_function2: // push object to test push(arg, EOBJECT_TYPE); // push arity push(arity, Type.INT_TYPE); // call object.testFunction2(nargs) mv.visitMethodInsn(INVOKEVIRTUAL, EOBJECT_NAME, "testFunction2", "(I)" + EFUN_DESCRIPTOR); mv.visitInsn(DUP); mv.visitVarInsn(ASTORE, scratch_reg); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); mv.visitVarInsn(ALOAD, scratch_reg); pop(arg, funType); return; } throw new Error("unhandled " + test); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitTest(erjang.beam.BeamOpcode, * int, erjang.beam.Arg[], erjang.beam.Arg, org.objectweb.asm.Type) */ @Override public void visitTest(BeamOpcode test, int failLabel, Arg[] args, Type outType) { switch (test) { case is_eq_exact: { if (args[0].kind == Kind.IMMEDIATE && args[0].value.equalsExactly(ESmall.ZERO)) { push(args[1], EOBJECT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, EOBJECT_NAME, "is_zero", "()Z"); mv.visitJumpInsn(IFEQ, getLabel(failLabel)); return; } if (args[1].kind == Kind.IMMEDIATE && args[1].value.equalsExactly(ESmall.ZERO)) { push(args[0], EOBJECT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, EOBJECT_NAME, "is_zero", "()Z"); mv.visitJumpInsn(IFEQ, getLabel(failLabel)); return; } } case is_ne_exact: case is_ne: case is_eq: { // if arg[0] is object type, and arg[1] is not, then swap // args. if (args[0].type.equals(EOBJECT_TYPE) && !args[1].type.equals(EOBJECT_TYPE)) { Arg t = args[0]; args[0] = args[1]; args[1] = t; } } case is_lt: case is_ge: { // this particular case can be coded as a java instruction // instruction if ((test == BeamOpcode.is_eq_exact || test == BeamOpcode.is_eq) && (args[0].type.equals(EATOM_TYPE) || args[1].type .equals(EATOM_TYPE))) { push(args[0], EOBJECT_TYPE); push(args[1], EOBJECT_TYPE); mv.visitJumpInsn(IF_ACMPNE, getLabel(failLabel)); return; } for (int i = 0; i < args.length; i++) { push(args[i], EOBJECT_TYPE); } mv.visitMethodInsn(INVOKEVIRTUAL, EOBJECT_NAME, test.name(), "(" + EOBJECT_DESC + ")Z"); if (failLabel != 0) { mv.visitJumpInsn(IFEQ, getLabel(failLabel)); } else { throw new Error("test with no fail label?"); } // if (test == BeamOpcode.is_eq_exact && !Type.VOID_TYPE.equals(outType)) { if (args[0].type.equals(EOBJECT_TYPE) && args[0].kind.isReg()) { push(args[1], outType); args[0].type = outType; pop(args[0], outType); } else if (args[1].type.equals(EOBJECT_TYPE) && args[1].kind.isReg()) { push(args[0], outType); args[1].type = outType; pop(args[1], outType); } } return; } } throw new Error("unhandled " + test); } /** * @param test * @return */ private String test2name(BeamOpcode test) { return test.name(); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitInsn(erjang.beam.BeamOpcode, * erjang.beam.Arg, erjang.beam.Arg) */ @Override public void visitInsn(BeamOpcode opcode, Arg arg1, Arg arg2) { switch (opcode) { case fconv: case move: case fmove: if (arg1.kind == Kind.F) { push(arg1, Type.DOUBLE_TYPE); if (arg2.kind == Kind.F) { pop(arg2, Type.DOUBLE_TYPE); } else { emit_convert(Type.DOUBLE_TYPE, EDOUBLE_TYPE); pop(arg2, EDOUBLE_TYPE); } } else { if (arg2.kind == Kind.F) { push(arg1, Type.DOUBLE_TYPE); pop(arg2, Type.DOUBLE_TYPE); } else { push(arg1, arg1.type); pop(arg2, arg1.type); } } break; default: throw new Error("unhandled: " + opcode); } } /** * @param test * @param args * @return */ private Method get_test_bif(BeamOpcode test, Type type) { if (!type.getInternalName().startsWith("erjang/E")) { throw new Error("expecting EObject"); } switch (test) { case is_nonempty_list: return IS_NONEMPTY_LIST_TEST; case is_nil: return IS_NIL_TEST; case is_boolean: return IS_BOOLEAN_TEST; case is_number: return IS_NUMBER_TEST; case is_float: return IS_FLOAT_TEST; case is_atom: return IS_ATOM_TEST; case is_list: return IS_LIST_TEST; case is_tuple: return IS_TUPLE_TEST; case is_integer: return IS_INTEGER_TEST; case is_binary: return IS_BINARY_TEST; case is_bitstr: return IS_BITSTRING_TEST; case is_pid: return IS_PID_TEST; case is_port: return IS_PORT_TEST; case is_reference: return IS_REFERENCE_TEST; case is_function: return IS_FUNCTION_TEST; case is_function2: return IS_FUNCTION2_TEST; case is_map: return IS_MAP_TEST; } throw new Error("unhandled " + test); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitInsn(erjang.beam.BeamOpcode, * erjang.beam.Arg[]) */ @Override public void visitInsn(BeamOpcode opcode, Arg[] ys) { if (opcode == BeamOpcode.allocate_zero || opcode == BeamOpcode.allocate_heap_zero) { mv.visitFieldInsn(GETSTATIC, ERT_NAME, "NIL", ENIL_TYPE.getDescriptor()); for (int i = 0; i < ys.length; i++) { if (i != (ys.length - 1)) mv.visitInsn(DUP); pop(ys[i], ENIL_TYPE); } return; } else if (opcode == BeamOpcode.get_list) { push(ys[0], ECONS_TYPE); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKEVIRTUAL, ECONS_TYPE.getInternalName(), "head", "()" + EOBJECT_DESC); pop(ys[1], EOBJECT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, ECONS_TYPE.getInternalName(), "tail", "()" + EOBJECT_DESC); pop(ys[2], EOBJECT_TYPE); return; } ensure_exception_handler_in_place(); if (opcode == BeamOpcode.apply || opcode == BeamOpcode.apply_last) { int arity = ys.length - 2; push(ys[ys.length - 2], EOBJECT_TYPE); // push mod push(ys[ys.length - 1], EOBJECT_TYPE); // push fun push_int(arity); // push arity mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "resolve_fun", "(" + EOBJECT_DESC + EOBJECT_DESC + "I)" + EFUN_DESCRIPTOR); String funtype = EFUN_NAME + arity; mv.visitMethodInsn(INVOKESTATIC, funtype, "cast", "(" + EOBJECT_DESC + ")L" + funtype + ";"); mv.visitVarInsn(ALOAD, 0); // push eproc int loops = 0; for (int i = 0; i <= ys.length - 3; i++) { push(ys[i], EOBJECT_TYPE); loops += 1; } if (loops != arity) { // invariant for above complicated loop logic. throw new InternalError("bad args here"); } boolean is_tail = opcode == BeamOpcode.apply_last; if (is_tail) { mv.visitMethodInsn(INVOKEVIRTUAL, funtype, "invoke_tail", EUtil.getSignature(arity, true)); mv.visitInsn(ARETURN); } else { mv.visitMethodInsn(INVOKEVIRTUAL, funtype, "invoke", EUtil.getSignature(arity, true)); mv.visitVarInsn(ASTORE, xregs[0]); } return; } else if (opcode == BeamOpcode.send) { mv.visitVarInsn(ALOAD, 0); for (int i = 0; i < ys.length; i++) { push(ys[i], EOBJECT_TYPE); } mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "send", EUtil.getSignature(ys.length, true)); mv.visitVarInsn(ASTORE, xregs[0]); return; } throw new Error("unhandled:" + opcode); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitInsn(erjang.beam.BeamOpcode, * erjang.beam.ExtFunc, int) */ @Override public void visitInsn(BeamOpcode opcode, ExtFun efun, Arg[] freevars, int index, int old_index, EBinary uniq, int old_uniq) { ensure_exception_handler_in_place(); if (opcode == BeamOpcode.make_fun2) { CompilerVisitor.this.register_lambda(efun.fun, efun.arity, freevars.length, index, old_index, uniq, old_uniq); String inner = EUtil.getFunClassName(self_type, efun, freevars.length); mv.visitTypeInsn(NEW, inner); mv.visitInsn(DUP); // load proc mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, EPROC_NAME, "self_handle", "()" + Type.getDescriptor(EInternalPID.class)); // String funtype = EFUN_NAME + efun.no; for (int i = 0; i < freevars.length; i++) { push(freevars[i], EOBJECT_TYPE); } StringBuilder sb = new StringBuilder("("); sb.append(EPID_TYPE.getDescriptor()); for (int i = 0; i < freevars.length; i++) { sb.append(EOBJECT_DESC); } sb.append(")V"); // sb.append("L").append(funtype).append(";"); String gen_fun_desc = sb.toString(); mv.visitMethodInsn(INVOKESPECIAL, inner, "<init>", gen_fun_desc); mv.visitVarInsn(ASTORE, xregs[0]); return; } throw new Error(); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitSelectValue(erjang.beam.Arg, * int, erjang.beam.Arg[], int[]) */ @Override public void visitSelectValue(Arg in, int failLabel, Arg[] values, int[] targets) { boolean all_small_ints = true; for (int i = 0; i < values.length; i++) { if (!(values[i].value instanceof ESmall)) { all_small_ints = false; break; } } if (all_small_ints) { int[] ivals = new int[values.length]; Label[] label = new Label[values.length]; Set<Label> targetset = new HashSet<Label>(); for (int i = 0; i < values.length; i++) { ivals[i] = values[i].value.asInt(); label[i] = getLabel(targets[i]); targetset.add(label[i]); } if (!in.type.equals(ESMALL_TYPE) && !in.type.equals(Type.INT_TYPE)) { push(in, EOBJECT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, EOBJECT_NAME, "testSmall", "()Lerjang/ESmall;"); mv.visitJumpInsn(IFNULL, getLabel(failLabel)); } sort(ivals, label); if (targetset.size() == 2 && ivals.length > 8 && is_byte_values(ivals)) { Label[] labelset = targetset.toArray(new Label[2]); BitSet bs0 = new BitSet(16); BitSet bs1 = new BitSet(16); for (int i = 0; i < ivals.length; i++) { if (label[i] == labelset[0]) { bs0.set(ivals[i], true); } else { bs1.set(ivals[i], true); } } String name0; if((name0=bitsets.get(bs0)) == null) { name0 = getConstantName(bs0, bitsets.size()); bitsets.put(bs0, name0); } String name1; if((name1=bitsets.get(bs1)) == null) { name1 = getConstantName(bs1, bitsets.size()); bitsets.put(bs1, name1); } mv.visitFieldInsn(GETSTATIC, self_type.getInternalName(), name0, Type.getDescriptor(BitSet.class)); push(in, Type.INT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(BitSet.class), "get", "(I)Z"); mv.visitJumpInsn(IFNE, labelset[0]); mv.visitFieldInsn(GETSTATIC, self_type.getInternalName(), name1, Type.getDescriptor(BitSet.class)); push(in, Type.INT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(BitSet.class), "get", "(I)Z"); mv.visitJumpInsn(IFNE, labelset[1]); mv.visitJumpInsn(GOTO, getLabel(failLabel)); } else if (ivals.length > 4 && is_bitset_member_test(ivals, label)) { BitSet bs = new BitSet(16); for (int i = 0; i < ivals.length; i++) { bs.set(ivals[i], true); } String name; if((name=bitsets.get(bs)) == null) { name = getConstantName(bs, bitsets.size()); bitsets.put(bs, name); } mv.visitFieldInsn(GETSTATIC, self_type.getInternalName(), name, Type.getDescriptor(BitSet.class)); push(in, Type.INT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(BitSet.class), "get", "(I)Z"); mv.visitJumpInsn(IFEQ, getLabel(failLabel)); mv.visitJumpInsn(GOTO, label[0]); } else { push(in, Type.INT_TYPE); mv.visitLookupSwitchInsn(getLabel(failLabel), ivals, label); } return; } if (values.length < ATOM_SELECT_IF_ELSE_LIMIT) { boolean all_atoms = true; for (int i = 0; i < values.length; i++) { if (!(values[i].value instanceof EAtom)) { all_atoms = false; break; } } if (all_atoms) { for (int i = 0; i < values.length; i++) { push(in, in.type); push(values[i], values[i].type); mv.visitJumpInsn(IF_ACMPEQ, getLabel(targets[i])); } mv.visitJumpInsn(GOTO, getLabel(failLabel)); return; } } class Case implements Comparable<Case> { final Arg arg; final Label label; /** * @param arg * @param label2 */ public Case(Arg arg, Label label) { this.arg = arg; this.label = label; } EObject value() { return arg.value; } /* * (non-Javadoc) * * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override public int compareTo(Case o) { int h = hashCode(); int ho = o.hashCode(); if (h < ho) return -1; if (h > ho) return 1; return value().erlangCompareTo(o.value()); } } Map<Integer, List<Case>> cases = new TreeMap<Integer, List<Case>>(); for (int i = 0; i < values.length; i++) { int hash = values[i].value.hashCode(); List<Case> c = cases.get(hash); if (c == null) { cases.put(hash, c = new ArrayList<Case>()); } c.add(new Case(values[i], getLabel(targets[i]))); } int[] hashes = new int[cases.size()]; Label[] tests = new Label[cases.size()]; List<Case>[] idx_cases = new List[cases.size()]; int idx = 0; for (Map.Entry<Integer, List<Case>> c : cases.entrySet()) { hashes[idx] = c.getKey(); idx_cases[idx] = c.getValue(); tests[idx++] = new Label(); } push(in, EOBJECT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I"); mv.visitLookupSwitchInsn(getLabel(failLabel), hashes, tests); for (int i = 0; i < idx_cases.length; i++) { mv.visitLabel(tests[i]); for (Case c : idx_cases[i]) { Arg val_j = c.arg; Label target_j = c.label; if (val_j.type.equals(EATOM_TYPE)) { push(in, in.type); push(val_j, val_j.type); mv.visitJumpInsn(IF_ACMPEQ, target_j); } else { if (in.type == val_j.type) { push(in, in.type); push(val_j, val_j.type); mv.visitMethodInsn(INVOKESTATIC, ERT_NAME, "eq", "(" + in.type.getDescriptor() + in.type.getDescriptor() + ")Z"); } else { push(in, EOBJECT_TYPE); push(val_j, EOBJECT_TYPE); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z"); } mv.visitJumpInsn(IFNE, target_j); } } mv.visitJumpInsn(GOTO, getLabel(failLabel)); } } private boolean is_byte_values(int[] ivals) { for (int i = 0; i < ivals.length; i++) { if (ivals[i] < 0) return false; if (ivals[i] >= (1<<16)) return false; } return true; } private boolean is_bitset_member_test(int[] ivals, Label[] label) { if (ivals.length < 2) return false; for (int i = 0; i < ivals.length; i++) { if (label[i] != label[0]) return false; if (ivals[i] < 0 || ivals[i] >= 1<<16) return false; } return true; } /** * @param ivals * @param label */ private int[] sort(int[] ivals, Label[] label) { Label[] orig_labels = label.clone(); int[] orig_ivals = ivals.clone(); int[] res = new int[ivals.length]; Arrays.sort(ivals); next_val: for (int i = 0; i < ivals.length; i++) { int find = ivals[i]; for (int p = 0; p < orig_ivals.length; p++) { int was = orig_ivals[p]; if (find == was) { res[p] = i; label[i] = orig_labels[p]; continue next_val; } } } return res; } /** class that we use to sort the labels for select_arity switch */ class TupleArityLabel implements Comparable<TupleArityLabel> { Label cast_label = new Label(); Label target; int arity; public TupleArityLabel(int arity, Label target) { this.arity = arity; this.target = target; } @Override public int compareTo(TupleArityLabel o) { if (this.arity < o.arity) return -1; if (this.arity == o.arity) return 0; return 1; } } /** Switch based on arity of incoming <code>in</code> tuple value. */ @Override public void visitSelectTuple(Arg in, int failLabel, int[] arities, int[] targets) { push(in, ETUPLE_TYPE); // if (in.type == null) { mv.visitTypeInsn(CHECKCAST, ETUPLE_NAME); // } mv.visitMethodInsn(INVOKEVIRTUAL, ETUPLE_NAME, "arity", "()I"); TupleArityLabel[] cases = new TupleArityLabel[targets.length]; for (int i = 0; i < targets.length; i++) { cases[i] = new TupleArityLabel(arities[i], getLabel(targets[i])); } Arrays.sort(cases); Label[] casts = new Label[cases.length]; int[] values = new int[cases.length]; for (int i = 0; i < cases.length; i++) { values[i] = cases[i].arity; casts[i] = cases[i].cast_label; } mv.visitLookupSwitchInsn(getLabel(failLabel), values, casts); for (int i = 0; i < cases.length; i++) { mv.visitLabel(cases[i].cast_label); // NOTE: unfortunately, we have to use a cast here. There // is no real way around it, except maybe make some // special ETuple methods for small tuple sizes? // that is an option for future optimization of pattern // match. push(in, ETUPLE_TYPE); mv.visitTypeInsn(CHECKCAST, getTubleType(cases[i].arity) .getInternalName()); pop(in, getTubleType(cases[i].arity)); mv.visitJumpInsn(GOTO, cases[i].target); } } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitJump(int) */ @Override public void visitJump(int label) { mv.visitJumpInsn(GOTO, getLabel(label)); } /** * @param i * @return */ private Type getTubleType(int i) { return Type.getType("L" + ETUPLE_NAME + i + ";"); } /* * (non-Javadoc) * * @see erjang.beam.BlockVisitor2#visitCall(erjang.beam.ExtFunc, * erjang.beam.Arg[], boolean, boolean) */ @Override public void visitCall(ExtFun fun, Arg[] args, boolean is_tail, boolean isExternal) { ensure_exception_handler_in_place(); // are we self-recursive? boolean is_local_self_recursion = is_tail && !isExternal && fun.arity == ASMFunctionAdapter.this.arity && fun.mod == CompilerVisitor.this.module_name && fun.fun == ASMFunctionAdapter.this.fun_name; if (is_local_self_recursion) { mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, ETASK_NAME, "check_exit", "()V"); // System.out.println("self-recursive in " + fun); mv.visitJumpInsn(GOTO, getLabel(ASMFunctionAdapter.this.startLabel)); return; } Label label; FunWithArgs fwa = new FunWithArgs(fun, args); if (is_tail && (label = local_self_call.get(fwa)) != null) { mv.visitJumpInsn(GOTO, label); return; } else if (is_tail) { label = new Label(); mv.visitLabel(label); ASMFunctionAdapter.this.local_self_call.put(fwa, label); } BuiltInFunction bif = BIFUtil.getMethod(fun.mod.getName(), fun.fun.getName(), args, false, false); if (bif != null || isExternal || uses_on_load) { if (bif == null) { String field; if (isExternal) field = CompilerVisitor.this .getExternalFunction(fun); else /* local w/ on_load */ field = EUtil.getJavaName(fun.fun, fun.arity); String funTypeName = EFUN_NAME + args.length; EFunCG.ensure(args.length); mv.visitFieldInsn(GETSTATIC, self_type.getInternalName(), field, "L" + funTypeName + ";"); mv.visitVarInsn(ALOAD, 0); for (int i = 0; i < args.length; i++) { push(args[i], EOBJECT_TYPE); } mv.visitMethodInsn(INVOKEVIRTUAL, funTypeName, (is_tail && !isExitFunc(fun)) ? "invoke_tail" : "invoke", EUtil.getSignature( args.length, true)); } else if (bif.isVirtual()) { // System.err.println("DIRECT "+bif); push(args[0], bif.owner); int off = 0; if (bif.getArgumentTypes().length > 0 && bif.getArgumentTypes()[0].equals(EPROC_TYPE)) { mv.visitVarInsn(ALOAD, 0); off = 1; } for (int i = 1; i < args.length; i++) { push(args[i], bif.getArgumentTypes()[off - 1]); } mv.visitMethodInsn(INVOKEVIRTUAL, bif.owner.getInternalName(), bif.getName(), bif.getDescriptor()); } else { // System.err.println("DIRECT "+bif); int off = 0; if (bif.getArgumentTypes().length > 0 && bif.getArgumentTypes()[0].equals(EPROC_TYPE)) { mv.visitVarInsn(ALOAD, 0); off = 1; } for (int i = 0; i < args.length; i++) { push(args[i], bif.getArgumentTypes()[off + i]); } if (is_tail && fun.mod == am_erlang && fun.fun == am_apply && fun.arity == 3) { mv.visitMethodInsn(INVOKESTATIC, "erjang/ERT", "apply_last", "(Lerjang/EProc;Lerjang/EObject;Lerjang/EObject;Lerjang/EObject;)Lerjang/EObject;"); } else { mv.visitMethodInsn(INVOKESTATIC, bif.owner.getInternalName(), bif.getName(), bif.getDescriptor()); } } if (is_tail || isExitFunc(fun)) { mv.visitInsn(ARETURN); } else { mv.visitVarInsn(ASTORE, xregs[0]); } } else { mv.visitVarInsn(ALOAD, 0); for (int i = 0; i < args.length; i++) { push(args[i], EOBJECT_TYPE); } FunInfo target = funInfos.get(new FunID(fun.mod, fun.name(), fun.arity)); if (!is_tail && target.may_return_tail_marker && Boolean.getBoolean("erjang.inline_calls") ) { mv.visitMethodInsn(INVOKESTATIC, self_type.getInternalName(), EUtil.getJavaName(fun.fun, fun.arity), EUtil.getSignature(fun.arity, true)); Label done = new Label(); Label loop = new Label(); // - val mv.visitLabel(loop); // - val mv.visitInsn(DUP); // - val val if (EProc.TAIL_MARKER == null) { mv.visitJumpInsn(IFNONNULL, done); } else { mv.visitFieldInsn(GETSTATIC, EPROC_NAME, "TAIL_MARKER", EOBJECT_DESC); mv.visitJumpInsn(IF_ACMPNE, done); } // - val mv.visitInsn(POP); // - mv.visitVarInsn(ALOAD, 0); // - proc mv.visitFieldInsn(GETFIELD, EPROC_NAME, "tail", EFUN_DESCRIPTOR); // - fun mv.visitVarInsn(ALOAD, 0); // - fun proc mv.visitMethodInsn(INVOKEVIRTUAL, EFUN_NAME, (funInfo.is_pausable ? "go" : "go2"), GO_DESC); // - val mv.visitJumpInsn(GOTO, loop); mv.visitLabel(done); // - val mv.visitVarInsn(ASTORE, xregs[0]); } else { mv.visitMethodInsn( INVOKESTATIC, self_type.getInternalName(), EUtil.getJavaName(fun.fun, fun.arity) + (is_tail ? "$tail" : (target.may_return_tail_marker ? "$call" : "")), EUtil.getSignature( args.length, true)); if (is_tail) { mv.visitInsn(ARETURN); } else { mv.visitVarInsn(ASTORE, xregs[0]); } } } } /** * @param fun * @return */ private boolean isExitFunc(ExtFun fun) { if (fun.mod == ERLANG_ATOM) { if (fun.fun == CodeAtoms.EXIT_ATOM && fun.arity == 1) return true; if (fun.fun == CodeAtoms.ERROR_ATOM && (fun.arity == 1 || fun.arity == 2)) return true; if (fun.fun == CodeAtoms.NIF_ERROR_ATOM && (fun.arity == 1)) return true; if (fun.fun == CodeAtoms.THROW_ATOM && fun.arity == 1) return true; } return false; } } } final static Method IS_NONEMPTY_LIST_TEST = Method .getMethod("erjang.ECons testNonEmptyList()"); final static Method IS_LIST_TEST = Method .getMethod("erjang.ECons testCons()"); final static Method IS_TUPLE_TEST = Method .getMethod("erjang.ETuple testTuple()"); final static Method IS_INTEGER_TEST = Method .getMethod("erjang.EInteger testInteger()"); final static Method IS_ATOM_TEST = Method .getMethod("erjang.EAtom testAtom()"); final static Method IS_FLOAT_TEST = Method .getMethod("erjang.EDouble testFloat()"); final static Method IS_NIL_TEST = Method.getMethod("erjang.ENil testNil()"); final static Method IS_BOOLEAN_TEST = Method .getMethod("erjang.EAtom testBoolean()"); final static Method IS_NUMBER_TEST = Method .getMethod("erjang.ENumber testNumber()"); final static Method IS_BINARY_TEST = Method .getMethod("erjang.EBinary testBinary()"); final static Method IS_BITSTRING_TEST = Method .getMethod("erjang.EBitString testBitString()"); final static Method IS_PID_TEST = Method.getMethod("erjang.EPID testPID()"); final static Method IS_PORT_TEST = Method .getMethod("erjang.EPort testPort()"); final static Method IS_REFERENCE_TEST = Method.getMethod(ERef.class .getName() + " testReference()"); final static Method IS_FUNCTION_TEST = Method .getMethod("erjang.EFun testFunction()"); final static Method IS_FUNCTION2_TEST = Method .getMethod("erjang.EFun testFunction(int nargs)"); final static Method IS_MAP_TEST = Method .getMethod("erjang.EMap testMap()"); Map<String, ExtFun> imported = new HashMap<String, ExtFun>(); private Map<FunID, FunInfo> funInfos; /** * @param fun * @return */ public String getExternalFunction(ExtFun fun) { String name = EUtil.getJavaName(fun); if (!imported.containsKey(name)) { imported.put(name, fun); } return name; } static class Lambda { private final EAtom fun; private final int arity; private final int freevars; private final int index; private final int old_index; private final int old_uniq; private final EBinary uniq; public Lambda(EAtom fun, int arity, int freevars, int index, int old_index, EBinary uniq, int old_uniq) { this.fun = fun; this.arity = arity; this.freevars = freevars; this.index = index; this.old_index = old_index; this.uniq = uniq; this.old_uniq = old_uniq; } } /** * @param fun * @param arity_plus * @param length * @param index * TODO * @param old_index * TODO * @param uniq * TODO * @param old_uniq */ public void register_lambda(EAtom fun, int arity_plus, int freevars, int index, int old_index, EBinary uniq, int old_uniq) { lambdas_xx.put(new FunID(module_name, fun, arity_plus), new Lambda(fun, arity_plus, freevars, index, old_index, uniq, old_uniq)); } public Lambda get_lambda_freevars(EAtom fun, int arity_plus) { return lambdas_xx.get(new FunID(module_name, fun, arity_plus)); } public static byte[] make_invoker(String module, String function, Type self_type, String mname, String fname, int arity, boolean proc, boolean exported, boolean is_guard, Lambda lambda, Type return_type, boolean is_tail_call, final boolean is_pausable) { if (is_guard) exported = false; int freevars = lambda == null ? 0 : lambda.freevars; String outer_name = self_type.getInternalName(); String inner_name = "FN_" + mname; String full_inner_name = outer_name + "$" + inner_name; ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); int residual_arity = arity - freevars; final String super_class_name; if (is_guard) { super_class_name = EFUN_NAME + residual_arity + "Guard"; EFunCG.ensure_guard(residual_arity); } else if (exported) { super_class_name = EFUN_NAME + residual_arity + "Exported"; EFunCG.ensure_exported(residual_arity); } else { super_class_name = EFUN_NAME + residual_arity; EFunCG.ensure(residual_arity); } cw.visit(V1_6, ACC_FINAL | ACC_PUBLIC, full_inner_name, null, super_class_name, null); if (lambda != null) { cw.visitField(ACC_STATIC | ACC_PUBLIC | ACC_FINAL, "index", "I", null, new Integer(lambda.index)); cw.visitField(ACC_STATIC | ACC_PUBLIC | ACC_FINAL, "old_index", "I", null, new Integer(lambda.old_index)); cw.visitField(ACC_STATIC | ACC_PUBLIC | ACC_FINAL, "old_uniq", "I", null, new Integer(lambda.old_uniq)); /** */ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "encode", "(" + Type.getDescriptor(EOutputStream.class) + ")V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, full_inner_name, "pid", EPID_TYPE.getDescriptor()); mv.visitLdcInsn(module); mv.visitFieldInsn(GETSTATIC, full_inner_name, "old_index", "I"); mv.visitInsn(I2L); mv.visitLdcInsn(new Integer(arity)); mv.visitFieldInsn(GETSTATIC, outer_name, "module_md5", EBINARY_TYPE.getDescriptor()); mv.visitFieldInsn(GETSTATIC, full_inner_name, "index", "I"); mv.visitInsn(I2L); mv.visitFieldInsn(GETSTATIC, full_inner_name, "old_uniq", "I"); mv.visitInsn(I2L); mv.visitLdcInsn(new Integer(freevars)); mv.visitTypeInsn(ANEWARRAY, EOBJECT_NAME); for (int i = 0; i < freevars; i++) { mv.visitInsn(DUP); if (i <= 5) { mv.visitInsn(ICONST_0 + i); } else { mv.visitLdcInsn(new Integer(i)); } mv.visitVarInsn(ALOAD, 0); // load self mv.visitFieldInsn(GETFIELD, full_inner_name, "fv" + i, EOBJECT_DESC); mv.visitInsn(AASTORE); } mv.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(EOutputStream.class), "write_fun", "(" + EPID_TYPE.getDescriptor() + "Ljava/lang/String;" + "JI" + EBINARY_TYPE.getDescriptor() + "JJ" + Type.getDescriptor(EObject[].class) + ")V"); mv.visitInsn(RETURN); mv.visitMaxs(10, 3); mv.visitEnd(); } make_constructor(cw, module, function, full_inner_name, outer_name, super_class_name, lambda, exported); if (is_guard) { make_direct_invoke_method(cw, outer_name, fname, arity, proc, return_type, is_pausable); } else { make_go_method(cw, outer_name, fname, full_inner_name, arity, proc, freevars, return_type, is_tail_call, is_pausable); make_go2_method(cw, outer_name, fname, full_inner_name, arity, proc, freevars, return_type, is_tail_call, is_pausable); } return cw.toByteArray(); } private static void make_constructor(ClassWriter cw, String module_name, String function_name, String full_inner_name, String outer_name, String super_class_name, Lambda lambda, boolean exported) { StringBuilder sb = new StringBuilder("("); int freevars = lambda == null ? 0 : lambda.freevars; if (lambda != null) { sb.append(EPID_TYPE.getDescriptor()); cw.visitField(ACC_PUBLIC | ACC_FINAL, "pid", EPID_TYPE.getDescriptor(), null, null); } // create the free vars for (int i = 0; i < freevars; i++) { cw.visitField(ACC_PUBLIC | ACC_FINAL, "fv" + i, EOBJECT_DESC, null, null); sb.append(EOBJECT_DESC); } sb.append(")V"); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", sb.toString(), null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); if (exported) { mv.visitLdcInsn(module_name); mv.visitLdcInsn(function_name); mv.visitMethodInsn(INVOKESPECIAL, super_class_name, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V"); } else { mv.visitMethodInsn(INVOKESPECIAL, super_class_name, "<init>", "()V"); } if (lambda != null) { mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitFieldInsn(PUTFIELD, full_inner_name, "pid", EPID_TYPE.getDescriptor()); } for (int i = 0; i < freevars; i++) { mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, i + 2); mv.visitFieldInsn(PUTFIELD, full_inner_name, "fv" + i, EOBJECT_DESC); } mv.visitInsn(RETURN); mv.visitMaxs(3, 3); mv.visitEnd(); if (lambda != null) { mv = cw.visitMethod(ACC_PROTECTED, "get_env", "()" + ESEQ_DESC, null, null); mv.visitCode(); mv.visitFieldInsn(GETSTATIC, ERT_NAME, "NIL", ENIL_TYPE.getDescriptor()); for (int i = freevars - 1; i >= 0; i--) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, full_inner_name, "fv" + i, EOBJECT_DESC); mv.visitMethodInsn(INVOKEVIRTUAL, ESEQ_NAME, "cons", "(" + EOBJECT_DESC + ")" + ESEQ_DESC); } mv.visitInsn(ARETURN); mv.visitMaxs(3, 3); mv.visitEnd(); mv = cw.visitMethod(ACC_PROTECTED, "get_pid", "()" + EOBJECT_DESC, null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, full_inner_name, "pid", EPID_TYPE.getDescriptor()); mv.visitInsn(ARETURN); mv.visitMaxs(3, 3); mv.visitEnd(); mv = cw.visitMethod(ACC_PROTECTED, "get_id", "()" + Type.getDescriptor(FunID.class), null, null); mv.visitCode(); mv.visitFieldInsn(GETSTATIC, outer_name, anon_fun_name(lambda), Type.getDescriptor(LocalFunID.class)); mv.visitInsn(ARETURN); mv.visitMaxs(3, 3); mv.visitEnd(); } } public static void make_invoketail_method(ClassWriter cw, String full_inner, int arity, int freevars) { MethodVisitor mv; mv = cw.visitMethod(ACC_PUBLIC | ACC_FINAL, "invoke_tail", EUtil.getSignature(arity - freevars, true), null, null); mv.visitCode(); for (int i = 0; i < arity - freevars; i++) { mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, i + 2); mv.visitFieldInsn(PUTFIELD, EPROC_NAME, "arg" + i, EOBJECT_DESC); } mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(PUTFIELD, EPROC_NAME, "tail", EFUN_DESCRIPTOR); if (EProc.TAIL_MARKER == null) { mv.visitInsn(ACONST_NULL); } else { mv.visitFieldInsn(GETSTATIC, EPROC_NAME, "TAIL_MARKER", EOBJECT_DESC); } mv.visitInsn(ARETURN); mv.visitMaxs(arity + 2, arity + 2); mv.visitEnd(); } private static void make_go_method(ClassWriter cw, String outer_name, String mname, String full_inner, int arity, boolean proc, int freevars, Type returnType, boolean isTailCall, boolean isPausable) { if (!isPausable) return; MethodVisitor mv; mv = cw.visitMethod(ACC_PUBLIC, "go", GO_DESC, null, PAUSABLE_EX); mv.visitCode(); for (int i = 0; i < arity - freevars; 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 - freevars; i++) { mv.visitVarInsn(ALOAD, 1); mv.visitInsn(ACONST_NULL); mv.visitFieldInsn(PUTFIELD, EPROC_NAME, "arg" + i, EOBJECT_DESC); } if (proc) mv.visitVarInsn(ALOAD, 1); for (int i = 0; i < arity - freevars; i++) { mv.visitVarInsn(ALOAD, i + 2); } for (int i = 0; i < freevars; i++) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, full_inner, "fv" + i, EOBJECT_DESC); } mv.visitMethodInsn(INVOKESTATIC, outer_name, mname, EUtil.getSignature(arity, proc, returnType)); mv.visitInsn(ARETURN); mv.visitMaxs(arity + 2, arity + 2); mv.visitEnd(); cw.visitEnd(); } private static void make_go2_method(ClassWriter cw, String outer_name, String mname, String full_inner, int arity, boolean proc, int freevars, Type returnType, boolean isTailCall, boolean isPausable) { if (isPausable) { if (ModuleAnalyzer.log.isLoggable(Level.FINE)) { ModuleAnalyzer.log.fine("not generating go2 (pausable) for " + full_inner); } return; } MethodVisitor mv; mv = cw.visitMethod(ACC_PUBLIC, "go2", GO_DESC, null, null); mv.visitCode(); for (int i = 0; i < arity - freevars; 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 - freevars; i++) { mv.visitVarInsn(ALOAD, 1); mv.visitInsn(ACONST_NULL); mv.visitFieldInsn(PUTFIELD, EPROC_NAME, "arg" + i, EOBJECT_DESC); } if (proc) mv.visitVarInsn(ALOAD, 1); for (int i = 0; i < arity - freevars; i++) { mv.visitVarInsn(ALOAD, i + 2); } for (int i = 0; i < freevars; i++) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, full_inner, "fv" + i, EOBJECT_DESC); } mv.visitMethodInsn(INVOKESTATIC, outer_name, mname, EUtil.getSignature(arity, proc, returnType)); mv.visitInsn(ARETURN); mv.visitMaxs(arity + 2, arity + 2); mv.visitEnd(); cw.visitEnd(); } /** make_direct_invoke_method: Perform a direct call, with no * tail-recursino handling. * Used for EFunXxGuard classes. */ private static void make_direct_invoke_method(ClassWriter cw, String outer_name, String mname, int arity, boolean proc, Type returnType, boolean isPausable) { MethodVisitor mv; mv = cw.visitMethod(ACC_PUBLIC, "invoke", EUtil.getSignature(arity, true), null, isPausable ? PAUSABLE_EX : null); mv.visitCode(); if (proc) mv.visitVarInsn(ALOAD, 1); for (int i = 0; i < arity; i++) { mv.visitVarInsn(ALOAD, i+2); } mv.visitMethodInsn(INVOKESTATIC, outer_name, mname, EUtil.getSignature(arity, proc, returnType)); // TODO: Special handling if returnType != EObject - or assert that this isn't the case. mv.visitInsn(ARETURN); mv.visitMaxs(arity + 2, arity + 2); mv.visitEnd(); } public void setFunInfos(Map<FunID, FunInfo> funInfos) { this.funInfos = funInfos; } } /** Active exception handler */ class EXHandler { int handler_beam_label; Label begin, end, target; BeamExceptionHandler beam_exh; }