Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
clojure.lang.Compiler Maven / Gradle / Ivy
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Aug 21, 2007 */
package clojure.lang;
//*
import clojure.asm.*;
import clojure.asm.Type;
import clojure.asm.commons.GeneratorAdapter;
import clojure.asm.commons.Method;
import java.io.*;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.Executable;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
//*/
/*
import org.objectweb.asm.*;
import org.objectweb.asm.commons.Method;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.util.TraceClassVisitor;
import org.objectweb.asm.util.CheckClassAdapter;
//*/
public class Compiler implements Opcodes{
static final Symbol DEF = Symbol.intern("def");
static final Symbol LOOP = Symbol.intern("loop*");
static final Symbol RECUR = Symbol.intern("recur");
static final Symbol IF = Symbol.intern("if");
static final Symbol LET = Symbol.intern("let*");
static final Symbol LETFN = Symbol.intern("letfn*");
static final Symbol DO = Symbol.intern("do");
static final Symbol FN = Symbol.intern("fn*");
static final Symbol FNONCE = (Symbol) Symbol.intern("fn*").withMeta(RT.map(Keyword.intern(null, "once"), RT.T));
static final Symbol QUOTE = Symbol.intern("quote");
static final Symbol THE_VAR = Symbol.intern("var");
static final Symbol DOT = Symbol.intern(".");
static final Symbol ASSIGN = Symbol.intern("set!");
//static final Symbol TRY_FINALLY = Symbol.intern("try-finally");
static final Symbol TRY = Symbol.intern("try");
static final Symbol CATCH = Symbol.intern("catch");
static final Symbol FINALLY = Symbol.intern("finally");
static final Symbol THROW = Symbol.intern("throw");
static final Symbol MONITOR_ENTER = Symbol.intern("monitor-enter");
static final Symbol MONITOR_EXIT = Symbol.intern("monitor-exit");
static final Symbol IMPORT = Symbol.intern("clojure.core", "import*");
//static final Symbol INSTANCE = Symbol.intern("instance?");
static final Symbol DEFTYPE = Symbol.intern("deftype*");
static final Symbol CASE = Symbol.intern("case*");
//static final Symbol THISFN = Symbol.intern("thisfn");
static final Symbol CLASS = Symbol.intern("Class");
static final Symbol NEW = Symbol.intern("new");
static final Symbol THIS = Symbol.intern("this");
static final Symbol REIFY = Symbol.intern("reify*");
//static final Symbol UNQUOTE = Symbol.intern("unquote");
//static final Symbol UNQUOTE_SPLICING = Symbol.intern("unquote-splicing");
//static final Symbol SYNTAX_QUOTE = Symbol.intern("clojure.core", "syntax-quote");
static final Symbol LIST = Symbol.intern("clojure.core", "list");
static final Symbol HASHMAP = Symbol.intern("clojure.core", "hash-map");
static final Symbol VECTOR = Symbol.intern("clojure.core", "vector");
static final Symbol IDENTITY = Symbol.intern("clojure.core", "identity");
static final Symbol _AMP_ = Symbol.intern("&");
static final Symbol ISEQ = Symbol.intern("clojure.lang.ISeq");
static final Keyword loadNs = Keyword.intern(null, "load-ns");
static final Keyword inlineKey = Keyword.intern(null, "inline");
static final Keyword inlineAritiesKey = Keyword.intern(null, "inline-arities");
static final Keyword staticKey = Keyword.intern(null, "static");
static final Keyword arglistsKey = Keyword.intern(null, "arglists");
static final Symbol INVOKE_STATIC = Symbol.intern("invokeStatic");
static final Keyword volatileKey = Keyword.intern(null, "volatile");
static final Keyword implementsKey = Keyword.intern(null, "implements");
static final String COMPILE_STUB_PREFIX = "compile__stub";
static final Keyword protocolKey = Keyword.intern(null, "protocol");
static final Keyword onKey = Keyword.intern(null, "on");
static Keyword dynamicKey = Keyword.intern("dynamic");
static final Keyword redefKey = Keyword.intern(null, "redef");
static final Symbol NS = Symbol.intern("ns");
static final Symbol IN_NS = Symbol.intern("in-ns");
//static final Symbol IMPORT = Symbol.intern("import");
//static final Symbol USE = Symbol.intern("use");
//static final Symbol IFN = Symbol.intern("clojure.lang", "IFn");
static final public IPersistentMap specials = PersistentHashMap.create(
DEF, new DefExpr.Parser(),
LOOP, new LetExpr.Parser(),
RECUR, new RecurExpr.Parser(),
IF, new IfExpr.Parser(),
CASE, new CaseExpr.Parser(),
LET, new LetExpr.Parser(),
LETFN, new LetFnExpr.Parser(),
DO, new BodyExpr.Parser(),
FN, null,
QUOTE, new ConstantExpr.Parser(),
THE_VAR, new TheVarExpr.Parser(),
IMPORT, new ImportExpr.Parser(),
DOT, new HostExpr.Parser(),
ASSIGN, new AssignExpr.Parser(),
DEFTYPE, new NewInstanceExpr.DeftypeParser(),
REIFY, new NewInstanceExpr.ReifyParser(),
// TRY_FINALLY, new TryFinallyExpr.Parser(),
TRY, new TryExpr.Parser(),
THROW, new ThrowExpr.Parser(),
MONITOR_ENTER, new MonitorEnterExpr.Parser(),
MONITOR_EXIT, new MonitorExitExpr.Parser(),
// INSTANCE, new InstanceExpr.Parser(),
// IDENTICAL, new IdenticalExpr.Parser(),
//THISFN, null,
CATCH, null,
FINALLY, null,
// CLASS, new ClassExpr.Parser(),
NEW, new NewExpr.Parser(),
// UNQUOTE, null,
// UNQUOTE_SPLICING, null,
// SYNTAX_QUOTE, null,
_AMP_, null
);
private static final int MAX_POSITIONAL_ARITY = 20;
private static final Type OBJECT_TYPE;
private static final Type KEYWORD_TYPE = Type.getType(Keyword.class);
private static final Type VAR_TYPE = Type.getType(Var.class);
private static final Type SYMBOL_TYPE = Type.getType(Symbol.class);
//private static final Type NUM_TYPE = Type.getType(Num.class);
private static final Type IFN_TYPE = Type.getType(IFn.class);
private static final Type AFUNCTION_TYPE = Type.getType(AFunction.class);
private static final Type RT_TYPE = Type.getType(RT.class);
private static final Type NUMBERS_TYPE = Type.getType(Numbers.class);
final static Type CLASS_TYPE = Type.getType(Class.class);
final static Type NS_TYPE = Type.getType(Namespace.class);
final static Type UTIL_TYPE = Type.getType(Util.class);
final static Type REFLECTOR_TYPE = Type.getType(Reflector.class);
final static Type THROWABLE_TYPE = Type.getType(Throwable.class);
final static Type BOOLEAN_OBJECT_TYPE = Type.getType(Boolean.class);
final static Type IPERSISTENTMAP_TYPE = Type.getType(IPersistentMap.class);
final static Type IOBJ_TYPE = Type.getType(IObj.class);
final static Type TUPLE_TYPE = Type.getType(Tuple.class);
final static Method createTupleMethods[] = {Method.getMethod("clojure.lang.IPersistentVector create()"),
Method.getMethod("clojure.lang.IPersistentVector create(Object)"),
Method.getMethod("clojure.lang.IPersistentVector create(Object,Object)"),
Method.getMethod("clojure.lang.IPersistentVector create(Object,Object,Object)"),
Method.getMethod("clojure.lang.IPersistentVector create(Object,Object,Object,Object)"),
Method.getMethod("clojure.lang.IPersistentVector create(Object,Object,Object,Object,Object)"),
Method.getMethod("clojure.lang.IPersistentVector create(Object,Object,Object,Object,Object,Object)")
};
private static final Type[][] ARG_TYPES;
//private static final Type[] EXCEPTION_TYPES = {Type.getType(Exception.class)};
private static final Type[] EXCEPTION_TYPES = {};
static
{
OBJECT_TYPE = Type.getType(Object.class);
ARG_TYPES = new Type[MAX_POSITIONAL_ARITY + 2][];
for(int i = 0; i <= MAX_POSITIONAL_ARITY; ++i)
{
Type[] a = new Type[i];
for(int j = 0; j < i; j++)
a[j] = OBJECT_TYPE;
ARG_TYPES[i] = a;
}
Type[] a = new Type[MAX_POSITIONAL_ARITY + 1];
for(int j = 0; j < MAX_POSITIONAL_ARITY; j++)
a[j] = OBJECT_TYPE;
a[MAX_POSITIONAL_ARITY] = Type.getType("[Ljava/lang/Object;");
ARG_TYPES[MAX_POSITIONAL_ARITY + 1] = a;
}
//symbol->localbinding
static final public Var LOCAL_ENV = Var.create(null).setDynamic();
//vector
static final public Var LOOP_LOCALS = Var.create().setDynamic();
//Label
static final public Var LOOP_LABEL = Var.create().setDynamic();
//vector
static final public Var CONSTANTS = Var.create().setDynamic();
//IdentityHashMap
static final public Var CONSTANT_IDS = Var.create().setDynamic();
//vector
static final public Var KEYWORD_CALLSITES = Var.create().setDynamic();
//vector
static final public Var PROTOCOL_CALLSITES = Var.create().setDynamic();
//set
static final public Var VAR_CALLSITES = Var.create().setDynamic();
//keyword->constid
static final public Var KEYWORDS = Var.create().setDynamic();
//var->constid
static final public Var VARS = Var.create().setDynamic();
//FnFrame
static final public Var METHOD = Var.create(null).setDynamic();
//null or not
static final public Var IN_CATCH_FINALLY = Var.create(null).setDynamic();
static final public Var METHOD_RETURN_CONTEXT = Var.create(null).setDynamic();
static final public Var NO_RECUR = Var.create(null).setDynamic();
//DynamicClassLoader
static final public Var LOADER = Var.create().setDynamic();
//String
static final public Var SOURCE = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*source-path*"), "NO_SOURCE_FILE").setDynamic();
//String
static final public Var SOURCE_PATH = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*file*"), "NO_SOURCE_PATH").setDynamic();
//String
static final public Var COMPILE_PATH = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*compile-path*"), null).setDynamic();
//boolean
static final public Var COMPILE_FILES = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*compile-files*"), Boolean.FALSE).setDynamic();
static final public Var INSTANCE = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("instance?"));
static final public Var ADD_ANNOTATIONS = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("add-annotations"));
static final public Keyword disableLocalsClearingKey = Keyword.intern("disable-locals-clearing");
static final public Keyword directLinkingKey = Keyword.intern("direct-linking");
static final public Keyword elideMetaKey = Keyword.intern("elide-meta");
static final public Var COMPILER_OPTIONS;
static public Object getCompilerOption(Keyword k){
return RT.get(COMPILER_OPTIONS.deref(),k);
}
static
{
Object compilerOptions = null;
for (Map.Entry e : System.getProperties().entrySet())
{
String name = (String) e.getKey();
String v = (String) e.getValue();
if (name.startsWith("clojure.compiler."))
{
compilerOptions = RT.assoc(compilerOptions,
RT.keyword(null, name.substring(1 + name.lastIndexOf('.'))),
RT.readString(v));
}
}
COMPILER_OPTIONS = Var.intern(Namespace.findOrCreate(Symbol.intern("clojure.core")),
Symbol.intern("*compiler-options*"), compilerOptions).setDynamic();
}
static Object elideMeta(Object m){
Collection elides = (Collection) getCompilerOption(elideMetaKey);
if(elides != null)
{
for(Object k : elides)
{
// System.out.println("Eliding:" + k + " : " + RT.get(m, k));
m = RT.dissoc(m, k);
}
// System.out.println("Remaining: " + RT.keys(m));
}
return m;
}
//Integer
static final public Var LINE = Var.create(0).setDynamic();
static final public Var COLUMN = Var.create(0).setDynamic();
static int lineDeref(){
return ((Number)LINE.deref()).intValue();
}
static int columnDeref(){
return ((Number)COLUMN.deref()).intValue();
}
//Integer
static final public Var LINE_BEFORE = Var.create(0).setDynamic();
static final public Var COLUMN_BEFORE = Var.create(0).setDynamic();
static final public Var LINE_AFTER = Var.create(0).setDynamic();
static final public Var COLUMN_AFTER = Var.create(0).setDynamic();
//Integer
static final public Var NEXT_LOCAL_NUM = Var.create(0).setDynamic();
//Integer
static final public Var RET_LOCAL_NUM = Var.create().setDynamic();
static final public Var COMPILE_STUB_SYM = Var.create(null).setDynamic();
static final public Var COMPILE_STUB_CLASS = Var.create(null).setDynamic();
//PathNode chain
static final public Var CLEAR_PATH = Var.create(null).setDynamic();
//tail of PathNode chain
static final public Var CLEAR_ROOT = Var.create(null).setDynamic();
//LocalBinding -> Set
static final public Var CLEAR_SITES = Var.create(null).setDynamic();
public enum C{
STATEMENT, //value ignored
EXPRESSION, //value required
RETURN, //tail position relative to enclosing recur frame
EVAL
}
private class Recur {};
static final public Class RECUR_CLASS = Recur.class;
interface Expr{
Object eval() ;
void emit(C context, ObjExpr objx, GeneratorAdapter gen);
boolean hasJavaClass() ;
Class getJavaClass() ;
}
public static abstract class UntypedExpr implements Expr{
public Class getJavaClass(){
throw new IllegalArgumentException("Has no Java class");
}
public boolean hasJavaClass(){
return false;
}
}
interface IParser{
Expr parse(C context, Object form) ;
}
static boolean isSpecial(Object sym){
return specials.containsKey(sym);
}
static boolean inTailCall(C context) {
return (context == C.RETURN) && (METHOD_RETURN_CONTEXT.deref() != null) && (IN_CATCH_FINALLY.deref() == null);
}
static Symbol resolveSymbol(Symbol sym){
//already qualified or classname?
if(sym.name.indexOf('.') > 0)
return sym;
if(sym.ns != null)
{
Namespace ns = namespaceFor(sym);
if(ns == null || (ns.name.name == null ? sym.ns == null : ns.name.name.equals(sym.ns)))
{
Class ac = HostExpr.maybeArrayClass(sym);
if(ac != null)
return Util.arrayTypeToSymbol(ac);
return sym;
}
return Symbol.intern(ns.name.name, sym.name);
}
Object o = currentNS().getMapping(sym);
if(o == null)
return Symbol.intern(currentNS().name.name, sym.name);
else if(o instanceof Class)
return Symbol.intern(null, ((Class) o).getName());
else if(o instanceof Var)
{
Var v = (Var) o;
return Symbol.intern(v.ns.name.name, v.sym.name);
}
return null;
}
static class DefExpr implements Expr{
public final Var var;
public final Expr init;
public final Expr meta;
public final boolean initProvided;
public final boolean isDynamic;
public final String source;
public final int line;
public final int column;
final static Method bindRootMethod = Method.getMethod("void bindRoot(Object)");
final static Method setTagMethod = Method.getMethod("void setTag(clojure.lang.Symbol)");
final static Method setMetaMethod = Method.getMethod("void setMeta(clojure.lang.IPersistentMap)");
final static Method setDynamicMethod = Method.getMethod("clojure.lang.Var setDynamic(boolean)");
final static Method symintern = Method.getMethod("clojure.lang.Symbol intern(String, String)");
public DefExpr(String source, int line, int column, Var var, Expr init, Expr meta, boolean initProvided, boolean isDynamic){
this.source = source;
this.line = line;
this.column = column;
this.var = var;
this.init = init;
this.meta = meta;
this.isDynamic = isDynamic;
this.initProvided = initProvided;
}
private boolean includesExplicitMetadata(MapExpr expr) {
for(int i=0; i < expr.keyvals.count(); i += 2)
{
Keyword k = ((KeywordExpr) expr.keyvals.nth(i)).k;
if ((k != RT.FILE_KEY) &&
(k != RT.DECLARED_KEY) &&
(k != RT.LINE_KEY) &&
(k != RT.COLUMN_KEY))
return true;
}
return false;
}
public Object eval() {
try
{
if(initProvided)
{
// if(init instanceof FnExpr && ((FnExpr) init).closes.count()==0)
// var.bindRoot(new FnLoaderThunk((FnExpr) init,var));
// else
var.bindRoot(init.eval());
}
if(meta != null)
{
IPersistentMap metaMap = (IPersistentMap) meta.eval();
if (initProvided || true)//includesExplicitMetadata((MapExpr) meta))
var.setMeta(metaMap);
}
return var.setDynamic(isDynamic);
}
catch(Throwable e)
{
if(!(e instanceof CompilerException))
throw new CompilerException(source, line, column, Compiler.DEF, CompilerException.PHASE_EXECUTION, e);
else
throw (CompilerException) e;
}
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
objx.emitVar(gen, var);
if(isDynamic)
{
gen.push(isDynamic);
gen.invokeVirtual(VAR_TYPE, setDynamicMethod);
}
if(meta != null)
{
if (initProvided || true)//includesExplicitMetadata((MapExpr) meta))
{
gen.dup();
meta.emit(C.EXPRESSION, objx, gen);
gen.checkCast(IPERSISTENTMAP_TYPE);
gen.invokeVirtual(VAR_TYPE, setMetaMethod);
}
}
if(initProvided)
{
gen.dup();
if(init instanceof FnExpr)
{
((FnExpr)init).emitForDefn(objx, gen);
}
else
init.emit(C.EXPRESSION, objx, gen);
gen.invokeVirtual(VAR_TYPE, bindRootMethod);
}
if(context == C.STATEMENT)
gen.pop();
}
public boolean hasJavaClass(){
return true;
}
public Class getJavaClass(){
return Var.class;
}
static class Parser implements IParser{
public Expr parse(C context, Object form) {
//(def x) or (def x initexpr) or (def x "docstring" initexpr)
String docstring = null;
if(RT.count(form) == 4 && (RT.third(form) instanceof String)) {
docstring = (String) RT.third(form);
form = RT.list(RT.first(form), RT.second(form), RT.fourth(form));
}
if(RT.count(form) > 3)
throw Util.runtimeException("Too many arguments to def");
else if(RT.count(form) < 2)
throw Util.runtimeException("Too few arguments to def");
else if(!(RT.second(form) instanceof Symbol))
throw Util.runtimeException("First argument to def must be a Symbol");
Symbol sym = (Symbol) RT.second(form);
Var v = lookupVar(sym, true);
if(v == null)
throw Util.runtimeException("Can't refer to qualified var that doesn't exist");
if(!v.ns.equals(currentNS()))
{
if(sym.ns == null)
{
v = currentNS().intern(sym);
registerVar(v);
}
// throw Util.runtimeException("Name conflict, can't def " + sym + " because namespace: " + currentNS().name +
// " refers to:" + v);
else
throw Util.runtimeException("Can't create defs outside of current ns");
}
IPersistentMap mm = sym.meta();
boolean isDynamic = RT.booleanCast(RT.get(mm,dynamicKey));
if(isDynamic)
v.setDynamic();
if(!isDynamic && sym.name.startsWith("*") && sym.name.endsWith("*") && sym.name.length() > 2)
{
RT.errPrintWriter().format("Warning: %1$s not declared dynamic and thus is not dynamically rebindable, "
+"but its name suggests otherwise. Please either indicate ^:dynamic %1$s or change the name. (%2$s:%3$d)\n",
sym, SOURCE_PATH.get(), LINE.get());
}
if(RT.booleanCast(RT.get(mm, arglistsKey)))
{
IPersistentMap vm = v.meta();
//vm = (IPersistentMap) RT.assoc(vm,staticKey,RT.T);
//drop quote
vm = (IPersistentMap) RT.assoc(vm,arglistsKey,RT.second(mm.valAt(arglistsKey)));
v.setMeta(vm);
}
Object source_path = SOURCE_PATH.get();
source_path = source_path == null ? "NO_SOURCE_FILE" : source_path;
mm = (IPersistentMap) RT.assoc(mm, RT.LINE_KEY, LINE.get()).assoc(RT.COLUMN_KEY, COLUMN.get()).assoc(RT.FILE_KEY, source_path);
if (docstring != null)
mm = (IPersistentMap) RT.assoc(mm, RT.DOC_KEY, docstring);
// mm = mm.without(RT.DOC_KEY)
// .without(Keyword.intern(null, "arglists"))
// .without(RT.FILE_KEY)
// .without(RT.LINE_KEY)
// .without(RT.COLUMN_KEY)
// .without(Keyword.intern(null, "ns"))
// .without(Keyword.intern(null, "name"))
// .without(Keyword.intern(null, "added"))
// .without(Keyword.intern(null, "static"));
mm = (IPersistentMap) elideMeta(mm);
Expr meta = mm.count()==0 ? null:analyze(context == C.EVAL ? context : C.EXPRESSION, mm);
return new DefExpr((String) SOURCE.deref(), lineDeref(), columnDeref(),
v, analyze(context == C.EVAL ? context : C.EXPRESSION, RT.third(form), v.sym.name),
meta, RT.count(form) == 3, isDynamic);
}
}
}
public static class AssignExpr implements Expr{
public final AssignableExpr target;
public final Expr val;
public AssignExpr(AssignableExpr target, Expr val){
this.target = target;
this.val = val;
}
public Object eval() {
return target.evalAssign(val);
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
target.emitAssign(context, objx, gen, val);
}
public boolean hasJavaClass() {
return val.hasJavaClass();
}
public Class getJavaClass() {
return val.getJavaClass();
}
static class Parser implements IParser{
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
if(RT.length(form) != 3)
throw new IllegalArgumentException("Malformed assignment, expecting (set! target val)");
Expr target = analyze(C.EXPRESSION, RT.second(form));
if(!(target instanceof AssignableExpr))
throw new IllegalArgumentException("Invalid assignment target");
return new AssignExpr((AssignableExpr) target, analyze(C.EXPRESSION, RT.third(form)));
}
}
}
public static class VarExpr implements Expr, AssignableExpr{
public final Var var;
public final Object tag;
final static Method getMethod = Method.getMethod("Object get()");
final static Method setMethod = Method.getMethod("Object set(Object)");
Class jc;
public VarExpr(Var var, Symbol tag){
this.var = var;
this.tag = tag != null ? tag : var.getTag();
}
public Object eval() {
return var.deref();
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
objx.emitVarValue(gen,var);
if(context == C.STATEMENT)
{
gen.pop();
}
}
public boolean hasJavaClass(){
return tag != null;
}
public Class getJavaClass() {
if (jc == null)
jc = HostExpr.tagToClass(tag);
return jc;
}
public Object evalAssign(Expr val) {
return var.set(val.eval());
}
public void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen,
Expr val){
objx.emitVar(gen, var);
val.emit(C.EXPRESSION, objx, gen);
gen.invokeVirtual(VAR_TYPE, setMethod);
if(context == C.STATEMENT)
gen.pop();
}
}
public static class TheVarExpr implements Expr{
public final Var var;
public TheVarExpr(Var var){
this.var = var;
}
public Object eval() {
return var;
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
objx.emitVar(gen, var);
if(context == C.STATEMENT)
gen.pop();
}
public boolean hasJavaClass(){
return true;
}
public Class getJavaClass() {
return Var.class;
}
static class Parser implements IParser{
public Expr parse(C context, Object form) {
Symbol sym = (Symbol) RT.second(form);
Var v = lookupVar(sym, false);
if(v != null)
return new TheVarExpr(v);
throw Util.runtimeException("Unable to resolve var: " + sym + " in this context");
}
}
}
public static class KeywordExpr extends LiteralExpr{
public final Keyword k;
public KeywordExpr(Keyword k){
this.k = k;
}
Object val(){
return k;
}
public Object eval() {
return k;
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
objx.emitKeyword(gen, k);
if(context == C.STATEMENT)
gen.pop();
}
public boolean hasJavaClass(){
return true;
}
public Class getJavaClass() {
return Keyword.class;
}
}
public static class ImportExpr implements Expr{
public final String c;
final static Method forNameMethod = Method.getMethod("Class classForNameNonLoading(String)");
final static Method importClassMethod = Method.getMethod("Class importClass(Class)");
final static Method derefMethod = Method.getMethod("Object deref()");
public ImportExpr(String c){
this.c = c;
}
public Object eval() {
Namespace ns = (Namespace) RT.CURRENT_NS.deref();
ns.importClass(RT.classForNameNonLoading(c));
return null;
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
gen.getStatic(RT_TYPE,"CURRENT_NS",VAR_TYPE);
gen.invokeVirtual(VAR_TYPE, derefMethod);
gen.checkCast(NS_TYPE);
gen.push(c);
gen.invokeStatic(RT_TYPE, forNameMethod);
gen.invokeVirtual(NS_TYPE, importClassMethod);
if(context == C.STATEMENT)
gen.pop();
}
public boolean hasJavaClass(){
return false;
}
public Class getJavaClass() {
throw new IllegalArgumentException("ImportExpr has no Java class");
}
static class Parser implements IParser{
public Expr parse(C context, Object form) {
return new ImportExpr((String) RT.second(form));
}
}
}
public static abstract class LiteralExpr implements Expr{
abstract Object val();
public Object eval(){
return val();
}
}
static interface AssignableExpr{
Object evalAssign(Expr val) ;
void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen, Expr val);
}
static public interface MaybePrimitiveExpr extends Expr{
public boolean canEmitPrimitive();
public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen);
}
static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{
final static Type BOOLEAN_TYPE = Type.getType(Boolean.class);
final static Type CHAR_TYPE = Type.getType(Character.class);
final static Type INTEGER_TYPE = Type.getType(Integer.class);
final static Type LONG_TYPE = Type.getType(Long.class);
final static Type FLOAT_TYPE = Type.getType(Float.class);
final static Type DOUBLE_TYPE = Type.getType(Double.class);
final static Type SHORT_TYPE = Type.getType(Short.class);
final static Type BYTE_TYPE = Type.getType(Byte.class);
final static Type NUMBER_TYPE = Type.getType(Number.class);
final static Method charValueMethod = Method.getMethod("char charValue()");
final static Method booleanValueMethod = Method.getMethod("boolean booleanValue()");
final static Method charValueOfMethod = Method.getMethod("Character valueOf(char)");
final static Method intValueOfMethod = Method.getMethod("Integer valueOf(int)");
final static Method longValueOfMethod = Method.getMethod("Long valueOf(long)");
final static Method floatValueOfMethod = Method.getMethod("Float valueOf(float)");
final static Method doubleValueOfMethod = Method.getMethod("Double valueOf(double)");
final static Method shortValueOfMethod = Method.getMethod("Short valueOf(short)");
final static Method byteValueOfMethod = Method.getMethod("Byte valueOf(byte)");
final static Method intValueMethod = Method.getMethod("int intValue()");
final static Method longValueMethod = Method.getMethod("long longValue()");
final static Method floatValueMethod = Method.getMethod("float floatValue()");
final static Method doubleValueMethod = Method.getMethod("double doubleValue()");
final static Method byteValueMethod = Method.getMethod("byte byteValue()");
final static Method shortValueMethod = Method.getMethod("short shortValue()");
final static Method fromIntMethod = Method.getMethod("clojure.lang.Num from(int)");
final static Method fromLongMethod = Method.getMethod("clojure.lang.Num from(long)");
final static Method fromDoubleMethod = Method.getMethod("clojure.lang.Num from(double)");
//*
public static void emitBoxReturn(ObjExpr objx, GeneratorAdapter gen, Class returnType){
if(returnType.isPrimitive())
{
if(returnType == boolean.class)
{
Label falseLabel = gen.newLabel();
Label endLabel = gen.newLabel();
gen.ifZCmp(GeneratorAdapter.EQ, falseLabel);
gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE);
gen.goTo(endLabel);
gen.mark(falseLabel);
gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);
// NIL_EXPR.emit(C.EXPRESSION, fn, gen);
gen.mark(endLabel);
}
else if(returnType == void.class)
{
NIL_EXPR.emit(C.EXPRESSION, objx, gen);
}
else if(returnType == char.class)
{
gen.invokeStatic(CHAR_TYPE, charValueOfMethod);
}
else
{
if(returnType == int.class)
{
gen.invokeStatic(INTEGER_TYPE, intValueOfMethod);
// gen.visitInsn(I2L);
// gen.invokeStatic(NUMBERS_TYPE, Method.getMethod("Number num(long)"));
}
else if(returnType == float.class)
{
gen.invokeStatic(FLOAT_TYPE, floatValueOfMethod);
// gen.visitInsn(F2D);
// gen.invokeStatic(DOUBLE_TYPE, doubleValueOfMethod);
}
else if(returnType == double.class)
gen.invokeStatic(DOUBLE_TYPE, doubleValueOfMethod);
else if(returnType == long.class)
gen.invokeStatic(NUMBERS_TYPE, Method.getMethod("Number num(long)"));
else if(returnType == byte.class)
gen.invokeStatic(BYTE_TYPE, byteValueOfMethod);
else if(returnType == short.class)
gen.invokeStatic(SHORT_TYPE, shortValueOfMethod);
}
}
}
//*/
public static void emitUnboxArg(ObjExpr objx, GeneratorAdapter gen, Class paramType){
if(paramType.isPrimitive())
{
if(paramType == boolean.class)
{
gen.checkCast(BOOLEAN_TYPE);
gen.invokeVirtual(BOOLEAN_TYPE, booleanValueMethod);
// Label falseLabel = gen.newLabel();
// Label endLabel = gen.newLabel();
// gen.ifNull(falseLabel);
// gen.push(1);
// gen.goTo(endLabel);
// gen.mark(falseLabel);
// gen.push(0);
// gen.mark(endLabel);
}
else if(paramType == char.class)
{
gen.checkCast(CHAR_TYPE);
gen.invokeVirtual(CHAR_TYPE, charValueMethod);
}
else
{
// System.out.println("NOT fnexpr for defn var: " + var + "init: " + init.getClass());
Method m = null;
gen.checkCast(NUMBER_TYPE);
if(RT.booleanCast(RT.UNCHECKED_MATH.deref()))
{
if(paramType == int.class)
m = Method.getMethod("int uncheckedIntCast(Object)");
else if(paramType == float.class)
m = Method.getMethod("float uncheckedFloatCast(Object)");
else if(paramType == double.class)
m = Method.getMethod("double uncheckedDoubleCast(Object)");
else if(paramType == long.class)
m = Method.getMethod("long uncheckedLongCast(Object)");
else if(paramType == byte.class)
m = Method.getMethod("byte uncheckedByteCast(Object)");
else if(paramType == short.class)
m = Method.getMethod("short uncheckedShortCast(Object)");
}
else
{
if(paramType == int.class)
m = Method.getMethod("int intCast(Object)");
else if(paramType == float.class)
m = Method.getMethod("float floatCast(Object)");
else if(paramType == double.class)
m = Method.getMethod("double doubleCast(Object)");
else if(paramType == long.class)
m = Method.getMethod("long longCast(Object)");
else if(paramType == byte.class)
m = Method.getMethod("byte byteCast(Object)");
else if(paramType == short.class)
m = Method.getMethod("short shortCast(Object)");
}
gen.invokeStatic(RT_TYPE, m);
}
}
else
{
gen.checkCast(Type.getType(paramType));
}
}
static class Parser implements IParser{
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
//(. x fieldname-sym) or
//(. x 0-ary-method)
// (. x methodname-sym args+)
// (. x (methodname-sym args?))
if(RT.length(form) < 3)
throw new IllegalArgumentException("Malformed member expression, expecting (. target member ...)");
//determine static or instance
//static target must be symbol, either fully.qualified.Classname or Classname that has been imported
int line = lineDeref();
int column = columnDeref();
String source = (String) SOURCE.deref();
Class c = maybeClass(RT.second(form), false);
//at this point c will be non-null if static
Expr instance = null;
if(c == null)
instance = analyze(context == C.EVAL ? context : C.EXPRESSION, RT.second(form));
boolean maybeField = RT.length(form) == 3 && (RT.third(form) instanceof Symbol);
if(maybeField && !(((Symbol)RT.third(form)).name.charAt(0) == '-'))
{
Symbol sym = (Symbol) RT.third(form);
if(c != null)
maybeField = Reflector.getMethods(c, 0, munge(sym.name), true).size() == 0;
else if(instance != null && instance.hasJavaClass() && instance.getJavaClass() != null)
maybeField = Reflector.getMethods(instance.getJavaClass(), 0, munge(sym.name), false).size() == 0;
}
if(maybeField) //field
{
Symbol sym = (((Symbol)RT.third(form)).name.charAt(0) == '-') ?
Symbol.intern(((Symbol)RT.third(form)).name.substring(1))
:(Symbol) RT.third(form);
Symbol tag = tagOf(form);
if(c != null) {
return new StaticFieldExpr(line, column, c, munge(sym.name), tag);
} else
return new InstanceFieldExpr(line, column, instance, munge(sym.name), tag, (((Symbol)RT.third(form)).name.charAt(0) == '-'));
}
else
{
ISeq call = (ISeq) ((RT.third(form) instanceof ISeq) ? RT.third(form) : RT.next(RT.next(form)));
if(!(RT.first(call) instanceof Symbol))
throw new IllegalArgumentException("Malformed member expression");
Symbol sym = (Symbol) RT.first(call);
Symbol tag = tagOf(form);
PersistentVector args = PersistentVector.EMPTY;
boolean tailPosition = inTailCall(context);
for(ISeq s = RT.next(call); s != null; s = s.next())
args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first()));
if(c != null)
return new StaticMethodExpr(source, line, column, tag, c, munge(sym.name), args, tailPosition);
else
return new InstanceMethodExpr(source, line, column, tag, instance, null, munge(sym.name), args, tailPosition);
}
}
}
public static Class maybeClass(Object form, boolean stringOk) {
if(form instanceof Class)
return (Class) form;
Class c = null;
if(form instanceof Symbol)
{
Symbol sym = (Symbol) form;
if(sym.ns == null) //if ns-qualified can't be classname
{
if(Util.equals(sym,COMPILE_STUB_SYM.get()))
return (Class) COMPILE_STUB_CLASS.get();
if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')
c = RT.classForNameNonLoading(sym.name);
else
{
Object o = currentNS().getMapping(sym);
if(o instanceof Class)
c = (Class) o;
else if(LOCAL_ENV.deref() != null && ((java.util.Map)LOCAL_ENV.deref()).containsKey(form))
return null;
else
{
try{
c = RT.classForNameNonLoading(sym.name);
}
catch(Exception e){
// aargh
// leave c set to null -> return null
}
}
}
}
}
else if(stringOk && form instanceof String)
c = RT.classForNameNonLoading((String) form);
return c;
}
/*
private static String maybeClassName(Object form, boolean stringOk){
String className = null;
if(form instanceof Symbol)
{
Symbol sym = (Symbol) form;
if(sym.ns == null) //if ns-qualified can't be classname
{
if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')
className = sym.name;
else
{
IPersistentMap imports = (IPersistentMap) ((Var) RT.NS_IMPORTS.get()).get();
className = (String) imports.valAt(sym);
}
}
}
else if(stringOk && form instanceof String)
className = (String) form;
return className;
}
*/
public static Class maybeSpecialTag(Symbol sym) {
Class c = primClass(sym);
if (c != null)
return c;
else if(sym.name.equals("objects"))
c = Object[].class;
else if(sym.name.equals("ints"))
c = int[].class;
else if(sym.name.equals("longs"))
c = long[].class;
else if(sym.name.equals("floats"))
c = float[].class;
else if(sym.name.equals("doubles"))
c = double[].class;
else if(sym.name.equals("chars"))
c = char[].class;
else if(sym.name.equals("shorts"))
c = short[].class;
else if(sym.name.equals("bytes"))
c = byte[].class;
else if(sym.name.equals("booleans"))
c = boolean[].class;
return c;
}
static Class tagToClass(Object tag) {
Class c = null;
if(tag instanceof Symbol)
{
Symbol sym = (Symbol) tag;
if(sym.ns == null)
{
c = maybeSpecialTag(sym);
}
if(c == null)
c = HostExpr.maybeArrayClass(sym);
}
if(c == null)
c = maybeClass(tag, true);
if(c != null)
return c;
throw new IllegalArgumentException("Unable to resolve classname: " + tag);
}
static Class maybeArrayClass(Symbol sym) {
if(sym.ns == null || !Util.isPosDigit(sym.name))
return null;
int dim = sym.name.charAt(0) - '0';
Symbol componentClassName = Symbol.intern(null, sym.ns);
Class componentClass = primClass(componentClassName);
if(componentClass == null)
componentClass = maybeClass(componentClassName, false);
if(componentClass == null)
throw Util.sneakyThrow(new ClassNotFoundException("Unable to resolve component classname: "
+ componentClassName));
StringBuilder arrayDescriptor = new StringBuilder();
for(int i=0; i hintedSig;
private final Symbol methodSymbol;
private final String methodName;
private final MethodKind kind;
private enum MethodKind {
CTOR, INSTANCE, STATIC
}
public QualifiedMethodExpr(Class methodClass, Symbol sym){
c = methodClass;
methodSymbol = sym;
hintedSig = tagsToClasses(paramTagsOf(sym));
if(sym.name.startsWith(".")) {
kind = MethodKind.INSTANCE;
methodName = sym.name.substring(1);
}
else if(sym.name.equals("new")) {
kind = MethodKind.CTOR;
methodName = sym.name;
}
else {
kind = MethodKind.STATIC;
methodName = sym.name;
}
}
// Expr impl - invocation, convert to fn expr
@Override
public Object eval() {
return buildThunk(C.EVAL, this).eval();
}
@Override
public void emit(C context, ObjExpr objx, GeneratorAdapter gen) {
buildThunk(context, this).emit(context, objx, gen);
}
// Expr impl - method value, always an AFn
@Override
public boolean hasJavaClass() {
return true;
}
@Override
public Class getJavaClass() {
return AFn.class;
}
// TBD: caching/reuse of thunks
private static FnExpr buildThunk(C context, QualifiedMethodExpr qmexpr) {
// When qualified symbol has param-tags:
// (fn invoke__Class_meth ([this? args*] (methodSymbol this? args*)))
// When no param-tags:
// (fn invoke__Class_meth ([this?] (methodSymbol this?))
// ([this? arg1] (methodSymbol this? arg1)) ...)
IPersistentCollection form = PersistentVector.EMPTY;
Symbol instanceParam = qmexpr.kind == MethodKind.INSTANCE ? THIS : null;
String thunkName = "invoke__" + qmexpr.c.getSimpleName() + "_" + qmexpr.methodSymbol.name;
Set arities = (qmexpr.hintedSig != null) ? PersistentHashSet.create(qmexpr.hintedSig.size())
: aritySet(qmexpr.c, qmexpr.methodName, qmexpr.kind);
for(Object a : arities) {
int arity = (int) a;
IPersistentVector params = buildParams(instanceParam, arity);
ISeq body = RT.listStar(qmexpr.methodSymbol, params.seq());
form = RT.conj(form, RT.list(params, body));
}
ISeq thunkForm = RT.listStar(Symbol.intern("fn"), Symbol.intern(thunkName), RT.seq(form));
return (FnExpr) analyzeSeq(context, thunkForm, thunkName);
}
private static IPersistentVector buildParams(Symbol instanceParam, int arity) {
IPersistentVector params = PersistentVector.EMPTY;
if(instanceParam != null) params = params.cons(instanceParam);
for(int i = 0; i();
List methods = methodsWithName(c, methodName, kind);
for(Executable exec : methods)
res.add(exec.getParameterCount());
return res;
}
// Returns a list of methods or ctors matching the name and kind given.
// Otherwise, will throw if the information provided results in no matches
private static List methodsWithName(Class c, String methodName, MethodKind kind) {
if (kind == MethodKind.CTOR) {
List ctors = Arrays.asList(c.getConstructors());
if(ctors.isEmpty())
throw noMethodWithNameException(c, methodName, kind);
return ctors;
}
final Executable[] methods = c.getMethods();
List res = Arrays.stream(methods)
.filter(m -> m.getName().equals(methodName))
.filter(m -> {
switch(kind) {
case STATIC: return isStaticMethod(m);
case INSTANCE: return isInstanceMethod(m);
default: return false;
}
})
.collect(Collectors.toList());
if(res.isEmpty())
throw noMethodWithNameException(c, methodName, kind);
return res;
}
static Executable resolveHintedMethod(Class c, String methodName, MethodKind kind, List hintedSig) {
List methods = methodsWithName(c, methodName, kind);
final int arity = hintedSig.size();
List filteredMethods = methods.stream()
.filter(m -> m.getParameterCount() == arity)
.filter(m -> !m.isSynthetic()) // remove bridge/lambda methods
.filter(m -> signatureMatches(hintedSig, m))
.collect(Collectors.toList());
if(filteredMethods.size() == 1)
return filteredMethods.get(0);
else
throw paramTagsDontResolveException(c, methodName, hintedSig);
}
static IllegalArgumentException noMethodWithNameException(Class c, String methodName, MethodKind kind) {
return new IllegalArgumentException("Error - no matches found for "
+ (kind != MethodKind.CTOR ? kind.toString().toLowerCase() + " " : "")
+ methodDescription(c, methodName));
}
static IllegalArgumentException paramTagsDontResolveException(Class c, String methodName, List hintedSig) {
IPersistentVector paramTags = PersistentVector.create(hintedSig.stream()
.map(tag -> tag == null ? PARAM_TAG_ANY : tag)
.collect(Collectors.toList()));
return new IllegalArgumentException("Error - param-tags " + paramTags
+ " insufficient to resolve "
+ methodDescription(c, methodName));
}
}
final static Symbol PARAM_TAG_ANY = Symbol.intern(null, "_");
private static IPersistentVector paramTagsOf(Symbol sym){
Object paramTags = RT.get(RT.meta(sym), RT.PARAM_TAGS_KEY);
if(paramTags != null && !(paramTags instanceof IPersistentVector))
throw new IllegalArgumentException("param-tags of symbol "
+ sym + " should be a vector.");
return (IPersistentVector) paramTags;
}
// calls tagToClass on every element, unless it encounters _ which becomes null
private static List tagsToClasses(IPersistentVector paramTags) {
if(paramTags == null) return null;
List sig = new ArrayList<>();
for (ISeq s = RT.seq(paramTags); s!=null; s = s.next()) {
Object t = s.first();
if (t.equals(PARAM_TAG_ANY))
sig.add(null);
else
sig.add(HostExpr.tagToClass(t));
}
return sig;
}
private static boolean signatureMatches(List sig, Executable method)
{
Class[] methodSig = method.getParameterTypes();
if(methodSig.length != sig.size()) return false;
for (int i = 0; i < methodSig.length; i++)
if (sig.get(i) != null && !sig.get(i).equals(methodSig[i]))
return false;
return true;
};
static boolean isStaticMethod(Executable method) {
return method instanceof java.lang.reflect.Method && Modifier.isStatic(method.getModifiers());
}
static boolean isInstanceMethod(Executable method) {
return method instanceof java.lang.reflect.Method && !Modifier.isStatic(method.getModifiers());
}
static boolean isConstructor(Executable method) {
return method instanceof Constructor;
}
private static void checkMethodArity(Executable method, int argCount) {
if(method.getParameterCount() != argCount)
throw new IllegalArgumentException("Invocation of "
+ methodDescription(method.getDeclaringClass(),
(method instanceof Constructor) ? "new" : method.getName())
+ " expected " + method.getParameterCount() + " arguments, but received " + argCount);
}
private static String methodDescription(Class c, String methodName) {
boolean isCtor = c != null && methodName.equals("new");
String type = isCtor ? "constructor" : "method";
return type + (isCtor ? "" : " " + methodName) + " in class " + c.getName();
}
static abstract class FieldExpr extends HostExpr{
}
static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{
public final Expr target;
public final Class targetClass;
public final java.lang.reflect.Field field;
public final String fieldName;
public final int line;
public final int column;
public final Symbol tag;
public final boolean requireField;
final static Method invokeNoArgInstanceMember = Method.getMethod("Object invokeNoArgInstanceMember(Object,String,boolean)");
final static Method setInstanceFieldMethod = Method.getMethod("Object setInstanceField(Object,String,Object)");
Class jc;
public InstanceFieldExpr(int line, int column, Expr target, String fieldName, Symbol tag, boolean requireField) {
this.target = target;
this.targetClass = target.hasJavaClass() ? target.getJavaClass() : null;
this.field = targetClass != null ? Reflector.getField(targetClass, fieldName, false) : null;
this.fieldName = fieldName;
this.line = line;
this.column = column;
this.tag = tag;
this.requireField = requireField;
if(field == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
{
if(targetClass == null)
{
RT.errPrintWriter()
.format("Reflection warning, %s:%d:%d - reference to field %s can't be resolved.\n",
SOURCE_PATH.deref(), line, column, fieldName);
}
else
{
RT.errPrintWriter()
.format("Reflection warning, %s:%d:%d - reference to field %s on %s can't be resolved.\n",
SOURCE_PATH.deref(), line, column, fieldName, targetClass.getName());
}
}
}
public Object eval() {
return Reflector.invokeNoArgInstanceMember(target.eval(), fieldName, requireField);
}
public boolean canEmitPrimitive(){
return targetClass != null && field != null &&
Util.isPrimitive(field.getType());
}
public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
if(targetClass != null && field != null)
{
target.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.checkCast(getType(targetClass));
gen.getField(getType(targetClass), fieldName, Type.getType(field.getType()));
}
else
throw new UnsupportedOperationException("Unboxed emit of unknown member");
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
if(targetClass != null && field != null)
{
target.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.checkCast(getType(targetClass));
gen.getField(getType(targetClass), fieldName, Type.getType(field.getType()));
//if(context != C.STATEMENT)
HostExpr.emitBoxReturn(objx, gen, field.getType());
if(context == C.STATEMENT)
{
gen.pop();
}
}
else
{
target.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.push(fieldName);
gen.push(requireField);
gen.invokeStatic(REFLECTOR_TYPE, invokeNoArgInstanceMember);
if(context == C.STATEMENT)
gen.pop();
}
}
public boolean hasJavaClass() {
return field != null || tag != null;
}
public Class getJavaClass() {
if (jc == null)
jc = tag != null ? HostExpr.tagToClass(tag) : field.getType();
return jc;
}
public Object evalAssign(Expr val) {
return Reflector.setInstanceField(target.eval(), fieldName, val.eval());
}
public void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen,
Expr val){
if(targetClass != null && field != null)
{
target.emit(C.EXPRESSION, objx, gen);
gen.checkCast(getType(targetClass));
val.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.dupX1();
HostExpr.emitUnboxArg(objx, gen, field.getType());
gen.putField(getType(targetClass), fieldName, Type.getType(field.getType()));
}
else
{
target.emit(C.EXPRESSION, objx, gen);
gen.push(fieldName);
val.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.invokeStatic(REFLECTOR_TYPE, setInstanceFieldMethod);
}
if(context == C.STATEMENT)
gen.pop();
}
}
static class StaticFieldExpr extends FieldExpr implements AssignableExpr{
//final String className;
public final String fieldName;
public final Class c;
public final java.lang.reflect.Field field;
public final Symbol tag;
// final static Method getStaticFieldMethod = Method.getMethod("Object getStaticField(String,String)");
// final static Method setStaticFieldMethod = Method.getMethod("Object setStaticField(String,String,Object)");
final int line;
final int column;
Class jc;
public StaticFieldExpr(int line, int column, Class c, String fieldName, Symbol tag) {
//this.className = className;
this.fieldName = fieldName;
this.line = line;
this.column = column;
//c = Class.forName(className);
this.c = c;
try
{
field = c.getField(fieldName);
}
catch(NoSuchFieldException e)
{
for (java.lang.reflect.Method m: c.getMethods())
if (fieldName.equals(m.getName()) && (Modifier.isStatic(m.getModifiers())))
throw new IllegalArgumentException("No matching method " +
fieldName +
" found taking 0 args for " +
c);
throw Util.sneakyThrow(e);
}
this.tag = tag;
}
public Object eval() {
return Reflector.getStaticField(c, fieldName);
}
public boolean canEmitPrimitive(){
return Util.isPrimitive(field.getType());
}
public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
gen.visitLineNumber(line, gen.mark());
gen.getStatic(Type.getType(c), fieldName, Type.getType(field.getType()));
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
gen.visitLineNumber(line, gen.mark());
gen.getStatic(Type.getType(c), fieldName, Type.getType(field.getType()));
//if(context != C.STATEMENT)
HostExpr.emitBoxReturn(objx, gen, field.getType());
if(context == C.STATEMENT)
{
gen.pop();
}
// gen.push(className);
// gen.push(fieldName);
// gen.invokeStatic(REFLECTOR_TYPE, getStaticFieldMethod);
}
public boolean hasJavaClass(){
return true;
}
public Class getJavaClass() {
//Class c = Class.forName(className);
//java.lang.reflect.Field field = c.getField(fieldName);
if (jc == null)
jc =tag != null ? HostExpr.tagToClass(tag) : field.getType();
return jc;
}
public Object evalAssign(Expr val) {
return Reflector.setStaticField(c, fieldName, val.eval());
}
public void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen,
Expr val){
val.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.dup();
HostExpr.emitUnboxArg(objx, gen, field.getType());
gen.putStatic(Type.getType(c), fieldName, Type.getType(field.getType()));
if(context == C.STATEMENT)
gen.pop();
}
}
static Class maybePrimitiveType(Expr e){
if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive())
{
Class c = e.getJavaClass();
if(Util.isPrimitive(c))
return c;
}
return null;
}
static class FISupport {
private static final IPersistentSet AFN_FIS = RT.set(Callable.class, Runnable.class, Comparator.class);
private static final IPersistentSet OBJECT_METHODS = RT.set("equals", "toString", "hashCode");
// Return FI method if:
// 1) Target is a functional interface and not already implemented by AFn
// 2) Target method matches one of our fn invoker methods (0 < arity <= 10)
static java.lang.reflect.Method maybeFIMethod(Class target) {
if (target != null && target.isAnnotationPresent(FunctionalInterface.class)
&& !AFN_FIS.contains(target)) {
java.lang.reflect.Method[] methods = target.getMethods();
for (java.lang.reflect.Method method : methods) {
// We do not support arity=0 (e.g. Supplier) b/c not functional - use IDeref instead
if (method.getParameterCount() > 0 && method.getParameterCount() <= 10
&& Modifier.isAbstract(method.getModifiers())
&& !OBJECT_METHODS.contains(method.getName()))
return method;
}
}
return null;
}
// Invokers support only long, double, Object params; widen numerics
private static Class toInvokerParamType(Class c) {
if (c.equals(Byte.TYPE) || c.equals(Short.TYPE) || c.equals(Integer.TYPE) || c.equals(Long.TYPE)) {
return Long.TYPE;
} else if (c.equals(Float.TYPE) || c.equals(Double.TYPE)) {
return Double.TYPE;
}
return Object.class;
}
/**
* If targetClass is FI and has an adaptable functional method
* Find fn invoker method matching adaptable method of FI
* Emit bytecode for (expr is emitted):
* if(expr instanceof IFn)
* invokeDynamic(targetMethod, fnInvokerImplMethod)
* Else emit nothing
*/
static boolean maybeEmitFIAdapter(ObjExpr objx, GeneratorAdapter gen, Expr expr, Class targetClass) {
// Optimization:
// if(expr instanceof QualifiedMethodExpr)
// emitInvokeDynamic(targetMethod, QME method) // DON'T emit expr
java.lang.reflect.Method targetMethod = maybeFIMethod(targetClass);
if (targetMethod == null)
return false;
// compute fn invoker method
int paramCount = targetMethod.getParameterCount();
Class[] invokerParams = new Class[paramCount + 1];
invokerParams[0] = IFn.class; // close over Ifn as first arg
StringBuilder invokeMethodBuilder = new StringBuilder("invoke");
for (int i = 0; i < paramCount; i++) {
// FnInvokers only has prims for first 2 args
invokerParams[i + 1] = paramCount <= 2 ? toInvokerParamType(targetMethod.getParameterTypes()[i]) : Object.class;
invokeMethodBuilder.append(FnInvokers.encodeInvokerType(invokerParams[i + 1]));
}
char invokerReturnCode = FnInvokers.encodeInvokerType(targetMethod.getReturnType());
invokeMethodBuilder.append(invokerReturnCode);
String invokerMethodName = invokeMethodBuilder.toString();
// Emit adapter to fn invoker method
Type samType = Type.getType(targetClass);
Type ifnType = Type.getType(IFn.class);
try {
java.lang.reflect.Method fnInvokerMethod = FnInvokers.class.getMethod(invokerMethodName, invokerParams);
// if(exp instanceof IFn) { emitInvokeDynamic(targetMethod, fnInvokerMethod) }
expr.emit(C.EXPRESSION, objx, gen);
gen.dup();
gen.instanceOf(ifnType);
// if not instanceof IFn, go to end
Label endLabel = gen.newLabel();
gen.ifZCmp(Opcodes.IFEQ, endLabel);
// else adapt fn invoker method as impl for target method
emitInvokeDynamicAdapter(gen, targetClass, targetMethod, FnInvokers.class, fnInvokerMethod);
// end - checkcast that we have the target FI type
gen.mark(endLabel);
gen.checkCast(samType);
return true;
} catch (NoSuchMethodException e) {
throw Util.sneakyThrow(e); // should never happen
}
}
// LambdaMetafactory.metafactory() method handle for lambda bootstrap
private static final Handle LMF_HANDLE =
new Handle(Opcodes.H_INVOKESTATIC,
"java/lang/invoke/LambdaMetafactory",
"metafactory",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",
false);
/**
* Emit an invokedynamic to adapt an implMethod to act as a targetMethod.
*
* implMethod may be a static method, a constructor, or an instance method. If it is an
* instance method, the first argument is the invocation instance.
*
* The implMethod may close over objects on the stack - these are passed as the initial arguments
* to implMethod. The trailing arguments must match the targetMethod arguments.
*
* See: https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html
* @param gen ASM code generator, expects any closed-overs to be on the stack already
* @param targetClass The target class
* @param targetMethod The target method
* @param implClass The impl class
* @param implMethod The impl method that will be adapted, takes closed-overs + args of targetMethod
*/
static void emitInvokeDynamicAdapter(GeneratorAdapter gen,
Class targetClass, java.lang.reflect.Method targetMethod,
Class implClass, Executable implMethod) {
// Impl method - takes closed overs (on stack now) + args (when called)
Class[] implParams = implMethod.getParameterTypes();
Class retClass = isConstructor(implMethod) ? implClass
: ((java.lang.reflect.Method) implMethod).getReturnType();
int opCode = isConstructor(implMethod) ? Opcodes.H_INVOKESPECIAL :
(isStaticMethod(implMethod) ? Opcodes.H_INVOKESTATIC :
Opcodes.H_INVOKEVIRTUAL);
Handle implHandle = new Handle(opCode,
Type.getInternalName(implClass),
implMethod.getName(),
MethodType.methodType(retClass, implParams).toMethodDescriptorString(),
false);
// Adapter interface lambda-style: (closedOver*) -> targetType
int implArgCount = implParams.length;
if (isInstanceMethod(implMethod)) // instance is first "arg"
implArgCount++;
List lambdaParams = Arrays.asList(Arrays.copyOfRange(implParams, 0, implArgCount - targetMethod.getParameterCount()));
MethodType lambdaSig = MethodType.methodType(targetClass, lambdaParams);
Type targetType = Type.getType(targetMethod);
gen.visitInvokeDynamicInsn(
targetMethod.getName(),
lambdaSig.toMethodDescriptorString(), // adapter signature, closedOvers -> target
LMF_HANDLE, // bootstrap method handle: LambdaMetaFactory.metafactory()
new Object[]{targetType, implHandle, targetType}); // arg types of bootstrap method
}
}
static Class maybeJavaClass(Collection exprs){
Class match = null;
try
{
for (Expr e : exprs)
{
if (e instanceof ThrowExpr)
continue;
if (!e.hasJavaClass())
return null;
Class c = e.getJavaClass();
if (c == null)
return null;
if (match == null)
match = c;
else if (match != c)
return null;
}
}
catch(Exception e)
{
return null;
}
return match;
}
static abstract class MethodExpr extends HostExpr{
static void emitArgsAsArray(IPersistentVector args, ObjExpr objx, GeneratorAdapter gen){
gen.push(args.count());
gen.newArray(OBJECT_TYPE);
for(int i = 0; i < args.count(); i++)
{
gen.dup();
gen.push(i);
((Expr) args.nth(i)).emit(C.EXPRESSION, objx, gen);
gen.arrayStore(OBJECT_TYPE);
}
}
public static void emitTypedArgs(ObjExpr objx, GeneratorAdapter gen, Class[] parameterTypes, IPersistentVector args){
for(int i = 0; i < parameterTypes.length; i++)
{
Expr e = (Expr) args.nth(i);
try
{
final Class primc = maybePrimitiveType(e);
if(primc == parameterTypes[i])
{
final MaybePrimitiveExpr pe = (MaybePrimitiveExpr) e;
pe.emitUnboxed(C.EXPRESSION, objx, gen);
}
else if(primc == int.class && parameterTypes[i] == long.class)
{
final MaybePrimitiveExpr pe = (MaybePrimitiveExpr) e;
pe.emitUnboxed(C.EXPRESSION, objx, gen);
gen.visitInsn(I2L);
}
else if(primc == long.class && parameterTypes[i] == int.class)
{
final MaybePrimitiveExpr pe = (MaybePrimitiveExpr) e;
pe.emitUnboxed(C.EXPRESSION, objx, gen);
if(RT.booleanCast(RT.UNCHECKED_MATH.deref()))
gen.invokeStatic(RT_TYPE, Method.getMethod("int uncheckedIntCast(long)"));
else
gen.invokeStatic(RT_TYPE, Method.getMethod("int intCast(long)"));
}
else if(primc == float.class && parameterTypes[i] == double.class)
{
final MaybePrimitiveExpr pe = (MaybePrimitiveExpr) e;
pe.emitUnboxed(C.EXPRESSION, objx, gen);
gen.visitInsn(F2D);
}
else if(primc == double.class && parameterTypes[i] == float.class)
{
final MaybePrimitiveExpr pe = (MaybePrimitiveExpr) e;
pe.emitUnboxed(C.EXPRESSION, objx, gen);
gen.visitInsn(D2F);
}
else if(!FISupport.maybeEmitFIAdapter(objx, gen, e, parameterTypes[i]))
{
e.emit(C.EXPRESSION, objx, gen);
HostExpr.emitUnboxArg(objx, gen, parameterTypes[i]);
}
}
catch(Exception e1)
{
throw Util.sneakyThrow(e1);
}
}
}
}
static class InstanceMethodExpr extends MethodExpr{
public final Expr target;
public final String methodName;
public final IPersistentVector args;
public final String source;
public final int line;
public final int column;
public final Symbol tag;
public final boolean tailPosition;
public final java.lang.reflect.Method method;
public final Class qualifyingClass;
Class jc;
final static Method invokeInstanceMethodMethod =
Method.getMethod("Object invokeInstanceMethod(Object,String,Object[])");
final static Method invokeInstanceMethodOfClassMethod =
Method.getMethod("Object invokeInstanceMethodOfClass(Object,String,String,Object[])");
public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr target,
Class qualifyingClass, String methodName, java.lang.reflect.Method resolvedMethod,
IPersistentVector args, boolean tailPosition)
{
checkMethodArity(resolvedMethod, RT.count(args));
this.source = source;
this.line = line;
this.column = column;
this.args = args;
this.methodName = methodName;
this.target = target;
this.tag = tag;
this.tailPosition = tailPosition;
this.method = resolvedMethod;
this.qualifyingClass = qualifyingClass;
}
public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr target,
Class qualifyingClass, String methodName, IPersistentVector args, boolean tailPosition)
{
this.source = source;
this.line = line;
this.column = column;
this.args = args;
this.methodName = methodName;
this.target = target;
this.tag = tag;
this.tailPosition = tailPosition;
this.qualifyingClass = qualifyingClass;
Class contextClass = qualifyingClass != null ? qualifyingClass :
(target.hasJavaClass() ? target.getJavaClass() : null);
if(contextClass != null)
{
List methods = Reflector.getMethods(contextClass, args.count(), methodName, false);
if(methods.isEmpty())
{
method = null;
if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
{
RT.errPrintWriter()
.format("Reflection warning, %s:%d:%d - call to method %s on %s can't be resolved (no such method).\n",
SOURCE_PATH.deref(), line, column, methodName, contextClass.getName());
}
}
else
{
int methodidx = 0;
if(methods.size() > 1)
{
ArrayList params = new ArrayList();
ArrayList rets = new ArrayList();
for(int i = 0; i < methods.size(); i++)
{
java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i);
params.add(m.getParameterTypes());
rets.add(m.getReturnType());
}
methodidx = getMatchingParams(methodName, params, args, rets);
}
java.lang.reflect.Method m =
(java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null);
if(m != null && !Modifier.isPublic(m.getDeclaringClass().getModifiers()))
{
//public method of non-public class, try to find it in hierarchy
m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m);
}
method = m;
if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
{
RT.errPrintWriter()
.format("Reflection warning, %s:%d:%d - call to method %s on %s can't be resolved (argument types: %s).\n",
SOURCE_PATH.deref(), line, column, methodName, contextClass.getName(), getTypeStringForArgs(args));
}
}
}
else
{
method = null;
if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
{
RT.errPrintWriter()
.format("Reflection warning, %s:%d:%d - call to method %s can't be resolved (target class is unknown).\n",
SOURCE_PATH.deref(), line, column, methodName);
}
}
}
public Object eval() {
try
{
Object targetval = target.eval();
Object[] argvals = new Object[args.count()];
for(int i = 0; i < args.count(); i++)
argvals[i] = ((Expr) args.nth(i)).eval();
if(method != null)
{
LinkedList ms = new LinkedList();
ms.add(method);
return Reflector.invokeMatchingMethod(methodName, ms, targetval, argvals);
}
if(qualifyingClass != null)
return Reflector.invokeInstanceMethodOfClass(targetval, qualifyingClass, methodName, argvals);
else
return Reflector.invokeInstanceMethod(targetval, methodName, argvals);
}
catch(Throwable e)
{
if(!(e instanceof CompilerException))
throw new CompilerException(source, line, column, null, CompilerException.PHASE_EXECUTION, e);
else
throw (CompilerException) e;
}
}
public boolean canEmitPrimitive(){
return method != null && Util.isPrimitive(method.getReturnType());
}
public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
if(method != null)
{
Type type = Type.getType(method.getDeclaringClass());
target.emit(C.EXPRESSION, objx, gen);
//if(!method.getDeclaringClass().isInterface())
gen.checkCast(type);
MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args);
gen.visitLineNumber(line, gen.mark());
if(tailPosition && !objx.canBeDirect)
{
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearThis(gen);
}
Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method));
if(method.getDeclaringClass().isInterface())
gen.invokeInterface(type, m);
else
gen.invokeVirtual(type, m);
}
else
throw new UnsupportedOperationException("Unboxed emit of unknown member");
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
if(method != null)
{
Type type = Type.getType(method.getDeclaringClass());
target.emit(C.EXPRESSION, objx, gen);
//if(!method.getDeclaringClass().isInterface())
gen.checkCast(type);
MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args);
gen.visitLineNumber(line, gen.mark());
if(context == C.RETURN)
{
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method));
if(method.getDeclaringClass().isInterface())
gen.invokeInterface(type, m);
else
gen.invokeVirtual(type, m);
Class retClass = method.getReturnType();
if(context == C.STATEMENT)
{
if(retClass == long.class || retClass == double.class)
gen.pop2();
else if(retClass != void.class)
gen.pop();
}
else
HostExpr.emitBoxReturn(objx, gen, retClass);
}
else
{
target.emit(C.EXPRESSION, objx, gen);
if(qualifyingClass != null)
gen.push(qualifyingClass.getName());
gen.push(methodName);
emitArgsAsArray(args, objx, gen);
gen.visitLineNumber(line, gen.mark());
if(context == C.RETURN)
{
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
if(qualifyingClass != null)
gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodOfClassMethod);
else
gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodMethod);
if(context == C.STATEMENT)
gen.pop();
}
}
public boolean hasJavaClass(){
return method != null || tag != null;
}
public Class getJavaClass() {
if (jc == null)
jc = retType((tag!=null)?HostExpr.tagToClass(tag):null, (method!=null)?method.getReturnType():null);
return jc;
}
}
static class StaticMethodExpr extends MethodExpr{
//final String className;
public final Class c;
public final String methodName;
public final IPersistentVector args;
public final String source;
public final int line;
public final int column;
public final java.lang.reflect.Method method;
public final Symbol tag;
public final boolean tailPosition;
final static Method forNameMethod = Method.getMethod("Class classForName(String)");
final static Method invokeStaticMethodMethod =
Method.getMethod("Object invokeStaticMethod(Class,String,Object[])");
final static Keyword warnOnBoxedKeyword = Keyword.intern("warn-on-boxed");
Class jc;
public StaticMethodExpr(String source, int line, int column, Symbol tag, Class c,
String methodName, java.lang.reflect.Method preferredMethod, IPersistentVector args, boolean tailPosition)
{
checkMethodArity(preferredMethod, RT.count(args));
this.c = c;
this.methodName = methodName;
this.args = args;
this.source = source;
this.line = line;
this.column = column;
this.tag = tag;
this.tailPosition = tailPosition;
this.method = preferredMethod;
if(method != null && warnOnBoxedKeyword.equals(RT.UNCHECKED_MATH.deref()) && isBoxedMath(method))
{
RT.errPrintWriter()
.format("Boxed math warning, %s:%d:%d - call: %s.\n",
SOURCE_PATH.deref(), line, column, method.toString());
}
}
public StaticMethodExpr(String source, int line, int column, Symbol tag, Class c,
String methodName, IPersistentVector args, boolean tailPosition)
{
this.c = c;
this.methodName = methodName;
this.args = args;
this.source = source;
this.line = line;
this.column = column;
this.tag = tag;
this.tailPosition = tailPosition;
List methods = Reflector.getMethods(c, args.count(), methodName, true);
if(methods.isEmpty())
throw new IllegalArgumentException("No matching method " + methodName + " found taking "
+ args.count() + " args for " + c);
int methodidx = 0;
if(methods.size() > 1)
{
ArrayList params = new ArrayList();
ArrayList rets = new ArrayList();
for(int i = 0; i < methods.size(); i++)
{
java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i);
params.add(m.getParameterTypes());
rets.add(m.getReturnType());
}
methodidx = getMatchingParams(methodName, params, args, rets);
}
method = (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null);
if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
{
RT.errPrintWriter()
.format("Reflection warning, %s:%d:%d - call to static method %s on %s can't be resolved (argument types: %s).\n",
SOURCE_PATH.deref(), line, column, methodName, c.getName(), getTypeStringForArgs(args));
}
if(method != null && warnOnBoxedKeyword.equals(RT.UNCHECKED_MATH.deref()) && isBoxedMath(method))
{
RT.errPrintWriter()
.format("Boxed math warning, %s:%d:%d - call: %s.\n",
SOURCE_PATH.deref(), line, column, method.toString());
}
}
public static boolean isBoxedMath(java.lang.reflect.Method m) {
Class c = m.getDeclaringClass();
if(c.equals(Numbers.class))
{
WarnBoxedMath boxedMath = m.getAnnotation(WarnBoxedMath.class);
if(boxedMath != null)
return boxedMath.value();
Class[] argTypes = m.getParameterTypes();
for(Class argType : argTypes)
if(argType.equals(Object.class) || argType.equals(Number.class))
return true;
}
return false;
}
public Object eval() {
try
{
Object[] argvals = new Object[args.count()];
for(int i = 0; i < args.count(); i++)
argvals[i] = ((Expr) args.nth(i)).eval();
if(method != null)
{
LinkedList ms = new LinkedList();
ms.add(method);
return Reflector.invokeMatchingMethod(methodName, ms, null, argvals);
}
return Reflector.invokeStaticMethod(c, methodName, argvals);
}
catch(Throwable e)
{
if(!(e instanceof CompilerException))
throw new CompilerException(source, line, column, null, CompilerException.PHASE_EXECUTION, e);
else
throw (CompilerException) e;
}
}
public boolean canEmitPrimitive(){
return method != null && Util.isPrimitive(method.getReturnType());
}
public boolean canEmitIntrinsicPredicate(){
return method != null && RT.get(Intrinsics.preds, method.toString()) != null;
}
public void emitIntrinsicPredicate(C context, ObjExpr objx, GeneratorAdapter gen, Label falseLabel){
gen.visitLineNumber(line, gen.mark());
if(method != null)
{
MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args);
if(context == C.RETURN)
{
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
Object[] predOps = (Object[]) RT.get(Intrinsics.preds, method.toString());
for(int i=0;i 2)
throw Util.runtimeException("Too many arguments to throw, throw expects a single Throwable instance");
return new ThrowExpr(analyze(C.EXPRESSION, RT.second(form)));
}
}
}
static public boolean subsumes(Class[] c1, Class[] c2){
//presumes matching lengths
Boolean better = false;
for(int i = 0; i < c1.length; i++)
{
if(c1[i] != c2[i])// || c2[i].isPrimitive() && c1[i] == Object.class))
{
if(!c1[i].isPrimitive() && c2[i].isPrimitive()
//|| Number.class.isAssignableFrom(c1[i]) && c2[i].isPrimitive()
||
c2[i].isAssignableFrom(c1[i]))
better = true;
else
return false;
}
}
return better;
}
static String getTypeStringForArgs(IPersistentVector args){
StringBuilder sb = new StringBuilder();
for(int i = 0; i < args.count(); i++)
{
Expr arg = (Expr) args.nth(i);
if (i > 0) sb.append(", ");
sb.append((arg.hasJavaClass() && arg.getJavaClass() != null) ? arg.getJavaClass().getName() : "unknown");
}
return sb.toString();
}
static int getMatchingParams(String methodName, ArrayList paramlists, IPersistentVector argexprs,
List rets)
{
//presumes matching lengths
int matchIdx = -1;
boolean tied = false;
boolean foundExact = false;
for(int i = 0; i < paramlists.size(); i++)
{
boolean match = true;
ISeq aseq = argexprs.seq();
int exact = 0;
for(int p = 0; match && p < argexprs.count() && aseq != null; ++p, aseq = aseq.next())
{
Expr arg = (Expr) aseq.first();
Class aclass = arg.hasJavaClass() ? arg.getJavaClass() : Object.class;
Class pclass = paramlists.get(i)[p];
if(arg.hasJavaClass() && aclass == pclass)
exact++;
else
match = Reflector.paramArgTypeMatch(pclass, aclass);
}
if(exact == argexprs.count())
{
if(!foundExact || matchIdx == -1 || rets.get(matchIdx).isAssignableFrom(rets.get(i)))
matchIdx = i;
tied = false;
foundExact = true;
}
else if(match && !foundExact)
{
if(matchIdx == -1)
matchIdx = i;
else
{
if(subsumes(paramlists.get(i), paramlists.get(matchIdx)))
{
matchIdx = i;
tied = false;
}
else if(Arrays.equals(paramlists.get(matchIdx), paramlists.get(i)))
{
if(rets.get(matchIdx).isAssignableFrom(rets.get(i)))
matchIdx = i;
}
else if(!(subsumes(paramlists.get(matchIdx), paramlists.get(i))))
tied = true;
}
}
}
if(tied)
throw new IllegalArgumentException("More than one matching method found: " + methodName);
return matchIdx;
}
public static class NewExpr implements Expr{
public final IPersistentVector args;
public final Constructor ctor;
public final Class c;
final static Method invokeConstructorMethod =
Method.getMethod("Object invokeConstructor(Class,Object[])");
final static Method forNameMethod = Method.getMethod("Class classForName(String)");
public NewExpr(Class c, Constructor preferredConstructor, IPersistentVector args, int line, int column) {
checkMethodArity(preferredConstructor, RT.count(args));
this.args = args;
this.c = c;
this.ctor = preferredConstructor;
}
public NewExpr(Class c, IPersistentVector args, int line, int column) {
this.args = args;
this.c = c;
Constructor[] allctors = c.getConstructors();
ArrayList ctors = new ArrayList();
ArrayList params = new ArrayList();
ArrayList rets = new ArrayList();
for(int i = 0; i < allctors.length; i++)
{
Constructor ctor = allctors[i];
if(ctor.getParameterTypes().length == args.count())
{
ctors.add(ctor);
params.add(ctor.getParameterTypes());
rets.add(c);
}
}
if(ctors.isEmpty())
throw new IllegalArgumentException("No matching ctor found for " + c);
int ctoridx = 0;
if(ctors.size() > 1)
{
ctoridx = getMatchingParams(c.getName(), params, args, rets);
}
this.ctor = ctoridx >= 0 ? (Constructor) ctors.get(ctoridx) : null;
if(ctor == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
{
RT.errPrintWriter()
.format("Reflection warning, %s:%d:%d - call to %s ctor can't be resolved.\n",
SOURCE_PATH.deref(), line, column, c.getName());
}
}
public Object eval() {
Object[] argvals = new Object[args.count()];
for(int i = 0; i < args.count(); i++)
argvals[i] = ((Expr) args.nth(i)).eval();
if(this.ctor != null)
{
try
{
return ctor.newInstance(Reflector.boxArgs(ctor.getParameterTypes(), argvals));
}
catch(Exception e)
{
throw Util.sneakyThrow(e);
}
}
return Reflector.invokeConstructor(c, argvals);
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
if(this.ctor != null)
{
Type type = getType(c);
gen.newInstance(type);
gen.dup();
MethodExpr.emitTypedArgs(objx, gen, ctor.getParameterTypes(), args);
gen.invokeConstructor(type, new Method("", Type.getConstructorDescriptor(ctor)));
}
else
{
gen.push(destubClassName(c.getName()));
gen.invokeStatic(RT_TYPE, forNameMethod);
MethodExpr.emitArgsAsArray(args, objx, gen);
gen.invokeStatic(REFLECTOR_TYPE, invokeConstructorMethod);
}
if(context == C.STATEMENT)
gen.pop();
}
public boolean hasJavaClass(){
return true;
}
public Class getJavaClass() {
return c;
}
static class Parser implements IParser{
public Expr parse(C context, Object frm) {
int line = lineDeref();
int column = columnDeref();
ISeq form = (ISeq) frm;
//(new Classname args...)
if(form.count() < 2)
throw Util.runtimeException("wrong number of arguments, expecting: (new Classname args...)");
Class c = HostExpr.maybeClass(RT.second(form), false);
if(c == null)
throw new IllegalArgumentException("Unable to resolve classname: " + RT.second(form));
PersistentVector args = PersistentVector.EMPTY;
for(ISeq s = RT.next(RT.next(form)); s != null; s = s.next())
args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first()));
return new NewExpr(c, args, line, column);
}
}
}
public static class MetaExpr implements Expr{
public final Expr expr;
public final Expr meta;
final static Type IOBJ_TYPE = Type.getType(IObj.class);
final static Method withMetaMethod = Method.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)");
public MetaExpr(Expr expr, Expr meta){
this.expr = expr;
this.meta = meta;
}
public Object eval() {
return ((IObj) expr.eval()).withMeta((IPersistentMap) meta.eval());
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
expr.emit(C.EXPRESSION, objx, gen);
gen.checkCast(IOBJ_TYPE);
meta.emit(C.EXPRESSION, objx, gen);
gen.checkCast(IPERSISTENTMAP_TYPE);
gen.invokeInterface(IOBJ_TYPE, withMetaMethod);
if(context == C.STATEMENT)
{
gen.pop();
}
}
public boolean hasJavaClass() {
return expr.hasJavaClass();
}
public Class getJavaClass() {
return expr.getJavaClass();
}
}
public static class IfExpr implements Expr, MaybePrimitiveExpr{
public final Expr testExpr;
public final Expr thenExpr;
public final Expr elseExpr;
public final int line;
public final int column;
public IfExpr(int line, int column, Expr testExpr, Expr thenExpr, Expr elseExpr){
this.testExpr = testExpr;
this.thenExpr = thenExpr;
this.elseExpr = elseExpr;
this.line = line;
this.column = column;
}
public Object eval() {
Object t = testExpr.eval();
if(t != null && t != Boolean.FALSE)
return thenExpr.eval();
return elseExpr.eval();
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
doEmit(context, objx, gen,false);
}
public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
doEmit(context, objx, gen, true);
}
public void doEmit(C context, ObjExpr objx, GeneratorAdapter gen, boolean emitUnboxed){
Label nullLabel = gen.newLabel();
Label falseLabel = gen.newLabel();
Label endLabel = gen.newLabel();
gen.visitLineNumber(line, gen.mark());
if(testExpr instanceof StaticMethodExpr && ((StaticMethodExpr)testExpr).canEmitIntrinsicPredicate())
{
((StaticMethodExpr) testExpr).emitIntrinsicPredicate(C.EXPRESSION, objx, gen, falseLabel);
}
else if(maybePrimitiveType(testExpr) == boolean.class)
{
((MaybePrimitiveExpr) testExpr).emitUnboxed(C.EXPRESSION, objx, gen);
gen.ifZCmp(gen.EQ, falseLabel);
}
else
{
testExpr.emit(C.EXPRESSION, objx, gen);
gen.dup();
gen.ifNull(nullLabel);
gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);
gen.visitJumpInsn(IF_ACMPEQ, falseLabel);
}
if(emitUnboxed)
((MaybePrimitiveExpr)thenExpr).emitUnboxed(context, objx, gen);
else
thenExpr.emit(context, objx, gen);
gen.goTo(endLabel);
gen.mark(nullLabel);
gen.pop();
gen.mark(falseLabel);
if(emitUnboxed)
((MaybePrimitiveExpr)elseExpr).emitUnboxed(context, objx, gen);
else
elseExpr.emit(context, objx, gen);
gen.mark(endLabel);
}
public boolean hasJavaClass() {
return thenExpr.hasJavaClass()
&& elseExpr.hasJavaClass()
&&
(thenExpr.getJavaClass() == elseExpr.getJavaClass()
|| thenExpr.getJavaClass() == RECUR_CLASS
|| elseExpr.getJavaClass() == RECUR_CLASS
|| (thenExpr.getJavaClass() == null && !elseExpr.getJavaClass().isPrimitive())
|| (elseExpr.getJavaClass() == null && !thenExpr.getJavaClass().isPrimitive()));
}
public boolean canEmitPrimitive(){
try
{
return thenExpr instanceof MaybePrimitiveExpr
&& elseExpr instanceof MaybePrimitiveExpr
&& (thenExpr.getJavaClass() == elseExpr.getJavaClass()
|| thenExpr.getJavaClass() == RECUR_CLASS
|| elseExpr.getJavaClass() == RECUR_CLASS)
&& ((MaybePrimitiveExpr)thenExpr).canEmitPrimitive()
&& ((MaybePrimitiveExpr)elseExpr).canEmitPrimitive();
}
catch(Exception e)
{
return false;
}
}
public Class getJavaClass() {
Class thenClass = thenExpr.getJavaClass();
if(thenClass != null && thenClass != RECUR_CLASS)
return thenClass;
return elseExpr.getJavaClass();
}
static class Parser implements IParser{
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
//(if test then) or (if test then else)
if(form.count() > 4)
throw Util.runtimeException("Too many arguments to if");
else if(form.count() < 3)
throw Util.runtimeException("Too few arguments to if");
PathNode branch = new PathNode(PATHTYPE.BRANCH, (PathNode) CLEAR_PATH.get());
Expr testexpr = analyze(context == C.EVAL ? context : C.EXPRESSION, RT.second(form));
Expr thenexpr, elseexpr;
try {
Var.pushThreadBindings(
RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch)));
thenexpr = analyze(context, RT.third(form));
}
finally{
Var.popThreadBindings();
}
try {
Var.pushThreadBindings(
RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch)));
elseexpr = analyze(context, RT.fourth(form));
}
finally{
Var.popThreadBindings();
}
return new IfExpr(lineDeref(),
columnDeref(),
testexpr,
thenexpr,
elseexpr);
}
}
}
static final public IPersistentMap CHAR_MAP =
PersistentHashMap.create('-', "_",
// '.', "_DOT_",
':', "_COLON_",
'+', "_PLUS_",
'>', "_GT_",
'<', "_LT_",
'=', "_EQ_",
'~', "_TILDE_",
'!', "_BANG_",
'@', "_CIRCA_",
'#', "_SHARP_",
'\'', "_SINGLEQUOTE_",
'"', "_DOUBLEQUOTE_",
'%', "_PERCENT_",
'^', "_CARET_",
'&', "_AMPERSAND_",
'*', "_STAR_",
'|', "_BAR_",
'{', "_LBRACE_",
'}', "_RBRACE_",
'[', "_LBRACK_",
']', "_RBRACK_",
'/', "_SLASH_",
'\\', "_BSLASH_",
'?', "_QMARK_");
static final public IPersistentMap DEMUNGE_MAP;
static final public Pattern DEMUNGE_PATTERN;
static {
// DEMUNGE_MAP maps strings to characters in the opposite
// direction that CHAR_MAP does, plus it maps "$" to '/'
IPersistentMap m = RT.map("$", '/');
for(ISeq s = RT.seq(CHAR_MAP); s != null; s = s.next())
{
IMapEntry e = (IMapEntry) s.first();
Character origCh = (Character) e.key();
String escapeStr = (String) e.val();
m = m.assoc(escapeStr, origCh);
}
DEMUNGE_MAP = m;
// DEMUNGE_PATTERN searches for the first of any occurrence of
// the strings that are keys of DEMUNGE_MAP.
// Note: Regex matching rules mean that #"_|_COLON_" "_COLON_"
// returns "_", but #"_COLON_|_" "_COLON_" returns "_COLON_"
// as desired. Sorting string keys of DEMUNGE_MAP from longest to
// shortest ensures correct matching behavior, even if some strings are
// prefixes of others.
Object[] mungeStrs = RT.toArray(RT.keys(m));
Arrays.sort(mungeStrs, new Comparator() {
public int compare(Object s1, Object s2) {
return ((String) s2).length() - ((String) s1).length();
}});
StringBuilder sb = new StringBuilder();
boolean first = true;
for(Object s : mungeStrs)
{
String escapeStr = (String) s;
if (!first)
sb.append("|");
first = false;
sb.append("\\Q");
sb.append(escapeStr);
sb.append("\\E");
}
DEMUNGE_PATTERN = Pattern.compile(sb.toString());
}
static public String munge(String name){
StringBuilder sb = new StringBuilder();
for(char c : name.toCharArray())
{
String sub = (String) CHAR_MAP.valAt(c);
if(sub != null)
sb.append(sub);
else
sb.append(c);
}
return sb.toString();
}
static public String demunge(String mungedName){
StringBuilder sb = new StringBuilder();
Matcher m = DEMUNGE_PATTERN.matcher(mungedName);
int lastMatchEnd = 0;
while (m.find())
{
int start = m.start();
int end = m.end();
// Keep everything before the match
sb.append(mungedName.substring(lastMatchEnd, start));
lastMatchEnd = end;
// Replace the match with DEMUNGE_MAP result
Character origCh = (Character) DEMUNGE_MAP.valAt(m.group());
sb.append(origCh);
}
// Keep everything after the last match
sb.append(mungedName.substring(lastMatchEnd));
return sb.toString();
}
public static class EmptyExpr implements Expr{
public final Object coll;
final static Type HASHMAP_TYPE = Type.getType(PersistentArrayMap.class);
final static Type HASHSET_TYPE = Type.getType(PersistentHashSet.class);
final static Type VECTOR_TYPE = Type.getType(PersistentVector.class);
final static Type IVECTOR_TYPE = Type.getType(IPersistentVector.class);
final static Type TUPLE_TYPE = Type.getType(Tuple.class);
final static Type LIST_TYPE = Type.getType(PersistentList.class);
final static Type EMPTY_LIST_TYPE = Type.getType(PersistentList.EmptyList.class);
public EmptyExpr(Object coll){
this.coll = coll;
}
public Object eval() {
return coll;
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
if(coll instanceof IPersistentList)
gen.getStatic(LIST_TYPE, "EMPTY", EMPTY_LIST_TYPE);
else if(coll instanceof IPersistentVector)
gen.getStatic(VECTOR_TYPE, "EMPTY", VECTOR_TYPE);
else if(coll instanceof IPersistentMap)
gen.getStatic(HASHMAP_TYPE, "EMPTY", HASHMAP_TYPE);
else if(coll instanceof IPersistentSet)
gen.getStatic(HASHSET_TYPE, "EMPTY", HASHSET_TYPE);
else
throw new UnsupportedOperationException("Unknown Collection type");
if(context == C.STATEMENT)
{
gen.pop();
}
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
if(coll instanceof IPersistentList)
return IPersistentList.class;
else if(coll instanceof IPersistentVector)
return IPersistentVector.class;
else if(coll instanceof IPersistentMap)
return IPersistentMap.class;
else if(coll instanceof IPersistentSet)
return IPersistentSet.class;
else
throw new UnsupportedOperationException("Unknown Collection type");
}
}
public static class ListExpr implements Expr{
public final IPersistentVector args;
final static Method arrayToListMethod = Method.getMethod("clojure.lang.ISeq arrayToList(Object[])");
public ListExpr(IPersistentVector args){
this.args = args;
}
public Object eval() {
IPersistentVector ret = PersistentVector.EMPTY;
for(int i = 0; i < args.count(); i++)
ret = (IPersistentVector) ret.cons(((Expr) args.nth(i)).eval());
return ret.seq();
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
MethodExpr.emitArgsAsArray(args, objx, gen);
gen.invokeStatic(RT_TYPE, arrayToListMethod);
if(context == C.STATEMENT)
gen.pop();
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return IPersistentList.class;
}
}
public static class MapExpr implements Expr{
public final IPersistentVector keyvals;
final static Method mapMethod = Method.getMethod("clojure.lang.IPersistentMap map(Object[])");
final static Method mapUniqueKeysMethod = Method.getMethod("clojure.lang.IPersistentMap mapUniqueKeys(Object[])");
public MapExpr(IPersistentVector keyvals){
this.keyvals = keyvals;
}
public Object eval() {
Object[] ret = new Object[keyvals.count()];
for(int i = 0; i < keyvals.count(); i++)
ret[i] = ((Expr) keyvals.nth(i)).eval();
return RT.map(ret);
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
boolean allKeysConstant = true;
boolean allConstantKeysUnique = true;
IPersistentSet constantKeys = PersistentHashSet.EMPTY;
for(int i = 0; i < keyvals.count(); i+=2)
{
Expr k = (Expr) keyvals.nth(i);
if(k instanceof LiteralExpr)
{
Object kval = k.eval();
if (constantKeys.contains(kval))
allConstantKeysUnique = false;
else
constantKeys = (IPersistentSet)constantKeys.cons(kval);
}
else
allKeysConstant = false;
}
MethodExpr.emitArgsAsArray(keyvals, objx, gen);
if((allKeysConstant && allConstantKeysUnique) || (keyvals.count() <= 2))
gen.invokeStatic(RT_TYPE, mapUniqueKeysMethod);
else
gen.invokeStatic(RT_TYPE, mapMethod);
if(context == C.STATEMENT)
gen.pop();
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return IPersistentMap.class;
}
static public Expr parse(C context, IPersistentMap form) {
IPersistentVector keyvals = PersistentVector.EMPTY;
boolean keysConstant = true;
boolean valsConstant = true;
boolean allConstantKeysUnique = true;
IPersistentSet constantKeys = PersistentHashSet.EMPTY;
for(ISeq s = RT.seq(form); s != null; s = s.next())
{
IMapEntry e = (IMapEntry) s.first();
Expr k = analyze(context == C.EVAL ? context : C.EXPRESSION, e.key());
Expr v = analyze(context == C.EVAL ? context : C.EXPRESSION, e.val());
keyvals = (IPersistentVector) keyvals.cons(k);
keyvals = (IPersistentVector) keyvals.cons(v);
if(k instanceof LiteralExpr)
{
Object kval = k.eval();
if (constantKeys.contains(kval))
allConstantKeysUnique = false;
else
constantKeys = (IPersistentSet)constantKeys.cons(kval);
}
else
keysConstant = false;
if(!(v instanceof LiteralExpr))
valsConstant = false;
}
Expr ret = new MapExpr(keyvals);
if(form instanceof IObj && ((IObj) form).meta() != null)
return new MetaExpr(ret, MapExpr
.parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta()));
else if(keysConstant)
{
// TBD: Add more detail to exception thrown below.
if(!allConstantKeysUnique)
throw new IllegalArgumentException("Duplicate constant keys in map");
if(valsConstant)
{
IPersistentMap m = PersistentArrayMap.EMPTY;
for(int i=0;i 0 && params[params.length-1] == ISeq.class;
break;
}
else if(argcount > params.length
&& params.length > 0
&& params[params.length-1] == ISeq.class)
{
method = m;
variadic = true;
break;
}
}
}
if(method == null)
return null;
Class retClass = method.getReturnType();
Class[] paramClasses = method.getParameterTypes();
Type[] paramTypes = new Type[paramClasses.length];
for(int i = 0;i -1 && argcount >= restOffset))
return tagOf(sig);
}
return null;
}
public InvokeExpr(String source, int line, int column, Symbol tag, Expr fexpr, IPersistentVector args, boolean tailPosition) {
this.source = source;
this.fexpr = fexpr;
this.args = args;
this.line = line;
this.column = column;
this.tailPosition = tailPosition;
if(fexpr instanceof VarExpr)
{
Var fvar = ((VarExpr)fexpr).var;
Var pvar = (Var)RT.get(fvar.meta(), protocolKey);
if(pvar != null && PROTOCOL_CALLSITES.isBound())
{
this.isProtocol = true;
this.siteIndex = registerProtocolCallsite(((VarExpr)fexpr).var);
Object pon = RT.get(pvar.get(), onKey);
this.protocolOn = HostExpr.maybeClass(pon,false);
if(this.protocolOn != null)
{
IPersistentMap mmap = (IPersistentMap) RT.get(pvar.get(), methodMapKey);
Keyword mmapVal = (Keyword) mmap.valAt(Keyword.intern(fvar.sym));
if (mmapVal == null) {
throw new IllegalArgumentException(
"No method of interface: " + protocolOn.getName() +
" found for function: " + fvar.sym + " of protocol: " + pvar.sym +
" (The protocol method may have been defined before and removed.)");
}
String mname = munge(mmapVal.sym.toString());
List methods = Reflector.getMethods(protocolOn, args.count() - 1, mname, false);
if(methods.size() != 1)
throw new IllegalArgumentException(
"No single method: " + mname + " of interface: " + protocolOn.getName() +
" found for function: " + fvar.sym + " of protocol: " + pvar.sym);
this.onMethod = (java.lang.reflect.Method) methods.get(0);
}
}
}
if (tag != null) {
this.tag = tag;
} else if (fexpr instanceof VarExpr) {
Var v = ((VarExpr) fexpr).var;
Object arglists = RT.get(RT.meta(v), arglistsKey);
Object sigTag = sigTag(args.count(),v);
this.tag = sigTag == null ? ((VarExpr) fexpr).tag : sigTag;
} else {
this.tag = null;
}
}
public Object eval() {
try
{
IFn fn = (IFn) fexpr.eval();
PersistentVector argvs = PersistentVector.EMPTY;
for(int i = 0; i < args.count(); i++)
argvs = argvs.cons(((Expr) args.nth(i)).eval());
return fn.applyTo(RT.seq( Util.ret1(argvs, argvs = null) ));
}
catch(Throwable e)
{
if(!(e instanceof CompilerException))
throw new CompilerException(source, line, column, null, CompilerException.PHASE_EXECUTION, e);
else
throw (CompilerException) e;
}
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
if(isProtocol)
{
gen.visitLineNumber(line, gen.mark());
emitProto(context,objx,gen);
}
else
{
fexpr.emit(C.EXPRESSION, objx, gen);
gen.visitLineNumber(line, gen.mark());
gen.checkCast(IFN_TYPE);
emitArgsAndCall(0, context,objx,gen);
}
if(context == C.STATEMENT)
gen.pop();
}
public void emitProto(C context, ObjExpr objx, GeneratorAdapter gen){
Label onLabel = gen.newLabel();
Label callLabel = gen.newLabel();
Label endLabel = gen.newLabel();
Var v = ((VarExpr)fexpr).var;
Expr e = (Expr) args.nth(0);
e.emit(C.EXPRESSION, objx, gen);
gen.dup(); //target, target
gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class
gen.getStatic(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class,cached-class
gen.visitJumpInsn(IF_ACMPEQ, callLabel); //target
if(protocolOn != null)
{
gen.dup(); //target, target
gen.instanceOf(Type.getType(protocolOn));
gen.ifZCmp(GeneratorAdapter.NE, onLabel);
}
gen.dup(); //target, target
gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class
gen.putStatic(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target
gen.mark(callLabel); //target
objx.emitVar(gen, v);
gen.invokeVirtual(VAR_TYPE, Method.getMethod("Object getRawRoot()")); //target, proto-fn
gen.swap();
emitArgsAndCall(1, context,objx,gen);
gen.goTo(endLabel);
gen.mark(onLabel); //target
if(protocolOn != null)
{
gen.checkCast(Type.getType(protocolOn));
MethodExpr.emitTypedArgs(objx, gen, onMethod.getParameterTypes(), RT.subvec(args,1,args.count()));
if(context == C.RETURN)
{
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearLocals(gen);
}
Method m = new Method(onMethod.getName(), Type.getReturnType(onMethod), Type.getArgumentTypes(onMethod));
gen.invokeInterface(Type.getType(protocolOn), m);
HostExpr.emitBoxReturn(objx, gen, onMethod.getReturnType());
}
gen.mark(endLabel);
}
void emitArgsAndCall(int firstArgToEmit, C context, ObjExpr objx, GeneratorAdapter gen){
for(int i = firstArgToEmit; i < Math.min(MAX_POSITIONAL_ARITY, args.count()); i++)
{
Expr e = (Expr) args.nth(i);
e.emit(C.EXPRESSION, objx, gen);
}
if(args.count() > MAX_POSITIONAL_ARITY)
{
PersistentVector restArgs = PersistentVector.EMPTY;
for(int i = MAX_POSITIONAL_ARITY; i < args.count(); i++)
{
restArgs = restArgs.cons(args.nth(i));
}
MethodExpr.emitArgsAsArray(restArgs, objx, gen);
}
gen.visitLineNumber(line, gen.mark());
if(tailPosition && !objx.canBeDirect)
{
ObjMethod method = (ObjMethod) METHOD.deref();
method.emitClearThis(gen);
}
gen.invokeInterface(IFN_TYPE, new Method("invoke", OBJECT_TYPE, ARG_TYPES[Math.min(MAX_POSITIONAL_ARITY + 1,
args.count())]));
}
public boolean hasJavaClass() {
return tag != null;
}
public Class getJavaClass() {
if (jc == null)
jc = HostExpr.tagToClass(tag);
return jc;
}
static public Expr parse(C context, ISeq form) {
boolean tailPosition = inTailCall(context);
if(context != C.EVAL)
context = C.EXPRESSION;
Expr fexpr = analyze(context, form.first());
if(fexpr instanceof VarExpr && ((VarExpr)fexpr).var.equals(INSTANCE) && RT.count(form) == 3)
{
Expr sexpr = analyze(C.EXPRESSION, RT.second(form));
if(sexpr instanceof ConstantExpr)
{
Object val = ((ConstantExpr) sexpr).val();
if(val instanceof Class)
{
return new InstanceOfExpr((Class) val, analyze(context, RT.third(form)));
}
}
}
if(RT.booleanCast(getCompilerOption(directLinkingKey))
&& fexpr instanceof VarExpr
&& context != C.EVAL)
{
Var v = ((VarExpr)fexpr).var;
if(!v.isDynamic() && !RT.booleanCast(RT.get(v.meta(), redefKey, false)))
{
Symbol formtag = tagOf(form);
Object arglists = RT.get(RT.meta(v), arglistsKey);
int arity = RT.count(form.next());
Object sigtag = sigTag(arity, v);
Object vtag = RT.get(RT.meta(v), RT.TAG_KEY);
Expr ret = StaticInvokeExpr
.parse(v, RT.next(form), formtag != null ? formtag : sigtag != null ? sigtag : vtag, tailPosition);
if(ret != null)
{
// System.out.println("invoke direct: " + v);
return ret;
}
// System.out.println("NOT direct: " + v);
}
}
if(fexpr instanceof VarExpr && context != C.EVAL)
{
Var v = ((VarExpr)fexpr).var;
Object arglists = RT.get(RT.meta(v), arglistsKey);
int arity = RT.count(form.next());
for(ISeq s = RT.seq(arglists); s != null; s = s.next())
{
IPersistentVector args = (IPersistentVector) s.first();
if(args.count() == arity)
{
String primc = FnMethod.primInterface(args);
if(primc != null)
return analyze(context,
((IObj)RT.listStar(Symbol.intern(".invokePrim"),
((Symbol) form.first()).withMeta(RT.map(RT.TAG_KEY, Symbol.intern(primc))),
form.next())).withMeta((IPersistentMap)RT.conj(RT.meta(v), RT.meta(form))));
break;
}
}
}
if(fexpr instanceof KeywordExpr && RT.count(form) == 2 && KEYWORD_CALLSITES.isBound())
{
// fexpr = new ConstantExpr(new KeywordCallSite(((KeywordExpr)fexpr).k));
Expr target = analyze(context, RT.second(form));
return new KeywordInvokeExpr((String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form),
(KeywordExpr) fexpr, target);
}
// Preserving the existing static field bug that replaces a reference in parens with
// the field itself rather than trying to invoke the value in the field. This is
// an exception to the uniform Class/member qualification per CLJ-2806 ticket.
if(fexpr instanceof StaticFieldExpr)
return fexpr;
PersistentVector args = PersistentVector.EMPTY;
for(ISeq s = RT.seq(form.next()); s != null; s = s.next())
{
args = args.cons(analyze(context, s.first()));
}
if(fexpr instanceof QualifiedMethodExpr)
return toHostExpr((QualifiedMethodExpr)fexpr, (String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), tailPosition, args);
// if(args.count() > MAX_POSITIONAL_ARITY)
// throw new IllegalArgumentException(
// String.format("No more than %d args supported", MAX_POSITIONAL_ARITY));
return new InvokeExpr((String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), fexpr, args, tailPosition);
}
private static Expr toHostExpr(QualifiedMethodExpr qmexpr, String source, int line, int column, Symbol tag, boolean tailPosition, IPersistentVector args) {
if(qmexpr.hintedSig != null) {
Executable method = QualifiedMethodExpr.resolveHintedMethod(qmexpr.c, qmexpr.methodName, qmexpr.kind, qmexpr.hintedSig);
switch(qmexpr.kind) {
case CTOR:
return new NewExpr(qmexpr.c, (Constructor) method, args, line, column);
case INSTANCE:
return new InstanceMethodExpr(source, line, column, tag, (Expr) RT.first(args),
qmexpr.c, munge(qmexpr.methodName), (java.lang.reflect.Method) method,
PersistentVector.create(RT.next(args)),
tailPosition);
default:
return new StaticMethodExpr(source, line, column, tag, qmexpr.c,
munge(qmexpr.methodName), (java.lang.reflect.Method) method, args, tailPosition);
}
}
else {
switch(qmexpr.kind) {
case CTOR:
return new NewExpr(qmexpr.c, args, line, column);
case INSTANCE:
return new InstanceMethodExpr(source, line, column, tag, (Expr) RT.first(args), qmexpr.c,
munge(qmexpr.methodName), PersistentVector.create(RT.next(args)), tailPosition);
default:
return new StaticMethodExpr(source, line, column, tag, qmexpr.c,
munge(qmexpr.methodName), args, tailPosition);
}
}
}
}
static class SourceDebugExtensionAttribute extends Attribute{
public SourceDebugExtensionAttribute(){
super("SourceDebugExtension");
}
void writeSMAP(ClassWriter cw, String smap){
ByteVector bv = write(cw, null, -1, -1, -1);
bv.putUTF8(smap);
}
}
static public class FnExpr extends ObjExpr{
final static Type aFnType = Type.getType(AFunction.class);
final static Type restFnType = Type.getType(RestFn.class);
//if there is a variadic overload (there can only be one) it is stored here
FnMethod variadicMethod = null;
IPersistentCollection methods;
private boolean hasPrimSigs;
private boolean hasMeta;
private boolean hasEnclosingMethod;
// String superName = null;
Class jc;
public FnExpr(Object tag){
super(tag);
}
public boolean hasJavaClass() {
return true;
}
boolean supportsMeta(){
return hasMeta;
}
public Class getJavaClass() {
if (jc == null)
jc = tag != null ? HostExpr.tagToClass(tag) : AFunction.class;
return jc;
}
protected void emitMethods(ClassVisitor cv){
//override of invoke/doInvoke for each method
for(ISeq s = RT.seq(methods); s != null; s = s.next())
{
ObjMethod method = (ObjMethod) s.first();
method.emit(this, cv);
}
if(isVariadic())
{
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,
Method.getMethod("int getRequiredArity()"),
null,
null,
cv);
gen.visitCode();
gen.push(variadicMethod.reqParms.count());
gen.returnValue();
gen.endMethod();
}
}
static Expr parse(C context, ISeq form, String name) {
ISeq origForm = form;
FnExpr fn = new FnExpr(tagOf(form));
Keyword retkey = Keyword.intern(null, "rettag");
Object rettag = RT.get(RT.meta(form), retkey);
fn.src = form;
ObjMethod enclosingMethod = (ObjMethod) METHOD.deref();
fn.hasEnclosingMethod = enclosingMethod != null;
if(((IMeta) form.first()).meta() != null)
{
fn.onceOnly = RT.booleanCast(RT.get(RT.meta(form.first()), Keyword.intern(null, "once")));
// fn.superName = (String) RT.get(RT.meta(form.first()), Keyword.intern(null, "super-name"));
}
//fn.thisName = name;
String basename = (enclosingMethod != null ?
enclosingMethod.objx.name
: (munge(currentNS().name.name))) + "$";
Symbol nm = null;
if(RT.second(form) instanceof Symbol) {
nm = (Symbol) RT.second(form);
name = nm.name + "__" + RT.nextID();
} else {
if(name == null)
name = "fn__" + RT.nextID();
else if (enclosingMethod != null)
name += "__" + RT.nextID();
}
String simpleName = munge(name).replace(".", "_DOT_");
fn.name = basename + simpleName;
fn.internalName = fn.name.replace('.', '/');
fn.objtype = Type.getObjectType(fn.internalName);
ArrayList prims = new ArrayList();
try
{
Var.pushThreadBindings(
RT.mapUniqueKeys(CONSTANTS, PersistentVector.EMPTY,
CONSTANT_IDS, new IdentityHashMap(),
KEYWORDS, PersistentHashMap.EMPTY,
VARS, PersistentHashMap.EMPTY,
KEYWORD_CALLSITES, PersistentVector.EMPTY,
PROTOCOL_CALLSITES, PersistentVector.EMPTY,
VAR_CALLSITES, emptyVarCallSites(),
NO_RECUR, null
));
//arglist might be preceded by symbol naming this fn
if(nm != null)
{
fn.thisName = nm.name;
form = RT.cons(FN, RT.next(RT.next(form)));
}
//now (fn [args] body...) or (fn ([args] body...) ([args2] body2...) ...)
//turn former into latter
if(RT.second(form) instanceof IPersistentVector)
form = RT.list(FN, RT.next(form));
fn.line = lineDeref();
fn.column = columnDeref();
FnMethod[] methodArray = new FnMethod[MAX_POSITIONAL_ARITY + 1];
FnMethod variadicMethod = null;
boolean usesThis = false;
for(ISeq s = RT.next(form); s != null; s = RT.next(s))
{
FnMethod f = FnMethod.parse(fn, (ISeq) RT.first(s), rettag);
if(f.usesThis)
{
// System.out.println(fn.name + " use this");
usesThis = true;
}
if(f.isVariadic())
{
if(variadicMethod == null)
variadicMethod = f;
else
throw Util.runtimeException("Can't have more than 1 variadic overload");
}
else if(methodArray[f.reqParms.count()] == null)
methodArray[f.reqParms.count()] = f;
else
throw Util.runtimeException("Can't have 2 overloads with same arity");
if(f.prim != null)
prims.add(f.prim);
}
if(variadicMethod != null)
{
for(int i = variadicMethod.reqParms.count() + 1; i <= MAX_POSITIONAL_ARITY; i++)
if(methodArray[i] != null)
throw Util.runtimeException(
"Can't have fixed arity function with more params than variadic function");
}
fn.canBeDirect = !fn.hasEnclosingMethod && fn.closes.count() == 0 && !usesThis;
IPersistentCollection methods = null;
for(int i = 0; i < methodArray.length; i++)
if(methodArray[i] != null)
methods = RT.conj(methods, methodArray[i]);
if(variadicMethod != null)
methods = RT.conj(methods, variadicMethod);
if(fn.canBeDirect){
for(FnMethod fm : (Collection)methods)
{
if(fm.locals != null)
{
for(LocalBinding lb : (Collection)RT.keys(fm.locals))
{
if(lb.isArg)
lb.idx -= 1;
}
}
}
}
fn.methods = methods;
fn.variadicMethod = variadicMethod;
fn.keywords = (IPersistentMap) KEYWORDS.deref();
fn.vars = (IPersistentMap) VARS.deref();
fn.constants = (PersistentVector) CONSTANTS.deref();
fn.keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();
fn.protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref();
fn.varCallsites = (IPersistentSet) VAR_CALLSITES.deref();
fn.constantsID = RT.nextID();
// DynamicClassLoader loader = (DynamicClassLoader) LOADER.get();
// loader.registerConstants(fn.constantsID, fn.constants.toArray());
}
finally
{
Var.popThreadBindings();
}
fn.hasPrimSigs = prims.size() > 0;
IPersistentMap fmeta = RT.meta(origForm);
if(fmeta != null)
fmeta = fmeta.without(RT.LINE_KEY).without(RT.COLUMN_KEY).without(RT.FILE_KEY).without(retkey);
fn.hasMeta = RT.count(fmeta) > 0;
try
{
fn.compile(fn.isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFunction",
(prims.size() == 0)?
null
:prims.toArray(new String[prims.size()]),
fn.onceOnly);
}
catch(IOException e)
{
throw Util.sneakyThrow(e);
}
fn.getCompiledClass();
if(fn.supportsMeta())
{
//System.err.println(name + " supports meta");
return new MetaExpr(fn, MapExpr
.parse(context == C.EVAL ? context : C.EXPRESSION, fmeta));
}
else
return fn;
}
public final ObjMethod variadicMethod(){
return variadicMethod;
}
boolean isVariadic(){
return variadicMethod != null;
}
public final IPersistentCollection methods(){
return methods;
}
public void emitForDefn(ObjExpr objx, GeneratorAdapter gen){
// if(!hasPrimSigs && closes.count() == 0)
// {
// Type thunkType = Type.getType(FnLoaderThunk.class);
//// presumes var on stack
// gen.dup();
// gen.newInstance(thunkType);
// gen.dupX1();
// gen.swap();
// gen.push(internalName.replace('/','.'));
// gen.invokeConstructor(thunkType,Method.getMethod("void (clojure.lang.Var,String)"));
// }
// else
emit(C.EXPRESSION,objx,gen);
}
}
static public class ObjExpr implements Expr{
static final String CONST_PREFIX = "const__";
String name;
//String simpleName;
String internalName;
String thisName;
Type objtype;
public final Object tag;
//localbinding->itself
IPersistentMap closes = PersistentHashMap.EMPTY;
//localbndingexprs
IPersistentVector closesExprs = PersistentVector.EMPTY;
//symbols
IPersistentSet volatiles = PersistentHashSet.EMPTY;
//symbol->lb
IPersistentMap fields = null;
//hinted fields
IPersistentVector hintedFields = PersistentVector.EMPTY;
//Keyword->KeywordExpr
IPersistentMap keywords = PersistentHashMap.EMPTY;
IPersistentMap vars = PersistentHashMap.EMPTY;
Class compiledClass;
int line;
int column;
PersistentVector constants;
IPersistentSet usedConstants = PersistentHashSet.EMPTY;
int constantsID;
int altCtorDrops = 0;
IPersistentVector keywordCallsites;
IPersistentVector protocolCallsites;
IPersistentSet varCallsites;
boolean onceOnly = false;
Object src;
IPersistentMap opts = PersistentHashMap.EMPTY;
final static Method voidctor = Method.getMethod("void ()");
protected IPersistentMap classMeta;
protected boolean canBeDirect;
public final String name(){
return name;
}
// public final String simpleName(){
// return simpleName;
// }
public final String internalName(){
return internalName;
}
public final String thisName(){
return thisName;
}
public final Type objtype(){
return objtype;
}
public final IPersistentMap closes(){
return closes;
}
public final IPersistentMap keywords(){
return keywords;
}
public final IPersistentMap vars(){
return vars;
}
public final Class compiledClass(){
return compiledClass;
}
public final int line(){
return line;
}
public final int column(){
return column;
}
public final PersistentVector constants(){
return constants;
}
public final int constantsID(){
return constantsID;
}
final static Method kwintern = Method.getMethod("clojure.lang.Keyword intern(String, String)");
final static Method symintern = Method.getMethod("clojure.lang.Symbol intern(String)");
final static Method varintern =
Method.getMethod("clojure.lang.Var intern(clojure.lang.Symbol, clojure.lang.Symbol)");
final static Type DYNAMIC_CLASSLOADER_TYPE = Type.getType(DynamicClassLoader.class);
final static Method getClassMethod = Method.getMethod("Class getClass()");
final static Method getClassLoaderMethod = Method.getMethod("ClassLoader getClassLoader()");
final static Method getConstantsMethod = Method.getMethod("Object[] getConstants(int)");
final static Method readStringMethod = Method.getMethod("Object readString(String)");
final static Type ILOOKUP_SITE_TYPE = Type.getType(ILookupSite.class);
final static Type ILOOKUP_THUNK_TYPE = Type.getType(ILookupThunk.class);
final static Type KEYWORD_LOOKUPSITE_TYPE = Type.getType(KeywordLookupSite.class);
private DynamicClassLoader loader;
private byte[] bytecode;
public ObjExpr(Object tag){
this.tag = tag;
}
static String trimGenID(String name){
int i = name.lastIndexOf("__");
return i==-1?name:name.substring(0,i);
}
Type[] ctorTypes(){
IPersistentVector tv = !supportsMeta()?PersistentVector.EMPTY:RT.vector(IPERSISTENTMAP_TYPE);
for(ISeq s = RT.keys(closes); s != null; s = s.next())
{
LocalBinding lb = (LocalBinding) s.first();
if(lb.getPrimitiveType() != null)
tv = tv.cons(Type.getType(lb.getPrimitiveType()));
else
tv = tv.cons(OBJECT_TYPE);
}
Type[] ret = new Type[tv.count()];
for(int i = 0; i < tv.count(); i++)
ret[i] = (Type) tv.nth(i);
return ret;
}
void compile(String superName, String[] interfaceNames, boolean oneTimeUse) throws IOException{
//create bytecode for a class
//with name current_ns.defname[$letname]+
//anonymous fns get names fn__id
//derived from AFn/RestFn
ClassWriter cw = classWriter();
// ClassWriter cw = new ClassWriter(0);
ClassVisitor cv = cw;
// ClassVisitor cv = new TraceClassVisitor(new CheckClassAdapter(cw), new PrintWriter(System.out));
//ClassVisitor cv = new TraceClassVisitor(cw, new PrintWriter(System.out));
cv.visit(V1_8, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, internalName, null,superName,interfaceNames);
// superName != null ? superName :
// (isVariadic() ? "clojure/lang/RestFn" : "clojure/lang/AFunction"), null);
String source = (String) SOURCE.deref();
int lineBefore = (Integer) LINE_BEFORE.deref();
int lineAfter = (Integer) LINE_AFTER.deref() + 1;
int columnBefore = (Integer) COLUMN_BEFORE.deref();
int columnAfter = (Integer) COLUMN_AFTER.deref() + 1;
if(source != null && SOURCE_PATH.deref() != null)
{
//cv.visitSource(source, null);
String smap = "SMAP\n" +
((source.lastIndexOf('.') > 0) ?
source.substring(0, source.lastIndexOf('.'))
:source)
// : simpleName)
+ ".java\n" +
"Clojure\n" +
"*S Clojure\n" +
"*F\n" +
"+ 1 " + source + "\n" +
(String) SOURCE_PATH.deref() + "\n" +
"*L\n" +
String.format("%d#1,%d:%d\n", lineBefore, lineAfter - lineBefore, lineBefore) +
"*E";
cv.visitSource(source, smap);
}
addAnnotation(cv, classMeta);
// for(int i=0;i", Type.VOID_TYPE, ctorTypes());
GeneratorAdapter ctorgen = new GeneratorAdapter(ACC_PUBLIC,
m,
null,
null,
cv);
Label start = ctorgen.newLabel();
Label end = ctorgen.newLabel();
ctorgen.visitCode();
ctorgen.visitLineNumber(line, ctorgen.mark());
ctorgen.visitLabel(start);
ctorgen.loadThis();
// if(superName != null)
ctorgen.invokeConstructor(Type.getObjectType(superName), voidctor);
// else if(isVariadic()) //RestFn ctor takes reqArity arg
// {
// ctorgen.push(variadicMethod.reqParms.count());
// ctorgen.invokeConstructor(restFnType, restfnctor);
// }
// else
// ctorgen.invokeConstructor(aFnType, voidctor);
// if(vars.count() > 0)
// {
// ctorgen.loadThis();
// ctorgen.getStatic(VAR_TYPE,"rev",Type.INT_TYPE);
// ctorgen.push(-1);
// ctorgen.visitInsn(Opcodes.IADD);
// ctorgen.putField(objtype, "__varrev__", Type.INT_TYPE);
// }
if(supportsMeta())
{
ctorgen.loadThis();
ctorgen.visitVarInsn(IPERSISTENTMAP_TYPE.getOpcode(Opcodes.ILOAD), 1);
ctorgen.putField(objtype, "__meta", IPERSISTENTMAP_TYPE);
}
int a = supportsMeta()?2:1;
for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a)
{
LocalBinding lb = (LocalBinding) s.first();
ctorgen.loadThis();
Class primc = lb.getPrimitiveType();
if(primc != null)
{
ctorgen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), a);
ctorgen.putField(objtype, lb.name, Type.getType(primc));
if(primc == Long.TYPE || primc == Double.TYPE)
++a;
}
else
{
ctorgen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), a);
ctorgen.putField(objtype, lb.name, OBJECT_TYPE);
}
closesExprs = closesExprs.cons(new LocalBindingExpr(lb, null));
}
ctorgen.visitLabel(end);
ctorgen.returnValue();
ctorgen.endMethod();
if(altCtorDrops > 0)
{
//ctor that takes closed-overs and inits base + fields
Type[] ctorTypes = ctorTypes();
Type[] altCtorTypes = new Type[ctorTypes.length-altCtorDrops];
for(int i=0;i", Type.VOID_TYPE, altCtorTypes);
ctorgen = new GeneratorAdapter(ACC_PUBLIC,
alt,
null,
null,
cv);
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.loadArgs();
ctorgen.visitInsn(Opcodes.ACONST_NULL); //__meta
ctorgen.visitInsn(Opcodes.ACONST_NULL); //__extmap
ctorgen.visitInsn(Opcodes.ICONST_0); //__hash
ctorgen.visitInsn(Opcodes.ICONST_0); //__hasheq
ctorgen.invokeConstructor(objtype, new Method("", Type.VOID_TYPE, ctorTypes));
ctorgen.returnValue();
ctorgen.endMethod();
// alt ctor no __hash, __hasheq
altCtorTypes = new Type[ctorTypes.length-2];
for(int i=0;i", Type.VOID_TYPE, altCtorTypes);
ctorgen = new GeneratorAdapter(ACC_PUBLIC,
alt,
null,
null,
cv);
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.loadArgs();
ctorgen.visitInsn(Opcodes.ICONST_0); //__hash
ctorgen.visitInsn(Opcodes.ICONST_0); //__hasheq
ctorgen.invokeConstructor(objtype, new Method("", Type.VOID_TYPE, ctorTypes));
ctorgen.returnValue();
ctorgen.endMethod();
}
if(supportsMeta())
{
//ctor that takes closed-overs but not meta
Type[] ctorTypes = ctorTypes();
Type[] noMetaCtorTypes = new Type[ctorTypes.length-1];
for(int i=1;i", Type.VOID_TYPE, noMetaCtorTypes);
ctorgen = new GeneratorAdapter(ACC_PUBLIC,
alt,
null,
null,
cv);
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.visitInsn(Opcodes.ACONST_NULL); //null meta
ctorgen.loadArgs();
ctorgen.invokeConstructor(objtype, new Method("", Type.VOID_TYPE, ctorTypes));
ctorgen.returnValue();
ctorgen.endMethod();
//meta()
Method meth = Method.getMethod("clojure.lang.IPersistentMap meta()");
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,
meth,
null,
null,
cv);
gen.visitCode();
gen.loadThis();
gen.getField(objtype,"__meta",IPERSISTENTMAP_TYPE);
gen.returnValue();
gen.endMethod();
//withMeta()
meth = Method.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)");
gen = new GeneratorAdapter(ACC_PUBLIC,
meth,
null,
null,
cv);
gen.visitCode();
gen.newInstance(objtype);
gen.dup();
gen.loadArg(0);
for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a)
{
LocalBinding lb = (LocalBinding) s.first();
gen.loadThis();
Class primc = lb.getPrimitiveType();
if(primc != null)
{
gen.getField(objtype, lb.name, Type.getType(primc));
}
else
{
gen.getField(objtype, lb.name, OBJECT_TYPE);
}
}
gen.invokeConstructor(objtype, new Method("", Type.VOID_TYPE, ctorTypes));
gen.returnValue();
gen.endMethod();
}
emitStatics(cv);
emitMethods(cv);
//static fields for constants
for(int i = 0; i < constants.count(); i++)
{
if(usedConstants.contains(i))
cv.visitField(ACC_PUBLIC + ACC_FINAL
+ ACC_STATIC, constantName(i), constantType(i).getDescriptor(),
null, null);
}
//static fields for lookup sites
for(int i = 0; i < keywordCallsites.count(); i++)
{
cv.visitField(ACC_FINAL
+ ACC_STATIC, siteNameStatic(i), KEYWORD_LOOKUPSITE_TYPE.getDescriptor(),
null, null);
cv.visitField(ACC_STATIC, thunkNameStatic(i), ILOOKUP_THUNK_TYPE.getDescriptor(),
null, null);
}
//static init for constants, keywords and vars
GeneratorAdapter clinitgen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC,
Method.getMethod("void ()"),
null,
null,
cv);
clinitgen.visitCode();
clinitgen.visitLineNumber(line, clinitgen.mark());
if(constants.count() > 0)
{
emitConstants(clinitgen);
}
if(keywordCallsites.count() > 0)
emitKeywordCallsites(clinitgen);
/*
for(int i=0;i(clojure.lang.Keyword)"));
clinitgen.dup();
clinitgen.putStatic(objtype, siteNameStatic(i), KEYWORD_LOOKUPSITE_TYPE);
clinitgen.putStatic(objtype, thunkNameStatic(i), ILOOKUP_THUNK_TYPE);
}
}
protected void emitStatics(ClassVisitor gen){
}
protected void emitMethods(ClassVisitor gen){
}
void emitListAsObjectArray(Object value, GeneratorAdapter gen){
gen.push(((List) value).size());
gen.newArray(OBJECT_TYPE);
int i = 0;
for(Iterator it = ((List) value).iterator(); it.hasNext(); i++)
{
gen.dup();
gen.push(i);
emitValue(it.next(), gen);
gen.arrayStore(OBJECT_TYPE);
}
}
void emitValue(Object value, GeneratorAdapter gen){
boolean partial = true;
//System.out.println(value.getClass().toString());
if(value == null)
gen.visitInsn(Opcodes.ACONST_NULL);
else if(value instanceof String)
{
gen.push((String) value);
}
else if(value instanceof Boolean)
{
if(((Boolean) value).booleanValue())
gen.getStatic(BOOLEAN_OBJECT_TYPE, "TRUE", BOOLEAN_OBJECT_TYPE);
else
gen.getStatic(BOOLEAN_OBJECT_TYPE,"FALSE",BOOLEAN_OBJECT_TYPE);
}
else if(value instanceof Integer)
{
gen.push(((Integer) value).intValue());
gen.invokeStatic(Type.getType(Integer.class), Method.getMethod("Integer valueOf(int)"));
}
else if(value instanceof Long)
{
gen.push(((Long) value).longValue());
gen.invokeStatic(Type.getType(Long.class), Method.getMethod("Long valueOf(long)"));
}
else if(value instanceof Double)
{
gen.push(((Double) value).doubleValue());
gen.invokeStatic(Type.getType(Double.class), Method.getMethod("Double valueOf(double)"));
}
else if(value instanceof Character)
{
gen.push(((Character) value).charValue());
gen.invokeStatic(Type.getType(Character.class), Method.getMethod("Character valueOf(char)"));
}
else if(value instanceof Class)
{
Class cc = (Class)value;
if(cc.isPrimitive())
{
Type bt;
if ( cc == boolean.class ) bt = Type.getType(Boolean.class);
else if ( cc == byte.class ) bt = Type.getType(Byte.class);
else if ( cc == char.class ) bt = Type.getType(Character.class);
else if ( cc == double.class ) bt = Type.getType(Double.class);
else if ( cc == float.class ) bt = Type.getType(Float.class);
else if ( cc == int.class ) bt = Type.getType(Integer.class);
else if ( cc == long.class ) bt = Type.getType(Long.class);
else if ( cc == short.class ) bt = Type.getType(Short.class);
else throw Util.runtimeException(
"Can't embed unknown primitive in code: " + value);
gen.getStatic( bt, "TYPE", Type.getType(Class.class) );
}
else
{
gen.push(destubClassName(cc.getName()));
gen.invokeStatic(RT_TYPE, Method.getMethod("Class classForName(String)"));
}
}
else if(value instanceof Symbol)
{
gen.push(((Symbol) value).ns);
gen.push(((Symbol) value).name);
gen.invokeStatic(Type.getType(Symbol.class),
Method.getMethod("clojure.lang.Symbol intern(String,String)"));
}
else if(value instanceof Keyword)
{
gen.push(((Keyword) value).sym.ns);
gen.push(((Keyword) value).sym.name);
gen.invokeStatic(RT_TYPE,
Method.getMethod("clojure.lang.Keyword keyword(String,String)"));
}
// else if(value instanceof KeywordCallSite)
// {
// emitValue(((KeywordCallSite) value).k.sym, gen);
// gen.invokeStatic(Type.getType(KeywordCallSite.class),
// Method.getMethod("clojure.lang.KeywordCallSite create(clojure.lang.Symbol)"));
// }
else if(value instanceof Var)
{
Var var = (Var) value;
gen.push(var.ns.name.toString());
gen.push(var.sym.toString());
gen.invokeStatic(RT_TYPE, Method.getMethod("clojure.lang.Var var(String,String)"));
}
else if(value instanceof IType)
{
Method ctor = new Method("", Type.getConstructorDescriptor(value.getClass().getConstructors()[0]));
gen.newInstance(Type.getType(value.getClass()));
gen.dup();
IPersistentVector fields = (IPersistentVector) Reflector.invokeStaticMethod(value.getClass(), "getBasis", new Object[]{});
for(ISeq s = RT.seq(fields); s != null; s = s.next())
{
Symbol field = (Symbol) s.first();
Class k = tagClass(tagOf(field));
Object val = Reflector.getInstanceField(value, munge(field.name));
emitValue(val, gen);
if(k.isPrimitive())
{
Type b = Type.getType(boxClass(k));
String p = Type.getType(k).getDescriptor();
String n = k.getName();
gen.invokeVirtual(b, new Method(n+"Value", "()"+p));
}
}
gen.invokeConstructor(Type.getType(value.getClass()), ctor);
}
else if(value instanceof IRecord)
{
Method createMethod = Method.getMethod(value.getClass().getName() + " create(clojure.lang.IPersistentMap)");
emitValue(PersistentArrayMap.create((java.util.Map) value), gen);
gen.invokeStatic(getType(value.getClass()), createMethod);
}
else if(value instanceof IPersistentMap)
{
List entries = new ArrayList();
for(Map.Entry entry : (Set) ((Map) value).entrySet())
{
entries.add(entry.getKey());
entries.add(entry.getValue());
}
emitListAsObjectArray(entries, gen);
gen.invokeStatic(RT_TYPE,
Method.getMethod("clojure.lang.IPersistentMap map(Object[])"));
}
else if(value instanceof IPersistentVector)
{
IPersistentVector args = (IPersistentVector) value;
if(args.count() <= Tuple.MAX_SIZE)
{
for(int i = 0; i < args.count(); i++) {
emitValue(args.nth(i), gen);
}
gen.invokeStatic(TUPLE_TYPE, createTupleMethods[args.count()]);
}
else
{
emitListAsObjectArray(value, gen);
gen.invokeStatic(RT_TYPE, Method.getMethod(
"clojure.lang.IPersistentVector vector(Object[])"));
}
}
else if(value instanceof PersistentHashSet)
{
ISeq vs = RT.seq(value);
if(vs == null)
gen.getStatic(Type.getType(PersistentHashSet.class),"EMPTY",Type.getType(PersistentHashSet.class));
else
{
emitListAsObjectArray(vs, gen);
gen.invokeStatic(Type.getType(PersistentHashSet.class), Method.getMethod(
"clojure.lang.PersistentHashSet create(Object[])"));
}
}
else if(value instanceof ISeq || value instanceof IPersistentList)
{
emitListAsObjectArray(value, gen);
gen.invokeStatic(Type.getType(java.util.Arrays.class),
Method.getMethod("java.util.List asList(Object[])"));
gen.invokeStatic(Type.getType(PersistentList.class),
Method.getMethod(
"clojure.lang.IPersistentList create(java.util.List)"));
}
else if(value instanceof Pattern)
{
emitValue(value.toString(), gen);
gen.invokeStatic(Type.getType(Pattern.class),
Method.getMethod("java.util.regex.Pattern compile(String)"));
}
else
{
String cs = null;
try
{
cs = RT.printString(value);
// System.out.println("WARNING SLOW CODE: " + Util.classOf(value) + " -> " + cs);
}
catch(Exception e)
{
throw Util.runtimeException(
"Can't embed object in code, maybe print-dup not defined: " +
value);
}
if(cs.length() == 0)
throw Util.runtimeException(
"Can't embed unreadable object in code: " + value);
if(cs.startsWith("#<"))
throw Util.runtimeException(
"Can't embed unreadable object in code: " + cs);
gen.push(cs);
gen.invokeStatic(RT_TYPE, readStringMethod);
partial = false;
}
if(partial)
{
if(value instanceof IObj && RT.count(((IObj) value).meta()) > 0)
{
gen.checkCast(IOBJ_TYPE);
Object m = ((IObj) value).meta();
emitValue(elideMeta(m), gen);
gen.checkCast(IPERSISTENTMAP_TYPE);
gen.invokeInterface(IOBJ_TYPE,
Method.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)"));
}
}
}
void emitConstants(GeneratorAdapter clinitgen){
try
{
Var.pushThreadBindings(RT.map(RT.PRINT_DUP, RT.T));
for(int i = 0; i < constants.count(); i++)
{
if(usedConstants.contains(i))
{
emitValue(constants.nth(i), clinitgen);
clinitgen.checkCast(constantType(i));
clinitgen.putStatic(objtype, constantName(i), constantType(i));
}
}
}
finally
{
Var.popThreadBindings();
}
}
boolean isMutable(LocalBinding lb){
return isVolatile(lb) ||
RT.booleanCast(RT.contains(fields, lb.sym)) &&
RT.booleanCast(RT.get(lb.sym.meta(), Keyword.intern("unsynchronized-mutable")));
}
boolean isVolatile(LocalBinding lb){
return RT.booleanCast(RT.contains(fields, lb.sym)) &&
RT.booleanCast(RT.get(lb.sym.meta(), Keyword.intern("volatile-mutable")));
}
boolean isDeftype(){
return fields != null;
}
boolean supportsMeta(){
return !isDeftype();
}
void emitClearCloses(GeneratorAdapter gen){
// int a = 1;
// for(ISeq s = RT.keys(closes); s != null; s = s.next(), ++a)
// {
// LocalBinding lb = (LocalBinding) s.first();
// Class primc = lb.getPrimitiveType();
// if(primc == null)
// {
// gen.loadThis();
// gen.visitInsn(Opcodes.ACONST_NULL);
// gen.putField(objtype, lb.name, OBJECT_TYPE);
// }
// }
}
synchronized Class getCompiledClass(){
if(compiledClass == null)
// if(RT.booleanCast(COMPILE_FILES.deref()))
// compiledClass = RT.classForName(name);//loader.defineClass(name, bytecode);
// else
{
loader = (DynamicClassLoader) LOADER.deref();
compiledClass = loader.defineClass(name, bytecode, src);
}
return compiledClass;
}
public Object eval() {
if(isDeftype())
return null;
try
{
return getCompiledClass().getDeclaredConstructor().newInstance();
}
catch(Exception e)
{
throw Util.sneakyThrow(e);
}
}
public void emitLetFnInits(GeneratorAdapter gen, ObjExpr objx, IPersistentSet letFnLocals){
//objx arg is enclosing objx, not this
gen.checkCast(objtype);
for(ISeq s = RT.keys(closes); s != null; s = s.next())
{
LocalBinding lb = (LocalBinding) s.first();
if(letFnLocals.contains(lb))
{
Class primc = lb.getPrimitiveType();
gen.dup();
if(primc != null)
{
objx.emitUnboxedLocal(gen, lb);
gen.putField(objtype, lb.name, Type.getType(primc));
}
else
{
objx.emitLocal(gen, lb, false);
gen.putField(objtype, lb.name, OBJECT_TYPE);
}
}
}
gen.pop();
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
//emitting a Fn means constructing an instance, feeding closed-overs from enclosing scope, if any
//objx arg is enclosing objx, not this
// getCompiledClass();
if(isDeftype())
{
gen.visitInsn(Opcodes.ACONST_NULL);
}
else
{
gen.newInstance(objtype);
gen.dup();
if(supportsMeta())
gen.visitInsn(Opcodes.ACONST_NULL);
for(ISeq s = RT.seq(closesExprs); s != null; s = s.next())
{
LocalBindingExpr lbe = (LocalBindingExpr) s.first();
LocalBinding lb = lbe.b;
if(lb.getPrimitiveType() != null)
objx.emitUnboxedLocal(gen, lb);
else
objx.emitLocal(gen, lb, lbe.shouldClear);
}
gen.invokeConstructor(objtype, new Method("", Type.VOID_TYPE, ctorTypes()));
}
if(context == C.STATEMENT)
gen.pop();
}
public boolean hasJavaClass() {
return true;
}
Class jc;
public Class getJavaClass() {
if (jc == null)
jc = (compiledClass != null) ? compiledClass
: (tag != null) ? HostExpr.tagToClass(tag)
: IFn.class;
return jc;
}
public void emitAssignLocal(GeneratorAdapter gen, LocalBinding lb,Expr val){
if(!isMutable(lb))
throw new IllegalArgumentException("Cannot assign to non-mutable: " + lb.name);
Class primc = lb.getPrimitiveType();
gen.loadThis();
if(primc != null)
{
if(!(val instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr) val).canEmitPrimitive()))
throw new IllegalArgumentException("Must assign primitive to primitive mutable: " + lb.name);
MaybePrimitiveExpr me = (MaybePrimitiveExpr) val;
me.emitUnboxed(C.EXPRESSION, this, gen);
gen.putField(objtype, lb.name, Type.getType(primc));
}
else
{
val.emit(C.EXPRESSION, this, gen);
gen.putField(objtype, lb.name, OBJECT_TYPE);
}
}
private void emitLocal(GeneratorAdapter gen, LocalBinding lb, boolean clear){
if(closes.containsKey(lb))
{
Class primc = lb.getPrimitiveType();
gen.loadThis();
if(primc != null)
{
gen.getField(objtype, lb.name, Type.getType(primc));
HostExpr.emitBoxReturn(this, gen, primc);
}
else
{
gen.getField(objtype, lb.name, OBJECT_TYPE);
if(onceOnly && clear && lb.canBeCleared)
{
gen.loadThis();
gen.visitInsn(Opcodes.ACONST_NULL);
gen.putField(objtype, lb.name, OBJECT_TYPE);
}
}
}
else
{
int argoff = canBeDirect ?0:1;
Class primc = lb.getPrimitiveType();
// String rep = lb.sym.name + " " + lb.toString().substring(lb.toString().lastIndexOf('@'));
if(lb.isArg)
{
gen.loadArg(lb.idx-argoff);
if(primc != null)
HostExpr.emitBoxReturn(this, gen, primc);
else
{
if(clear && lb.canBeCleared)
{
// System.out.println("clear: " + rep);
gen.visitInsn(Opcodes.ACONST_NULL);
gen.storeArg(lb.idx - argoff);
}
else
{
// System.out.println("use: " + rep);
}
}
}
else
{
if(primc != null)
{
gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), lb.idx);
HostExpr.emitBoxReturn(this, gen, primc);
}
else
{
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), lb.idx);
if(clear && lb.canBeCleared)
{
// System.out.println("clear: " + rep);
gen.visitInsn(Opcodes.ACONST_NULL);
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), lb.idx);
}
else
{
// System.out.println("use: " + rep);
}
}
}
}
}
private void emitUnboxedLocal(GeneratorAdapter gen, LocalBinding lb){
int argoff = canBeDirect ?0:1;
Class primc = lb.getPrimitiveType();
if(closes.containsKey(lb))
{
gen.loadThis();
gen.getField(objtype, lb.name, Type.getType(primc));
}
else if(lb.isArg)
gen.loadArg(lb.idx-argoff);
else
gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ILOAD), lb.idx);
}
public void emitVar(GeneratorAdapter gen, Var var){
Integer i = (Integer) vars.valAt(var);
emitConstant(gen, i);
//gen.getStatic(fntype, munge(var.sym.toString()), VAR_TYPE);
}
final static Method varGetMethod = Method.getMethod("Object get()");
final static Method varGetRawMethod = Method.getMethod("Object getRawRoot()");
public void emitVarValue(GeneratorAdapter gen, Var v){
Integer i = (Integer) vars.valAt(v);
if(!v.isDynamic())
{
emitConstant(gen, i);
gen.invokeVirtual(VAR_TYPE, varGetRawMethod);
}
else
{
emitConstant(gen, i);
gen.invokeVirtual(VAR_TYPE, varGetMethod);
}
}
public void emitKeyword(GeneratorAdapter gen, Keyword k){
Integer i = (Integer) keywords.valAt(k);
emitConstant(gen, i);
// gen.getStatic(fntype, munge(k.sym.toString()), KEYWORD_TYPE);
}
public void emitConstant(GeneratorAdapter gen, int id){
usedConstants = (IPersistentSet) usedConstants.cons(id);
gen.getStatic(objtype, constantName(id), constantType(id));
}
String constantName(int id){
return CONST_PREFIX + id;
}
String siteName(int n){
return "__site__" + n;
}
String siteNameStatic(int n){
return siteName(n) + "__";
}
String thunkName(int n){
return "__thunk__" + n;
}
String cachedClassName(int n){
return "__cached_class__" + n;
}
String cachedVarName(int n){
return "__cached_var__" + n;
}
String varCallsiteName(int n){
return "__var__callsite__" + n;
}
String thunkNameStatic(int n){
return thunkName(n) + "__";
}
Type constantType(int id){
Object o = constants.nth(id);
Class c = clojure.lang.Util.classOf(o);
if(c!= null && Modifier.isPublic(c.getModifiers()))
{
//can't emit derived fn types due to visibility
if(LazySeq.class.isAssignableFrom(c))
return Type.getType(ISeq.class);
else if(c == Keyword.class)
return Type.getType(Keyword.class);
// else if(c == KeywordCallSite.class)
// return Type.getType(KeywordCallSite.class);
else if(RestFn.class.isAssignableFrom(c))
return Type.getType(RestFn.class);
else if(AFn.class.isAssignableFrom(c))
return Type.getType(AFn.class);
else if(c == Var.class)
return Type.getType(Var.class);
else if(c == String.class)
return Type.getType(String.class);
// return Type.getType(c);
}
return OBJECT_TYPE;
}
}
enum PATHTYPE {
PATH, BRANCH;
}
static class PathNode{
final PATHTYPE type;
final PathNode parent;
PathNode(PATHTYPE type, PathNode parent) {
this.type = type;
this.parent = parent;
}
}
static PathNode clearPathRoot(){
return (PathNode) CLEAR_ROOT.get();
}
enum PSTATE{
REQ, REST, DONE
}
public static class FnMethod extends ObjMethod{
//localbinding->localbinding
PersistentVector reqParms = PersistentVector.EMPTY;
LocalBinding restParm = null;
Type[] argtypes;
Class[] argclasses;
Class retClass;
String prim ;
public FnMethod(ObjExpr objx, ObjMethod parent){
super(objx, parent);
}
static public char classChar(Object x){
Class c = null;
if(x instanceof Class)
c = (Class) x;
else if(x instanceof Symbol)
c = primClass((Symbol) x);
if(c == null || !c.isPrimitive())
return 'O';
if(c == long.class)
return 'L';
if(c == double.class)
return 'D';
throw new IllegalArgumentException("Only long and double primitives are supported");
}
static public String primInterface(IPersistentVector arglist) {
StringBuilder sb = new StringBuilder();
for(int i=0;i 4)
throw new IllegalArgumentException("fns taking primitives support only 4 or fewer args");
if(prim)
return "clojure.lang.IFn$" + ret;
return null;
}
static FnMethod parse(ObjExpr objx, ISeq form, Object rettag) {
//([args] body...)
IPersistentVector parms = (IPersistentVector) RT.first(form);
ISeq body = RT.next(form);
try
{
FnMethod method = new FnMethod(objx, (ObjMethod) METHOD.deref());
method.line = lineDeref();
method.column = columnDeref();
//register as the current method and set up a new env frame
PathNode pnode = (PathNode) CLEAR_PATH.get();
if(pnode == null)
pnode = new PathNode(PATHTYPE.PATH,null);
Var.pushThreadBindings(
RT.mapUniqueKeys(
METHOD, method,
LOCAL_ENV, LOCAL_ENV.deref(),
LOOP_LOCALS, null,
NEXT_LOCAL_NUM, 0
,CLEAR_PATH, pnode
,CLEAR_ROOT, pnode
,CLEAR_SITES, PersistentHashMap.EMPTY
,METHOD_RETURN_CONTEXT, RT.T
));
method.prim = primInterface(parms);
if(method.prim != null)
method.prim = method.prim.replace('.', '/');
if(rettag instanceof String)
rettag = Symbol.intern(null, (String) rettag);
if(!(rettag instanceof Symbol))
rettag = null;
if(rettag != null)
{
String retstr = ((Symbol)rettag).getName();
if(!(retstr.equals("long") || retstr.equals("double")))
rettag = null;
}
method.retClass = tagClass(tagOf(parms)!=null?tagOf(parms):rettag);
if(method.retClass.isPrimitive()){
if(!(method.retClass == double.class || method.retClass == long.class))
throw new IllegalArgumentException("Only long and double primitives are supported");
}
else
method.retClass = Object.class;
//register 'this' as local 0
//registerLocal(THISFN, null, null);
// if(!canBeDirect)
// {
if(objx.thisName != null)
registerLocal(Symbol.intern(objx.thisName), null, null,false);
else
getAndIncLocalNum();
// }
PSTATE state = PSTATE.REQ;
PersistentVector argLocals = PersistentVector.EMPTY;
ArrayList argtypes = new ArrayList();
ArrayList argclasses = new ArrayList();
for(int i = 0; i < parms.count(); i++)
{
if(!(parms.nth(i) instanceof Symbol))
throw new IllegalArgumentException("fn params must be Symbols");
Symbol p = (Symbol) parms.nth(i);
if(p.getNamespace() != null)
throw Util.runtimeException("Can't use qualified name as parameter: " + p);
if(p.equals(_AMP_))
{
// if(canBeDirect)
// throw Util.runtimeException("Variadic fns cannot be static");
if(state == PSTATE.REQ)
state = PSTATE.REST;
else
throw Util.runtimeException("Invalid parameter list");
}
else
{
Class pc = primClass(tagClass(tagOf(p)));
// if(pc.isPrimitive() && !canBeDirect)
// {
// pc = Object.class;
// p = (Symbol) ((IObj) p).withMeta((IPersistentMap) RT.assoc(RT.meta(p), RT.TAG_KEY, null));
// }
// throw Util.runtimeException("Non-static fn can't have primitive parameter: " + p);
if(pc.isPrimitive() && !(pc == double.class || pc == long.class))
throw new IllegalArgumentException("Only long and double primitives are supported: " + p);
if(state == PSTATE.REST && tagOf(p) != null)
throw Util.runtimeException("& arg cannot have type hint");
if(state == PSTATE.REST && method.prim != null)
throw Util.runtimeException("fns taking primitives cannot be variadic");
if(state == PSTATE.REST)
pc = ISeq.class;
argtypes.add(Type.getType(pc));
argclasses.add(pc);
LocalBinding lb = pc.isPrimitive() ?
registerLocal(p, null, new MethodParamExpr(pc), true)
: registerLocal(p, state == PSTATE.REST ? ISEQ : tagOf(p), null, true);
argLocals = argLocals.cons(lb);
switch(state)
{
case REQ:
method.reqParms = method.reqParms.cons(lb);
break;
case REST:
method.restParm = lb;
state = PSTATE.DONE;
break;
default:
throw Util.runtimeException("Unexpected parameter");
}
}
}
if(method.reqParms.count() > MAX_POSITIONAL_ARITY)
throw Util.runtimeException("Can't specify more than " + MAX_POSITIONAL_ARITY + " params");
LOOP_LOCALS.set(argLocals);
method.argLocals = argLocals;
// if(canBeDirect)
method.argtypes = argtypes.toArray(new Type[argtypes.size()]);
method.argclasses = argclasses.toArray(new Class[argtypes.size()]);
if(method.prim != null)
{
for(int i = 0; i < method.argclasses.length; i++)
{
if(method.argclasses[i] == long.class || method.argclasses[i] == double.class)
getAndIncLocalNum();
}
}
method.body = (new BodyExpr.Parser()).parse(C.RETURN, body);
return method;
}
finally
{
Var.popThreadBindings();
}
}
public void emit(ObjExpr fn, ClassVisitor cv){
if(fn.canBeDirect)
{
// System.out.println("emit static: " + fn.name);
doEmitStatic(fn, cv);
}
else if(prim != null)
{
// System.out.println("emit prim: " + fn.name);
doEmitPrim(fn, cv);
}
else
{
// System.out.println("emit normal: " + fn.name);
doEmit(fn,cv);
}
}
public void doEmitStatic(ObjExpr fn, ClassVisitor cv){
// System.out.println("emit static:" + fn.name);
Type returnType = Type.getType(retClass);
// if (retClass == double.class || retClass == long.class)
// returnType = getReturnType();
// else returnType = OBJECT_TYPE;
Method ms = new Method("invokeStatic", returnType, argtypes);
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC,
ms,
null,
//todo don't hardwire this
EXCEPTION_TYPES,
cv);
gen.visitCode();
Label loopLabel = gen.mark();
gen.visitLineNumber(line, loopLabel);
try
{
Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));
emitBody(objx, gen, retClass, body);
Label end = gen.mark();
for(ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next())
{
LocalBinding lb = (LocalBinding) lbs.first();
gen.visitLocalVariable(lb.name, argtypes[lb.idx].getDescriptor(), null, loopLabel, end, lb.idx);
}
}
finally
{
Var.popThreadBindings();
}
gen.returnValue();
//gen.visitMaxs(1, 1);
gen.endMethod();
//generate the regular invoke, calling the static method
Method m = new Method(getMethodName(), OBJECT_TYPE, getArgTypes());
gen = new GeneratorAdapter(ACC_PUBLIC,
m,
null,
//todo don't hardwire this
EXCEPTION_TYPES,
cv);
gen.visitCode();
for(int i = 0; i < argtypes.length; i++)
{
gen.loadArg(i);
HostExpr.emitUnboxArg(fn, gen, argclasses[i]);
if(!argclasses[i].isPrimitive())
{
gen.visitInsn(Opcodes.ACONST_NULL);
gen.storeArg(i);
}
}
Label callLabel = gen.mark();
gen.visitLineNumber(line, callLabel);
gen.invokeStatic(objx.objtype, ms);
if(Type.LONG_TYPE.equals(returnType) || Type.DOUBLE_TYPE.equals(returnType)) {
gen.valueOf(returnType);
} else {
gen.box(returnType);
}
gen.returnValue();
//gen.visitMaxs(1, 1);
gen.endMethod();
//generate primInvoke if prim
if(prim != null)
{
if (retClass == double.class || retClass == long.class)
returnType = getReturnType();
else returnType = OBJECT_TYPE;
Method pm = new Method("invokePrim", returnType, argtypes);
gen = new GeneratorAdapter(ACC_PUBLIC + ACC_FINAL,
pm,
null,
//todo don't hardwire this
EXCEPTION_TYPES,
cv);
gen.visitCode();
for(int i = 0; i < argtypes.length; i++)
{
gen.loadArg(i);
if(!argclasses[i].isPrimitive())
{
gen.visitInsn(Opcodes.ACONST_NULL);
gen.storeArg(i);
}
}
gen.invokeStatic(objx.objtype, ms);
gen.returnValue();
//gen.visitMaxs(1, 1);
gen.endMethod();
}
}
public void doEmitPrim(ObjExpr fn, ClassVisitor cv){
Type returnType;
if (retClass == double.class || retClass == long.class)
returnType = getReturnType();
else returnType = OBJECT_TYPE;
Method ms = new Method("invokePrim", returnType, argtypes);
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_FINAL,
ms,
null,
//todo don't hardwire this
EXCEPTION_TYPES,
cv);
gen.visitCode();
Label loopLabel = gen.mark();
gen.visitLineNumber(line, loopLabel);
try
{
Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));
emitBody(objx, gen, retClass, body);
Label end = gen.mark();
gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0);
for(ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next())
{
LocalBinding lb = (LocalBinding) lbs.first();
gen.visitLocalVariable(lb.name, argtypes[lb.idx-1].getDescriptor(), null, loopLabel, end, lb.idx);
}
}
finally
{
Var.popThreadBindings();
}
gen.returnValue();
//gen.visitMaxs(1, 1);
gen.endMethod();
//generate the regular invoke, calling the prim method
Method m = new Method(getMethodName(), OBJECT_TYPE, getArgTypes());
gen = new GeneratorAdapter(ACC_PUBLIC,
m,
null,
//todo don't hardwire this
EXCEPTION_TYPES,
cv);
gen.visitCode();
gen.loadThis();
for(int i = 0; i < argtypes.length; i++)
{
gen.loadArg(i);
HostExpr.emitUnboxArg(fn, gen, argclasses[i]);
}
gen.invokeInterface(Type.getType("L"+prim+";"), ms);
Type targetReturnType = getReturnType();
if(Type.LONG_TYPE.equals(targetReturnType) || Type.DOUBLE_TYPE.equals(targetReturnType)) {
gen.valueOf(targetReturnType);
} else {
gen.box(targetReturnType);
}
gen.returnValue();
//gen.visitMaxs(1, 1);
gen.endMethod();
}
public void doEmit(ObjExpr fn, ClassVisitor cv){
Method m = new Method(getMethodName(), getReturnType(), getArgTypes());
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,
m,
null,
//todo don't hardwire this
EXCEPTION_TYPES,
cv);
gen.visitCode();
Label loopLabel = gen.mark();
gen.visitLineNumber(line, loopLabel);
try
{
Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));
body.emit(C.RETURN, fn, gen);
Label end = gen.mark();
gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0);
for(ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next())
{
LocalBinding lb = (LocalBinding) lbs.first();
gen.visitLocalVariable(lb.name, "Ljava/lang/Object;", null, loopLabel, end, lb.idx);
}
}
finally
{
Var.popThreadBindings();
}
gen.returnValue();
//gen.visitMaxs(1, 1);
gen.endMethod();
}
public final PersistentVector reqParms(){
return reqParms;
}
public final LocalBinding restParm(){
return restParm;
}
boolean isVariadic(){
return restParm != null;
}
int numParams(){
return reqParms.count() + (isVariadic() ? 1 : 0);
}
String getMethodName(){
return isVariadic()?"doInvoke":"invoke";
}
Type getReturnType(){
if(prim != null) //objx.isStatic)
return Type.getType(retClass);
return OBJECT_TYPE;
}
Type[] getArgTypes(){
if(isVariadic() && reqParms.count() == MAX_POSITIONAL_ARITY)
{
Type[] ret = new Type[MAX_POSITIONAL_ARITY + 1];
for(int i = 0;ilocalbinding
IPersistentMap locals = null;
//num->localbinding
IPersistentMap indexlocals = null;
Expr body = null;
ObjExpr objx;
PersistentVector argLocals;
int maxLocal = 0;
int line;
int column;
boolean usesThis = false;
PersistentHashSet localsUsedInCatchFinally = PersistentHashSet.EMPTY;
protected IPersistentMap methodMeta;
public final IPersistentMap locals(){
return locals;
}
public final Expr body(){
return body;
}
public final ObjExpr objx(){
return objx;
}
public final PersistentVector argLocals(){
return argLocals;
}
public final int maxLocal(){
return maxLocal;
}
public final int line(){
return line;
}
public final int column(){
return column;
}
public ObjMethod(ObjExpr objx, ObjMethod parent){
this.parent = parent;
this.objx = objx;
}
static void emitBody(ObjExpr objx, GeneratorAdapter gen, Class retClass, Expr body) {
MaybePrimitiveExpr be = (MaybePrimitiveExpr) body;
if(Util.isPrimitive(retClass) && be.canEmitPrimitive())
{
Class bc = maybePrimitiveType(be);
if(bc == retClass)
be.emitUnboxed(C.RETURN, objx, gen);
else if(retClass == long.class && bc == int.class)
{
be.emitUnboxed(C.RETURN, objx, gen);
gen.visitInsn(I2L);
}
else if(retClass == double.class && bc == float.class)
{
be.emitUnboxed(C.RETURN, objx, gen);
gen.visitInsn(F2D);
}
else if(retClass == int.class && bc == long.class)
{
be.emitUnboxed(C.RETURN, objx, gen);
gen.invokeStatic(RT_TYPE, Method.getMethod("int intCast(long)"));
}
else if(retClass == float.class && bc == double.class)
{
be.emitUnboxed(C.RETURN, objx, gen);
gen.visitInsn(D2F);
}
else
throw new IllegalArgumentException("Mismatched primitive return, expected: "
+ retClass + ", had: " + be.getJavaClass());
}
else
{
body.emit(C.RETURN, objx, gen);
if(retClass == void.class)
{
gen.pop();
}
else
gen.unbox(Type.getType(retClass));
}
}
abstract int numParams();
abstract String getMethodName();
abstract Type getReturnType();
abstract Type[] getArgTypes();
public void emit(ObjExpr fn, ClassVisitor cv){
Method m = new Method(getMethodName(), getReturnType(), getArgTypes());
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC,
m,
null,
//todo don't hardwire this
EXCEPTION_TYPES,
cv);
gen.visitCode();
Label loopLabel = gen.mark();
gen.visitLineNumber(line, loopLabel);
try
{
Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel, METHOD, this));
body.emit(C.RETURN, fn, gen);
Label end = gen.mark();
gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0);
for(ISeq lbs = argLocals.seq(); lbs != null; lbs = lbs.next())
{
LocalBinding lb = (LocalBinding) lbs.first();
gen.visitLocalVariable(lb.name, "Ljava/lang/Object;", null, loopLabel, end, lb.idx);
}
}
finally
{
Var.popThreadBindings();
}
gen.returnValue();
//gen.visitMaxs(1, 1);
gen.endMethod();
}
void emitClearLocals(GeneratorAdapter gen){
}
void emitClearLocalsOld(GeneratorAdapter gen){
for(int i=0;i 0)
{
// Object dummy;
if(sites != null)
{
for(ISeq s = sites.seq();s!=null;s = s.next())
{
LocalBindingExpr o = (LocalBindingExpr) s.first();
PathNode common = commonPath(clearPath,o.clearPath);
if(common != null && common.type == PATHTYPE.PATH)
o.shouldClear = false;
// else
// dummy = null;
}
}
if(clearRoot == b.clearPathRoot)
{
this.shouldClear = true;
sites = RT.conj(sites,this);
CLEAR_SITES.set(RT.assoc(CLEAR_SITES.get(), b, sites));
}
// else
// dummy = null;
}
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval locals");
}
public boolean canEmitPrimitive(){
return b.getPrimitiveType() != null;
}
public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
objx.emitUnboxedLocal(gen, b);
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
if(context != C.STATEMENT)
objx.emitLocal(gen, b, shouldClear);
}
public Object evalAssign(Expr val) {
throw new UnsupportedOperationException("Can't eval locals");
}
public void emitAssign(C context, ObjExpr objx, GeneratorAdapter gen, Expr val){
objx.emitAssignLocal(gen, b,val);
if(context != C.STATEMENT)
objx.emitLocal(gen, b, false);
}
public boolean hasJavaClass() {
return tag != null || b.hasJavaClass();
}
Class jc;
public Class getJavaClass() {
if (jc == null) {
if(tag != null)
jc = HostExpr.tagToClass(tag);
else
jc = b.getJavaClass();
}
return jc;
}
}
public static class BodyExpr implements Expr, MaybePrimitiveExpr{
PersistentVector exprs;
public final PersistentVector exprs(){
return exprs;
}
public BodyExpr(PersistentVector exprs){
this.exprs = exprs;
}
static class Parser implements IParser{
public Expr parse(C context, Object frms) {
ISeq forms = (ISeq) frms;
if(Util.equals(RT.first(forms), DO))
forms = RT.next(forms);
PersistentVector exprs = PersistentVector.EMPTY;
for(; forms != null; forms = forms.next())
{
Expr e = (context != C.EVAL &&
(context == C.STATEMENT || forms.next() != null)) ?
analyze(C.STATEMENT, forms.first())
:
analyze(context, forms.first());
exprs = exprs.cons(e);
}
if(exprs.count() == 0)
exprs = exprs.cons(NIL_EXPR);
return new BodyExpr(exprs);
}
}
public Object eval() {
Object ret = null;
for(Object o : exprs)
{
Expr e = (Expr) o;
ret = e.eval();
}
return ret;
}
public boolean canEmitPrimitive(){
return lastExpr() instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr)lastExpr()).canEmitPrimitive();
}
public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
for(int i = 0; i < exprs.count() - 1; i++)
{
Expr e = (Expr) exprs.nth(i);
e.emit(C.STATEMENT, objx, gen);
}
MaybePrimitiveExpr last = (MaybePrimitiveExpr) exprs.nth(exprs.count() - 1);
last.emitUnboxed(context, objx, gen);
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
for(int i = 0; i < exprs.count() - 1; i++)
{
Expr e = (Expr) exprs.nth(i);
e.emit(C.STATEMENT, objx, gen);
}
Expr last = (Expr) exprs.nth(exprs.count() - 1);
last.emit(context, objx, gen);
}
public boolean hasJavaClass() {
return lastExpr().hasJavaClass();
}
public Class getJavaClass() {
return lastExpr().getJavaClass();
}
private Expr lastExpr(){
return (Expr) exprs.nth(exprs.count() - 1);
}
}
public static class BindingInit{
LocalBinding binding;
Expr init;
public final LocalBinding binding(){
return binding;
}
public final Expr init(){
return init;
}
public BindingInit(LocalBinding binding, Expr init){
this.binding = binding;
this.init = init;
}
}
public static class LetFnExpr implements Expr{
public final PersistentVector bindingInits;
public final Expr body;
public LetFnExpr(PersistentVector bindingInits, Expr body){
this.bindingInits = bindingInits;
this.body = body;
}
static class Parser implements IParser{
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
//(letfns* [var (fn [args] body) ...] body...)
if(!(RT.second(form) instanceof IPersistentVector))
throw new IllegalArgumentException("Bad binding form, expected vector");
IPersistentVector bindings = (IPersistentVector) RT.second(form);
if((bindings.count() % 2) != 0)
throw new IllegalArgumentException("Bad binding form, expected matched symbol expression pairs");
ISeq body = RT.next(RT.next(form));
if(context == C.EVAL)
return analyze(context, RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref(),
NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref());
try
{
Var.pushThreadBindings(dynamicBindings);
//pre-seed env (like Lisp labels)
PersistentVector lbs = PersistentVector.EMPTY;
for(int i = 0; i < bindings.count(); i += 2)
{
if(!(bindings.nth(i) instanceof Symbol))
throw new IllegalArgumentException(
"Bad binding form, expected symbol, got: " + bindings.nth(i));
Symbol sym = (Symbol) bindings.nth(i);
if(sym.getNamespace() != null)
throw Util.runtimeException("Can't let qualified name: " + sym);
LocalBinding lb = registerLocal(sym, tagOf(sym), null,false);
lb.canBeCleared = false;
lbs = lbs.cons(lb);
}
PersistentVector bindingInits = PersistentVector.EMPTY;
for(int i = 0; i < bindings.count(); i += 2)
{
Symbol sym = (Symbol) bindings.nth(i);
Expr init = analyze(C.EXPRESSION, bindings.nth(i + 1), sym.name);
LocalBinding lb = (LocalBinding) lbs.nth(i / 2);
lb.init = init;
BindingInit bi = new BindingInit(lb, init);
bindingInits = bindingInits.cons(bi);
}
return new LetFnExpr(bindingInits, (new BodyExpr.Parser()).parse(context, body));
}
finally
{
Var.popThreadBindings();
}
}
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval letfns");
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
for(int i = 0; i < bindingInits.count(); i++)
{
BindingInit bi = (BindingInit) bindingInits.nth(i);
gen.visitInsn(Opcodes.ACONST_NULL);
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);
}
IPersistentSet lbset = PersistentHashSet.EMPTY;
for(int i = 0; i < bindingInits.count(); i++)
{
BindingInit bi = (BindingInit) bindingInits.nth(i);
lbset = (IPersistentSet) lbset.cons(bi.binding);
bi.init.emit(C.EXPRESSION, objx, gen);
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);
}
for(int i = 0; i < bindingInits.count(); i++)
{
BindingInit bi = (BindingInit) bindingInits.nth(i);
ObjExpr fe = (ObjExpr) bi.init;
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ILOAD), bi.binding.idx);
fe.emitLetFnInits(gen, objx, lbset);
}
Label loopLabel = gen.mark();
body.emit(context, objx, gen);
Label end = gen.mark();
// gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0);
for(ISeq bis = bindingInits.seq(); bis != null; bis = bis.next())
{
BindingInit bi = (BindingInit) bis.first();
String lname = bi.binding.name;
if(lname.endsWith("__auto__"))
lname += RT.nextID();
Class primc = maybePrimitiveType(bi.init);
if(primc != null)
gen.visitLocalVariable(lname, Type.getDescriptor(primc), null, loopLabel, end,
bi.binding.idx);
else
gen.visitLocalVariable(lname, "Ljava/lang/Object;", null, loopLabel, end, bi.binding.idx);
}
}
public boolean hasJavaClass() {
return body.hasJavaClass();
}
public Class getJavaClass() {
return body.getJavaClass();
}
}
public static class LetExpr implements Expr, MaybePrimitiveExpr{
public final PersistentVector bindingInits;
public final Expr body;
public final boolean isLoop;
public LetExpr(PersistentVector bindingInits, Expr body, boolean isLoop){
this.bindingInits = bindingInits;
this.body = body;
this.isLoop = isLoop;
}
static class Parser implements IParser{
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
//(let [var val var2 val2 ...] body...)
boolean isLoop = RT.first(form).equals(LOOP);
if(!(RT.second(form) instanceof IPersistentVector))
throw new IllegalArgumentException("Bad binding form, expected vector");
IPersistentVector bindings = (IPersistentVector) RT.second(form);
if((bindings.count() % 2) != 0)
throw new IllegalArgumentException("Bad binding form, expected matched symbol expression pairs");
ISeq body = RT.next(RT.next(form));
if(context == C.EVAL
|| (context == C.EXPRESSION && isLoop))
return analyze(context, RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
ObjMethod method = (ObjMethod) METHOD.deref();
IPersistentMap backupMethodLocals = method.locals;
IPersistentMap backupMethodIndexLocals = method.indexlocals;
IPersistentVector recurMismatches = PersistentVector.EMPTY;
for (int i = 0; i < bindings.count()/2; i++)
{
recurMismatches = recurMismatches.cons(RT.F);
}
//may repeat once for each binding with a mismatch, return breaks
while(true){
IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref(),
NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref());
method.locals = backupMethodLocals;
method.indexlocals = backupMethodIndexLocals;
PathNode looproot = new PathNode(PATHTYPE.PATH, (PathNode) CLEAR_PATH.get());
PathNode clearroot = new PathNode(PATHTYPE.PATH,looproot);
PathNode clearpath = new PathNode(PATHTYPE.PATH,looproot);
if(isLoop)
dynamicBindings = dynamicBindings.assoc(LOOP_LOCALS, null);
try
{
Var.pushThreadBindings(dynamicBindings);
PersistentVector bindingInits = PersistentVector.EMPTY;
PersistentVector loopLocals = PersistentVector.EMPTY;
for(int i = 0; i < bindings.count(); i += 2)
{
if(!(bindings.nth(i) instanceof Symbol))
throw new IllegalArgumentException(
"Bad binding form, expected symbol, got: " + bindings.nth(i));
Symbol sym = (Symbol) bindings.nth(i);
if(sym.getNamespace() != null)
throw Util.runtimeException("Can't let qualified name: " + sym);
Expr init = analyze(C.EXPRESSION, bindings.nth(i + 1), sym.name);
if(isLoop)
{
if(recurMismatches != null && RT.booleanCast(recurMismatches.nth(i/2)))
{
init = new StaticMethodExpr("", 0, 0, null, RT.class, "box", RT.vector(init), false);
if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
RT.errPrintWriter().println("Auto-boxing loop arg: " + sym);
}
else if(maybePrimitiveType(init) == int.class)
init = new StaticMethodExpr("", 0, 0, null, RT.class, "longCast", RT.vector(init), false);
else if(maybePrimitiveType(init) == float.class)
init = new StaticMethodExpr("", 0, 0, null, RT.class, "doubleCast", RT.vector(init), false);
}
//sequential enhancement of env (like Lisp let*)
try
{
if(isLoop)
{
Var.pushThreadBindings(
RT.map(CLEAR_PATH, clearpath,
CLEAR_ROOT, clearroot,
NO_RECUR, null));
}
LocalBinding lb = registerLocal(sym, tagOf(sym), init,false);
BindingInit bi = new BindingInit(lb, init);
bindingInits = bindingInits.cons(bi);
if(isLoop)
loopLocals = loopLocals.cons(lb);
}
finally
{
if(isLoop)
Var.popThreadBindings();
}
}
if(isLoop)
LOOP_LOCALS.set(loopLocals);
Expr bodyExpr;
boolean moreMismatches = false;
try {
if(isLoop)
{
Object methodReturnContext = context == C.RETURN ? METHOD_RETURN_CONTEXT.deref() : null;
Var.pushThreadBindings(
RT.map(CLEAR_PATH, clearpath,
CLEAR_ROOT, clearroot,
NO_RECUR, null,
METHOD_RETURN_CONTEXT, methodReturnContext));
}
bodyExpr = (new BodyExpr.Parser()).parse(isLoop ? C.RETURN : context, body);
}
finally{
if(isLoop)
{
Var.popThreadBindings();
for(int i = 0;i< loopLocals.count();i++)
{
LocalBinding lb = (LocalBinding) loopLocals.nth(i);
if(lb.recurMistmatch)
{
recurMismatches = (IPersistentVector)recurMismatches.assoc(i, RT.T);
moreMismatches = true;
}
}
}
}
if(!moreMismatches)
return new LetExpr(bindingInits, bodyExpr, isLoop);
}
finally
{
Var.popThreadBindings();
}
}
}
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval let/loop");
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
doEmit(context, objx, gen, false);
}
public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
doEmit(context, objx, gen, true);
}
public void doEmit(C context, ObjExpr objx, GeneratorAdapter gen, boolean emitUnboxed){
HashMap bindingLabels = new HashMap();
for(int i = 0; i < bindingInits.count(); i++)
{
BindingInit bi = (BindingInit) bindingInits.nth(i);
Class primc = maybePrimitiveType(bi.init);
if(primc != null)
{
((MaybePrimitiveExpr) bi.init).emitUnboxed(C.EXPRESSION, objx, gen);
gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ISTORE), bi.binding.idx);
}
else
{
Class bindingClass = HostExpr.maybeClass(bi.binding.tag, true);
if(!FISupport.maybeEmitFIAdapter(objx, gen, bi.init, bindingClass))
bi.init.emit(C.EXPRESSION, objx, gen);
if (!bi.binding.used && bi.binding.canBeCleared)
gen.pop();
else
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);
}
bindingLabels.put(bi, gen.mark());
}
Label loopLabel = gen.mark();
if(isLoop)
{
try
{
Var.pushThreadBindings(RT.map(LOOP_LABEL, loopLabel));
if(emitUnboxed)
((MaybePrimitiveExpr)body).emitUnboxed(context, objx, gen);
else
body.emit(context, objx, gen);
}
finally
{
Var.popThreadBindings();
}
}
else
{
if(emitUnboxed)
((MaybePrimitiveExpr)body).emitUnboxed(context, objx, gen);
else
body.emit(context, objx, gen);
}
Label end = gen.mark();
// gen.visitLocalVariable("this", "Ljava/lang/Object;", null, loopLabel, end, 0);
for(ISeq bis = bindingInits.seq(); bis != null; bis = bis.next())
{
BindingInit bi = (BindingInit) bis.first();
String lname = bi.binding.name;
if(lname.endsWith("__auto__"))
lname += RT.nextID();
Class primc = maybePrimitiveType(bi.init);
if(primc != null)
gen.visitLocalVariable(lname, Type.getDescriptor(primc), null, bindingLabels.get(bi), end,
bi.binding.idx);
else
gen.visitLocalVariable(lname, "Ljava/lang/Object;", null, bindingLabels.get(bi), end, bi.binding.idx);
}
}
public boolean hasJavaClass() {
return body.hasJavaClass();
}
public Class getJavaClass() {
return body.getJavaClass();
}
public boolean canEmitPrimitive(){
return body instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr)body).canEmitPrimitive();
}
}
public static class RecurExpr implements Expr, MaybePrimitiveExpr{
public final IPersistentVector args;
public final IPersistentVector loopLocals;
final int line;
final int column;
final String source;
public RecurExpr(IPersistentVector loopLocals, IPersistentVector args, int line, int column, String source){
this.loopLocals = loopLocals;
this.args = args;
this.line = line;
this.column = column;
this.source = source;
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval recur");
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
Label loopLabel = (Label) LOOP_LABEL.deref();
if(loopLabel == null)
throw new IllegalStateException();
for(int i = 0; i < loopLocals.count(); i++)
{
LocalBinding lb = (LocalBinding) loopLocals.nth(i);
Expr arg = (Expr) args.nth(i);
if(lb.getPrimitiveType() != null)
{
Class primc = lb.getPrimitiveType();
final Class pc = maybePrimitiveType(arg);
if(pc == primc)
((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
else if(primc == long.class && pc == int.class)
{
((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
gen.visitInsn(I2L);
}
else if(primc == double.class && pc == float.class)
{
((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
gen.visitInsn(F2D);
}
else if(primc == int.class && pc == long.class)
{
((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
gen.invokeStatic(RT_TYPE, Method.getMethod("int intCast(long)"));
}
else if(primc == float.class && pc == double.class)
{
((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
gen.visitInsn(D2F);
}
else
{
// if(true)//RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
throw new IllegalArgumentException
// RT.errPrintWriter().println
(//source + ":" + line +
" recur arg for primitive local: " +
lb.name + " is not matching primitive, had: " +
(arg.hasJavaClass() ? arg.getJavaClass().getName():"Object") +
", needed: " +
primc.getName());
// arg.emit(C.EXPRESSION, objx, gen);
// HostExpr.emitUnboxArg(objx,gen,primc);
}
}
else
{
arg.emit(C.EXPRESSION, objx, gen);
}
}
for(int i = loopLocals.count() - 1; i >= 0; i--)
{
LocalBinding lb = (LocalBinding) loopLocals.nth(i);
Class primc = lb.getPrimitiveType();
if(lb.isArg)
gen.storeArg(lb.idx-(objx.canBeDirect ?0:1));
else
{
if(primc != null)
gen.visitVarInsn(Type.getType(primc).getOpcode(Opcodes.ISTORE), lb.idx);
else
gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), lb.idx);
}
}
gen.goTo(loopLabel);
}
public boolean hasJavaClass() {
return true;
}
public Class getJavaClass() {
return RECUR_CLASS;
}
static class Parser implements IParser{
public Expr parse(C context, Object frm) {
int line = lineDeref();
int column = columnDeref();
String source = (String) SOURCE.deref();
ISeq form = (ISeq) frm;
IPersistentVector loopLocals = (IPersistentVector) LOOP_LOCALS.deref();
if(context != C.RETURN || loopLocals == null)
throw new UnsupportedOperationException("Can only recur from tail position");
if(NO_RECUR.deref() != null)
throw new UnsupportedOperationException("Cannot recur across try");
PersistentVector args = PersistentVector.EMPTY;
for(ISeq s = RT.seq(form.next()); s != null; s = s.next())
{
args = args.cons(analyze(C.EXPRESSION, s.first()));
}
if(args.count() != loopLocals.count())
throw new IllegalArgumentException(
String.format("Mismatched argument count to recur, expected: %d args, got: %d",
loopLocals.count(), args.count()));
for(int i = 0;i< loopLocals.count();i++)
{
LocalBinding lb = (LocalBinding) loopLocals.nth(i);
Class primc = lb.getPrimitiveType();
if(primc != null)
{
boolean mismatch = false;
final Class pc = maybePrimitiveType((Expr) args.nth(i));
if(primc == long.class)
{
if(!(pc == long.class
|| pc == int.class
|| pc == short.class
|| pc == char.class
|| pc == byte.class))
mismatch = true;
}
else if(primc == double.class)
{
if(!(pc == double.class
|| pc == float.class))
mismatch = true;
}
if(mismatch)
{
lb.recurMistmatch = true;
if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
RT.errPrintWriter().println
(source + ":" + line +
" recur arg for primitive local: " +
lb.name + " is not matching primitive, had: " +
(pc != null ? pc.getName():"Object") +
", needed: " +
primc.getName());
}
}
}
return new RecurExpr(loopLocals, args, line, column, source);
}
}
public boolean canEmitPrimitive() {
return true;
}
public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
emit(context, objx, gen);
}
}
private static LocalBinding registerLocal(Symbol sym, Symbol tag, Expr init, boolean isArg) {
int num = getAndIncLocalNum();
LocalBinding b = new LocalBinding(num, sym, tag, init, isArg, clearPathRoot());
IPersistentMap localsMap = (IPersistentMap) LOCAL_ENV.deref();
LOCAL_ENV.set(RT.assoc(localsMap, b.sym, b));
ObjMethod method = (ObjMethod) METHOD.deref();
method.locals = (IPersistentMap) RT.assoc(method.locals, b, b);
method.indexlocals = (IPersistentMap) RT.assoc(method.indexlocals, num, b);
return b;
}
private static int getAndIncLocalNum(){
int num = ((Number) NEXT_LOCAL_NUM.deref()).intValue();
ObjMethod m = (ObjMethod) METHOD.deref();
if(num > m.maxLocal)
m.maxLocal = num;
NEXT_LOCAL_NUM.set(num + 1);
return num;
}
public static Expr analyze(C context, Object form) {
return analyze(context, form, null);
}
private static Expr analyze(C context, Object form, String name) {
//todo symbol macro expansion?
try
{
if(form instanceof LazySeq)
{
Object mform = form;
form = RT.seq(form);
if(form == null)
form = PersistentList.EMPTY;
form = ((IObj)form).withMeta(RT.meta(mform));
}
if(form == null)
return NIL_EXPR;
else if(form == Boolean.TRUE)
return TRUE_EXPR;
else if(form == Boolean.FALSE)
return FALSE_EXPR;
Class fclass = form.getClass();
if(fclass == Symbol.class)
return analyzeSymbol((Symbol) form);
else if(fclass == Keyword.class)
return registerKeyword((Keyword) form);
else if(form instanceof Number)
return NumberExpr.parse((Number) form);
else if(fclass == String.class)
return new StringExpr(((String) form).intern());
// else if(fclass == Character.class)
// return new CharExpr((Character) form);
else if(form instanceof IPersistentCollection
&& !(form instanceof IRecord)
&& !(form instanceof IType)
&& ((IPersistentCollection) form).count() == 0)
{
Expr ret = new EmptyExpr(form);
if(RT.meta(form) != null)
ret = new MetaExpr(ret, MapExpr
.parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta()));
return ret;
}
else if(form instanceof ISeq)
return analyzeSeq(context, (ISeq) form, name);
else if(form instanceof IPersistentVector)
return VectorExpr.parse(context, (IPersistentVector) form);
else if(form instanceof IRecord)
return new ConstantExpr(form);
else if(form instanceof IType)
return new ConstantExpr(form);
else if(form instanceof IPersistentMap)
return MapExpr.parse(context, (IPersistentMap) form);
else if(form instanceof IPersistentSet)
return SetExpr.parse(context, (IPersistentSet) form);
// else
//throw new UnsupportedOperationException();
return new ConstantExpr(form);
}
catch(Throwable e)
{
if(!(e instanceof CompilerException))
throw new CompilerException((String) SOURCE_PATH.deref(), lineDeref(), columnDeref(), e);
else
throw (CompilerException) e;
}
}
static public class CompilerException extends RuntimeException implements IExceptionInfo{
final public String source;
final public int line;
final public Object data;
// Error keys
final static public String ERR_NS = "clojure.error";
final static public Keyword ERR_SOURCE = Keyword.intern(ERR_NS, "source");
final static public Keyword ERR_LINE = Keyword.intern(ERR_NS, "line");
final static public Keyword ERR_COLUMN = Keyword.intern(ERR_NS, "column");
final static public Keyword ERR_PHASE = Keyword.intern(ERR_NS, "phase");
final static public Keyword ERR_SYMBOL = Keyword.intern(ERR_NS, "symbol");
// Compile error phases
final static public Keyword PHASE_READ = Keyword.intern(null, "read-source");
final static public Keyword PHASE_MACRO_SYNTAX_CHECK = Keyword.intern(null, "macro-syntax-check");
final static public Keyword PHASE_MACROEXPANSION = Keyword.intern(null, "macroexpansion");
final static public Keyword PHASE_COMPILE_SYNTAX_CHECK = Keyword.intern(null, "compile-syntax-check");
final static public Keyword PHASE_COMPILATION = Keyword.intern(null, "compilation");
final static public Keyword PHASE_EXECUTION = Keyword.intern(null, "execution");
final static public Keyword SPEC_PROBLEMS = Keyword.intern("clojure.spec.alpha", "problems");
// Class compile exception
public CompilerException(String source, int line, int column, Throwable cause){
this(source, line, column, null, cause);
}
public CompilerException(String source, int line, int column, Symbol sym, Throwable cause){
this(source, line, column, sym, PHASE_COMPILE_SYNTAX_CHECK, cause);
}
public CompilerException(String source, int line, int column, Symbol sym, Keyword phase, Throwable cause){
super(makeMsg(source, line, column, sym, phase, cause), cause);
this.source = source;
this.line = line;
Associative m = RT.map(ERR_PHASE, phase, ERR_LINE, line, ERR_COLUMN, column);
if(source != null) m = RT.assoc(m, ERR_SOURCE, source);
if(sym != null) m = RT.assoc(m, ERR_SYMBOL, sym);
this.data = m;
}
public IPersistentMap getData(){
return (IPersistentMap)data;
}
private static String verb(Keyword phase) {
if(PHASE_READ.equals(phase)){
return "reading source";
} else if(PHASE_COMPILE_SYNTAX_CHECK.equals(phase) || PHASE_COMPILATION.equals(phase)){
return "compiling";
} else {
return "macroexpanding";
}
}
public static String makeMsg(String source, int line, int column, Symbol sym, Keyword phase, Throwable cause){
return (PHASE_MACROEXPANSION.equals(phase) ? "Unexpected error " : "Syntax error ") +
verb(phase) + " " + (sym != null ? sym + " " : "") +
"at (" + (source != null && !source.equals("NO_SOURCE_PATH") ? (source + ":") : "") +
line + ":" + column + ").";
}
public String toString(){
Throwable cause = getCause();
if(cause != null) {
if (cause instanceof IExceptionInfo) {
IPersistentMap data = (IPersistentMap)((IExceptionInfo)cause).getData();
if(PHASE_MACRO_SYNTAX_CHECK.equals(data.valAt(ERR_PHASE)) && data.valAt(SPEC_PROBLEMS) != null) {
return String.format("%s", getMessage());
} else {
return String.format("%s%n%s", getMessage(), cause.getMessage());
}
}
}
return getMessage();
}
}
static public Var isMacro(Object op) {
//no local macros for now
if(op instanceof Symbol && referenceLocal((Symbol) op) != null)
return null;
if(op instanceof Symbol || op instanceof Var)
{
Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false, false);
if(v != null && v.isMacro())
{
if(v.ns != currentNS() && !v.isPublic())
throw new IllegalStateException("var: " + v + " is not public");
return v;
}
}
return null;
}
static public IFn isInline(Object op, int arity) {
//no local inlines for now
if(op instanceof Symbol && referenceLocal((Symbol) op) != null)
return null;
if(op instanceof Symbol || op instanceof Var)
{
Var v = (op instanceof Var) ? (Var) op : lookupVar((Symbol) op, false);
if(v != null)
{
if(v.ns != currentNS() && !v.isPublic())
throw new IllegalStateException("var: " + v + " is not public");
IFn ret = (IFn) RT.get(v.meta(), inlineKey);
if(ret != null)
{
IFn arityPred = (IFn) RT.get(v.meta(), inlineAritiesKey);
if(arityPred == null || RT.booleanCast(arityPred.invoke(arity)))
return ret;
}
}
}
return null;
}
public static boolean namesStaticMember(Symbol sym){
return sym.ns != null && namespaceFor(sym) == null;
}
public static Object preserveTag(ISeq src, Object dst) {
Symbol tag = tagOf(src);
if (tag != null && dst instanceof IObj) {
IPersistentMap meta = RT.meta(dst);
return ((IObj) dst).withMeta((IPersistentMap) RT.assoc(meta, RT.TAG_KEY, tag));
}
return dst;
}
private static volatile Var MACRO_CHECK = null;
private static volatile boolean MACRO_CHECK_LOADING = false;
private static final Object MACRO_CHECK_LOCK = new Object();
private static Var ensureMacroCheck() throws ClassNotFoundException, IOException {
if(MACRO_CHECK == null) {
synchronized(MACRO_CHECK_LOCK) {
if(MACRO_CHECK == null) {
MACRO_CHECK_LOADING = true;
RT.load("clojure/spec/alpha");
RT.load("clojure/core/specs/alpha");
MACRO_CHECK = Var.find(Symbol.intern("clojure.spec.alpha", "macroexpand-check"));
MACRO_CHECK_LOADING = false;
}
}
}
return MACRO_CHECK;
}
public static void checkSpecs(Var v, ISeq form) {
if(RT.CHECK_SPECS && !MACRO_CHECK_LOADING) {
try {
ensureMacroCheck().applyTo(RT.cons(v, RT.list(form.next())));
} catch(Exception e) {
throw new CompilerException((String) SOURCE_PATH.deref(), lineDeref(), columnDeref(), v.toSymbol(), CompilerException.PHASE_MACRO_SYNTAX_CHECK, e);
}
}
}
public static Object macroexpand1(Object x) {
if(x instanceof ISeq)
{
ISeq form = (ISeq) x;
Object op = RT.first(form);
if(isSpecial(op))
return x;
//macro expansion
Var v = isMacro(op);
if(v != null)
{
checkSpecs(v, form);
try
{
ISeq args = RT.cons(form, RT.cons(Compiler.LOCAL_ENV.get(), form.next()));
return v.applyTo(args);
}
catch(ArityException e)
{
// hide the 2 extra params for a macro
if(e.name.equals(munge(v.ns.name.name) + "$" + munge(v.sym.name))) {
throw new ArityException(e.actual - 2, e.name);
} else {
throw e;
}
}
catch(CompilerException e)
{
throw e;
}
catch(IllegalArgumentException | IllegalStateException | ExceptionInfo e)
{
throw new CompilerException((String) SOURCE_PATH.deref(), lineDeref(), columnDeref(),
(op instanceof Symbol ? (Symbol) op : null),
CompilerException.PHASE_MACRO_SYNTAX_CHECK,
e);
}
catch(Throwable e)
{
throw new CompilerException((String) SOURCE_PATH.deref(), lineDeref(), columnDeref(),
(op instanceof Symbol ? (Symbol) op : null),
(e.getClass().equals(Exception.class) ? CompilerException.PHASE_MACRO_SYNTAX_CHECK : CompilerException.PHASE_MACROEXPANSION),
e);
}
} else
{
if(op instanceof Symbol)
{
Symbol sym = (Symbol) op;
String sname = sym.name;
//(.substring s 2 5) => (. s substring 2 5)
// ns == null ensures that Class/.instanceMethod isn't expanded to . form
if(sym.name.charAt(0) == '.' && sym.ns == null)
{
if(RT.length(form) < 2)
throw new IllegalArgumentException(
"Malformed member expression, expecting (.member target ...)");
Symbol meth = Symbol.intern(sname.substring(1));
Object target = RT.second(form);
if(HostExpr.maybeClass(target, false) != null)
{
target = ((IObj)RT.list(IDENTITY, target)).withMeta(RT.map(RT.TAG_KEY,CLASS));
}
return preserveTag(form, RT.listStar(DOT, target, meth, form.next().next()));
}
else
{
//(s.substring 2 5) => (. s substring 2 5)
//also (package.class.name ...) (. package.class name ...)
int idx = sname.lastIndexOf('.');
// if(idx > 0 && idx < sname.length() - 1)
// {
// Symbol target = Symbol.intern(sname.substring(0, idx));
// Symbol meth = Symbol.intern(sname.substring(idx + 1));
// return RT.listStar(DOT, target, meth, form.rest());
// }
//(StringBuilder. "foo") => (new StringBuilder "foo")
//else
if(idx == sname.length() - 1)
return RT.listStar(NEW, Symbol.intern(sname.substring(0, idx)), form.next());
}
}
}
}
return x;
}
static Object macroexpand(Object form) {
Object exf = macroexpand1(form);
if(exf != form)
return macroexpand(exf);
return form;
}
private static Expr analyzeSeq(C context, ISeq form, String name) {
Object line = lineDeref();
Object column = columnDeref();
if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))
line = RT.meta(form).valAt(RT.LINE_KEY);
if(RT.meta(form) != null && RT.meta(form).containsKey(RT.COLUMN_KEY))
column = RT.meta(form).valAt(RT.COLUMN_KEY);
Var.pushThreadBindings(
RT.map(LINE, line, COLUMN, column));
Object op = null;
try
{
Object me = macroexpand1(form);
if(me != form)
return analyze(context, me, name);
op = RT.first(form);
if(op == null)
throw new IllegalArgumentException("Can't call nil, form: " + form);
IFn inline = isInline(op, RT.count(RT.next(form)));
if(inline != null)
return analyze(context, preserveTag(form, inline.applyTo(RT.next(form))));
IParser p;
if(op.equals(FN))
return FnExpr.parse(context, form, name);
else if((p = (IParser) specials.valAt(op)) != null)
return p.parse(context, form);
else
return InvokeExpr.parse(context, form);
}
catch(Throwable e)
{
Symbol s = (op != null && op instanceof Symbol) ? (Symbol)op : null;
if(!(e instanceof CompilerException)) {
throw new CompilerException((String) SOURCE_PATH.deref(), lineDeref(), columnDeref(), s, e);
}
else
throw (CompilerException) e;
}
finally
{
Var.popThreadBindings();
}
}
// DEPRECATED
static String errorMsg(String source, int line, int column, String s){
return String.format("%s, compiling:(%s:%d:%d)", s, source, line, column);
}
public static Object eval(Object form) {
return eval(form, true);
}
public static Object eval(Object form, boolean freshLoader) {
boolean createdLoader = false;
if(true)//!LOADER.isBound())
{
Var.pushThreadBindings(RT.map(LOADER, RT.makeClassLoader()));
createdLoader = true;
}
try
{
IPersistentMap meta = RT.meta(form);
Object line = (meta != null ? meta.valAt(RT.LINE_KEY, LINE.deref()) : LINE.deref());
Object column = (meta != null ? meta.valAt(RT.COLUMN_KEY, COLUMN.deref()) : COLUMN.deref());
IPersistentMap bindings = RT.mapUniqueKeys(LINE, line, COLUMN, column);
if(meta != null) {
Object eval_file = meta.valAt(RT.EVAL_FILE_KEY);
if(eval_file != null) {
bindings = bindings.assoc(SOURCE_PATH, eval_file);
try {
bindings = bindings.assoc(SOURCE, new File((String)eval_file).getName());
} catch (Throwable t) {
}
}
}
Var.pushThreadBindings(bindings);
try
{
form = macroexpand(form);
if(form instanceof ISeq && Util.equals(RT.first(form), DO))
{
ISeq s = RT.next(form);
for(; RT.next(s) != null; s = RT.next(s))
eval(RT.first(s), false);
return eval(RT.first(s), false);
}
else if((form instanceof IType) ||
(form instanceof IPersistentCollection
&& !(RT.first(form) instanceof Symbol
&& ((Symbol) RT.first(form)).name.startsWith("def"))))
{
ObjExpr fexpr = (ObjExpr) analyze(C.EXPRESSION, RT.list(FN, PersistentVector.EMPTY, form),
"eval" + RT.nextID());
IFn fn = (IFn) fexpr.eval();
return fn.invoke();
}
else
{
Expr expr = analyze(C.EVAL, form);
return expr.eval();
}
}
finally
{
Var.popThreadBindings();
}
}
finally
{
if(createdLoader)
Var.popThreadBindings();
}
}
private static int registerConstant(Object o){
if(!CONSTANTS.isBound())
return -1;
PersistentVector v = (PersistentVector) CONSTANTS.deref();
IdentityHashMap ids = (IdentityHashMap) CONSTANT_IDS.deref();
Integer i = ids.get(o);
if(i != null)
return i;
CONSTANTS.set(RT.conj(v, o));
ids.put(o, v.count());
return v.count();
}
private static KeywordExpr registerKeyword(Keyword keyword){
if(!KEYWORDS.isBound())
return new KeywordExpr(keyword);
IPersistentMap keywordsMap = (IPersistentMap) KEYWORDS.deref();
Object id = RT.get(keywordsMap, keyword);
if(id == null)
{
KEYWORDS.set(RT.assoc(keywordsMap, keyword, registerConstant(keyword)));
}
return new KeywordExpr(keyword);
// KeywordExpr ke = (KeywordExpr) RT.get(keywordsMap, keyword);
// if(ke == null)
// KEYWORDS.set(RT.assoc(keywordsMap, keyword, ke = new KeywordExpr(keyword)));
// return ke;
}
private static int registerKeywordCallsite(Keyword keyword){
if(!KEYWORD_CALLSITES.isBound())
throw new IllegalAccessError("KEYWORD_CALLSITES is not bound");
IPersistentVector keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();
keywordCallsites = keywordCallsites.cons(keyword);
KEYWORD_CALLSITES.set(keywordCallsites);
return keywordCallsites.count()-1;
}
private static int registerProtocolCallsite(Var v){
if(!PROTOCOL_CALLSITES.isBound())
throw new IllegalAccessError("PROTOCOL_CALLSITES is not bound");
IPersistentVector protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref();
protocolCallsites = protocolCallsites.cons(v);
PROTOCOL_CALLSITES.set(protocolCallsites);
return protocolCallsites.count()-1;
}
private static void registerVarCallsite(Var v){
if(!VAR_CALLSITES.isBound())
throw new IllegalAccessError("VAR_CALLSITES is not bound");
IPersistentCollection varCallsites = (IPersistentCollection) VAR_CALLSITES.deref();
varCallsites = varCallsites.cons(v);
VAR_CALLSITES.set(varCallsites);
// return varCallsites.count()-1;
}
static ISeq fwdPath(PathNode p1){
ISeq ret = null;
for(;p1 != null;p1 = p1.parent)
ret = RT.cons(p1,ret);
return ret;
}
static PathNode commonPath(PathNode n1, PathNode n2){
ISeq xp = fwdPath(n1);
ISeq yp = fwdPath(n2);
if(RT.first(xp) != RT.first(yp))
return null;
while(RT.second(xp) != null && RT.second(xp) == RT.second(yp))
{
xp = xp.next();
yp = yp.next();
}
return (PathNode) RT.first(xp);
}
static void addAnnotation(Object visitor, IPersistentMap meta){
if(meta != null && ADD_ANNOTATIONS.isBound())
ADD_ANNOTATIONS.invoke(visitor, meta);
}
static void addParameterAnnotation(Object visitor, IPersistentMap meta, int i){
if(meta != null && ADD_ANNOTATIONS.isBound())
ADD_ANNOTATIONS.invoke(visitor, meta, i);
}
private static Expr analyzeSymbol(Symbol sym) {
Symbol tag = tagOf(sym);
if(sym.ns == null) //ns-qualified syms are always Vars
{
LocalBinding b = referenceLocal(sym);
if(b != null)
{
return new LocalBindingExpr(b, tag);
}
}
else
{
if(namespaceFor(sym) == null && !Util.isPosDigit(sym.name))
{
Symbol nsSym = Symbol.intern(sym.ns);
Class c = HostExpr.maybeClass(nsSym, false);
if(c != null)
{
if(Reflector.getField(c, sym.name, true) != null)
return new StaticFieldExpr(lineDeref(), columnDeref(), c, sym.name, tag);
else
return new QualifiedMethodExpr(c, sym);
}
}
}
//Var v = lookupVar(sym, false);
// Var v = lookupVar(sym, false);
// if(v != null)
// return new VarExpr(v, tag);
Object o = resolve(sym);
if(o instanceof Var)
{
Var v = (Var) o;
if(isMacro(v) != null)
throw Util.runtimeException("Can't take value of a macro: " + v);
if(RT.booleanCast(RT.get(v.meta(),RT.CONST_KEY)))
return analyze(C.EXPRESSION, RT.list(QUOTE, v.get()));
registerVar(v);
return new VarExpr(v, tag);
}
else if(o instanceof Class)
return new ConstantExpr(o);
else if(o instanceof Symbol)
return new UnresolvedVarExpr((Symbol) o);
throw Util.runtimeException("Unable to resolve symbol: " + sym + " in this context");
}
static String destubClassName(String className){
//skip over prefix + '.' or '/'
if(className.startsWith(COMPILE_STUB_PREFIX))
return className.substring(COMPILE_STUB_PREFIX.length()+1);
return className;
}
static Type getType(Class c){
String descriptor = Type.getType(c).getDescriptor();
if(descriptor.startsWith("L"))
descriptor = "L" + destubClassName(descriptor.substring(1));
return Type.getType(descriptor);
}
static Object resolve(Symbol sym, boolean allowPrivate) {
return resolveIn(currentNS(), sym, allowPrivate);
}
static Object resolve(Symbol sym) {
return resolveIn(currentNS(), sym, false);
}
static Namespace namespaceFor(Symbol sym){
return namespaceFor(currentNS(), sym);
}
static Namespace namespaceFor(Namespace inns, Symbol sym){
//note, presumes non-nil sym.ns
// first check against currentNS' aliases...
Symbol nsSym = Symbol.intern(sym.ns);
Namespace ns = inns.lookupAlias(nsSym);
if(ns == null)
{
// ...otherwise check the Namespaces map.
ns = Namespace.find(nsSym);
}
return ns;
}
static public Object resolveIn(Namespace n, Symbol sym, boolean allowPrivate) {
//note - ns-qualified vars must already exist
if(sym.ns != null)
{
Namespace ns = namespaceFor(n, sym);
if(ns == null)
{
Class ac = HostExpr.maybeArrayClass(sym);
if(ac != null)
return ac;
throw Util.runtimeException("No such namespace: " + sym.ns);
}
Var v = ns.findInternedVar(Symbol.intern(sym.name));
if(v == null)
throw Util.runtimeException("No such var: " + sym);
else if(v.ns != currentNS() && !v.isPublic() && !allowPrivate)
throw new IllegalStateException("var: " + sym + " is not public");
return v;
}
else if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')
{
return RT.classForName(sym.name);
}
else if(sym.equals(NS))
return RT.NS_VAR;
else if(sym.equals(IN_NS))
return RT.IN_NS_VAR;
else
{
if(Util.equals(sym, COMPILE_STUB_SYM.get()))
return COMPILE_STUB_CLASS.get();
Object o = n.getMapping(sym);
if(o == null)
{
if(RT.booleanCast(RT.ALLOW_UNRESOLVED_VARS.deref()))
{
return sym;
}
else
{
throw Util.runtimeException("Unable to resolve symbol: " + sym + " in this context");
}
}
return o;
}
}
static public Object maybeResolveIn(Namespace n, Symbol sym) {
//note - ns-qualified vars must already exist
if(sym.ns != null)
{
Namespace ns = namespaceFor(n, sym);
if(ns == null)
return HostExpr.maybeArrayClass(sym);
Var v = ns.findInternedVar(Symbol.intern(sym.name));
if(v == null)
return null;
return v;
}
else if(sym.name.indexOf('.') > 0 && !sym.name.endsWith(".")
|| sym.name.charAt(0) == '[')
{
try {
return RT.classForName(sym.name);
} catch (Exception e) {
if (e instanceof ClassNotFoundException)
return null;
else
return Util.sneakyThrow(e);
}
}
else if(sym.equals(NS))
return RT.NS_VAR;
else if(sym.equals(IN_NS))
return RT.IN_NS_VAR;
else
{
Object o = n.getMapping(sym);
return o;
}
}
static Var lookupVar(Symbol sym, boolean internNew, boolean registerMacro) {
Var var = null;
//note - ns-qualified vars in other namespaces must already exist
if(sym.ns != null)
{
Namespace ns = namespaceFor(sym);
if(ns == null)
return null;
//throw Util.runtimeException("No such namespace: " + sym.ns);
Symbol name = Symbol.intern(sym.name);
if(internNew && ns == currentNS())
var = currentNS().intern(name);
else
var = ns.findInternedVar(name);
}
else if(sym.equals(NS))
var = RT.NS_VAR;
else if(sym.equals(IN_NS))
var = RT.IN_NS_VAR;
else
{
//is it mapped?
Object o = currentNS().getMapping(sym);
if(o == null)
{
//introduce a new var in the current ns
if(internNew)
var = currentNS().intern(Symbol.intern(sym.name));
}
else if(o instanceof Var)
{
var = (Var) o;
}
else
{
throw Util.runtimeException("Expecting var, but " + sym + " is mapped to " + o);
}
}
if(var != null && (!var.isMacro() || registerMacro))
registerVar(var);
return var;
}
static Var lookupVar(Symbol sym, boolean internNew) {
return lookupVar(sym, internNew, true);
}
private static void registerVar(Var var) {
if(!VARS.isBound())
return;
IPersistentMap varsMap = (IPersistentMap) VARS.deref();
Object id = RT.get(varsMap, var);
if(id == null)
{
VARS.set(RT.assoc(varsMap, var, registerConstant(var)));
}
// if(varsMap != null && RT.get(varsMap, var) == null)
// VARS.set(RT.assoc(varsMap, var, var));
}
static Namespace currentNS(){
return (Namespace) RT.CURRENT_NS.deref();
}
static void closeOver(LocalBinding b, ObjMethod method){
if(b != null && method != null)
{
LocalBinding lb = (LocalBinding) RT.get(method.locals, b);
if(lb == null)
{
method.objx.closes = (IPersistentMap) RT.assoc(method.objx.closes, b, b);
closeOver(b, method.parent);
}
else {
if(lb.idx == 0)
method.usesThis = true;
if(IN_CATCH_FINALLY.deref() != null)
{
method.localsUsedInCatchFinally = (PersistentHashSet) method.localsUsedInCatchFinally.cons(b.idx);
}
}
}
}
static LocalBinding referenceLocal(Symbol sym) {
if(!LOCAL_ENV.isBound())
return null;
LocalBinding b = (LocalBinding) RT.get(LOCAL_ENV.deref(), sym);
if(b != null)
{
ObjMethod method = (ObjMethod) METHOD.deref();
if(b.idx == 0)
method.usesThis = true;
closeOver(b, method);
}
return b;
}
private static Symbol tagOf(Object o){
Object tag = RT.get(RT.meta(o), RT.TAG_KEY);
if(tag instanceof Symbol)
return (Symbol) tag;
else if(tag instanceof String)
return Symbol.intern(null, (String) tag);
return null;
}
public static Object loadFile(String file) throws IOException{
// File fo = new File(file);
// if(!fo.exists())
// return null;
FileInputStream f = new FileInputStream(file);
try
{
return load(new InputStreamReader(f, RT.UTF8), new File(file).getAbsolutePath(), (new File(file)).getName());
}
finally
{
f.close();
}
}
public static Object load(Reader rdr) {
return load(rdr, null, "NO_SOURCE_FILE");
}
static void consumeWhitespaces(LineNumberingPushbackReader pushbackReader) {
int ch = LispReader.read1(pushbackReader);
while(LispReader.isWhitespace(ch))
ch = LispReader.read1(pushbackReader);
LispReader.unread(pushbackReader, ch);
}
private static final Object OPTS_COND_ALLOWED = RT.mapUniqueKeys(LispReader.OPT_READ_COND, LispReader.COND_ALLOW);
private static Object readerOpts(String sourceName) {
if(sourceName != null && sourceName.endsWith(".cljc"))
return OPTS_COND_ALLOWED;
else
return null;
}
public static Object load(Reader rdr, String sourcePath, String sourceName) {
Object EOF = new Object();
Object ret = null;
LineNumberingPushbackReader pushbackReader =
(rdr instanceof LineNumberingPushbackReader) ? (LineNumberingPushbackReader) rdr :
new LineNumberingPushbackReader(rdr);
consumeWhitespaces(pushbackReader);
Var.pushThreadBindings(
RT.mapUniqueKeys(LOADER, RT.makeClassLoader(),
SOURCE_PATH, sourcePath,
SOURCE, sourceName,
METHOD, null,
LOCAL_ENV, null,
LOOP_LOCALS, null,
NEXT_LOCAL_NUM, 0,
RT.READEVAL, RT.T,
RT.CURRENT_NS, RT.CURRENT_NS.deref(),
LINE_BEFORE, pushbackReader.getLineNumber(),
COLUMN_BEFORE, pushbackReader.getColumnNumber(),
LINE_AFTER, pushbackReader.getLineNumber(),
COLUMN_AFTER, pushbackReader.getColumnNumber()
,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()
,RT.WARN_ON_REFLECTION, RT.WARN_ON_REFLECTION.deref()
,RT.DATA_READERS, RT.DATA_READERS.deref()
));
Object readerOpts = readerOpts(sourceName);
try
{
for(Object r = LispReader.read(pushbackReader, false, EOF, false, readerOpts); r != EOF;
r = LispReader.read(pushbackReader, false, EOF, false, readerOpts))
{
consumeWhitespaces(pushbackReader);
LINE_AFTER.set(pushbackReader.getLineNumber());
COLUMN_AFTER.set(pushbackReader.getColumnNumber());
ret = eval(r,false);
LINE_BEFORE.set(pushbackReader.getLineNumber());
COLUMN_BEFORE.set(pushbackReader.getColumnNumber());
}
}
catch(LispReader.ReaderException e)
{
throw new CompilerException(sourcePath, e.line, e.column, null, CompilerException.PHASE_READ, e.getCause());
}
catch(Throwable e)
{
if(!(e instanceof CompilerException))
throw new CompilerException(sourcePath, (Integer) LINE_BEFORE.deref(), (Integer) COLUMN_BEFORE.deref(), null, CompilerException.PHASE_EXECUTION, e);
else
throw (CompilerException) e;
}
finally
{
Var.popThreadBindings();
}
return ret;
}
static public void writeClassFile(String internalName, byte[] bytecode) throws IOException{
String genPath = (String) COMPILE_PATH.deref();
if(genPath == null)
throw Util.runtimeException("*compile-path* not set");
String[] dirs = internalName.split("/");
String p = genPath;
for(int i = 0; i < dirs.length - 1; i++)
{
p += File.separator + dirs[i];
(new File(p)).mkdir();
}
String path = genPath + File.separator + internalName + ".class";
File cf = new File(path);
cf.createNewFile();
FileOutputStream cfs = new FileOutputStream(cf);
try
{
cfs.write(bytecode);
cfs.flush();
}
finally
{
cfs.close();
}
}
public static void pushNS(){
Var.pushThreadBindings(PersistentHashMap.create(Var.intern(Symbol.intern("clojure.core"),
Symbol.intern("*ns*")).setDynamic(), null));
}
public static void pushNSandLoader(ClassLoader loader){
Var.pushThreadBindings(RT.map(Var.intern(Symbol.intern("clojure.core"),
Symbol.intern("*ns*")).setDynamic(),
null,
RT.FN_LOADER_VAR, loader,
RT.READEVAL, RT.T
));
}
public static ILookupThunk getLookupThunk(Object target, Keyword k){
return null; //To change body of created methods use File | Settings | File Templates.
}
static void compile1(GeneratorAdapter gen, ObjExpr objx, Object form) {
Object line = lineDeref();
Object column = columnDeref();
if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))
line = RT.meta(form).valAt(RT.LINE_KEY);
if(RT.meta(form) != null && RT.meta(form).containsKey(RT.COLUMN_KEY))
column = RT.meta(form).valAt(RT.COLUMN_KEY);
Var.pushThreadBindings(
RT.map(LINE, line, COLUMN, column
,LOADER, RT.makeClassLoader()
));
try
{
form = macroexpand(form);
if(form instanceof ISeq && Util.equals(RT.first(form), DO))
{
for(ISeq s = RT.next(form); s != null; s = RT.next(s))
{
compile1(gen, objx, RT.first(s));
}
}
else
{
Expr expr = analyze(C.EVAL, form);
objx.keywords = (IPersistentMap) KEYWORDS.deref();
objx.vars = (IPersistentMap) VARS.deref();
objx.constants = (PersistentVector) CONSTANTS.deref();
expr.emit(C.EXPRESSION, objx, gen);
expr.eval();
}
}
finally
{
Var.popThreadBindings();
}
}
public static Object compile(Reader rdr, String sourcePath, String sourceName) throws IOException{
if(COMPILE_PATH.deref() == null)
throw Util.runtimeException("*compile-path* not set");
Object EOF = new Object();
Object ret = null;
LineNumberingPushbackReader pushbackReader =
(rdr instanceof LineNumberingPushbackReader) ? (LineNumberingPushbackReader) rdr :
new LineNumberingPushbackReader(rdr);
Var.pushThreadBindings(
RT.mapUniqueKeys(SOURCE_PATH, sourcePath,
SOURCE, sourceName,
METHOD, null,
LOCAL_ENV, null,
LOOP_LOCALS, null,
NEXT_LOCAL_NUM, 0,
RT.READEVAL, RT.T,
RT.CURRENT_NS, RT.CURRENT_NS.deref(),
LINE_BEFORE, pushbackReader.getLineNumber(),
COLUMN_BEFORE, pushbackReader.getColumnNumber(),
LINE_AFTER, pushbackReader.getLineNumber(),
COLUMN_AFTER, pushbackReader.getColumnNumber(),
CONSTANTS, PersistentVector.EMPTY,
CONSTANT_IDS, new IdentityHashMap(),
KEYWORDS, PersistentHashMap.EMPTY,
VARS, PersistentHashMap.EMPTY
,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()
,RT.WARN_ON_REFLECTION, RT.WARN_ON_REFLECTION.deref()
,RT.DATA_READERS, RT.DATA_READERS.deref()
// ,LOADER, RT.makeClassLoader()
));
try
{
//generate loader class
ObjExpr objx = new ObjExpr(null);
objx.internalName = sourcePath.replace(File.separator, "/").substring(0, sourcePath.lastIndexOf('.'))
+ RT.LOADER_SUFFIX;
objx.objtype = Type.getObjectType(objx.internalName);
ClassWriter cw = classWriter();
ClassVisitor cv = cw;
cv.visit(V1_8, ACC_PUBLIC + ACC_SUPER, objx.internalName, null, "java/lang/Object", null);
//static load method
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC,
Method.getMethod("void load ()"),
null,
null,
cv);
gen.visitCode();
Object readerOpts = readerOpts(sourceName);
for(Object r = LispReader.read(pushbackReader, false, EOF, false, readerOpts); r != EOF;
r = LispReader.read(pushbackReader, false, EOF, false, readerOpts))
{
LINE_AFTER.set(pushbackReader.getLineNumber());
COLUMN_AFTER.set(pushbackReader.getColumnNumber());
compile1(gen, objx, r);
LINE_BEFORE.set(pushbackReader.getLineNumber());
COLUMN_BEFORE.set(pushbackReader.getColumnNumber());
}
//end of load
gen.returnValue();
gen.endMethod();
//static fields for constants
for(int i = 0; i < objx.constants.count(); i++)
{
if(objx.usedConstants.contains(i))
cv.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, objx.constantName(i), objx.constantType(i).getDescriptor(),
null, null);
}
final int INITS_PER = 100;
int numInits = objx.constants.count() / INITS_PER;
if(objx.constants.count() % INITS_PER != 0)
++numInits;
for(int n = 0;n ()"),
null,
null,
cv);
clinitgen.visitCode();
Label startTry = clinitgen.newLabel();
Label endTry = clinitgen.newLabel();
Label end = clinitgen.newLabel();
Label finallyLabel = clinitgen.newLabel();
// if(objx.constants.count() > 0)
// {
// objx.emitConstants(clinitgen);
// }
for(int n = 0;n mmap;
Map> covariants;
public NewInstanceExpr(Object tag){
super(tag);
}
static class DeftypeParser implements IParser{
public Expr parse(C context, final Object frm) {
ISeq rform = (ISeq) frm;
//(deftype* tagname classname [fields] :implements [interfaces] :tag tagname methods*)
rform = RT.next(rform);
String tagname = ((Symbol) rform.first()).getName();
rform = rform.next();
Symbol classname = (Symbol) rform.first();
rform = rform.next();
IPersistentVector fields = (IPersistentVector) rform.first();
rform = rform.next();
IPersistentMap opts = PersistentHashMap.EMPTY;
while(rform != null && rform.first() instanceof Keyword)
{
opts = opts.assoc(rform.first(), RT.second(rform));
rform = rform.next().next();
}
ObjExpr ret = build((IPersistentVector)RT.get(opts,implementsKey,PersistentVector.EMPTY),fields,null,tagname, classname,
(Symbol) RT.get(opts,RT.TAG_KEY),rform, frm, opts);
return ret;
}
}
static class ReifyParser implements IParser{
public Expr parse(C context, Object frm) {
//(reify this-name? [interfaces] (method-name [args] body)*)
ISeq form = (ISeq) frm;
ObjMethod enclosingMethod = (ObjMethod) METHOD.deref();
String basename = enclosingMethod != null ?
(trimGenID(enclosingMethod.objx.name) + "$")
: (munge(currentNS().name.name) + "$");
String simpleName = "reify__" + RT.nextID();
String classname = basename + simpleName;
ISeq rform = RT.next(form);
IPersistentVector interfaces = ((IPersistentVector) RT.first(rform)).cons(Symbol.intern("clojure.lang.IObj"));
rform = RT.next(rform);
ObjExpr ret = build(interfaces, null, null, classname, Symbol.intern(classname), null, rform, frm, null);
if(frm instanceof IObj && ((IObj) frm).meta() != null)
return new MetaExpr(ret, MapExpr
.parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) frm).meta()));
else
return ret;
}
}
static ObjExpr build(IPersistentVector interfaceSyms, IPersistentVector fieldSyms, Symbol thisSym,
String tagName, Symbol className,
Symbol typeTag, ISeq methodForms, Object frm, IPersistentMap opts) {
NewInstanceExpr ret = new NewInstanceExpr(null);
ret.src = frm;
ret.name = className.toString();
ret.classMeta = RT.meta(className);
ret.internalName = ret.name.replace('.', '/');
ret.objtype = Type.getObjectType(ret.internalName);
ret.opts = opts;
if(thisSym != null)
ret.thisName = thisSym.name;
if(fieldSyms != null)
{
IPersistentMap fmap = PersistentHashMap.EMPTY;
Object[] closesvec = new Object[2 * fieldSyms.count()];
for(int i=0;i= 0 && (((Symbol)fieldSyms.nth(i)).name.equals("__meta")
|| ((Symbol)fieldSyms.nth(i)).name.equals("__extmap")
|| ((Symbol)fieldSyms.nth(i)).name.equals("__hash")
|| ((Symbol)fieldSyms.nth(i)).name.equals("__hasheq")
);--i)
ret.altCtorDrops++;
}
//todo - set up volatiles
// ret.volatiles = PersistentHashSet.create(RT.seq(RT.get(ret.optionsMap, volatileKey)));
PersistentVector interfaces = PersistentVector.EMPTY;
for(ISeq s = RT.seq(interfaceSyms);s!=null;s = s.next())
{
Class c = (Class) resolve((Symbol) s.first());
if(!c.isInterface())
throw new IllegalArgumentException("only interfaces are supported, had: " + c.getName());
interfaces = interfaces.cons(c);
}
Class superClass = Object.class;
Map[] mc = gatherMethods(superClass,RT.seq(interfaces));
Map overrideables = mc[0];
Map covariants = mc[1];
ret.mmap = overrideables;
ret.covariants = covariants;
String[] inames = interfaceNames(interfaces);
Class stub = compileStub(slashname(superClass),ret, inames, frm);
Symbol thistag = Symbol.intern(null,stub.getName());
try
{
Var.pushThreadBindings(
RT.mapUniqueKeys(CONSTANTS, PersistentVector.EMPTY,
CONSTANT_IDS, new IdentityHashMap(),
KEYWORDS, PersistentHashMap.EMPTY,
VARS, PersistentHashMap.EMPTY,
KEYWORD_CALLSITES, PersistentVector.EMPTY,
PROTOCOL_CALLSITES, PersistentVector.EMPTY,
VAR_CALLSITES, emptyVarCallSites(),
NO_RECUR, null));
if(ret.isDeftype())
{
Var.pushThreadBindings(RT.mapUniqueKeys(METHOD, null,
LOCAL_ENV, ret.fields
, COMPILE_STUB_SYM, Symbol.intern(null, tagName)
, COMPILE_STUB_CLASS, stub));
ret.hintedFields = RT.subvec(fieldSyms, 0, fieldSyms.count() - ret.altCtorDrops);
}
//now (methodname [args] body)*
ret.line = lineDeref();
ret.column = columnDeref();
IPersistentCollection methods = null;
for(ISeq s = methodForms; s != null; s = RT.next(s))
{
NewInstanceMethod m = NewInstanceMethod.parse(ret, (ISeq) RT.first(s),thistag, overrideables);
methods = RT.conj(methods, m);
}
ret.methods = methods;
ret.keywords = (IPersistentMap) KEYWORDS.deref();
ret.vars = (IPersistentMap) VARS.deref();
ret.constants = (PersistentVector) CONSTANTS.deref();
ret.constantsID = RT.nextID();
ret.keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();
ret.protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref();
ret.varCallsites = (IPersistentSet) VAR_CALLSITES.deref();
}
finally
{
if(ret.isDeftype())
Var.popThreadBindings();
Var.popThreadBindings();
}
try
{
ret.compile(slashname(superClass),inames,false);
}
catch(IOException e)
{
throw Util.sneakyThrow(e);
}
ret.getCompiledClass();
return ret;
}
/***
* Current host interop uses reflection, which requires pre-existing classes
* Work around this by:
* Generate a stub class that has the same interfaces and fields as the class we are generating.
* Use it as a type hint for this, and bind the simple name of the class to this stub (in resolve etc)
* Unmunge the name (using a magic prefix) on any code gen for classes
*/
static Class compileStub(String superName, NewInstanceExpr ret, String[] interfaceNames, Object frm){
ClassWriter cw = classWriter();
ClassVisitor cv = cw;
cv.visit(V1_8, ACC_PUBLIC + ACC_SUPER, COMPILE_STUB_PREFIX + "/" + ret.internalName,
null,superName,interfaceNames);
//instance fields for closed-overs
for(ISeq s = RT.keys(ret.closes); s != null; s = s.next())
{
LocalBinding lb = (LocalBinding) s.first();
int access = ACC_PUBLIC + (ret.isVolatile(lb) ? ACC_VOLATILE :
ret.isMutable(lb) ? 0 :
ACC_FINAL);
if(lb.getPrimitiveType() != null)
cv.visitField(access
, lb.name, Type.getType(lb.getPrimitiveType()).getDescriptor(),
null, null);
else
//todo - when closed-overs are fields, use more specific types here and in ctor and emitLocal?
cv.visitField(access
, lb.name, OBJECT_TYPE.getDescriptor(), null, null);
}
//ctor that takes closed-overs and does nothing
Method m = new Method("", Type.VOID_TYPE, ret.ctorTypes());
GeneratorAdapter ctorgen = new GeneratorAdapter(ACC_PUBLIC,
m,
null,
null,
cv);
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.invokeConstructor(Type.getObjectType(superName), voidctor);
ctorgen.returnValue();
ctorgen.endMethod();
if(ret.altCtorDrops > 0)
{
Type[] ctorTypes = ret.ctorTypes();
Type[] altCtorTypes = new Type[ctorTypes.length-ret.altCtorDrops];
for(int i=0;i", Type.VOID_TYPE, altCtorTypes);
ctorgen = new GeneratorAdapter(ACC_PUBLIC,
alt,
null,
null,
cv);
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.loadArgs();
ctorgen.visitInsn(Opcodes.ACONST_NULL); //__meta
ctorgen.visitInsn(Opcodes.ACONST_NULL); //__extmap
ctorgen.visitInsn(Opcodes.ICONST_0); //__hash
ctorgen.visitInsn(Opcodes.ICONST_0); //__hasheq
ctorgen.invokeConstructor(Type.getObjectType(COMPILE_STUB_PREFIX + "/" + ret.internalName),
new Method("", Type.VOID_TYPE, ctorTypes));
ctorgen.returnValue();
ctorgen.endMethod();
// alt ctor no __hash, __hasheq
altCtorTypes = new Type[ctorTypes.length-2];
for(int i=0;i", Type.VOID_TYPE, altCtorTypes);
ctorgen = new GeneratorAdapter(ACC_PUBLIC,
alt,
null,
null,
cv);
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.loadArgs();
ctorgen.visitInsn(Opcodes.ICONST_0); //__hash
ctorgen.visitInsn(Opcodes.ICONST_0); //__hasheq
ctorgen.invokeConstructor(Type.getObjectType(COMPILE_STUB_PREFIX + "/" + ret.internalName),
new Method("", Type.VOID_TYPE, ctorTypes));
ctorgen.returnValue();
ctorgen.endMethod();
}
//end of class
cv.visitEnd();
byte[] bytecode = cw.toByteArray();
DynamicClassLoader loader = (DynamicClassLoader) LOADER.deref();
return loader.defineClass(COMPILE_STUB_PREFIX + "." + ret.name, bytecode, frm);
}
static String[] interfaceNames(IPersistentVector interfaces){
int icnt = interfaces.count();
String[] inames = icnt > 0 ? new String[icnt] : null;
for(int i=0;i this.hintedFields.count())
{
//create(IPersistentMap)
String className = name.replace('.', '/');
int i = 1;
int fieldCount = hintedFields.count();
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC + ACC_STATIC, "create", "(Lclojure/lang/IPersistentMap;)L"+className+";", null, null);
mv.visitCode();
for(ISeq s = RT.seq(hintedFields); s!=null; s=s.next(), i++)
{
String bName = ((Symbol)s.first()).name;
Class k = tagClass(tagOf(s.first()));
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(bName);
mv.visitMethodInsn(INVOKESTATIC, "clojure/lang/Keyword", "intern", "(Ljava/lang/String;)Lclojure/lang/Keyword;");
mv.visitInsn(ACONST_NULL);
mv.visitMethodInsn(INVOKEINTERFACE, "clojure/lang/IPersistentMap", "valAt", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
if(k.isPrimitive())
{
mv.visitTypeInsn(CHECKCAST, Type.getType(boxClass(k)).getInternalName());
}
mv.visitVarInsn(ASTORE, i);
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(bName);
mv.visitMethodInsn(INVOKESTATIC, "clojure/lang/Keyword", "intern", "(Ljava/lang/String;)Lclojure/lang/Keyword;");
mv.visitMethodInsn(INVOKEINTERFACE, "clojure/lang/IPersistentMap", "without", "(Ljava/lang/Object;)Lclojure/lang/IPersistentMap;");
mv.visitVarInsn(ASTORE, 0);
}
mv.visitTypeInsn(Opcodes.NEW, className);
mv.visitInsn(DUP);
Method ctor = new Method("", Type.VOID_TYPE, ctorTypes());
if(hintedFields.count() > 0)
for(i=1; i<=fieldCount; i++)
{
mv.visitVarInsn(ALOAD, i);
Class k = tagClass(tagOf(hintedFields.nth(i-1)));
if(k.isPrimitive())
{
String b = Type.getType(boxClass(k)).getInternalName();
String p = Type.getType(k).getDescriptor();
String n = k.getName();
mv.visitMethodInsn(INVOKEVIRTUAL, b, n+"Value", "()"+p);
}
}
mv.visitInsn(ACONST_NULL); //__meta
mv.visitVarInsn(ALOAD, 0); //__extmap
mv.visitMethodInsn(INVOKESTATIC, "clojure/lang/RT", "seqOrElse", "(Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitInsn(ICONST_0); //__hash
mv.visitInsn(ICONST_0); //__hasheq
mv.visitMethodInsn(INVOKESPECIAL, className, "", ctor.getDescriptor());
mv.visitInsn(ARETURN);
mv.visitMaxs(4+fieldCount, 1+fieldCount);
mv.visitEnd();
}
}
}
protected void emitMethods(ClassVisitor cv){
for(ISeq s = RT.seq(methods); s != null; s = s.next())
{
ObjMethod method = (ObjMethod) s.first();
method.emit(this, cv);
}
//emit bridge methods
for(Map.Entry> e : covariants.entrySet())
{
java.lang.reflect.Method m = mmap.get(e.getKey());
Class[] params = m.getParameterTypes();
Type[] argTypes = new Type[params.length];
for(int i = 0; i < params.length; i++)
{
argTypes[i] = Type.getType(params[i]);
}
Method target = new Method(m.getName(), Type.getType(m.getReturnType()), argTypes);
for(Class retType : e.getValue())
{
Method meth = new Method(m.getName(), Type.getType(retType), argTypes);
GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_BRIDGE,
meth,
null,
//todo don't hardwire this
EXCEPTION_TYPES,
cv);
gen.visitCode();
gen.loadThis();
gen.loadArgs();
gen.invokeInterface(Type.getType(m.getDeclaringClass()),target);
gen.returnValue();
gen.endMethod();
}
}
}
static public IPersistentVector msig(java.lang.reflect.Method m){
return RT.vector(m.getName(), RT.seq(m.getParameterTypes()),m.getReturnType());
}
static void considerMethod(java.lang.reflect.Method m, Map mm){
IPersistentVector mk = msig(m);
int mods = m.getModifiers();
if(!(mm.containsKey(mk)
|| !(Modifier.isPublic(mods) || Modifier.isProtected(mods))
|| Modifier.isStatic(mods)
|| Modifier.isFinal(mods)))
{
mm.put(mk, m);
}
}
static void gatherMethods(Class c, Map mm){
for(; c != null; c = c.getSuperclass())
{
for(java.lang.reflect.Method m : c.getDeclaredMethods())
considerMethod(m, mm);
for(java.lang.reflect.Method m : c.getMethods())
considerMethod(m, mm);
}
}
static public Map[] gatherMethods(Class sc, ISeq interfaces){
Map allm = new HashMap();
gatherMethods(sc, allm);
for(; interfaces != null; interfaces = interfaces.next())
gatherMethods((Class) interfaces.first(), allm);
Map mm = new HashMap();
Map> covariants = new HashMap>();
for(Object o : allm.entrySet())
{
Map.Entry e = (Map.Entry) o;
IPersistentVector mk = (IPersistentVector) e.getKey();
mk = (IPersistentVector) mk.pop();
java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue();
if(mm.containsKey(mk)) //covariant return
{
Set cvs = covariants.get(mk);
if(cvs == null)
{
cvs = new HashSet();
covariants.put(mk,cvs);
}
java.lang.reflect.Method om = mm.get(mk);
if(om.getReturnType().isAssignableFrom(m.getReturnType()))
{
cvs.add(om.getReturnType());
mm.put(mk, m);
}
else
cvs.add(m.getReturnType());
}
else
mm.put(mk, m);
}
return new Map[]{mm,covariants};
}
}
public static class NewInstanceMethod extends ObjMethod{
String name;
Type[] argTypes;
Type retType;
Class retClass;
Class[] exclasses;
static Symbol dummyThis = Symbol.intern(null,"dummy_this_dlskjsdfower");
private IPersistentVector parms;
public NewInstanceMethod(ObjExpr objx, ObjMethod parent){
super(objx, parent);
}
int numParams(){
return argLocals.count();
}
String getMethodName(){
return name;
}
Type getReturnType(){
return retType;
}
Type[] getArgTypes(){
return argTypes;
}
static public IPersistentVector msig(String name,Class[] paramTypes){
return RT.vector(name,RT.seq(paramTypes));
}
static NewInstanceMethod parse(ObjExpr objx, ISeq form, Symbol thistag,
Map overrideables) {
//(methodname [this-name args*] body...)
//this-name might be nil
NewInstanceMethod method = new NewInstanceMethod(objx, (ObjMethod) METHOD.deref());
Symbol dotname = (Symbol)RT.first(form);
Symbol name = (Symbol) Symbol.intern(null,munge(dotname.name)).withMeta(RT.meta(dotname));
IPersistentVector parms = (IPersistentVector) RT.second(form);
if(parms.count() == 0)
{
throw new IllegalArgumentException("Must supply at least one argument for 'this' in: " + dotname);
}
Symbol thisName = (Symbol) parms.nth(0);
parms = RT.subvec(parms,1,parms.count());
ISeq body = RT.next(RT.next(form));
try
{
method.line = lineDeref();
method.column = columnDeref();
//register as the current method and set up a new env frame
PathNode pnode = new PathNode(PATHTYPE.PATH, (PathNode) CLEAR_PATH.get());
Var.pushThreadBindings(
RT.mapUniqueKeys(
METHOD, method,
LOCAL_ENV, LOCAL_ENV.deref(),
LOOP_LOCALS, null,
NEXT_LOCAL_NUM, 0
,CLEAR_PATH, pnode
,CLEAR_ROOT, pnode
,CLEAR_SITES, PersistentHashMap.EMPTY
,METHOD_RETURN_CONTEXT, RT.T
));
//register 'this' as local 0
if(thisName != null)
registerLocal((thisName == null) ? dummyThis:thisName,thistag, null,false);
else
getAndIncLocalNum();
PersistentVector argLocals = PersistentVector.EMPTY;
method.retClass = tagClass(tagOf(name));
method.argTypes = new Type[parms.count()];
boolean hinted = tagOf(name) != null;
Class[] pclasses = new Class[parms.count()];
Symbol[] psyms = new Symbol[parms.count()];
for(int i = 0; i < parms.count(); i++)
{
if(!(parms.nth(i) instanceof Symbol))
throw new IllegalArgumentException("params must be Symbols");
Symbol p = (Symbol) parms.nth(i);
Object tag = tagOf(p);
if(tag != null)
hinted = true;
if(p.getNamespace() != null)
p = Symbol.intern(p.name);
Class pclass = tagClass(tag);
pclasses[i] = pclass;
psyms[i] = p;
}
Map matches = findMethodsWithNameAndArity(name.name, parms.count(), overrideables);
Object mk = msig(name.name, pclasses);
java.lang.reflect.Method m = null;
if(matches.size() > 0)
{
//multiple methods
if(matches.size() > 1)
{
//must be hinted and match one method
if(!hinted)
throw new IllegalArgumentException("Must hint overloaded method: " + name.name);
m = (java.lang.reflect.Method) matches.get(mk);
if(m == null)
throw new IllegalArgumentException("Can't find matching overloaded method: " + name.name);
if(m.getReturnType() != method.retClass)
throw new IllegalArgumentException("Mismatched return type: " + name.name +
", expected: " + m.getReturnType().getName() + ", had: " + method.retClass.getName());
}
else //one match
{
//if hinted, validate match,
if(hinted)
{
m = (java.lang.reflect.Method) matches.get(mk);
if(m == null)
throw new IllegalArgumentException("Can't find matching method: " + name.name +
", leave off hints for auto match.");
if(m.getReturnType() != method.retClass)
throw new IllegalArgumentException("Mismatched return type: " + name.name +
", expected: " + m.getReturnType().getName() + ", had: " + method.retClass.getName());
}
else //adopt found method sig
{
m = (java.lang.reflect.Method) matches.values().iterator().next();
method.retClass = m.getReturnType();
pclasses = m.getParameterTypes();
}
}
}
// else if(findMethodsWithName(name.name,allmethods).size()>0)
// throw new IllegalArgumentException("Can't override/overload method: " + name.name);
else
throw new IllegalArgumentException("Can't define method not in interfaces: " + name.name);
//else
//validate unque name+arity among additional methods
method.retType = Type.getType(method.retClass);
method.exclasses = m.getExceptionTypes();
for(int i = 0; i < parms.count(); i++)
{
LocalBinding lb = registerLocal(psyms[i], null, new MethodParamExpr(pclasses[i]),true);
argLocals = argLocals.assocN(i,lb);
method.argTypes[i] = Type.getType(pclasses[i]);
}
for(int i = 0; i < parms.count(); i++)
{
if(pclasses[i] == long.class || pclasses[i] == double.class)
getAndIncLocalNum();
}
LOOP_LOCALS.set(argLocals);
method.name = name.name;
method.methodMeta = RT.meta(name);
method.parms = parms;
method.argLocals = argLocals;
method.body = (new BodyExpr.Parser()).parse(C.RETURN, body);
return method;
}
finally
{
Var.popThreadBindings();
}
}
private static Map findMethodsWithNameAndArity(String name, int arity, Map mm){
Map ret = new HashMap();
for(Object o : mm.entrySet())
{
Map.Entry e = (Map.Entry) o;
java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue();
if(name.equals(m.getName()) && m.getParameterTypes().length == arity)
ret.put(e.getKey(), e.getValue());
}
return ret;
}
private static Map findMethodsWithName(String name, Map mm){
Map ret = new HashMap();
for(Object o : mm.entrySet())
{
Map.Entry e = (Map.Entry) o;
java.lang.reflect.Method m = (java.lang.reflect.Method) e.getValue();
if(name.equals(m.getName()))
ret.put(e.getKey(), e.getValue());
}
return ret;
}
public void emit(ObjExpr obj, ClassVisitor cv){
Method m = new Method(getMethodName(), getReturnType(), getArgTypes());
Type[] extypes = null;
if(exclasses.length > 0)
{
extypes = new Type[exclasses.length];
for(int i=0;i tests;
public final HashMap thens;
public final Keyword switchType;
public final Keyword testType;
public final Set skipCheck;
public final Class returnType;
public final int line;
public final int column;
final static Type NUMBER_TYPE = Type.getType(Number.class);
final static Method intValueMethod = Method.getMethod("int intValue()");
final static Method hashMethod = Method.getMethod("int hash(Object)");
final static Method hashCodeMethod = Method.getMethod("int hashCode()");
final static Method equivMethod = Method.getMethod("boolean equiv(Object, Object)");
final static Keyword compactKey = Keyword.intern(null, "compact");
final static Keyword sparseKey = Keyword.intern(null, "sparse");
final static Keyword hashIdentityKey = Keyword.intern(null, "hash-identity");
final static Keyword hashEquivKey = Keyword.intern(null, "hash-equiv");
final static Keyword intKey = Keyword.intern(null, "int");
//(case* expr shift mask default map table-type test-type skip-check?)
public CaseExpr(int line, int column, LocalBindingExpr expr, int shift, int mask, int low, int high, Expr defaultExpr,
SortedMap tests,HashMap thens, Keyword switchType, Keyword testType, Set skipCheck){
this.expr = expr;
this.shift = shift;
this.mask = mask;
this.low = low;
this.high = high;
this.defaultExpr = defaultExpr;
this.tests = tests;
this.thens = thens;
this.line = line;
this.column = column;
if (switchType != compactKey && switchType != sparseKey)
throw new IllegalArgumentException("Unexpected switch type: "+switchType);
this.switchType = switchType;
if (testType != intKey && testType != hashEquivKey && testType != hashIdentityKey)
throw new IllegalArgumentException("Unexpected test type: "+switchType);
this.testType = testType;
this.skipCheck = skipCheck;
Collection returns = new ArrayList(thens.values());
returns.add(defaultExpr);
this.returnType = maybeJavaClass(returns);
if(RT.count(skipCheck) > 0 && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
{
RT.errPrintWriter()
.format("Performance warning, %s:%d:%d - hash collision of some case test constants; if selected, those entries will be tested sequentially.\n",
SOURCE_PATH.deref(), line, column);
}
}
public boolean hasJavaClass(){
return returnType != null;
}
public boolean canEmitPrimitive(){
return Util.isPrimitive(returnType);
}
public Class getJavaClass(){
return returnType;
}
public Object eval() {
throw new UnsupportedOperationException("Can't eval case");
}
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
doEmit(context, objx, gen, false);
}
public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
doEmit(context, objx, gen, true);
}
public void doEmit(C context, ObjExpr objx, GeneratorAdapter gen, boolean emitUnboxed){
Label defaultLabel = gen.newLabel();
Label endLabel = gen.newLabel();
SortedMap labels = new TreeMap();
for(Integer i : tests.keySet())
{
labels.put(i, gen.newLabel());
}
gen.visitLineNumber(line, gen.mark());
Class primExprClass = maybePrimitiveType(expr);
Type primExprType = primExprClass == null ? null : Type.getType(primExprClass);
if (testType == intKey)
emitExprForInts(objx, gen, primExprType, defaultLabel);
else
emitExprForHashes(objx, gen);
if (switchType == sparseKey)
{
Label[] la = new Label[labels.size()];
la = labels.values().toArray(la);
int[] ints = Numbers.int_array(tests.keySet());
gen.visitLookupSwitchInsn(defaultLabel, ints, la);
}
else
{
Label[] la = new Label[(high-low)+1];
for(int i=low;i<=high;i++)
{
la[i-low] = labels.containsKey(i) ? labels.get(i) : defaultLabel;
}
gen.visitTableSwitchInsn(low, high, defaultLabel, la);
}
for(Integer i : labels.keySet())
{
gen.mark(labels.get(i));
if (testType == intKey)
emitThenForInts(objx, gen, primExprType, tests.get(i), thens.get(i), defaultLabel, emitUnboxed);
else if (RT.contains(skipCheck, i) == RT.T)
emitExpr(objx, gen, thens.get(i), emitUnboxed);
else
emitThenForHashes(objx, gen, tests.get(i), thens.get(i), defaultLabel, emitUnboxed);
gen.goTo(endLabel);
}
gen.mark(defaultLabel);
emitExpr(objx, gen, defaultExpr, emitUnboxed);
gen.mark(endLabel);
if(context == C.STATEMENT)
gen.pop();
}
private boolean isShiftMasked(){
return mask != 0;
}
private void emitShiftMask(GeneratorAdapter gen){
if (isShiftMasked())
{
gen.push(shift);
gen.visitInsn(ISHR);
gen.push(mask);
gen.visitInsn(IAND);
}
}
private void emitExprForInts(ObjExpr objx, GeneratorAdapter gen, Type exprType, Label defaultLabel){
if (exprType == null)
{
if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
{
RT.errPrintWriter()
.format("Performance warning, %s:%d:%d - case has int tests, but tested expression is not primitive.\n",
SOURCE_PATH.deref(), line, column);
}
expr.emit(C.EXPRESSION, objx, gen);
gen.instanceOf(NUMBER_TYPE);
gen.ifZCmp(GeneratorAdapter.EQ, defaultLabel);
expr.emit(C.EXPRESSION, objx, gen);
gen.checkCast(NUMBER_TYPE);
gen.invokeVirtual(NUMBER_TYPE, intValueMethod);
emitShiftMask(gen);
}
else if (exprType == Type.LONG_TYPE
|| exprType == Type.INT_TYPE
|| exprType == Type.SHORT_TYPE
|| exprType == Type.BYTE_TYPE)
{
expr.emitUnboxed(C.EXPRESSION, objx, gen);
gen.cast(exprType, Type.INT_TYPE);
emitShiftMask(gen);
}
else
{
gen.goTo(defaultLabel);
}
}
private void emitThenForInts(ObjExpr objx, GeneratorAdapter gen, Type exprType, Expr test, Expr then, Label defaultLabel, boolean emitUnboxed){
if (exprType == null)
{
expr.emit(C.EXPRESSION, objx, gen);
test.emit(C.EXPRESSION, objx, gen);
gen.invokeStatic(UTIL_TYPE, equivMethod);
gen.ifZCmp(GeneratorAdapter.EQ, defaultLabel);
emitExpr(objx, gen, then, emitUnboxed);
}
else if (exprType == Type.LONG_TYPE)
{
((NumberExpr)test).emitUnboxed(C.EXPRESSION, objx, gen);
expr.emitUnboxed(C.EXPRESSION, objx, gen);
gen.ifCmp(Type.LONG_TYPE, GeneratorAdapter.NE, defaultLabel);
emitExpr(objx, gen, then, emitUnboxed);
}
else if (exprType == Type.INT_TYPE
|| exprType == Type.SHORT_TYPE
|| exprType == Type.BYTE_TYPE)
{
if (isShiftMasked())
{
((NumberExpr)test).emitUnboxed(C.EXPRESSION, objx, gen);
expr.emitUnboxed(C.EXPRESSION, objx, gen);
gen.cast(exprType, Type.LONG_TYPE);
gen.ifCmp(Type.LONG_TYPE, GeneratorAdapter.NE, defaultLabel);
}
// else direct match
emitExpr(objx, gen, then, emitUnboxed);
}
else
{
gen.goTo(defaultLabel);
}
}
private void emitExprForHashes(ObjExpr objx, GeneratorAdapter gen){
expr.emit(C.EXPRESSION, objx, gen);
gen.invokeStatic(UTIL_TYPE,hashMethod);
emitShiftMask(gen);
}
private void emitThenForHashes(ObjExpr objx, GeneratorAdapter gen, Expr test, Expr then, Label defaultLabel, boolean emitUnboxed){
expr.emit(C.EXPRESSION, objx, gen);
test.emit(C.EXPRESSION, objx, gen);
if(testType == hashIdentityKey)
{
gen.visitJumpInsn(IF_ACMPNE, defaultLabel);
}
else
{
gen.invokeStatic(UTIL_TYPE, equivMethod);
gen.ifZCmp(GeneratorAdapter.EQ, defaultLabel);
}
emitExpr(objx, gen, then, emitUnboxed);
}
private static void emitExpr(ObjExpr objx, GeneratorAdapter gen, Expr expr, boolean emitUnboxed){
if (emitUnboxed && expr instanceof MaybePrimitiveExpr)
((MaybePrimitiveExpr)expr).emitUnboxed(C.EXPRESSION,objx,gen);
else
expr.emit(C.EXPRESSION,objx,gen);
}
static class Parser implements IParser{
//(case* expr shift mask default map table-type test-type skip-check?)
//prepared by case macro and presumed correct
//case macro binds actual expr in let so expr is always a local,
//no need to worry about multiple evaluation
public Expr parse(C context, Object frm) {
ISeq form = (ISeq) frm;
if(context == C.EVAL)
return analyze(context, RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
IPersistentVector args = LazilyPersistentVector.create(form.next());
Object exprForm = args.nth(0);
int shift = ((Number)args.nth(1)).intValue();
int mask = ((Number)args.nth(2)).intValue();
Object defaultForm = args.nth(3);
Map caseMap = (Map)args.nth(4);
Keyword switchType = ((Keyword)args.nth(5));
Keyword testType = ((Keyword)args.nth(6));
Set skipCheck = RT.count(args) < 8 ? null : (Set)args.nth(7);
ISeq keys = RT.keys(caseMap);
int low = ((Number)RT.first(keys)).intValue();
int high = ((Number)RT.nth(keys, RT.count(keys)-1)).intValue();
LocalBindingExpr testexpr = (LocalBindingExpr) analyze(C.EXPRESSION, exprForm);
testexpr.shouldClear = false;
SortedMap tests = new TreeMap();
HashMap thens = new HashMap();
PathNode branch = new PathNode(PATHTYPE.BRANCH, (PathNode) CLEAR_PATH.get());
for(Object o : caseMap.entrySet())
{
Map.Entry e = (Map.Entry) o;
Integer minhash = ((Number)e.getKey()).intValue();
Object pair = e.getValue(); // [test-val then-expr]
Expr testExpr = testType == intKey
? NumberExpr.parse(((Number)RT.first(pair)).intValue())
: new ConstantExpr(RT.first(pair));
tests.put(minhash, testExpr);
Expr thenExpr;
try {
Var.pushThreadBindings(
RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch)));
thenExpr = analyze(context, RT.second(pair));
}
finally{
Var.popThreadBindings();
}
thens.put(minhash, thenExpr);
}
Expr defaultExpr;
try {
Var.pushThreadBindings(
RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,branch)));
defaultExpr = analyze(context, args.nth(3));
}
finally{
Var.popThreadBindings();
}
int line = ((Number)LINE.deref()).intValue();
int column = ((Number)COLUMN.deref()).intValue();
return new CaseExpr(line, column, testexpr, shift, mask, low, high,
defaultExpr, tests, thens, switchType, testType, skipCheck);
}
}
}
static IPersistentCollection emptyVarCallSites(){return PersistentHashSet.EMPTY;}
static public ClassWriter classWriter() {
return new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES) {
protected String getCommonSuperClass (final String type1, final String type2) {
return "java/lang/Object";
// if (!(type1.equals("java/lang/Object") || type2.equals("java/lang/Object"))) {
// RT.errPrintWriter()
// .format("stack map frame \"%s\" and \"%s\" on %s:%d:%d \n",
// type1, type2,
// SOURCE_PATH.deref(), LINE.deref(), COLUMN.deref());
// }
}
};
}
}