445 lines
12 KiB
Plaintext
445 lines
12 KiB
Plaintext
/* :folding=explicit:collapseFolds=1: */
|
|
|
|
/*
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2003, 2004 Slava Pestov.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
package factor;
|
|
|
|
import factor.db.*;
|
|
import factor.compiler.*;
|
|
import java.io.*;
|
|
import java.util.*;
|
|
import org.objectweb.asm.*;
|
|
|
|
/**
|
|
* A word definition.
|
|
*
|
|
* The pickled form is an unparsed list. The car of the list is the word,
|
|
* the cdr is toList().
|
|
*/
|
|
public abstract class FactorWordDefinition
|
|
implements Constants, PersistentObject
|
|
{
|
|
public static final String ENCODING = "UTF8";
|
|
|
|
private Workspace workspace;
|
|
private long id;
|
|
|
|
public FactorWord word;
|
|
|
|
public boolean compileFailed;
|
|
|
|
//{{{ FactorWordDefinition constructor
|
|
/**
|
|
* A new definition.
|
|
*/
|
|
public FactorWordDefinition(FactorWord word, Workspace workspace)
|
|
{
|
|
this(workspace,workspace == null
|
|
? 0L : workspace.nextID());
|
|
this.word = word;
|
|
} //}}}
|
|
|
|
//{{{ FactorWordDefinition constructor
|
|
/**
|
|
* A blank definition, about to be unpickled.
|
|
*/
|
|
public FactorWordDefinition(Workspace workspace, long id)
|
|
{
|
|
this.workspace = workspace;
|
|
this.id = id;
|
|
} //}}}
|
|
|
|
//{{{ FactorWordDefinition constructor
|
|
/**
|
|
* A definition that is not saved in the current workspace.
|
|
*/
|
|
public FactorWordDefinition(FactorWord word)
|
|
{
|
|
this.word = word;
|
|
} //}}}
|
|
|
|
public abstract void eval(FactorInterpreter interp)
|
|
throws Exception;
|
|
|
|
//{{{ fromList() method
|
|
public void fromList(Cons cons, FactorInterpreter interp)
|
|
throws FactorRuntimeException, PersistenceException
|
|
{
|
|
throw new PersistenceException("Cannot unpickle " + this);
|
|
} //}}}
|
|
|
|
//{{{ toList() method
|
|
public Cons toList(FactorInterpreter interp)
|
|
{
|
|
return new Cons(new FactorWord(null,getClass().getName()),null);
|
|
} //}}}
|
|
|
|
//{{{ getStackEffect() method
|
|
public final StackEffect getStackEffect(FactorInterpreter interp)
|
|
throws Exception
|
|
{
|
|
return getStackEffect(interp,new RecursiveState());
|
|
} //}}}
|
|
|
|
//{{{ getStackEffect() method
|
|
public final StackEffect getStackEffect(FactorInterpreter interp,
|
|
RecursiveState recursiveCheck)
|
|
throws Exception
|
|
{
|
|
FactorCompiler compiler = new FactorCompiler(interp);
|
|
recursiveCheck.add(word,new StackEffect(),null,null,null);
|
|
compileCallTo(null,compiler,recursiveCheck);
|
|
recursiveCheck.remove(word);
|
|
return compiler.getStackEffect();
|
|
} //}}}
|
|
/*
|
|
//{{{ getStackEffect() method
|
|
public void getStackEffect(RecursiveState recursiveCheck,
|
|
FactorCompiler compiler) throws Exception
|
|
{
|
|
compileCallTo(null,compiler,recursiveCheck);
|
|
} //}}}
|
|
*/
|
|
//{{{ compile() method
|
|
FactorWordDefinition compile(FactorInterpreter interp,
|
|
RecursiveState recursiveCheck) throws Exception
|
|
{
|
|
return this;
|
|
} //}}}
|
|
|
|
//{{{ getDefinition() method
|
|
protected Cons getDefinition(FactorInterpreter interp)
|
|
throws FactorCompilerException
|
|
{
|
|
Cons definition = toList(interp);
|
|
|
|
while(definition != null
|
|
&& definition.car instanceof FactorDocComment)
|
|
definition = definition.next();
|
|
|
|
return definition;
|
|
} //}}}
|
|
|
|
//{{{ compileCallTo() method
|
|
/**
|
|
* Compile a call to this word. Returns maximum JVM stack use.
|
|
*/
|
|
public void compileCallTo(CodeVisitor mw, FactorCompiler compiler,
|
|
RecursiveState recursiveCheck) throws Exception
|
|
{
|
|
// normal word
|
|
String defclass;
|
|
String defmethod;
|
|
StackEffect effect;
|
|
|
|
FactorClassLoader loader;
|
|
|
|
RecursiveForm rec = recursiveCheck.get(word);
|
|
if(rec != null && rec.active)
|
|
{
|
|
if(compiler.interp.verboseCompile)
|
|
System.err.println("Recursive call to " + rec);
|
|
effect = StackEffect.decompose(rec.effect,rec.baseCase);
|
|
|
|
// are we recursing back on a form inside the current
|
|
// method?
|
|
RecursiveForm last = recursiveCheck.last();
|
|
if(mw != null
|
|
&& recursiveCheck.allTails(rec)
|
|
&& last.className.equals(rec.className)
|
|
&& last.method.equals(rec.method))
|
|
{
|
|
if(compiler.interp.verboseCompile)
|
|
System.err.println(word + " is tail recursive");
|
|
// GOTO instad of INVOKEVIRTUAL; ie a loop!
|
|
compiler.normalizeStacks(mw);
|
|
mw.visitJumpInsn(GOTO,rec.label);
|
|
compiler.apply(effect);
|
|
return;
|
|
}
|
|
|
|
/* recursive method call! */
|
|
defclass = rec.className;
|
|
defmethod = rec.method;
|
|
loader = rec.loader;
|
|
|
|
if(mw != null && !defclass.equals(compiler.className))
|
|
compiler.loader.addDependency(defclass,loader);
|
|
}
|
|
else if(mw == null)
|
|
{
|
|
Cons definition = getDefinition(compiler.interp);
|
|
compiler.getStackEffect(definition,recursiveCheck);
|
|
return;
|
|
}
|
|
// not a recursive call but we're still not compiled
|
|
// its a bug in the compiler.
|
|
else if(this instanceof FactorCompoundDefinition)
|
|
{
|
|
throw new FactorCompilerException("You are an idiot!");
|
|
}
|
|
/* ordinary method call! */
|
|
else
|
|
{
|
|
defclass = getClass().getName().replace('.','/');
|
|
defmethod = "core";
|
|
effect = getStackEffect(compiler.interp,
|
|
new RecursiveState());
|
|
ClassLoader l = getClass().getClassLoader();
|
|
if(l instanceof FactorClassLoader)
|
|
{
|
|
loader = (FactorClassLoader)l;
|
|
compiler.loader.addDependency(
|
|
getClass().getName(),loader);
|
|
}
|
|
else
|
|
loader = null;
|
|
}
|
|
|
|
if(mw == null)
|
|
compiler.apply(effect);
|
|
else
|
|
{
|
|
mw.visitVarInsn(ALOAD,0);
|
|
compiler.generateArgs(mw,effect.inD,effect.inR,null);
|
|
String signature = effect.getCorePrototype();
|
|
mw.visitMethodInsn(INVOKESTATIC,defclass,defmethod,signature);
|
|
compiler.generateReturn(mw,effect.outD,effect.outR);
|
|
}
|
|
} //}}}
|
|
|
|
//{{{ compileNonRecursiveImmediate() method
|
|
/**
|
|
* Non-recursive immediate words are inlined.
|
|
*/
|
|
protected void compileNonRecursiveImmediate(CodeVisitor mw,
|
|
FactorCompiler compiler,
|
|
RecursiveState recursiveCheck,
|
|
StackEffect immediateEffect) throws Exception
|
|
{
|
|
Cons definition = toList(compiler.getInterpreter());
|
|
|
|
Cons endOfDocs = definition;
|
|
while(endOfDocs != null
|
|
&& endOfDocs.car instanceof FactorDocComment)
|
|
endOfDocs = endOfDocs.next();
|
|
|
|
compiler.compile(endOfDocs,mw,recursiveCheck);
|
|
} //}}}
|
|
|
|
//{{{ compileRecursiveImmediate() method
|
|
/**
|
|
* Recursive immediate words are compiled to an auxiliary method
|
|
* inside the compiled class definition.
|
|
*
|
|
* This must be done so that recursion has something to jump to.
|
|
*/
|
|
protected void compileRecursiveImmediate(CodeVisitor mw,
|
|
FactorCompiler compiler,
|
|
RecursiveState recursiveCheck,
|
|
StackEffect immediateEffect) throws Exception
|
|
{
|
|
Cons definition = toList(compiler.getInterpreter());
|
|
|
|
Cons endOfDocs = definition;
|
|
while(endOfDocs != null
|
|
&& endOfDocs.car instanceof FactorDocComment)
|
|
endOfDocs = endOfDocs.next();
|
|
|
|
String method = compiler.auxiliary(word,
|
|
endOfDocs,immediateEffect,recursiveCheck);
|
|
|
|
mw.visitVarInsn(ALOAD,0);
|
|
|
|
compiler.generateArgs(mw,immediateEffect.inD,
|
|
immediateEffect.inR,null);
|
|
|
|
String signature = immediateEffect.getCorePrototype();
|
|
|
|
mw.visitMethodInsn(INVOKESTATIC,compiler.className,
|
|
method,signature);
|
|
|
|
compiler.generateReturn(mw,
|
|
immediateEffect.outD,
|
|
immediateEffect.outR);
|
|
} //}}}
|
|
|
|
//{{{ compileImmediate() method
|
|
/**
|
|
* Compile a call to this word. Returns maximum JVM stack use.
|
|
*/
|
|
public void compileImmediate(CodeVisitor mw, FactorCompiler compiler,
|
|
RecursiveState recursiveCheck) throws Exception
|
|
{
|
|
Cons definition = getDefinition(compiler.interp);
|
|
|
|
if(mw == null)
|
|
{
|
|
compiler.compile(definition,null,recursiveCheck);
|
|
return;
|
|
}
|
|
|
|
// determine stack effect of this instantiation, and if its
|
|
// recursive.
|
|
|
|
FactorArrayStack savedDatastack = (FactorArrayStack)
|
|
compiler.datastack.clone();
|
|
FactorCallStack savedCallstack = (FactorCallStack)
|
|
compiler.callstack.clone();
|
|
StackEffect savedEffect = compiler.getStackEffect();
|
|
|
|
RecursiveState _recursiveCheck = (RecursiveState)
|
|
recursiveCheck.clone();
|
|
_recursiveCheck.last().effect = compiler.getStackEffect();
|
|
compileImmediate(null,compiler,_recursiveCheck);
|
|
|
|
boolean recursive = (_recursiveCheck.last().baseCase != null);
|
|
|
|
StackEffect effect = compiler.getStackEffect();
|
|
|
|
StackEffect immediateEffect = StackEffect.decompose(
|
|
savedEffect,compiler.getStackEffect());
|
|
|
|
// restore previous state.
|
|
|
|
FactorArrayStack afterDatastack = (FactorArrayStack)
|
|
compiler.datastack.clone();
|
|
FactorCallStack afterCallstack = (FactorCallStack)
|
|
compiler.callstack.clone();
|
|
|
|
compiler.datastack = (FactorArrayStack)savedDatastack.clone();
|
|
compiler.callstack = (FactorCallStack)savedCallstack.clone();
|
|
compiler.effect = savedEffect;
|
|
|
|
if(!recursive)
|
|
{
|
|
// not recursive; inline.
|
|
compileNonRecursiveImmediate(mw,compiler,recursiveCheck,
|
|
immediateEffect);
|
|
}
|
|
else
|
|
{
|
|
// recursive; must generate auxiliary method.
|
|
compileRecursiveImmediate(mw,compiler,recursiveCheck,
|
|
immediateEffect);
|
|
|
|
mergeStacks(savedDatastack,afterDatastack,compiler.datastack);
|
|
mergeStacks(savedCallstack,afterCallstack,compiler.callstack);
|
|
}
|
|
} //}}}
|
|
|
|
//{{{ mergeStacks() method
|
|
private void mergeStacks(FactorArrayStack s1, FactorArrayStack s2,
|
|
FactorArrayStack into)
|
|
{
|
|
for(int i = 0; i < s2.top; i++)
|
|
{
|
|
if(s1.top <= i)
|
|
break;
|
|
|
|
if(FactorLib.objectsEqual(s1.stack[i],
|
|
s2.stack[i]))
|
|
{
|
|
into.stack[i] = s1.stack[i];
|
|
}
|
|
}
|
|
} //}}}
|
|
|
|
//{{{ getWorkspace() method
|
|
/**
|
|
* Each persistent object is stored in one workspace only.
|
|
*/
|
|
public Workspace getWorkspace()
|
|
{
|
|
return workspace;
|
|
} //}}}
|
|
|
|
//{{{ getID() method
|
|
/**
|
|
* Each persistent object has an associated ID.
|
|
*/
|
|
public long getID()
|
|
{
|
|
return id;
|
|
} //}}}
|
|
|
|
//{{{ pickle() method
|
|
/**
|
|
* Each persistent object can turn itself into a byte array.
|
|
*/
|
|
public byte[] pickle(FactorInterpreter interp)
|
|
throws PersistenceException
|
|
{
|
|
try
|
|
{
|
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
|
|
|
Cons pickle = new Cons(word,toList(interp));
|
|
bytes.write((FactorReader.getVocabularyDeclaration(pickle)
|
|
+ FactorReader.unparseDBObject(pickle))
|
|
.getBytes(ENCODING));
|
|
|
|
return bytes.toByteArray();
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
// should not happen with byte array stream
|
|
throw new PersistenceException("Unexpected error",e);
|
|
}
|
|
} //}}}
|
|
|
|
//{{{ unpickle() method
|
|
/**
|
|
* Each persistent object can set its state to that in a byte array.
|
|
*/
|
|
public void unpickle(byte[] bytes, int offset, FactorInterpreter interp)
|
|
throws PersistenceException
|
|
{
|
|
try
|
|
{
|
|
String unparsed = new String(bytes,offset,
|
|
bytes.length - offset,ENCODING);
|
|
Cons pickle = (Cons)FactorReader.parseObject(unparsed,
|
|
interp);
|
|
word = (FactorWord)pickle.car;
|
|
fromList(pickle.next(),interp);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
// should not happen with byte array stream
|
|
throw new PersistenceException("Unexpected error",e);
|
|
}
|
|
} //}}}
|
|
|
|
//{{{ toString() method
|
|
public String toString()
|
|
{
|
|
return getClass().getName() + ": " + word;
|
|
} //}}}
|
|
}
|