Thanks, Mark. Here is a portion of what I am doing (written many years
ago):
public class ABCL {
private static Interpreter interpreter;
private static boolean invertCase = false;
private static Function makeWebServiceArgs;
private static int lispRelease = 0;
private static boolean once = true;
public static void init() { // only called once
interpreter = Interpreter.createInstance();
invertCase();
load("com/xxx/lisp/clos-utils");
load("com/xxx/lisp/package-lru");
load("com/xxx/lisp/utils");
load("com/xxx/lisp/mappings");
makeWebServiceArgs = findLispFunction("UTILS",
"make-web-service-args"); // this line is repeated in multiple places
}
private static void invertCase() {
interpreter.eval("(setf (readtable-case *readtable*) :invert)");
// make lisp case sensitive
invertCase = true;
}
public static String fixCase(String symbol) {
if (invertCase) {
int ucl = 0, lcl = 0;
char[] vec = symbol.toCharArray();
for (int i=0 ; i < vec.length && (ucl == 0 || lcl == 0) ; i++)
if (Character.isUpperCase(vec[i]))
ucl++;
else if (Character.isLowerCase(vec[i]))
lcl++;
if (ucl != 0 && lcl != 0 || ucl == 0 && lcl == 0)
return symbol;
else
if (ucl != 0)
return symbol.toLowerCase();
else
return symbol.toUpperCase();
} else
return symbol.toUpperCase();
}
public static void reset() {
// if (interpreter == null)
// return;
// try {
// interpreter.eval("(delete-package \"ARAHANT-UTILS\")");
// } catch (Throwable t) {
// }
if (interpreter == null)
return;
try {
interpreter.eval("(delete-package \"MAPPINGS\")");
} catch (Throwable t) {
}
if (interpreter == null)
return;
try {
interpreter.eval("(delete-package \"UTILS\")");
} catch (Throwable t) {
}
if (interpreter == null)
return;
try {
interpreter.eval("(delete-package \"PACKAGE-LRU\")");
} catch (Throwable t) {
}
if (interpreter == null)
return;
try {
interpreter.eval("(delete-package \"CLOS-UTILS\")");
} catch (Throwable t) {
}
load("com/xxx/lisp/clos-utils");
load("com/xxx/lisp/package-lru");
load("com/xxx/lisp/utils");
load("com/xxx/lisp/mappings");
makeWebServiceArgs = findLispFunction("UTILS",
"make-web-service-args"); // this line is repeated in multiple places
}
public static LispObject load(String fileName) {
return eval("(load \"" + FileSystemUtils.getSourcePath() +
fileName + "\")");
}
public static LispObject compileFile(String fileName) {
return eval("(compile-file \"" + FileSystemUtils.getSourcePath()
+ fileName + "\")");
}
public static void loadPackage(String lispPackage, String fileName)
throws Exception {
try {
eval("(package-lru:load-package \"" + lispPackage + "\" \"" +
FileSystemUtils.getSourcePath() + fileName + "\")");
} catch (Throwable t) {
// Convert Throwable to Exception
throw new Exception("Error loading lisp file " + fileName, t);
}
}
public static void packageDone(String lispPackage) {
if (FileSystemUtils.isUnderIDE())
eval("(package-lru:package-done-unload \"" + lispPackage + "\")");
else
eval("(package-lru:package-done \"" + lispPackage + "\")");
}
public static LispObject eval(String str) {
return interpreter.eval(str);
}
public static Function findLispFunction(String packageName, String
funName) {
if (packageName == null || packageName.isEmpty())
packageName = "CL-USER";
// else
// packageName = fixCase(packageName);
org.armedbear.lisp.Package lispPackage =
Packages.findPackage(packageName);
if (lispPackage == null)
throw new RuntimeException("Package " + packageName + " not found");
Symbol symbol = lispPackage.findAccessibleSymbol(fixCase(funName));
if (symbol == null)
throw new RuntimeException("Symbol " + packageName + ":" +
fixCase(funName) + " not found");
Function fun = (Function) symbol.getSymbolFunction();
return fun;
}
public static LispObject executeLispFunction(Function fun, Object
... args) {
LispObject [] jargs;
jargs = new LispObject[args.length];
for (int i=0 ; i < args.length ; i++)
jargs[i] = JavaObject.getInstance(args[i], true);
return fun.execute(jargs);
}
public static LispObject executeLisp(String packageName, String
funName, Object ... args) {
Function fun = findLispFunction(packageName, funName);
if (fun == null)
return null;
LispObject [] jargs;
jargs = new LispObject[args.length];
for (int i=0 ; i < args.length ; i++)
jargs[i] = JavaObject.getInstance(args[i], true);
return fun.execute(jargs);
}
public static LispObject executeLispArray(String packageName,
String funName, Object [] args) {
Function fun = findLispFunction(packageName, funName);
if (fun == null)
return null;
LispObject [] jargs;
jargs = new LispObject[args.length];
for (int i=0 ; i < args.length ; i++)
jargs[i] = JavaObject.getInstance(args[i], true);
return fun.execute(jargs);
}
public static Function getMakeWebServiceArgs() {
return makeWebServiceArgs;
}
@SuppressWarnings("unchecked")
public static Object LispObjectToJavaObject(LispObject obj) {
if (obj.atom())
if (obj.characterp())
return obj.princToString().charAt(0);
else if (obj.stringp())
return obj.princToString();
else if (obj.integerp())
return obj.intValue();
else if (obj.realp())
return obj.doubleValue();
else if (obj.listp())
return null;
else if (obj.constantp())
return true;
else
return obj.princToString();
else if (obj.listp()) {
LinkedList ll = new LinkedList();
while (!obj.endp()) {
ll.addLast(LispObjectToJavaObject(obj.car()));
obj = obj.cdr();
}
return ll;
} else if (obj.vectorp()) {
int len = obj.length();
Object [] vec = new Object[len];
for (int i=0 ; i < len ; i++)
vec[i] = LispObjectToJavaObject(obj.AREF(i));
return vec;
} else
return null;
}
public static LispObject JavaObjectToLispObject(Object jobj) {
if (jobj instanceof Boolean)
return ((Boolean)jobj) ? Lisp.T : Lisp.NIL;
else if (jobj instanceof Character)
return LispCharacter.getInstance((Character)jobj);
else if (jobj instanceof Short)
return LispInteger.getInstance((Short)jobj);
else if (jobj instanceof Integer)
return LispInteger.getInstance((Integer)jobj);
else if (jobj instanceof Long)
return LispInteger.getInstance((Long)jobj);
else if (jobj instanceof Float)
return SingleFloat.getInstance((Float)jobj);
else if (jobj instanceof Double)
return DoubleFloat.getInstance((Double)jobj);
else if (jobj instanceof String)
return new SimpleString((String)jobj);
else if (jobj instanceof StringBuilder)
return new SimpleString((StringBuilder)jobj);
else if (jobj instanceof LinkedList) {
LispObject lobj = Lisp.NIL;
ListIterator it = ((LinkedList) jobj).listIterator();
while (it.hasNext())
lobj = new Cons(JavaObjectToLispObject(it.next()), lobj);
return lobj;
} else if (jobj instanceof Set) {
LispObject lobj = Lisp.NIL;
Iterator it = ((Set) jobj).iterator();
while (it.hasNext())
lobj = new Cons(JavaObjectToLispObject(it.next()), lobj);
return lobj;
} else if (jobj instanceof Array) {
Array a = (Array) jobj;
int len = Array.getLength(a);
SimpleVector vec = new SimpleVector(len);
for (int i=0 ; i < len ; i++)
vec.setSlotValue(i, JavaObjectToLispObject(Array.get(a, i)));
return null;
}
return null;
}
public static void printStackTrace(Throwable e) {
try {
Function fun = findLispFunction("UTILS", "print-stack-trace");
if (fun != null) {
LispObject stackTrace = LispThread.currentThread().backtrace(0);
if (stackTrace != null && stackTrace != Lisp.NIL) {
System.err.println("Lisp execution error");
fun.execute(stackTrace);
}
}
} catch (Throwable t) {
}
e.printStackTrace();
}
public static int getLispRelease() {
return lispRelease;
}
public static void setLispRelease(int lispRelease) {
ABCL.lispRelease = lispRelease;
}
}
Post by Blake McBridePost by Blake McBrideGreetings,
Does ABCL safely support re-entrant and multi-entrant calls? What I
Re-entrant: on a single OS thread - my Java program calls into ABCL,
then ABCL calls into my Java application, and then the Java application
calls back into ABCL. So the stack has Java, ABCL, JAVA, and then ABCL
again.
Post by Blake McBrideMulti-entrant: my Java application has many threads. One of my threads
calls into ABCL. Then, while one thread is still in ABCL, another thread
evokes ABCL. So now we have two calls into ABCL by two independent Java/OS
threads running at the same time.
Post by Blake McBrideI understand the typical problems associated with application-level
shared variables. This is expected. The question revolves around ABCL's
internals. I presume ABCL would have some shared data that is internal to
ABCL. That's what I am unclear about. ABCL would have had to be designed
for these scenarios from the ground up.
Post by Blake McBrideThis is a little hard to test because if it can't always correctly
handle these situations, it may not become clear until certain scenarios
arrive. It may be hard for any "test" program I write to cause those
scenarios, so I thought this may be a known answer.
As long as one is referencing the org.armedbear.lisp.Interpreter singleton for
the calls into ABCL, everything should work fine. Most of the logic can be
understood by studying what [LispThread.java][1] does to mark global/local
special variables, and how each Java thread is associated with a LispThread
call stack.
[1]: https://gitlab.common-lisp.net/abcl/abcl/blob/master/src/
org/armedbear/lisp/LispThread.java#L55
How exactly are you calling into ABCL from Java? A snippet of code would be
useful.
--
"A screaming comes across the sky. It has happened before but there is nothing
to compare to it now."