diff options
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/CodeStore.java')
-rw-r--r-- | src/jdk/nashorn/internal/runtime/CodeStore.java | 314 |
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; } } } |