src/jdk/nashorn/internal/runtime/CodeStore.java

Wed, 05 Nov 2014 17:07:26 +0100

author
hannesw
date
Wed, 05 Nov 2014 17:07:26 +0100
changeset 1087
a119a11d49d8
parent 1028
d79265f2fa92
child 1104
c22dd9ae7ff0
permissions
-rw-r--r--

8062386: Different versions of nashorn use same code cache directory
Reviewed-by: lagergren, attila

hannesw@828 1 /*
hannesw@828 2 * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
hannesw@828 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
hannesw@828 4 *
hannesw@828 5 * This code is free software; you can redistribute it and/or modify it
hannesw@828 6 * under the terms of the GNU General Public License version 2 only, as
hannesw@828 7 * published by the Free Software Foundation. Oracle designates this
hannesw@828 8 * particular file as subject to the "Classpath" exception as provided
hannesw@828 9 * by Oracle in the LICENSE file that accompanied this code.
hannesw@828 10 *
hannesw@828 11 * This code is distributed in the hope that it will be useful, but WITHOUT
hannesw@828 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
hannesw@828 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
hannesw@828 14 * version 2 for more details (a copy is included in the LICENSE file that
hannesw@828 15 * accompanied this code).
hannesw@828 16 *
hannesw@828 17 * You should have received a copy of the GNU General Public License version
hannesw@828 18 * 2 along with this work; if not, write to the Free Software Foundation,
hannesw@828 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
hannesw@828 20 *
hannesw@828 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
hannesw@828 22 * or visit www.oracle.com if you need additional information or have any
hannesw@828 23 * questions.
hannesw@828 24 */
hannesw@828 25
hannesw@828 26 package jdk.nashorn.internal.runtime;
hannesw@828 27
hannesw@828 28 import java.io.BufferedInputStream;
hannesw@828 29 import java.io.BufferedOutputStream;
hannesw@828 30 import java.io.File;
hannesw@828 31 import java.io.FileInputStream;
hannesw@828 32 import java.io.FileOutputStream;
hannesw@828 33 import java.io.IOException;
hannesw@828 34 import java.io.ObjectInputStream;
hannesw@828 35 import java.io.ObjectOutputStream;
hannesw@828 36 import java.io.Serializable;
hannesw@1018 37 import java.security.AccessControlException;
hannesw@828 38 import java.security.AccessController;
hannesw@828 39 import java.security.PrivilegedActionException;
hannesw@828 40 import java.security.PrivilegedExceptionAction;
hannesw@1018 41 import java.util.Iterator;
hannesw@828 42 import java.util.Map;
hannesw@1018 43 import java.util.ServiceLoader;
hannesw@1087 44 import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
attila@963 45 import jdk.nashorn.internal.codegen.types.Type;
attila@963 46 import jdk.nashorn.internal.runtime.logging.DebugLogger;
attila@963 47 import jdk.nashorn.internal.runtime.logging.Loggable;
attila@963 48 import jdk.nashorn.internal.runtime.logging.Logger;
hannesw@1018 49 import jdk.nashorn.internal.runtime.options.Options;
hannesw@828 50
hannesw@828 51 /**
hannesw@828 52 * A code cache for persistent caching of compiled scripts.
hannesw@828 53 */
attila@963 54 @Logger(name="codestore")
hannesw@1018 55 public abstract class CodeStore implements Loggable {
hannesw@828 56
hannesw@1018 57 /**
hannesw@1018 58 * Permission needed to provide a CodeStore instance via ServiceLoader.
hannesw@1018 59 */
hannesw@1018 60 public final static String NASHORN_PROVIDE_CODE_STORE = "nashorn.provideCodeStore";
hannesw@828 61
hannesw@1018 62 private DebugLogger log;
hannesw@828 63
hannesw@828 64 /**
hannesw@828 65 * Constructor
hannesw@828 66 */
hannesw@1018 67 protected CodeStore() {
attila@963 68 }
attila@963 69
attila@963 70 @Override
attila@963 71 public DebugLogger initLogger(final Context context) {
hannesw@1018 72 log = context.getLogger(getClass());
hannesw@1018 73 return log;
attila@963 74 }
attila@963 75
attila@963 76 @Override
attila@963 77 public DebugLogger getLogger() {
attila@963 78 return log;
hannesw@828 79 }
hannesw@828 80
hannesw@1018 81 /**
hannesw@1018 82 * Returns a new code store instance.
hannesw@1018 83 *
hannesw@1018 84 * @param context the current context
hannesw@1018 85 * @return The instance
hannesw@1018 86 * @throws IOException If an error occurs
hannesw@1018 87 */
hannesw@1018 88 public static CodeStore newCodeStore(final Context context) throws IOException {
hannesw@1018 89 final Class<CodeStore> baseClass = CodeStore.class;
hannesw@828 90 try {
hannesw@1018 91 // security check first
hannesw@1018 92 final SecurityManager sm = System.getSecurityManager();
hannesw@1018 93 if (sm != null) {
hannesw@1018 94 sm.checkPermission(new RuntimePermission(NASHORN_PROVIDE_CODE_STORE));
hannesw@1018 95 }
hannesw@1018 96 final ServiceLoader<CodeStore> services = ServiceLoader.load(baseClass);
hannesw@1018 97 final Iterator<CodeStore> iterator = services.iterator();
hannesw@1018 98 if (iterator.hasNext()) {
hannesw@1018 99 final CodeStore store = iterator.next();
hannesw@1018 100 store.initLogger(context).info("using code store provider ", store.getClass().getCanonicalName());
hannesw@1018 101 return store;
hannesw@1018 102 }
hannesw@1018 103 } catch (final AccessControlException e) {
hannesw@1018 104 context.getLogger(CodeStore.class).warning("failed to load code store provider ", e);
hannesw@828 105 }
hannesw@1087 106 final CodeStore store = new DirectoryCodeStore(context);
hannesw@1018 107 store.initLogger(context);
hannesw@1018 108 return store;
hannesw@828 109 }
hannesw@828 110
hannesw@1018 111
hannesw@1018 112 /**
hannesw@1018 113 * Store a compiled script in the cache.
hannesw@1018 114 *
hannesw@1018 115 * @param functionKey the function key
hannesw@1018 116 * @param source the source
hannesw@1018 117 * @param mainClassName the main class name
hannesw@1018 118 * @param classBytes a map of class bytes
hannesw@1018 119 * @param initializers the function initializers
hannesw@1018 120 * @param constants the constants array
hannesw@1018 121 * @param compilationId the compilation id
lagergren@1028 122 *
lagergren@1028 123 * @return stored script
hannesw@1018 124 */
hannesw@1018 125 public StoredScript store(final String functionKey,
hannesw@1018 126 final Source source,
hannesw@1018 127 final String mainClassName,
hannesw@1018 128 final Map<String, byte[]> classBytes,
hannesw@1018 129 final Map<Integer, FunctionInitializer> initializers,
hannesw@1018 130 final Object[] constants,
hannesw@1018 131 final int compilationId) {
hannesw@1018 132 return store(functionKey, source, storedScriptFor(source, mainClassName, classBytes, initializers, constants, compilationId));
hannesw@1018 133 }
hannesw@1018 134
hannesw@1018 135 /**
hannesw@1018 136 * Stores a compiled script.
hannesw@1018 137 *
hannesw@1018 138 * @param functionKey the function key
hannesw@1018 139 * @param source the source
hannesw@1018 140 * @param script The compiled script
hannesw@1018 141 * @return The compiled script or {@code null} if not stored
hannesw@1018 142 */
hannesw@1018 143 public abstract StoredScript store(final String functionKey,
hannesw@1018 144 final Source source,
hannesw@1018 145 final StoredScript script);
hannesw@1018 146
hannesw@1018 147 /**
hannesw@1018 148 * Return a compiled script from the cache, or null if it isn't found.
hannesw@1018 149 *
hannesw@1018 150 * @param source the source
hannesw@1018 151 * @param functionKey the function key
hannesw@1018 152 * @return the stored script or null
hannesw@1018 153 */
hannesw@1018 154 public abstract StoredScript load(final Source source, final String functionKey);
hannesw@1018 155
hannesw@1018 156 /**
hannesw@1018 157 * Returns a new StoredScript instance.
hannesw@1018 158 *
lagergren@1028 159 * @param source the source
hannesw@1018 160 * @param mainClassName the main class name
hannesw@1018 161 * @param classBytes a map of class bytes
hannesw@1018 162 * @param initializers function initializers
hannesw@1018 163 * @param constants the constants array
hannesw@1018 164 * @param compilationId the compilation id
lagergren@1028 165 *
hannesw@1018 166 * @return The compiled script
hannesw@1018 167 */
hannesw@1018 168 public StoredScript storedScriptFor(final Source source, final String mainClassName,
hannesw@1018 169 final Map<String, byte[]> classBytes,
hannesw@1018 170 final Map<Integer, FunctionInitializer> initializers,
hannesw@1018 171 final Object[] constants, final int compilationId) {
hannesw@1018 172 for (final Object constant : constants) {
hannesw@1018 173 // Make sure all constant data is serializable
hannesw@1018 174 if (!(constant instanceof Serializable)) {
hannesw@1018 175 getLogger().warning("cannot store ", source, " non serializable constant ", constant);
hannesw@1018 176 return null;
hannesw@1018 177 }
hannesw@1018 178 }
hannesw@1018 179 return new StoredScript(compilationId, mainClassName, classBytes, initializers, constants);
attila@963 180 }
attila@963 181
attila@963 182 /**
attila@963 183 * Generate a string representing the function with {@code functionId} and {@code paramTypes}.
attila@963 184 * @param functionId function id
attila@963 185 * @param paramTypes parameter types
attila@963 186 * @return a string representing the function
attila@963 187 */
attila@963 188 public static String getCacheKey(final int functionId, final Type[] paramTypes) {
attila@963 189 final StringBuilder b = new StringBuilder().append(functionId);
attila@963 190 if(paramTypes != null && paramTypes.length > 0) {
attila@963 191 b.append('-');
attila@963 192 for(final Type t: paramTypes) {
attila@963 193 b.append(Type.getShortSignatureDescriptor(t));
attila@963 194 }
attila@963 195 }
attila@963 196 return b.toString();
attila@963 197 }
attila@963 198
hannesw@828 199 /**
hannesw@1018 200 * A store using a file system directory.
hannesw@828 201 */
hannesw@1018 202 public static class DirectoryCodeStore extends CodeStore {
hannesw@1018 203
hannesw@1018 204 // Default minimum size for storing a compiled script class
hannesw@1018 205 private final static int DEFAULT_MIN_SIZE = 1000;
hannesw@1018 206
hannesw@1018 207 private final File dir;
hannesw@1018 208 private final boolean readOnly;
hannesw@1018 209 private final int minSize;
hannesw@1018 210
hannesw@1018 211 /**
hannesw@1018 212 * Constructor
hannesw@1018 213 *
hannesw@1087 214 * @param context the current context
lagergren@1028 215 * @throws IOException if there are read/write problems with the cache and cache directory
hannesw@1018 216 */
hannesw@1087 217 public DirectoryCodeStore(final Context context) throws IOException {
hannesw@1087 218 this(context, Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"), false, DEFAULT_MIN_SIZE);
hannesw@828 219 }
hannesw@828 220
hannesw@1018 221 /**
hannesw@1018 222 * Constructor
hannesw@1018 223 *
hannesw@1087 224 * @param context the current context
hannesw@1018 225 * @param path directory to store code in
lagergren@1028 226 * @param readOnly is this a read only code store
hannesw@1018 227 * @param minSize minimum file size for caching scripts
lagergren@1028 228 * @throws IOException if there are read/write problems with the cache and cache directory
hannesw@1018 229 */
hannesw@1087 230 public DirectoryCodeStore(final Context context, final String path, final boolean readOnly, final int minSize) throws IOException {
hannesw@1087 231 this.dir = checkDirectory(path, context.getEnv(), readOnly);
hannesw@1018 232 this.readOnly = readOnly;
hannesw@1018 233 this.minSize = minSize;
hannesw@1018 234 }
hannesw@828 235
hannesw@1087 236 private static File checkDirectory(final String path, final ScriptEnvironment env, final boolean readOnly) throws IOException {
hannesw@1018 237 try {
hannesw@1018 238 return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() {
hannesw@1018 239 @Override
hannesw@1018 240 public File run() throws IOException {
hannesw@1087 241 final File dir = new File(path, getVersionDir(env)).getAbsoluteFile();
hannesw@1018 242 if (readOnly) {
hannesw@1018 243 if (!dir.exists() || !dir.isDirectory()) {
hannesw@1018 244 throw new IOException("Not a directory: " + dir.getPath());
hannesw@1018 245 } else if (!dir.canRead()) {
hannesw@1018 246 throw new IOException("Directory not readable: " + dir.getPath());
hannesw@1018 247 }
hannesw@1018 248 } else if (!dir.exists() && !dir.mkdirs()) {
hannesw@1018 249 throw new IOException("Could not create directory: " + dir.getPath());
hannesw@1018 250 } else if (!dir.isDirectory()) {
hannesw@1018 251 throw new IOException("Not a directory: " + dir.getPath());
hannesw@1018 252 } else if (!dir.canRead() || !dir.canWrite()) {
hannesw@1018 253 throw new IOException("Directory not readable or writable: " + dir.getPath());
hannesw@1018 254 }
hannesw@1018 255 return dir;
hannesw@828 256 }
hannesw@1018 257 });
hannesw@1018 258 } catch (final PrivilegedActionException e) {
hannesw@1018 259 throw (IOException) e.getException();
hannesw@828 260 }
hannesw@828 261 }
hannesw@828 262
hannesw@1087 263 private static String getVersionDir(final ScriptEnvironment env) throws IOException {
hannesw@1087 264 try {
hannesw@1087 265 final String versionDir = OptimisticTypesPersistence.getVersionDirName();
hannesw@1087 266 return env._optimistic_types ? versionDir + "_opt" : versionDir;
hannesw@1087 267 } catch (final Exception e) {
hannesw@1087 268 throw new IOException(e);
hannesw@1087 269 }
hannesw@1087 270 }
hannesw@1087 271
hannesw@1018 272 @Override
hannesw@1018 273 public StoredScript load(final Source source, final String functionKey) {
hannesw@1018 274 if (source.getLength() < minSize) {
hannesw@1018 275 return null;
hannesw@1018 276 }
hannesw@828 277
hannesw@1018 278 final File file = getCacheFile(source, functionKey);
hannesw@1018 279
hannesw@1018 280 try {
hannesw@1018 281 return AccessController.doPrivileged(new PrivilegedExceptionAction<StoredScript>() {
hannesw@1018 282 @Override
hannesw@1018 283 public StoredScript run() throws IOException, ClassNotFoundException {
hannesw@1018 284 if (!file.exists()) {
hannesw@1018 285 return null;
hannesw@1018 286 }
hannesw@1018 287 try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) {
hannesw@1018 288 final StoredScript storedScript = (StoredScript) in.readObject();
hannesw@1018 289 getLogger().info("loaded ", source, "-", functionKey);
hannesw@1018 290 return storedScript;
hannesw@1018 291 }
hannesw@828 292 }
hannesw@1018 293 });
hannesw@1018 294 } catch (final PrivilegedActionException e) {
hannesw@1018 295 getLogger().warning("failed to load ", source, "-", functionKey, ": ", e.getException());
hannesw@1018 296 return null;
hannesw@1018 297 }
hannesw@1018 298 }
hannesw@1018 299
hannesw@1018 300 @Override
hannesw@1018 301 public StoredScript store(final String functionKey, final Source source, final StoredScript script) {
hannesw@1018 302 if (readOnly || script == null || belowThreshold(source)) {
hannesw@1018 303 return null;
hannesw@1018 304 }
hannesw@1018 305
hannesw@1018 306 final File file = getCacheFile(source, functionKey);
hannesw@1018 307
hannesw@1018 308 try {
hannesw@1018 309 return AccessController.doPrivileged(new PrivilegedExceptionAction<StoredScript>() {
hannesw@1018 310 @Override
hannesw@1018 311 public StoredScript run() throws IOException {
hannesw@1018 312 try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
hannesw@1018 313 out.writeObject(script);
hannesw@1018 314 }
hannesw@1018 315 getLogger().info("stored ", source, "-", functionKey);
hannesw@1018 316 return script;
hannesw@1018 317 }
hannesw@1018 318 });
hannesw@1018 319 } catch (final PrivilegedActionException e) {
hannesw@1018 320 getLogger().warning("failed to store ", script, "-", functionKey, ": ", e.getException());
hannesw@1018 321 return null;
hannesw@1018 322 }
hannesw@1018 323 }
hannesw@1018 324
hannesw@1018 325
hannesw@1018 326 private File getCacheFile(final Source source, final String functionKey) {
hannesw@1018 327 return new File(dir, source.getDigest() + '-' + functionKey);
hannesw@1018 328 }
hannesw@1018 329
hannesw@1018 330 private boolean belowThreshold(final Source source) {
hannesw@1018 331 if (source.getLength() < minSize) {
hannesw@1018 332 getLogger().info("below size threshold ", source);
hannesw@1018 333 return true;
hannesw@1018 334 }
hannesw@1018 335 return false;
hannesw@828 336 }
hannesw@828 337 }
hannesw@828 338 }
hannesw@828 339

mercurial