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