Java tutorial
/** * Copyright (C) 2009 Kenji Nozawa * This file is part of LAPIN. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package lapin.comp.asm; import lapin.comp.ByteCodeGenerator; import lapin.comp.Callables; import lapin.comp.Classes; import lapin.comp.ClassInfo; import lapin.comp.Insts; import lapin.comp.MethodInfo; import lapin.function.Function; import lapin.function.LambdaList; import lapin.io.Printer; import lapin.lang.Data; import lapin.lang.Env; import lapin.lang.Lists; import lapin.lang.NotReachedException; import lapin.lang.Symbol; import lapin.lang.Symbols; import lapin.util.Logger; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; 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; /** * ByteCodeGenerator which uses * <a href="http://asm.ow2.org/">ASM</a>. * @see ByteCodeGenerator * @see ClassInfo * @see MethodInfo */ public final class ASMByteCodeGenerator extends ByteCodeGenerator { static private final Type TYPE_ENV = Type.getType(Env.class); static private final Type TYPE_LAMBDA_LIST = Type.getType(LambdaList.class); static private final Type TYPE_SYMBOL = Type.getType(Symbol.class); static private final Type TYPE_OBJECT = Type.getType(Object.class); static private final Type TYPE_STRING = Type.getType(String.class); static private final Type[] TYPE_NO_ARGS = new Type[] {}; /** * data structure used to generate the bytecode of the method body. * <pre> * lapin.function.Callable#call(Object,Env) * lapin.function.Callable[N]#call[N](Object,...,Object,Env) * lapin.function.Callable[N]r#call[N](Object,...,Object,Object,Env) * </pre> */ static class CallableInfo { /** method info stored in the class info */ MethodInfo mi; /** name of the interface to be implemented by the subr */ String interfaceName; /** * counter which manages the slot of the local var * used in the call (or call[N]) method */ int localVars; /** * param types for call (or call[N]) method */ Type[] paramTypes; /** * return type of call (or call[N]) method */ Type retType; /** table: (var, slot of local var) */ final HashMap localTable = new HashMap(); /** table: (tag, ASM label object) */ final HashMap labelTable = new HashMap(); CallableInfo(MethodInfo mi, Env env) { this.mi = mi; int nargs = mi.nargs(); boolean rest = mi.rest(); boolean implCallable = mi.implCallable(); Class type = implCallable ? Callables.getType(nargs, rest, true) : null; Class[] pTypes = mi.paramTypes(); Class rType = mi.retType(); this.interfaceName = implCallable ? type.getName() : null; this.paramTypes = new Type[pTypes.length]; for (int i = 0; i < pTypes.length; i++) { this.paramTypes[i] = Type.getType(pTypes[i]); } this.retType = Type.getType(rType); int slot = 0; if (nargs >= 0) { // slot for THIS localTable.put(Callables.LOCAL_SLOT_THIS, Data.toFixnum(slot)); slot += Classes.sizeOf(Function.class); for (int i = 0; i < nargs; i++) { // slot for method argument ARG[i] localTable.put(Callables.getArgSlot(i), Data.toFixnum(slot)); slot += Classes.sizeOf(pTypes[i]); } if (rest) { // slot for method argument REST localTable.put(Callables.LOCAL_SLOT_ARGS, Data.toFixnum(slot)); slot += Classes.sizeOf(pTypes[nargs]); // slot for method argument ENV localTable.put(Callables.LOCAL_SLOT_ENV, Data.toFixnum(slot)); slot += Classes.sizeOf(pTypes[nargs + 1]); } else { // slot for method argument ENV localTable.put(Callables.LOCAL_SLOT_ENV, Data.toFixnum(slot)); slot += Classes.sizeOf(pTypes[nargs]); } } else { // slot for THIS localTable.put(Callables.LOCAL_SLOT_THIS, Data.toFixnum(slot)); slot += Classes.sizeOf(Function.class); // slot for method argument ARGS localTable.put(Callables.LOCAL_SLOT_ARGS, Data.toFixnum(slot)); slot += Classes.sizeOf(pTypes[0]); // slot for method argument ENV localTable.put(Callables.LOCAL_SLOT_ENV, Data.toFixnum(slot)); slot += Classes.sizeOf(pTypes[1]); } this.localVars = slot; if (Logger.debuglevelp(env)) { Logger.debug("[asm:init] interface =~S", Lists.list(interfaceName), env); //Logger.debug("[asm:init] localVars =~S", // Lists.list(Data.toFixnum(localVars)), env); Logger.debug("[asm:init] methodName=~S", Lists.list(mi.name()), env); Logger.debug("[asm:init] params.len=~S", Lists.list(Data.toFixnum(pTypes.length)), env); } } }/* end of CallableInfo */ /** ASM ClassWriter object */ private ClassWriter _cw; /** list of CallableInfo */ private Object ciList = Symbols.NIL; /** table: (const obj, field name) */ private final IdentityHashMap constTable = new IdentityHashMap(); /** table: (var, field name) */ private final HashMap varTable = new HashMap(); /** table: (lambdaList, field name) */ private final HashMap llTable = new HashMap(); /** * a count of the instance field used in the compiledExpr. */ private int fields = 0; /** * flag that indicates whether this object has been used or not. */ private boolean finished = false; protected void init(Env env) { if (Logger.debuglevelp(env)) Logger.debug("[asm:init] className =~S", Lists.list(super.classInfo.classname()), env); // CallableInfo Iterator it = super.classInfo.methodIterator(); while (it.hasNext()) { MethodInfo mi = (MethodInfo) it.next(); CallableInfo ci = new CallableInfo(mi, env); ciList = Lists.cons(ci, ciList); } } protected byte[] doGenerate(Env env) { if (finished) { throw new NotReachedException("repeated use of the same object is not allowed", Symbols.NIL); } try { initASMObject(env); for (Object l = ciList; !Lists.isEnd(l); l = Lists.cdr(l)) { prepareCompilation((CallableInfo) Lists.car(l), env); } generateFields(env); generateClassInitializer(env); generateConstructor(env); for (Object l = ciList; !Lists.isEnd(l); l = Lists.cdr(l)) { generateCall((CallableInfo) Lists.car(l), env); } return getBytes(env); } finally { finished = true; } } private String toInternalName(String className) { if (className == null) throw new NullPointerException("className is null"); return className.replace('.', '/'); } private String toTypeDescriptor(String className) { if (className == null) throw new NullPointerException("className is null"); return "L" + className.replace('.', '/') + ";"; } private void initASMObject(Env env) { ArrayList al = new ArrayList(); for (Object l = ciList; !Lists.isEnd(l); l = Lists.cdr(l)) { CallableInfo ci = (CallableInfo) Lists.car(l); if (ci.mi.implCallable()) al.add(toInternalName(ci.interfaceName)); } String[] ifNames = new String[al.size()]; al.toArray(ifNames); // ClassWriter _cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); _cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, toInternalName(super.classInfo.classname()), null, "lapin/function/CompiledExpr", ifNames); } private void prepareCompilation(CallableInfo ci, Env env) { // instruction list int len = ci.mi.instLen(); // list of label-tag Object taglist = Symbols.NIL; // 1. arrange the catch-tags for (int i = 0; i < len; i++) { Object inst = ci.mi.getInst(i); if (Data.isSymbol(inst)) { continue; } Object id = Lists.car(inst); if (id == Insts.CATCH_FROM || id == Insts.CATCH_TO || id == Insts.CATCH_HANDLER) { if (i == len - 1) { throw new NotReachedException("~S cannot be put " + "at the tail of the code list: ~S.", Lists.list(id, inst)); } Symbol tag = Data.symbol(Lists.cadr(inst)); ci.mi.setInst(i, tag); if (Logger.debuglevelp(env)) Logger.debug("[asm:prepare]" + i + ":\tid ~S replaced to tag: ~S", Lists.list(id, tag), env); } } // 2. store info in xxxTable for (int i = 0; i < len; i++) { Object inst = ci.mi.getInst(i); if (Logger.tracelevelp(env)) Logger.trace("[asm:prepare]" + i + ":\t~S", Lists.list(inst), env); // inst is symbol if (Data.isSymbol(inst)) { // push tag to taglist Symbol tag = Data.symbol(inst); taglist = Lists.cons(tag, taglist); continue; } // process taglist (regist labelInfo) if (taglist != Symbols.NIL) { Label label = new Label(); for (Object l = taglist; !Lists.isEnd(l); l = Lists.cdr(l)) { Symbol tag = Data.symbol(Lists.car(l)); ci.labelTable.put(tag, label); if (Logger.tracelevelp(env)) Logger.trace("[asm:prepare]" + i + ":\ttag ~S" + " added in labelTable(~S)", Lists.list(tag, label), env); } // reset taglist taglist = Symbols.NIL; } // inst must be the form of (id <arg1> <arg2> ....) Object id = Lists.car(inst); if (id == Insts.CONST) { Object obj = Lists.cadr(inst); if (!constTable.containsKey(obj)) { constTable.put(obj, "_const_" + (fields++)); if (Logger.tracelevelp(env)) Logger.trace("[asm:prepare]" + i + ":\tobj ~S" + " added in constTable", Lists.list(obj), env); } } else if (id == Insts.VAR) { Symbol var = Data.symbol(Lists.cadr(inst)); if (!varTable.containsKey(var)) { varTable.put(var, "_var_" + (fields++)); if (Logger.tracelevelp(env)) Logger.trace("[asm:prepare]" + i + ":\tvar ~S" + " added in varTable", Lists.list(var), env); } } else if (id == Insts.LAMBDA_LIST) { LambdaList ll = Data.lambdaList(Lists.cadr(inst)); if (!llTable.containsKey(ll)) { llTable.put(ll, "_ll_" + (fields++)); if (Logger.tracelevelp(env)) Logger.trace("[asm:prepare]" + i + ":\tll ~S" + " added in llTable", Lists.list(ll.params()), env); } } else if (id == Insts.ENV_CHILD) { Object oldEnvVar = Lists.cadr(inst); Object newEnvVar = Lists.caddr(inst); Object oldVar = Lists.car(oldEnvVar); Object newVar = Lists.car(newEnvVar); Object oldSlot = Lists.cadr(oldEnvVar); Object newSlot = Lists.cadr(newEnvVar); if (!ci.localTable.containsKey(oldSlot)) { ci.localTable.put(oldSlot, Data.toFixnum(ci.localVars++)); if (Logger.tracelevelp(env)) Logger.trace("[asm:prepare]" + i + ":\toldSlot ~S" + " added in localTable", Lists.list(oldSlot), env); } if (Data.isSymbol(oldVar) && !varTable.containsKey(oldVar)) { varTable.put(oldVar, "_var_" + (fields++)); if (Logger.tracelevelp(env)) Logger.trace("[asm:prepare]" + i + ":\tvar ~S" + " added in varTable", Lists.list(oldVar), env); } if (!ci.localTable.containsKey(newSlot)) { ci.localTable.put(newSlot, Data.toFixnum(ci.localVars++)); if (Logger.tracelevelp(env)) Logger.trace("[asm:prepare]" + i + ":\tnewSlot ~S" + " added in localTable", Lists.list(newSlot), env); } if (Data.isSymbol(newVar) && !varTable.containsKey(newVar)) { varTable.put(newVar, "_var_" + (fields++)); if (Logger.tracelevelp(env)) Logger.trace("[asm:prepare]" + i + ":\tvar ~S" + " added in varTable", Lists.list(newVar), env); } } else if (id == Insts.LOAD || id == Insts.STORE) { Object localVar = Lists.cadr(inst); Object var = Lists.car(localVar); Object slot = Lists.cadr(localVar); Class type = Data.javaClass(Lists.caddr(localVar)); if (!ci.localTable.containsKey(slot)) { switch (Classes.sizeOf(type)) { case 1: ci.localTable.put(slot, Data.toFixnum(ci.localVars++)); break; case 2: Object slot2 = Data.toFixnum(Data.fixnum(slot).intValue() + 1); ci.localTable.put(slot, Data.toFixnum(ci.localVars++)); ci.localTable.put(slot2, Data.toFixnum(ci.localVars++)); break; default: throw new NotReachedException("unsupported type: ~S.", Lists.list(type)); } if (Logger.tracelevelp(env)) Logger.trace("[asm:prepare]" + i + ":\tslot ~S" + " added in localTable: type is ~S", Lists.list(slot, type), env); } if (Data.isSymbol(var) && !varTable.containsKey(var)) { varTable.put(var, "_var_" + (fields++)); if (Logger.tracelevelp(env)) Logger.trace("[asm:prepare]" + i + ":\tvar ~S" + " added in varTable", Lists.list(var), env); } } } if (taglist != Symbols.NIL) { throw new NotReachedException("unprocessed taglist: ~S.", Lists.list(taglist)); } if (Logger.debuglevelp(env)) { Logger.debug("[asm:prepare] ~S: localVars=~S", Lists.list(ci.mi.name(), Data.toFixnum(ci.localVars)), env); } } private void generateFields(Env env) { Iterator it; // fields for static subr (singleton) instance _cw.visitField(Opcodes.ACC_STATIC + Opcodes.ACC_VOLATILE, "SELF", toTypeDescriptor(super.classInfo.classname()), null, null).visitEnd(); // fields for constants it = constTable.keySet().iterator(); while (it.hasNext()) { Object key = it.next(); // key: const object String val = Data.string(constTable.get(key)); _cw.visitField(Opcodes.ACC_PRIVATE, val, TYPE_OBJECT.getDescriptor(), null, null).visitEnd(); } // fields for vars it = varTable.keySet().iterator(); while (it.hasNext()) { Object key = it.next(); // key: symbol (var) String val = Data.string(varTable.get(key)); _cw.visitField(Opcodes.ACC_PRIVATE, val, TYPE_SYMBOL.getDescriptor(), null, null).visitEnd(); } // fields for lls it = llTable.keySet().iterator(); while (it.hasNext()) { Object key = it.next(); // key: lambdaList String val = Data.string(llTable.get(key)); _cw.visitField(Opcodes.ACC_PRIVATE, val, TYPE_LAMBDA_LIST.getDescriptor(), null, null).visitEnd(); } } private void generateClassInitializer(Env env) { MethodVisitor mv = _cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null); // field for SELF (static field) mv.visitCode(); mv.visitInsn(Opcodes.ACONST_NULL); mv.visitFieldInsn(Opcodes.PUTSTATIC, toInternalName(super.classInfo.classname()), "SELF", toTypeDescriptor(super.classInfo.classname())); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } private void generateConstructor(Env env) { /* * local variables (reserved) * 0: this * 1: env */ MethodVisitor mv = _cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", //"(Llapin/lang/Env;)V", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { TYPE_ENV }), null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitLdcInsn(super.classInfo.name().pname()); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "lapin/function/CompiledExpr", "<init>", //"(Ljava/lang/String;)V"); Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { TYPE_STRING })); Iterator it; // fields for constants it = constTable.keySet().iterator(); while (it.hasNext()) { Object key = it.next(); // key: object (const) String val = Data.string(constTable.get(key)); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitLdcInsn(Printer.prin1ToString(key, env)); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "lapin/function/CompiledExpr", "toSexp", Type.getMethodDescriptor(TYPE_OBJECT, new Type[] { TYPE_STRING, TYPE_ENV })); mv.visitFieldInsn(Opcodes.PUTFIELD, toInternalName(super.classInfo.classname()), val, TYPE_OBJECT.getDescriptor()); } // fields for vars it = varTable.keySet().iterator(); while (it.hasNext()) { Object key = it.next(); // key: symbol (var) String val = Data.string(varTable.get(key)); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitLdcInsn(Printer.prin1ToString(key, env)); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "lapin/function/CompiledExpr", "toSexp", Type.getMethodDescriptor(TYPE_OBJECT, new Type[] { TYPE_STRING, TYPE_ENV })); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "lapin/lang/Data", "symbol", Type.getMethodDescriptor(TYPE_SYMBOL, new Type[] { TYPE_OBJECT })); mv.visitFieldInsn(Opcodes.PUTFIELD, toInternalName(super.classInfo.classname()), val, TYPE_SYMBOL.getDescriptor()); } // fields for lambdaLists it = llTable.keySet().iterator(); while (it.hasNext()) { Object key = it.next(); // key: lambdaList String val = Data.string(llTable.get(key)); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitLdcInsn(Printer.prin1ToString(Data.lambdaList(key).params(), env)); mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "lapin/function/CompiledExpr", "toLambdaList", Type.getMethodDescriptor(TYPE_LAMBDA_LIST, new Type[] { TYPE_STRING, TYPE_ENV })); mv.visitFieldInsn(Opcodes.PUTFIELD, toInternalName(super.classInfo.classname()), val, TYPE_LAMBDA_LIST.getDescriptor()); } // field for SELF (static field) mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.PUTSTATIC, toInternalName(super.classInfo.classname()), "SELF", toTypeDescriptor(super.classInfo.classname())); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } private void generateCall(CallableInfo ci, Env env) { /* * local variables * <Callable#call> * 0: this * 1: args (list of arguments) * 2: env * * <Callable0#call0> * 0: this * 1: env * * <Callable1#call1> * 0: this * 1: arg0 * 2: env * * <Callable2#call2> * 0: this * 1: arg0 * 2: arg1 * 3: env * * ... * */ MethodVisitor mv = _cw.visitMethod( ci.mi.implCallable() ? Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL : Opcodes.ACC_FINAL, ci.mi.name(), Type.getMethodDescriptor(ci.retType, ci.paramTypes), null, null); // instruction list int len = ci.mi.instLen(); // label Label label = null; // generate code for (int i = 0; i < len; i++) { Object inst = ci.mi.getInst(i); if (Logger.tracelevelp(env)) Logger.trace("[asm:gen]" + i + ":\t~S", Lists.list(inst), env); // inst is symbol // -> convert tag (Symbol) to label (ASMe Label object) if (Data.isSymbol(inst)) { Symbol tag = Data.symbol(inst); Label l = (Label) ci.labelTable.get(tag); if (l == null) { throw new NotReachedException("label is null: ~S.", Lists.list(tag)); } else if (l != label) { mv.visitLabel(l); label = l; if (Logger.tracelevelp(env)) Logger.trace("[asm:gen]" + i + ":\ttag ~S -> label ~S", Lists.list(tag, l), env); } else { if (Logger.tracelevelp(env)) Logger.trace("[asm:gen]" + i + ":\ttag ~S -> label ~S" + " (dup)", Lists.list(tag, l), env); } continue; } // inst must be the form of (id <arg1> <arg2> ....) Object id = Lists.car(inst); if (id == Insts.CONST) { /* push const on the stack. */ Object obj = Lists.cadr(inst); String val = Data.string(constTable.get(obj)); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.GETFIELD, toInternalName(super.classInfo.classname()), val, TYPE_OBJECT.getDescriptor()); } else if (id == Insts.VAR) { /* push var on the stack */ Object var = Lists.cadr(inst); String val = Data.string(varTable.get(var)); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.GETFIELD, toInternalName(super.classInfo.classname()), val, TYPE_SYMBOL.getDescriptor()); } else if (id == Insts.LAMBDA_LIST) { /* push lambdaList on the stack */ Object var = Lists.cadr(inst); /* push _ll_<i> on the stack */ String val = Data.string(llTable.get(var)); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitFieldInsn(Opcodes.GETFIELD, toInternalName(super.classInfo.classname()), val, TYPE_LAMBDA_LIST.getDescriptor()); } else if (id == Insts.ENV_GET) { /* env.get */ Class type = Data.javaClass(Lists.cadr(inst)); if (type.equals(int.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "getInt", Type.getMethodDescriptor(Type.INT_TYPE, new Type[] { TYPE_SYMBOL })); } else if (type.equals(double.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "getDouble", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[] { TYPE_SYMBOL })); } else if (type.equals(char.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "getChar", Type.getMethodDescriptor(Type.CHAR_TYPE, new Type[] { TYPE_SYMBOL })); } else if (Object.class.isAssignableFrom(type)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "get", Type.getMethodDescriptor(TYPE_OBJECT, new Type[] { TYPE_SYMBOL })); } else { throw new NotReachedException("unsupported type: ~S.", Lists.list(type)); } } else if (id == Insts.ENV_SET) { /* env.set */ Class type = Data.javaClass(Lists.cadr(inst)); if (type.equals(int.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "set", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { TYPE_SYMBOL, Type.INT_TYPE })); } else if (type.equals(double.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "set", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { TYPE_SYMBOL, Type.DOUBLE_TYPE })); } else if (type.equals(char.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "set", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { TYPE_SYMBOL, Type.CHAR_TYPE })); } else if (Object.class.isAssignableFrom(type)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "set", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { TYPE_SYMBOL, TYPE_OBJECT })); } else { throw new NotReachedException("unsupported type: ~S.", Lists.list(type)); } } else if (id == Insts.ENV_BIND) { /* env.bind */ Class type = Data.javaClass(Lists.cadr(inst)); if (type.equals(int.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "bind", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { TYPE_SYMBOL, Type.INT_TYPE })); } else if (type.equals(double.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "bind", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { TYPE_SYMBOL, Type.DOUBLE_TYPE })); } else if (type.equals(char.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "bind", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { TYPE_SYMBOL, Type.CHAR_TYPE })); } else if (Object.class.isAssignableFrom(type)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "bind", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { TYPE_SYMBOL, TYPE_OBJECT })); } else { throw new NotReachedException("unsupported type: ~S.", Lists.list(type)); } } else if (id == Insts.ENV_UNBIND) { /* env.unbind */ Class type = Data.javaClass(Lists.cadr(inst)); if (type.equals(int.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "unbindInt", Type.getMethodDescriptor(Type.INT_TYPE, new Type[] { TYPE_SYMBOL })); } else if (type.equals(double.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "unbindDouble", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[] { TYPE_SYMBOL })); } else if (type.equals(char.class)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "unbindChar", Type.getMethodDescriptor(Type.CHAR_TYPE, new Type[] { TYPE_SYMBOL })); } else if (Object.class.isAssignableFrom(type)) { mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "unbind", Type.getMethodDescriptor(TYPE_OBJECT, new Type[] { TYPE_SYMBOL })); } else { throw new NotReachedException("unsupported type: ~S.", Lists.list(type)); } } else if (id == Insts.ENV_CHILD) { /* env.child */ Object oldEnvVar = Lists.cadr(inst); Object newEnvVar = Lists.caddr(inst); Object oldSlot = Lists.cadr(oldEnvVar); Object newSlot = Lists.cadr(newEnvVar); int oldLocal = Data.fixnum(ci.localTable.get(oldSlot)).intValue(); int newLocal = Data.fixnum(ci.localTable.get(newSlot)).intValue(); if (Logger.tracelevelp(env)) Logger.trace("[asm:gen]" + i + ":\tenv-child: local ~S -> ~S", Lists.list(Data.toFixnum(oldLocal), Data.toFixnum(newLocal)), env); mv.visitVarInsn(Opcodes.ALOAD, oldLocal); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_ENV.getInternalName(), "child", Type.getMethodDescriptor(TYPE_ENV, TYPE_NO_ARGS)); mv.visitVarInsn(Opcodes.ASTORE, newLocal); } else if (id == Insts.CALL) { /* funcall */ int nargs = Data.fixnum(Lists.cadr(inst)).intValue(); String className = "lapin.eval.Funcall"; String methodName = nargs < 0 ? "funcall" : "funcall" + nargs; Class rType = Object.class; Class[] pTypes; if (nargs < 0) { pTypes = new Class[] { Function.class, Object.class, // list of args Env.class }; } else { pTypes = new Class[nargs + 2]; pTypes[0] = Function.class; for (int j = 0; j < nargs; j++) { pTypes[j + 1] = Object.class; } pTypes[nargs + 1] = Env.class; } Type retType = Type.getType(rType); Type[] paramTypes = new Type[pTypes.length]; for (int j = 0; j < pTypes.length; j++) paramTypes[j] = Type.getType(pTypes[j]); mv.visitMethodInsn(Opcodes.INVOKESTATIC, toInternalName(className), methodName, Type.getMethodDescriptor(retType, paramTypes)); } else if (id == Insts.CALL_DIRECT) { /* * public class Foo * extends CompiledExpr implements Callable2 { * public Object call2(Object arg0, Object arg1, Env env) { * ... * } * } */ MethodInfo mi = (MethodInfo) Lists.cadr(inst); String className = mi.classInfo().classname(); int nargs = mi.nargs(); boolean rest = mi.rest(); String methodName = mi.name(); Class rType = mi.retType(); Class[] pTypes = mi.paramTypes(); Type retType = Type.getType(rType); Type[] paramTypes = new Type[pTypes.length]; for (int j = 0; j < pTypes.length; j++) paramTypes[j] = Type.getType(pTypes[j]); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, toInternalName(className), methodName, Type.getMethodDescriptor(retType, paramTypes)); } else if (id == Insts.COMPILED_EXPR) { /* * public class Foo extends CompiledExpr { * static public Foo SELF; * ... * } */ String className = Data.string(Lists.cadr(inst)); String fieldName = "SELF"; String typeName = className; mv.visitFieldInsn(Opcodes.GETSTATIC, toInternalName(className), fieldName, toTypeDescriptor(typeName)); } else if (id == Insts.RETURN) { /* return */ Class type = Data.javaClass(Lists.cadr(inst)); if (type.equals(int.class) || type.equals(short.class) || type.equals(byte.class) || type.equals(char.class)) { mv.visitInsn(Opcodes.IRETURN); } else if (type.equals(long.class)) { mv.visitInsn(Opcodes.LRETURN); } else if (type.equals(float.class)) { mv.visitInsn(Opcodes.FRETURN); } else if (type.equals(double.class)) { mv.visitInsn(Opcodes.DRETURN); } else if (type.equals(void.class)) { //mv.visitInsn(Opcodes.RETURN); throw new NotReachedException("unsupported returnType: ~S.", Lists.list(type)); } else { mv.visitInsn(Opcodes.ARETURN); } } else if (id == Insts.IFEQ) { /* conditional jump */ Symbol tag = Data.symbol(Lists.cadr(inst)); Label l = (Label) ci.labelTable.get(tag); if (l == null) { throw new NotReachedException("label not found: ~S.", Lists.list(tag)); } mv.visitJumpInsn(Opcodes.IFEQ, l); } else if (id == Insts.IFNE) { /* conditional jump */ Symbol tag = Data.symbol(Lists.cadr(inst)); Label l = (Label) ci.labelTable.get(tag); if (l == null) { throw new NotReachedException("label not found: ~S.", Lists.list(tag)); } mv.visitJumpInsn(Opcodes.IFNE, l); } else if (id == Insts.GOTO) { /* jump */ Symbol tag = Data.symbol(Lists.cadr(inst)); Label l = (Label) ci.labelTable.get(tag); if (l == null) { throw new NotReachedException("label not found: ~S.", Lists.list(tag)); } mv.visitJumpInsn(Opcodes.GOTO, l); } else if (id == Insts.LOAD) { /* local -> stack */ Object localVar = Lists.cadr(inst); Object slot = Lists.cadr(localVar); Class type = Data.javaClass(Lists.caddr(localVar)); int local = Data.fixnum(ci.localTable.get(slot)).intValue(); int op = Type.getType(type).getOpcode(Opcodes.ILOAD); if (Logger.tracelevelp(env)) Logger.trace("[asm:gen]" + i + ":\tload: local=~S type=~S", Lists.list(Data.toFixnum(local), type), env); mv.visitVarInsn(op, local); } else if (id == Insts.STORE) { /* stack -> local */ Object localVar = Lists.cadr(inst); Object slot = Lists.cadr(localVar); Class type = Data.javaClass(Lists.caddr(localVar)); int local = Data.fixnum(ci.localTable.get(slot)).intValue(); int op = Type.getType(type).getOpcode(Opcodes.ISTORE); if (Logger.tracelevelp(env)) Logger.trace("[asm:gen]" + i + ":\tstore: local=~S type=~S", Lists.list(Data.toFixnum(local), type), env); mv.visitVarInsn(op, local); } else if (id == Insts.POP) { /* pop a value and discard it */ Class type = Data.javaClass(Lists.cadr(inst)); int op; switch (Classes.sizeOf(type)) { case 1: op = Opcodes.POP; break; case 2: op = Opcodes.POP2; break; default: throw new NotReachedException("unsupported type: ~S.", Lists.list(type)); } mv.visitInsn(op); } else if (id == Insts.DUP) { /* peek a value and duplicate it */ Class type = Data.javaClass(Lists.cadr(inst)); int op; switch (Classes.sizeOf(type)) { case 1: op = Opcodes.DUP; break; case 2: op = Opcodes.DUP2; break; default: throw new NotReachedException("unsupported type: ~S.", Lists.list(type)); } mv.visitInsn(op); } else if (id == Insts.PUSH) { /* push a constant */ Object val = Lists.cadr(inst); if (Data.isJavaBoolean(val)) { if (Data.javaBoolean(val).booleanValue()) mv.visitInsn(Opcodes.ICONST_1); else mv.visitInsn(Opcodes.ICONST_0); } else if (val instanceof Byte || val instanceof Short || val instanceof Integer) { int n = Data.javaNumber(val).intValue(); if (n == -1) mv.visitInsn(Opcodes.ICONST_M1); else if (n == 0) mv.visitInsn(Opcodes.ICONST_0); else if (n == 1) mv.visitInsn(Opcodes.ICONST_1); else if (n == 2) mv.visitInsn(Opcodes.ICONST_2); else if (n == 3) mv.visitInsn(Opcodes.ICONST_3); else if (n == 4) mv.visitInsn(Opcodes.ICONST_4); else if (n == 5) mv.visitInsn(Opcodes.ICONST_5); else if (Byte.MIN_VALUE <= n && n <= Byte.MAX_VALUE) mv.visitIntInsn(Opcodes.BIPUSH, n); else if (Short.MIN_VALUE <= n && n <= Short.MAX_VALUE) mv.visitIntInsn(Opcodes.SIPUSH, n); else mv.visitLdcInsn(Data.toFixnum(n)); } else if (val instanceof Long) { long n = Data.javaNumber(val).longValue(); if (n == 0L) mv.visitInsn(Opcodes.LCONST_0); else if (n == 1L) mv.visitInsn(Opcodes.LCONST_1); else mv.visitLdcInsn(val); } else if (val instanceof Float) { float n = Data.javaNumber(val).floatValue(); if (n == 0.0f) mv.visitInsn(Opcodes.FCONST_0); else if (n == 1.0f) mv.visitInsn(Opcodes.FCONST_1); else if (n == 2.0f) mv.visitInsn(Opcodes.FCONST_2); else mv.visitLdcInsn(val); } else if (val instanceof Double) { double n = Data.javaNumber(val).doubleValue(); if (n == 0.0) mv.visitInsn(Opcodes.DCONST_0); else if (n == 1.0) mv.visitInsn(Opcodes.DCONST_1); else mv.visitLdcInsn(val); } else if (Data.isCharacter(val)) { Character c = Data.character(val); int n = (int) c.charValue(); if (Byte.MIN_VALUE <= n && n <= Byte.MAX_VALUE) mv.visitIntInsn(Opcodes.BIPUSH, n); else if (Short.MIN_VALUE <= n && n <= Short.MAX_VALUE) mv.visitIntInsn(Opcodes.SIPUSH, n); else mv.visitLdcInsn(Data.toFixnum(n)); } else if (Data.isString(val)) { mv.visitLdcInsn(val); } else { throw new NotReachedException("cannot push: ~S.", Lists.list(val)); } } else if (id == Insts.GET) { Field f = Data.javaField(Lists.cadr(inst)); String fieldName = f.getName(); Class c = f.getDeclaringClass(); String className = c.getName(); Class t = f.getType(); String typeName = t.getName(); boolean isStatic = Classes.isStatic(f); int op = isStatic ? Opcodes.GETSTATIC : Opcodes.GETFIELD; mv.visitFieldInsn(op, toInternalName(className), fieldName, toTypeDescriptor(typeName)); } else if (id == Insts.PUT) { Field f = Data.javaField(Lists.cadr(inst)); String fieldName = f.getName(); Class c = f.getDeclaringClass(); String className = c.getName(); Class t = f.getType(); String typeName = t.getName(); boolean isStatic = Classes.isStatic(f); int op = isStatic ? Opcodes.PUTSTATIC : Opcodes.PUTFIELD; mv.visitFieldInsn(op, toInternalName(className), fieldName, toTypeDescriptor(typeName)); } else if (id == Insts.INVOKE) { Method m = Data.javaMethod(Lists.cadr(inst)); String methodName = m.getName(); Class c = m.getDeclaringClass(); String className = c.getName(); Class rType = m.getReturnType(); Class[] pTypes = m.getParameterTypes(); if (rType.equals(void.class)) { throw new NotReachedException("unsupported returnType: ~S.", Lists.list(rType)); } Type retType = Type.getType(rType); Type[] paramTypes = new Type[pTypes.length]; for (int j = 0; j < pTypes.length; j++) paramTypes[j] = Type.getType(pTypes[j]); boolean isStatic = Classes.isStatic(m); boolean isInterface = c.isInterface(); int op = isStatic ? Opcodes.INVOKESTATIC : isInterface ? Opcodes.INVOKEINTERFACE : Opcodes.INVOKEVIRTUAL; mv.visitMethodInsn(op, toInternalName(className), methodName, Type.getMethodDescriptor(retType, paramTypes)); } else if (id == Insts.CHECKCAST) { Class c = Data.javaClass(Lists.cadr(inst)); Type t = Type.getType(c); mv.visitTypeInsn(Opcodes.CHECKCAST, t.getInternalName()); } else if (id == Insts.THROW) { mv.visitInsn(Opcodes.ATHROW); } else if (id == Insts.CATCH) { Symbol tagS = Data.symbol(Lists.cadr(inst)); Symbol tagE = Data.symbol(Lists.caddr(inst)); Symbol tagH = Data.symbol(Lists.cadddr(inst)); String className; if (Lists.isEnd(Lists.cddddr(inst))) { className = null; } else { Class c = Data.javaClass(Lists.car(Lists.cddddr(inst))); className = toInternalName(c.getName()); } Label labelS = (Label) ci.labelTable.get(tagS); if (labelS == null) { throw new NotReachedException("label not found: ~S.", Lists.list(tagS)); } Label labelE = (Label) ci.labelTable.get(tagE); if (labelE == null) { throw new NotReachedException("label not found: ~S.", Lists.list(tagE)); } Label labelH = (Label) ci.labelTable.get(tagH); if (labelH == null) { throw new NotReachedException("label not found: ~S.", Lists.list(tagH)); } mv.visitTryCatchBlock(labelS, labelE, labelH, className); } //else if (id == Insts.CATCH_FROM || // id == Insts.CATCH_TO || // id == Insts.CATCH_HANDLER) { // /* nothing emitted */ // continue; //} else { throw new NotReachedException("unknown inst: ~S.", Lists.list(inst)); } } mv.visitMaxs(0, 0); mv.visitEnd(); } private byte[] getBytes(Env env) { return _cw.toByteArray(); } }