aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/runtime/CodeStore.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/CodeStore.java')
-rw-r--r--src/jdk/nashorn/internal/runtime/CodeStore.java314
1 files changed, 215 insertions, 99 deletions
diff --git a/src/jdk/nashorn/internal/runtime/CodeStore.java b/src/jdk/nashorn/internal/runtime/CodeStore.java
index 8d793203..a736cc36 100644
--- a/src/jdk/nashorn/internal/runtime/CodeStore.java
+++ b/src/jdk/nashorn/internal/runtime/CodeStore.java
@@ -34,51 +34,42 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.security.AccessControlException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
+import java.util.Iterator;
import java.util.Map;
+import java.util.ServiceLoader;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
+import jdk.nashorn.internal.runtime.options.Options;
/**
* A code cache for persistent caching of compiled scripts.
*/
@Logger(name="codestore")
-final class CodeStore implements Loggable {
-
- private final File dir;
- private final int minSize;
- private final DebugLogger log;
-
- // Default minimum size for storing a compiled script class
- private final static int DEFAULT_MIN_SIZE = 1000;
+public abstract class CodeStore implements Loggable {
/**
- * Constructor
- * @throws IOException
+ * Permission needed to provide a CodeStore instance via ServiceLoader.
*/
- public CodeStore(final Context context, final String path) throws IOException {
- this(context, path, DEFAULT_MIN_SIZE);
- }
+ public final static String NASHORN_PROVIDE_CODE_STORE = "nashorn.provideCodeStore";
+
+ private DebugLogger log;
/**
* Constructor
- * @param path directory to store code in
- * @param minSize minimum file size for caching scripts
- * @throws IOException
*/
- public CodeStore(final Context context, final String path, final int minSize) throws IOException {
- this.dir = checkDirectory(path);
- this.minSize = minSize;
- this.log = initLogger(context);
+ protected CodeStore() {
}
@Override
public DebugLogger initLogger(final Context context) {
- return context.getLogger(getClass());
+ log = context.getLogger(getClass());
+ return log;
}
@Override
@@ -86,29 +77,101 @@ final class CodeStore implements Loggable {
return log;
}
- private static File checkDirectory(final String path) throws IOException {
+ /**
+ * Returns a new code store instance.
+ *
+ * @param context the current context
+ * @return The instance
+ * @throws IOException If an error occurs
+ */
+ public static CodeStore newCodeStore(final Context context) throws IOException {
+ final Class<CodeStore> baseClass = CodeStore.class;
try {
- return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() {
- @Override
- public File run() throws IOException {
- final File dir = new File(path).getAbsoluteFile();
- if (!dir.exists() && !dir.mkdirs()) {
- throw new IOException("Could not create directory: " + dir.getPath());
- } else if (!dir.isDirectory()) {
- throw new IOException("Not a directory: " + dir.getPath());
- } else if (!dir.canRead() || !dir.canWrite()) {
- throw new IOException("Directory not readable or writable: " + dir.getPath());
- }
- return dir;
- }
- });
- } catch (final PrivilegedActionException e) {
- throw (IOException) e.getException();
+ // security check first
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission(NASHORN_PROVIDE_CODE_STORE));
+ }
+ final ServiceLoader<CodeStore> services = ServiceLoader.load(baseClass);
+ final Iterator<CodeStore> iterator = services.iterator();
+ if (iterator.hasNext()) {
+ final CodeStore store = iterator.next();
+ store.initLogger(context).info("using code store provider ", store.getClass().getCanonicalName());
+ return store;
+ }
+ } catch (final AccessControlException e) {
+ context.getLogger(CodeStore.class).warning("failed to load code store provider ", e);
}
+ final CodeStore store = new DirectoryCodeStore();
+ store.initLogger(context);
+ return store;
+ }
+
+
+ /**
+ * Store a compiled script in the cache.
+ *
+ * @param functionKey the function key
+ * @param source the source
+ * @param mainClassName the main class name
+ * @param classBytes a map of class bytes
+ * @param initializers the function initializers
+ * @param constants the constants array
+ * @param compilationId the compilation id
+ */
+ public StoredScript store(final String functionKey,
+ final Source source,
+ final String mainClassName,
+ final Map<String, byte[]> classBytes,
+ final Map<Integer, FunctionInitializer> initializers,
+ final Object[] constants,
+ final int compilationId) {
+ return store(functionKey, source, storedScriptFor(source, mainClassName, classBytes, initializers, constants, compilationId));
}
- private File getCacheFile(final Source source, final String functionKey) {
- return new File(dir, source.getDigest() + '-' + functionKey);
+ /**
+ * Stores a compiled script.
+ *
+ * @param functionKey the function key
+ * @param source the source
+ * @param script The compiled script
+ * @return The compiled script or {@code null} if not stored
+ */
+ public abstract StoredScript store(final String functionKey,
+ final Source source,
+ final StoredScript script);
+
+ /**
+ * Return a compiled script from the cache, or null if it isn't found.
+ *
+ * @param source the source
+ * @param functionKey the function key
+ * @return the stored script or null
+ */
+ public abstract StoredScript load(final Source source, final String functionKey);
+
+ /**
+ * Returns a new StoredScript instance.
+ *
+ * @param mainClassName the main class name
+ * @param classBytes a map of class bytes
+ * @param initializers function initializers
+ * @param constants the constants array
+ * @param compilationId the compilation id
+ * @return The compiled script
+ */
+ public StoredScript storedScriptFor(final Source source, final String mainClassName,
+ final Map<String, byte[]> classBytes,
+ final Map<Integer, FunctionInitializer> initializers,
+ final Object[] constants, final int compilationId) {
+ for (final Object constant : constants) {
+ // Make sure all constant data is serializable
+ if (!(constant instanceof Serializable)) {
+ getLogger().warning("cannot store ", source, " non serializable constant ", constant);
+ return null;
+ }
+ }
+ return new StoredScript(compilationId, mainClassName, classBytes, initializers, constants);
}
/**
@@ -129,77 +192,130 @@ final class CodeStore implements Loggable {
}
/**
- * Return a compiled script from the cache, or null if it isn't found.
- *
- * @param source the source
- * @param functionKey the function key
- * @return the stored script or null
+ * A store using a file system directory.
*/
- public StoredScript loadScript(final Source source, final String functionKey) {
- if (source.getLength() < minSize) {
- return null;
+ public static class DirectoryCodeStore extends CodeStore {
+
+ // Default minimum size for storing a compiled script class
+ private final static int DEFAULT_MIN_SIZE = 1000;
+
+ private final File dir;
+ private final boolean readOnly;
+ private final int minSize;
+
+ /**
+ * Constructor
+ *
+ * @throws IOException
+ */
+ public DirectoryCodeStore() throws IOException {
+ this(Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"), false, DEFAULT_MIN_SIZE);
}
- final File file = getCacheFile(source, functionKey);
+ /**
+ * Constructor
+ *
+ * @param path directory to store code in
+ * @param minSize minimum file size for caching scripts
+ * @throws IOException
+ */
+ public DirectoryCodeStore(final String path, final boolean readOnly, final int minSize) throws IOException {
+ this.dir = checkDirectory(path, readOnly);
+ this.readOnly = readOnly;
+ this.minSize = minSize;
+ }
- try {
- return AccessController.doPrivileged(new PrivilegedExceptionAction<StoredScript>() {
- @Override
- public StoredScript run() throws IOException, ClassNotFoundException {
- if (!file.exists()) {
- return null;
- }
- try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) {
- final StoredScript storedScript = (StoredScript) in.readObject();
- getLogger().info("loaded ", source, "-", functionKey);
- return storedScript;
+ private static File checkDirectory(final String path, final boolean readOnly) throws IOException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() {
+ @Override
+ public File run() throws IOException {
+ final File dir = new File(path).getAbsoluteFile();
+ if (readOnly) {
+ if (!dir.exists() || !dir.isDirectory()) {
+ throw new IOException("Not a directory: " + dir.getPath());
+ } else if (!dir.canRead()) {
+ throw new IOException("Directory not readable: " + dir.getPath());
+ }
+ } else if (!dir.exists() && !dir.mkdirs()) {
+ throw new IOException("Could not create directory: " + dir.getPath());
+ } else if (!dir.isDirectory()) {
+ throw new IOException("Not a directory: " + dir.getPath());
+ } else if (!dir.canRead() || !dir.canWrite()) {
+ throw new IOException("Directory not readable or writable: " + dir.getPath());
+ }
+ return dir;
}
- }
- });
- } catch (final PrivilegedActionException e) {
- getLogger().warning("failed to load ", source, "-", functionKey, ": ", e.getException());
- return null;
+ });
+ } catch (final PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
}
- }
- /**
- * Store a compiled script in the cache.
- *
- * @param functionKey the function key
- * @param source the source
- * @param mainClassName the main class name
- * @param classBytes a map of class bytes
- * @param constants the constants array
- */
- public void storeScript(final String functionKey, final Source source, final String mainClassName, final Map<String, byte[]> classBytes,
- final Map<Integer, FunctionInitializer> initializers, final Object[] constants, final int compilationId) {
- if (source.getLength() < minSize) {
- return;
- }
- for (final Object constant : constants) {
- // Make sure all constant data is serializable
- if (! (constant instanceof Serializable)) {
- getLogger().warning("cannot store ", source, " non serializable constant ", constant);
- return;
+ @Override
+ public StoredScript load(final Source source, final String functionKey) {
+ if (source.getLength() < minSize) {
+ return null;
+ }
+
+ final File file = getCacheFile(source, functionKey);
+
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<StoredScript>() {
+ @Override
+ public StoredScript run() throws IOException, ClassNotFoundException {
+ if (!file.exists()) {
+ return null;
+ }
+ try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) {
+ final StoredScript storedScript = (StoredScript) in.readObject();
+ getLogger().info("loaded ", source, "-", functionKey);
+ return storedScript;
+ }
+ }
+ });
+ } catch (final PrivilegedActionException e) {
+ getLogger().warning("failed to load ", source, "-", functionKey, ": ", e.getException());
+ return null;
}
}
- final File file = getCacheFile(source, functionKey);
- final StoredScript script = new StoredScript(compilationId, mainClassName, classBytes, initializers, constants);
+ @Override
+ public StoredScript store(final String functionKey, final Source source, final StoredScript script) {
+ if (readOnly || script == null || belowThreshold(source)) {
+ return null;
+ }
- try {
- AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
- @Override
- public Void run() throws IOException {
- try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
- out.writeObject(script);
+ final File file = getCacheFile(source, functionKey);
+
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<StoredScript>() {
+ @Override
+ public StoredScript run() throws IOException {
+ try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
+ out.writeObject(script);
+ }
+ getLogger().info("stored ", source, "-", functionKey);
+ return script;
}
- getLogger().info("stored ", source, "-", functionKey);
- return null;
- }
- });
- } catch (final PrivilegedActionException e) {
- getLogger().warning("failed to store ", script, "-", functionKey, ": ", e.getException());
+ });
+ } catch (final PrivilegedActionException e) {
+ getLogger().warning("failed to store ", script, "-", functionKey, ": ", e.getException());
+ return null;
+ }
+ }
+
+
+ private File getCacheFile(final Source source, final String functionKey) {
+ return new File(dir, source.getDigest() + '-' + functionKey);
+ }
+
+ private boolean belowThreshold(final Source source) {
+ if (source.getLength() < minSize) {
+ getLogger().info("below size threshold ", source);
+ return true;
+ }
+ return false;
}
}
}