Package erjang.beam

Source Code of erjang.beam.CompilerVisitor$ASMFunctionAdapter

/**
* 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.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 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)
          && !( tup != null && tup.arity()==5 && tup.elm(1) == ETuple.am_Elixir_Regex)
          ) {
        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;
      }

      /*
       * (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;
        }

        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)");

  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;
}
TOP

Related Classes of erjang.beam.CompilerVisitor$ASMFunctionAdapter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.