aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2011-06-17 09:40:04 -0700
committerShawn O. Pearce <sop@google.com>2011-06-17 09:49:29 -0700
commitfe8235a9799c0e6b0d49d1d46431e232ab26324d (patch)
treecbd23ee7dea41377ddb890adcc5da34ed182e8a5
parenta40c72dc588b60f85ca8ec4007a690cb2d6f17f1 (diff)
downloadprolog-cafe-fe8235a9799c0e6b0d49d1d46431e232ab26324d.tar.gz
Implement aggressive caching in PrologClassLoader
Rather than doing everything dynamically, implement the very commonly used entry point logic by looking up predicates in an internal hash map, reusing cached constructors when the predicate has already been validated as being a proper type the Prolog intepreter can access.
-rw-r--r--src/lang/PrologClassLoader.java213
1 files changed, 153 insertions, 60 deletions
diff --git a/src/lang/PrologClassLoader.java b/src/lang/PrologClassLoader.java
index d2b0330..a6625e9 100644
--- a/src/lang/PrologClassLoader.java
+++ b/src/lang/PrologClassLoader.java
@@ -1,5 +1,9 @@
package com.googlecode.prolog_cafe.lang;
+
+import static com.googlecode.prolog_cafe.lang.PredicateEncoder.encode;
import java.lang.reflect.Constructor;
+import java.util.concurrent.ConcurrentHashMap;
+
/**
* Prolog class loader.
*
@@ -8,52 +12,38 @@ import java.lang.reflect.Constructor;
* @version 1.1
*/
public class PrologClassLoader extends ClassLoader {
- /** Initialize using the {@link ClassLoader#getSystemClassLoader()}. */
- public PrologClassLoader() {
- }
+ private final ConcurrentHashMap<Key, CacheEntry> predicateCache =
+ new ConcurrentHashMap<Key, CacheEntry>();
- /**
- * Initialize using a specific parent ClassLoader.
- *
- * @param parent source for all predicates in this context.
- */
- public PrologClassLoader(ClassLoader parent) {
- super(parent);
- }
+ /** Initialize using the {@link ClassLoader#getSystemClassLoader()}. */
+ public PrologClassLoader() {
+ }
- /**
- * Returns a <code>java.lang.Class</code> object associated with the predicate
- * class with the given arguments.
- * @param pkg package name
- * @param functor predicate name
- * @param arity predicate arity
- * @param resolve If <code>true</code> then resolve the class
- * @return a <code>java.lang.Class</code> object associated with the predicate
- * class that corresponds to <code>pkg:functor/arity</code>
- * if exists, otherwise throws <code>ClassNotFoundException</code>.
- * @exception ClassNotFoundException
- */
- public Class loadPredicateClass(String pkg,
- String functor,
- int arity,
- boolean resolve) throws ClassNotFoundException {
- return loadClass(PredicateEncoder.encode(pkg, functor, arity), resolve);
- }
+ /**
+ * Initialize using a specific parent ClassLoader.
+ *
+ * @param parent source for all predicates in this context.
+ */
+ public PrologClassLoader(ClassLoader parent) {
+ super(parent);
+ }
- /**
- * Check whether the predicate class for the given arguments is defined.
- * @param pkg package name
- * @param functor predicate name
- * @param arity predicate arity
- * @return <code>true</code> if the predicate <code>pkg:functor/arity</code>
- * is defined, otherwise <code>false</code>.
- */
- public boolean definedPredicate(String pkg,
- String functor,
- int arity) {
- String cname = PredicateEncoder.encode(pkg, functor, arity);
- return getResource(cname.replace('.', '/') + ".class") != null;
+ /**
+ * Check whether the predicate class for the given arguments is defined.
+ *
+ * @param pkg package name
+ * @param functor predicate name
+ * @param arity predicate arity
+ * @return <code>true</code> if the predicate <code>pkg:functor/arity</code>
+ * is defined, otherwise <code>false</code>.
+ */
+ public boolean definedPredicate(String pkg, String functor, int arity) {
+ try {
+ return findPredicate(pkg, functor, arity) instanceof ValidPredicate;
+ } catch (ClassNotFoundException cnfe) {
+ return false;
}
+ }
/**
* Allocate a predicate and configure it with the specified arguments.
@@ -67,9 +57,6 @@ public class PrologClassLoader extends ClassLoader {
return predicate(pkg, functor, Success.SUCCESS, args);
}
- private static final SymbolTerm colon2 = SymbolTerm.intern(":", 2);
- private static final SymbolTerm slash2 = SymbolTerm.intern("/", 2);
-
/**
* Allocate a predicate and configure it with the specified arguments.
*
@@ -83,29 +70,135 @@ public class PrologClassLoader extends ClassLoader {
public Predicate predicate(String pkg, String functor, Operation cont, Term... args) {
int arity = args.length;
try {
- Class<Predicate> clazz = loadPredicateClass(pkg, functor, arity, true);
+ CacheEntry ent = findPredicate(pkg, functor, arity);
+ if (ent instanceof ValidPredicate) {
+ Object[] a = new Object[arity + 1];
+ for (int i = 0; i < arity; i++)
+ a[i] = args[i];
+ a[arity] = cont;
+ return ((ValidPredicate) ent).constructor.newInstance(a);
+ }
+ } catch (Exception cause) {
+ ExistenceException err2 = new ExistenceException(
+ "procedure",
+ term(pkg, functor, arity),
+ cause.toString());
+ err2.initCause(cause);
+ throw err2;
+ }
+ throw new ExistenceException("procedure", term(pkg, functor, arity), "NOT_FOUND");
+ }
+
+ private static StructureTerm term(String pkg, String functor, int arity) {
+ return new StructureTerm(":",
+ SymbolTerm.create(pkg),
+ new StructureTerm("/",
+ SymbolTerm.create(functor),
+ new IntegerTerm(arity)));
+ }
+
+ private CacheEntry findPredicate(String pkg, String functor, int arity)
+ throws ClassNotFoundException {
+ Key key = new Key(pkg, functor, arity);
+ CacheEntry entry = predicateCache.get(key);
+ if (entry == null) {
+ Class<?> clazz;
+ try {
+ clazz = Class.forName(
+ encode(pkg, functor, arity),
+ false /* avoid resolve */,
+ this);
+ } catch (ClassNotFoundException cnfe) {
+ predicateCache.put(key, NotFound.INSTANCE);
+ throw cnfe;
+ }
+
+ if (!Predicate.class.isAssignableFrom(clazz)) {
+ predicateCache.put(key, NotFound.INSTANCE);
+ throw new ClassNotFoundException(clazz.getName(),
+ new ClassCastException("Does not extend " + Predicate.class));
+ }
Class[] params = new Class[arity + 1];
for (int i = 0; i < arity; i++)
params[i] = Term.class;
params[arity] = Operation.class;
- Object[] a = new Object[arity + 1];
- for (int i = 0; i < arity; i++)
- a[i] = args[i];
- a[arity] = cont;
+ Constructor<Predicate> cons;
+ try {
+ cons = (Constructor<Predicate>) clazz.getDeclaredConstructor(params);
+ } catch (NoSuchMethodException e) {
+ predicateCache.put(key, NotFound.INSTANCE);
+ throw new ClassNotFoundException("Wrong constructor on " + clazz.getName(), e);
- Constructor<Predicate> cons = clazz.getDeclaredConstructor(params);
+ } catch (SecurityException e) {
+ predicateCache.put(key, NotFound.INSTANCE);
+ throw new ClassNotFoundException("Constructor not visible " + clazz.getName(), e);
+ }
cons.setAccessible(true);
- return cons.newInstance(a);
- } catch (Exception err) {
- Term[] fa = {SymbolTerm.create(functor), new IntegerTerm(arity)};
- Term[] r = {SymbolTerm.create(pkg), new StructureTerm(slash2, fa)};
- Term what = new StructureTerm(colon2, r);
-
- ExistenceException e2 = new ExistenceException("procedure", new StructureTerm(slash2, fa), err.toString());
- e2.initCause(err);
- throw e2;
+
+ try {
+ Class.forName(clazz.getName(), true /* resolve now */, this);
+ } catch (ClassNotFoundException e) {
+ predicateCache.put(key, NotFound.INSTANCE);
+ throw new ClassNotFoundException("Cannot initialize " + clazz.getName(), e);
+
+ } catch (RuntimeException e) {
+ predicateCache.put(key, NotFound.INSTANCE);
+ throw new ClassNotFoundException("Cannot initialize " + clazz.getName(), e);
+
+ } catch (LinkageError e) {
+ predicateCache.put(key, NotFound.INSTANCE);
+ throw new ClassNotFoundException("Cannot initialize " + clazz.getName(), e);
+ }
+
+ entry = new ValidPredicate(cons);
+ predicateCache.put(key, entry);
+ }
+ return entry;
+ }
+
+ private static final class Key {
+ final String pkg;
+ final String functor;
+ final int arity;
+
+ Key(String pkg, String functor, int arity) {
+ this.pkg = pkg;
+ this.functor = functor;
+ this.arity = arity;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = pkg.hashCode();
+ h = (h * 31) + functor.hashCode();
+ h = (h * 31) + arity;
+ return h;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof Key) {
+ Key b = (Key) other;
+ return arity == b.arity && pkg.equals(b.pkg) && functor.equals(b.functor);
+ }
+ return false;
+ }
+ }
+
+ private static abstract class CacheEntry {
+ }
+
+ private static class NotFound extends CacheEntry {
+ static final NotFound INSTANCE = new NotFound();
+ }
+
+ private static class ValidPredicate extends CacheEntry {
+ final Constructor<Predicate> constructor;
+
+ ValidPredicate(Constructor<Predicate> c) {
+ constructor = c;
}
}
}