changeset 958:19795e6f1ecf

8043956: Make code caching work with optimistic typing and lazy compilation Reviewed-by: attila, lagergren
author hannesw
date Fri, 08 Aug 2014 12:50:46 +0200
parents b850ada7a38d
children e32f2d049838
files make/project.properties src/jdk/nashorn/internal/codegen/CodeGenerator.java src/jdk/nashorn/internal/codegen/CompilationPhase.java src/jdk/nashorn/internal/codegen/CompileUnit.java src/jdk/nashorn/internal/codegen/Compiler.java src/jdk/nashorn/internal/codegen/ConstantData.java src/jdk/nashorn/internal/codegen/FindScopeDepths.java src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java src/jdk/nashorn/internal/codegen/types/Type.java src/jdk/nashorn/internal/ir/FunctionNode.java src/jdk/nashorn/internal/parser/AbstractParser.java src/jdk/nashorn/internal/parser/Parser.java src/jdk/nashorn/internal/runtime/AccessorProperty.java src/jdk/nashorn/internal/runtime/CodeInstaller.java src/jdk/nashorn/internal/runtime/CodeStore.java src/jdk/nashorn/internal/runtime/CompiledFunction.java src/jdk/nashorn/internal/runtime/CompiledFunctions.java src/jdk/nashorn/internal/runtime/CompiledScript.java src/jdk/nashorn/internal/runtime/Context.java src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java src/jdk/nashorn/internal/runtime/FunctionInitializer.java src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java src/jdk/nashorn/internal/runtime/ScriptFunctionData.java src/jdk/nashorn/internal/runtime/Source.java src/jdk/nashorn/internal/runtime/SpillProperty.java src/jdk/nashorn/internal/runtime/StoredScript.java src/jdk/nashorn/tools/Shell.java test/script/trusted/JDK-8006529.js test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java
diffstat 29 files changed, 891 insertions(+), 733 deletions(-) [+]
line wrap: on
line diff
--- a/make/project.properties	Thu Aug 07 19:19:46 2014 +0530
+++ b/make/project.properties	Fri Aug 08 12:50:46 2014 +0200
@@ -217,7 +217,7 @@
     ${test262.dir}/test/harness/sta.js
 
 # testmarkdown test root
-testmarkdown-test-sys-prop.test.js.roots=${testmarkdown.dir}    
+testmarkdown-test-sys-prop.test.js.roots=${testmarkdown.dir}
 
 # execute testmarkdown tests in shared nashorn context or not?
 testmarkdown-test-sys-prop.test.js.shared.context=false
@@ -227,7 +227,7 @@
     ${test.script.dir}${file.separator}markdown.js
 
 # testjfx test root
-testjfx-test-sys-prop.test.js.roots=${testjfx.dir}   
+testjfx-test-sys-prop.test.js.roots=${testjfx.dir}
 
 # execute testjfx tests in shared nashorn context or not?
 testjfx-test-sys-prop.test.js.shared.context=false
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Fri Aug 08 12:50:46 2014 +0200
@@ -250,8 +250,6 @@
 
     private final Deque<Label> scopeEntryLabels = new ArrayDeque<>();
 
-    private final Set<Integer> initializedFunctionIds = new HashSet<>();
-
     private static final Label METHOD_BOUNDARY = new Label("");
     private final Deque<Label> catchLabels = new ArrayDeque<>();
     // Number of live locals on entry to (and thus also break from) labeled blocks.
@@ -1872,6 +1870,7 @@
         // lingers around. Also, currently loading previously persisted optimistic types information only works if
         // we're on-demand compiling a function, so with this strategy the :program method can also have the warmup
         // benefit of using previously persisted types.
+        //
         // NOTE that this means the first compiled class will effectively just have a :createProgramFunction method, and
         // the RecompilableScriptFunctionData (RSFD) object in its constants array. It won't even have the :program
         // method. This is by design. It does mean that we're wasting one compiler execution (and we could minimize this
@@ -1879,11 +1878,7 @@
         // We could emit an initial separate compile unit with the initial version of :program in it to better utilize
         // the compilation pipeline, but that would need more invasive changes, as currently the assumption that
         // :program is emitted into the first compilation unit of the function lives in many places.
-        if(!onDemand && lazy && env._optimistic_types && functionNode.isProgram()) {
-            return true;
-        }
-
-        return false;
+        return !onDemand && lazy && env._optimistic_types && functionNode.isProgram();
     }
 
     @Override
@@ -4383,9 +4378,8 @@
             createFunction.end();
         }
 
-        if (addInitializer && !initializedFunctionIds.contains(fnId) && !compiler.isOnDemandCompilation()) {
-            functionNode.getCompileUnit().addFunctionInitializer(data, functionNode);
-            initializedFunctionIds.add(fnId);
+        if (addInitializer && !compiler.isOnDemandCompilation()) {
+            compiler.addFunctionInitializer(data, functionNode);
         }
 
         // We don't emit a ScriptFunction on stack for the outermost compiled function (as there's no code being
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Fri Aug 08 12:50:46 2014 +0200
@@ -61,6 +61,7 @@
 import jdk.nashorn.internal.ir.debug.PrintVisitor;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.CodeInstaller;
+import jdk.nashorn.internal.runtime.FunctionInitializer;
 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.logging.DebugLogger;
@@ -324,15 +325,15 @@
             final DebugLogger log = compiler.getLogger();
 
             log.fine("Clearing bytecode cache");
+            compiler.clearBytecode();
 
             for (final CompileUnit oldUnit : compiler.getCompileUnits()) {
-                CompileUnit newUnit = map.get(oldUnit);
                 assert map.get(oldUnit) == null;
                 final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName());
                 if (phases.isRestOfCompilation()) {
                     sb.append("$restOf");
                 }
-                newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight());
+                CompileUnit newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight());
                 log.fine("Creating new compile unit ", oldUnit, " => ", newUnit);
                 map.put(oldUnit, newUnit);
                 assert newUnit != null;
@@ -502,8 +503,7 @@
             long length = 0L;
 
             final CodeInstaller<ScriptEnvironment> codeInstaller = compiler.getCodeInstaller();
-
-            final Map<String, byte[]> bytecode = compiler.getBytecode();
+            final Map<String, byte[]>              bytecode      = compiler.getBytecode();
 
             for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
                 final String className = entry.getKey();
@@ -536,17 +536,18 @@
             // initialize function in the compile units
             for (final CompileUnit unit : compiler.getCompileUnits()) {
                 unit.setCode(installedClasses.get(unit.getUnitClassName()));
-                unit.initializeFunctionsCode();
             }
 
             if (!compiler.isOnDemandCompilation()) {
-                codeInstaller.storeCompiledScript(compiler.getSource(), compiler.getFirstCompileUnit().getUnitClassName(), bytecode, constants);
-            }
-
-            // remove installed bytecode from table in case compiler is reused
-            for (final String className : installedClasses.keySet()) {
-                log.fine("Removing installed class ", quote(className), " from bytecode table...");
-                compiler.removeClass(className);
+                // Initialize functions
+                final Map<Integer, FunctionInitializer> initializers = compiler.getFunctionInitializers();
+                if (initializers != null) {
+                    for (final Entry<Integer, FunctionInitializer> entry : initializers.entrySet()) {
+                        final FunctionInitializer initializer = entry.getValue();
+                        initializer.setCode(installedClasses.get(initializer.getClassName()));
+                        compiler.getScriptFunctionData(entry.getKey()).initializeCode(initializer);
+                    }
+                }
             }
 
             if (log.isEnabled()) {
--- a/src/jdk/nashorn/internal/codegen/CompileUnit.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java	Fri Aug 08 12:50:46 2014 +0200
@@ -46,36 +46,6 @@
 
     private Class<?> clazz;
 
-    private Set<FunctionInitializer> functionInitializers = new LinkedHashSet<>();
-
-    private static class FunctionInitializer {
-        final RecompilableScriptFunctionData data;
-        final FunctionNode functionNode;
-
-        FunctionInitializer(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
-            this.data = data;
-            this.functionNode = functionNode;
-        }
-
-        void initializeCode() {
-            data.initializeCode(functionNode);
-        }
-
-        @Override
-        public int hashCode() {
-            return data.hashCode() + 31 * functionNode.hashCode();
-        }
-
-        @Override
-        public boolean equals(final Object obj) {
-            if (obj == null || obj.getClass() != FunctionInitializer.class) {
-                return false;
-            }
-            final FunctionInitializer other = (FunctionInitializer)obj;
-            return data == other.data && functionNode == other.functionNode;
-        }
-    }
-
     CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) {
         this.className    = className;
         this.weight       = initialWeight;
@@ -108,29 +78,6 @@
         this.classEmitter = null;
     }
 
-    void addFunctionInitializer(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
-        functionInitializers.add(new FunctionInitializer(data, functionNode));
-    }
-
-    /**
-     * Returns true if this compile unit is responsible for initializing the specified function data with specified
-     * function node.
-     * @param data the function data to check
-     * @param functionNode the function node to check
-     * @return true if this unit is responsible for initializing the function data with the function node, otherwise
-     * false
-     */
-    public boolean isInitializing(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
-        return functionInitializers.contains(new FunctionInitializer(data, functionNode));
-    }
-
-    void initializeFunctionsCode() {
-        for(final FunctionInitializer init : functionInitializers) {
-            init.initializeCode();
-        }
-        functionInitializers = Collections.emptySet();
-    }
-
     /**
      * Add weight to this compile unit
      * @param w weight to add
--- a/src/jdk/nashorn/internal/codegen/Compiler.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java	Fri Aug 08 12:50:46 2014 +0200
@@ -50,7 +50,6 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 import java.util.logging.Level;
-
 import jdk.internal.dynalink.support.NameCodec;
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
 import jdk.nashorn.internal.codegen.types.Type;
@@ -60,6 +59,7 @@
 import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
 import jdk.nashorn.internal.runtime.CodeInstaller;
 import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.FunctionInitializer;
 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.ScriptObject;
@@ -89,8 +89,6 @@
 
     private final String sourceName;
 
-    private final String sourceURL;
-
     private final boolean optimistic;
 
     private final Map<String, byte[]> bytecode;
@@ -309,21 +307,19 @@
     /**
      * Constructor
      *
-     * @param context                  context
-     * @param env                      script environment
-     * @param installer                code installer
-     * @param source                   source to compile
-     * @param sourceURL                source URL, or null if not present
-     * @param isStrict                 is this a strict compilation
+     * @param context   context
+     * @param env       script environment
+     * @param installer code installer
+     * @param source    source to compile
+     * @param isStrict  is this a strict compilation
      */
     public Compiler(
             final Context context,
             final ScriptEnvironment env,
             final CodeInstaller<ScriptEnvironment> installer,
             final Source source,
-            final String sourceURL,
             final boolean isStrict) {
-        this(context, env, installer, source, sourceURL, isStrict, false, null, null, null, null, null, null);
+        this(context, env, installer, source, isStrict, false, null, null, null, null, null, null);
     }
 
     /**
@@ -333,7 +329,6 @@
      * @param env                      script environment
      * @param installer                code installer
      * @param source                   source to compile
-     * @param sourceURL                source URL, or null if not present
      * @param isStrict                 is this a strict compilation
      * @param isOnDemand               is this an on demand compilation
      * @param compiledFunction         compiled function, if any
@@ -348,7 +343,6 @@
             final ScriptEnvironment env,
             final CodeInstaller<ScriptEnvironment> installer,
             final Source source,
-            final String sourceURL,
             final boolean isStrict,
             final boolean isOnDemand,
             final RecompilableScriptFunctionData compiledFunction,
@@ -365,8 +359,7 @@
         this.bytecode                 = new LinkedHashMap<>();
         this.log                      = initLogger(context);
         this.source                   = source;
-        this.sourceURL                = sourceURL;
-        this.sourceName               = FunctionNode.getSourceName(source, sourceURL);
+        this.sourceName               = FunctionNode.getSourceName(source);
         this.onDemand                 = isOnDemand;
         this.compiledFunction         = compiledFunction;
         this.types                    = types;
@@ -411,6 +404,15 @@
             sb.append(compilationId).append('$');
         }
 
+        if (types != null && compiledFunction.getFunctionNodeId() > 0) {
+            sb.append(compiledFunction.getFunctionNodeId());
+            final Type[] paramTypes = types.getParameterTypes(compiledFunction.getFunctionNodeId());
+            for (final Type t : paramTypes) {
+                sb.append(Type.getShortSignatureDescriptor(t));
+            }
+            sb.append('$');
+        }
+
         sb.append(Compiler.safeSourceName(env, installer, source));
 
         return sb.toString();
@@ -559,8 +561,11 @@
         return Collections.unmodifiableMap(bytecode);
     }
 
-    byte[] getBytecode(final String className) {
-        return bytecode.get(className);
+    /**
+     * Reset bytecode cache for compiler reuse.
+     */
+    void clearBytecode() {
+        bytecode.clear();
     }
 
     CompileUnit getFirstCompileUnit() {
@@ -584,15 +589,6 @@
         bytecode.put(name, code);
     }
 
-    void removeClass(final String name) {
-        assert bytecode.get(name) != null;
-        bytecode.remove(name);
-    }
-
-    String getSourceURL() {
-        return sourceURL;
-    }
-
     String nextCompileUnitName() {
         final StringBuilder sb = new StringBuilder(firstCompileUnitName);
         final int cuid = nextCompileUnitId.getAndIncrement();
@@ -603,8 +599,51 @@
         return sb.toString();
     }
 
-    void clearCompileUnits() {
-        compileUnits.clear();
+    Map<Integer, FunctionInitializer> functionInitializers;
+
+    void addFunctionInitializer(final RecompilableScriptFunctionData functionData, final FunctionNode functionNode) {
+        if (functionInitializers == null) {
+            functionInitializers = new HashMap<>();
+        }
+        if (!functionInitializers.containsKey(functionData)) {
+            functionInitializers.put(functionData.getFunctionNodeId(), new FunctionInitializer(functionNode));
+        }
+    }
+
+    Map<Integer, FunctionInitializer> getFunctionInitializers() {
+        return functionInitializers;
+    }
+
+    /**
+     * Persist current compilation with the given {@code cacheKey}.
+     * @param cacheKey cache key
+     * @param functionNode function node
+     */
+    public void persistClassInfo(final String cacheKey, final FunctionNode functionNode) {
+        if (cacheKey != null && env._persistent_cache) {
+            Map<Integer, FunctionInitializer> initializers;
+            // If this is an on-demand compilation create a function initializer for the function being compiled.
+            // Otherwise use function initializer map generated by codegen.
+            if (functionInitializers == null) {
+                initializers = new HashMap<>();
+                final FunctionInitializer initializer = new FunctionInitializer(functionNode, getInvalidatedProgramPoints());
+                initializers.put(functionNode.getId(), initializer);
+            } else {
+                initializers = functionInitializers;
+            }
+            final String mainClassName = getFirstCompileUnit().getUnitClassName();
+            installer.storeScript(cacheKey, source, mainClassName, bytecode, initializers, constantData.toArray(), compilationId);
+        }
+    }
+
+    /**
+     * Make sure the next compilation id is greater than {@code value}.
+     * @param value compilation id value
+     */
+    public static void updateCompilationId(final int value) {
+        if (value >= COMPILATION_ID.get()) {
+            COMPILATION_ID.set(value + 1);
+        }
     }
 
     CompileUnit addCompileUnit(final long initialWeight) {
--- a/src/jdk/nashorn/internal/codegen/ConstantData.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/ConstantData.java	Fri Aug 08 12:50:46 2014 +0200
@@ -30,7 +30,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
 
 /**
@@ -121,12 +120,7 @@
         private final int hashCode;
 
         public PropertyMapWrapper(final PropertyMap map) {
-            int hash = 0;
-            for (final Property property : map.getProperties()) {
-                hash = hash << 7 ^ hash >> 7;
-                hash ^= property.hashCode();
-            }
-            this.hashCode = hash;
+            this.hashCode = Arrays.hashCode(map.getProperties());
             this.propertyMap = map;
         }
 
@@ -137,14 +131,8 @@
 
         @Override
         public boolean equals(final Object other) {
-            if (!(other instanceof PropertyMapWrapper)) {
-                return false;
-            }
-
-            final Property[] ownProperties = propertyMap.getProperties();
-            final Property[] otherProperties = ((PropertyMapWrapper) other).propertyMap.getProperties();
-
-            return Arrays.equals(ownProperties, otherProperties);
+            return other instanceof PropertyMapWrapper &&
+                    Arrays.equals(propertyMap.getProperties(), ((PropertyMapWrapper) other).propertyMap.getProperties());
         }
     }
 
--- a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java	Fri Aug 08 12:50:46 2014 +0200
@@ -217,7 +217,6 @@
                 allocatorClassName,
                 allocatorMap,
                 nestedFunctions,
-                compiler.getSourceURL(),
                 externalSymbolDepths.get(fnId),
                 internalSymbols.get(fnId)
                 );
--- a/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java	Fri Aug 08 12:50:46 2014 +0200
@@ -80,12 +80,11 @@
         final StringBuilder b = new StringBuilder(48);
         // Base64-encode the digest of the source, and append the function id.
         b.append(source.getDigest()).append('-').append(functionId);
-        // Finally, if this is a parameter-type specialized version of the function, add the parameter types to the file
-        // name.
+        // Finally, if this is a parameter-type specialized version of the function, add the parameter types to the file name.
         if(paramTypes != null && paramTypes.length > 0) {
             b.append('-');
             for(final Type t: paramTypes) {
-                b.append(t.getBytecodeStackType());
+                b.append(Type.getShortSignatureDescriptor(t));
             }
         }
         return new LocationDescriptor(new File(cacheDir, b.toString()));
@@ -117,25 +116,10 @@
             @Override
             public Void run() {
                 synchronized(getFileLock(file)) {
-                    try (final FileOutputStream out = new FileOutputStream(file);) {
+                    try (final FileOutputStream out = new FileOutputStream(file)) {
                         out.getChannel().lock(); // lock exclusive
                         final DataOutputStream dout = new DataOutputStream(new BufferedOutputStream(out));
-                        dout.writeInt(optimisticTypes.size());
-                        for(final Map.Entry<Integer, Type> e: optimisticTypes.entrySet()) {
-                            dout.writeInt(e.getKey());
-                            final byte typeChar;
-                            final Type type = e.getValue();
-                            if(type == Type.OBJECT) {
-                                typeChar = 'L';
-                            } else if(type == Type.NUMBER) {
-                                typeChar = 'D';
-                            } else if(type == Type.LONG) {
-                                typeChar = 'J';
-                            } else {
-                                throw new AssertionError();
-                            }
-                            dout.write(typeChar);
-                        }
+                        Type.writeTypeMap(optimisticTypes, dout);
                         dout.flush();
                     } catch(final Exception e) {
                         reportError("write", file, e);
@@ -166,24 +150,10 @@
                         return null;
                     }
                     synchronized(getFileLock(file)) {
-                        try (final FileInputStream in = new FileInputStream(file);) {
+                        try (final FileInputStream in = new FileInputStream(file)) {
                             in.getChannel().lock(0, Long.MAX_VALUE, true); // lock shared
                             final DataInputStream din = new DataInputStream(new BufferedInputStream(in));
-                            final Map<Integer, Type> map = new TreeMap<>();
-                            final int size = din.readInt();
-                            for(int i = 0; i < size; ++i) {
-                                final int pp = din.readInt();
-                                final int typeChar = din.read();
-                                final Type type;
-                                switch(typeChar) {
-                                case 'L': type = Type.OBJECT; break;
-                                case 'D': type = Type.NUMBER; break;
-                                case 'J': type = Type.LONG; break;
-                                default: throw new AssertionError();
-                                }
-                                map.put(pp, type);
-                            }
-                            return map;
+                            return Type.readTypeMap(din);
                         }
                     }
                 } catch (final Exception e) {
@@ -276,7 +246,7 @@
     private static String getVersionDirName() throws Exception {
         final URL url = OptimisticTypesPersistence.class.getResource("");
         final String protocol = url.getProtocol();
-        if(protocol.equals("jar")) {
+        if (protocol.equals("jar")) {
             // Normal deployment: nashorn.jar
             final String jarUrlFile = url.getFile();
             final String filePath = jarUrlFile.substring(0, jarUrlFile.indexOf('!'));
@@ -310,12 +280,12 @@
         for(final File f: dir.listFiles()) {
             if(f.getName().endsWith(".class")) {
                 final long lastModified = f.lastModified();
-                if(lastModified > currentMax) {
+                if (lastModified > currentMax) {
                     currentMax = lastModified;
                 }
-            } else if(f.isDirectory()) {
+            } else if (f.isDirectory()) {
                 final long lastModified = getLastModifiedClassFile(f, currentMax);
-                if(lastModified > currentMax) {
+                if (lastModified > currentMax) {
                     currentMax = lastModified;
                 }
             }
@@ -325,7 +295,7 @@
 
     private static Object[] createLockArray() {
         final Object[] lockArray = new Object[Runtime.getRuntime().availableProcessors() * 2];
-        for(int i = 0; i < lockArray.length; ++i) {
+        for (int i = 0; i < lockArray.length; ++i) {
             lockArray[i] = new Object();
         }
         return lockArray;
--- a/src/jdk/nashorn/internal/codegen/types/Type.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/types/Type.java	Fri Aug 08 12:50:46 2014 +0200
@@ -48,10 +48,15 @@
 import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
 
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
 import java.lang.invoke.CallSite;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+import java.util.Map;
+import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import jdk.internal.org.objectweb.asm.Handle;
@@ -204,6 +209,20 @@
     }
 
     /**
+     * Return a character representing {@code type} in a method signature.
+     *
+     * @param type parameter type
+     * @return descriptor character
+     */
+    public static char getShortSignatureDescriptor(final Type type) {
+        // Use 'Z' for boolean parameters as we need to distinguish from int
+        if (type instanceof BooleanType) {
+            return 'Z';
+        }
+        return type.getBytecodeStackType();
+    }
+
+    /**
      * Return the type for an internal type, package private - do not use
      * outside code gen
      *
@@ -275,6 +294,64 @@
         return types;
     }
 
+    /**
+     * Write a map of {@code int} to {@code Type} to an output stream. This is used to store deoptimization state.
+     *
+     * @param typeMap the type map
+     * @param output data output
+     * @throws IOException
+     */
+    public static void writeTypeMap(Map<Integer, Type> typeMap, final DataOutput output) throws IOException {
+        if (typeMap == null) {
+            output.writeInt(0);
+        } else {
+            output.writeInt(typeMap.size());
+            for(Map.Entry<Integer, Type> e: typeMap.entrySet()) {
+                output.writeInt(e.getKey());
+                final byte typeChar;
+                final Type type = e.getValue();
+                if(type == Type.OBJECT) {
+                    typeChar = 'L';
+                } else if (type == Type.NUMBER) {
+                    typeChar = 'D';
+                } else if (type == Type.LONG) {
+                    typeChar = 'J';
+                } else {
+                    throw new AssertionError();
+                }
+                output.writeByte(typeChar);
+            }
+        }
+    }
+
+    /**
+     * Read a map of {@code int} to {@code Type} from an input stream. This is used to store deoptimization state.
+     *
+     * @param input data input
+     * @return type map
+     * @throws IOException
+     */
+    public static Map<Integer, Type> readTypeMap(DataInput input) throws IOException {
+        final int size = input.readInt();
+        if (size == 0) {
+            return null;
+        }
+        final Map<Integer, Type> map = new TreeMap<>();
+        for(int i = 0; i < size; ++i) {
+            final int pp = input.readInt();
+            final int typeChar = input.readByte();
+            final Type type;
+            switch(typeChar) {
+                case 'L': type = Type.OBJECT; break;
+                case 'D': type = Type.NUMBER; break;
+                case 'J': type = Type.LONG; break;
+                default: throw new AssertionError();
+            }
+            map.put(pp, type);
+        }
+        return map;
+    }
+
     static jdk.internal.org.objectweb.asm.Type getInternalType(final String className) {
         return jdk.internal.org.objectweb.asm.Type.getType(className);
     }
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/ir/FunctionNode.java	Fri Aug 08 12:50:46 2014 +0200
@@ -30,7 +30,6 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Objects;
 import java.util.Set;
 import java.util.function.Function;
 import jdk.nashorn.internal.codegen.CompileUnit;
@@ -150,9 +149,6 @@
     /** Function flags. */
     private final int flags;
 
-    /** //@ sourceURL or //# sourceURL for program function nodes */
-    private final String sourceURL;
-
     /** Line number of function start */
     private final int lineNumber;
 
@@ -269,7 +265,6 @@
      * @param parameters parameter list
      * @param kind       kind of function as in {@link FunctionNode.Kind}
      * @param flags      initial flags
-     * @param sourceURL  sourceURL specified in script (optional)
      */
     public FunctionNode(
         final Source source,
@@ -283,8 +278,7 @@
         final String name,
         final List<IdentNode> parameters,
         final FunctionNode.Kind kind,
-        final int flags,
-        final String sourceURL) {
+        final int flags) {
         super(token, finish);
 
         this.source           = source;
@@ -300,7 +294,6 @@
         this.compilationState = EnumSet.of(CompilationState.INITIALIZED);
         this.declaredSymbols  = new HashSet<>();
         this.flags            = flags;
-        this.sourceURL        = sourceURL;
         this.compileUnit      = null;
         this.body             = null;
         this.thisProperties   = 0;
@@ -311,7 +304,6 @@
         final FunctionNode functionNode,
         final long lastToken,
         final int flags,
-        final String sourceURL,
         final String name,
         final Type returnType,
         final CompileUnit compileUnit,
@@ -324,7 +316,6 @@
 
         this.lineNumber       = functionNode.lineNumber;
         this.flags            = flags;
-        this.sourceURL        = sourceURL;
         this.name             = name;
         this.returnType       = returnType;
         this.compileUnit      = compileUnit;
@@ -384,56 +375,18 @@
      * @return name for the script source
      */
     public String getSourceName() {
-        return getSourceName(source, sourceURL);
+        return getSourceName(source);
     }
 
     /**
      * Static source name getter
      *
-     * @param source
-     * @param sourceURL
+     * @param source the source
      * @return source name
      */
-    public static String getSourceName(final Source source, final String sourceURL) {
-        return sourceURL != null ? sourceURL : source.getName();
-    }
-
-    /**
-     * get the sourceURL
-     * @return the sourceURL
-     */
-    public String getSourceURL() {
-        return sourceURL;
-    }
-
-    /**
-     * Set the sourceURL
-     *
-     * @param lc lexical context
-     * @param newSourceURL source url string to set
-     * @return function node or a new one if state was changed
-     */
-    public FunctionNode setSourceURL(final LexicalContext lc, final String newSourceURL) {
-        if (Objects.equals(sourceURL, newSourceURL)) {
-            return this;
-        }
-
-        return Node.replaceInLexicalContext(
-                lc,
-                this,
-                new FunctionNode(
-                        this,
-                        lastToken,
-                        flags,
-                        newSourceURL,
-                        name,
-                        returnType,
-                        compileUnit,
-                        compilationState,
-                        body,
-                        parameters,
-                        thisProperties,
-                        rootClass));
+    public static String getSourceName(final Source source) {
+        final String explicitURL = source.getExplicitURL();
+        return explicitURL != null ? explicitURL : source.getName();
     }
 
     /**
@@ -504,7 +457,6 @@
                         this,
                         lastToken,
                         flags,
-                        sourceURL,
                         name,
                         returnType,
                         compileUnit,
@@ -576,7 +528,6 @@
                         this,
                         lastToken,
                         flags,
-                        sourceURL,
                         name,
                         returnType,
                         compileUnit,
@@ -733,7 +684,6 @@
                             (body.needsScope() ?
                                     FunctionNode.HAS_SCOPE_BLOCK :
                                     0),
-                        sourceURL,
                         name,
                         returnType,
                         compileUnit,
@@ -829,7 +779,6 @@
                         this,
                         lastToken,
                         flags,
-                        sourceURL,
                         name,
                         returnType,
                         compileUnit,
@@ -890,7 +839,6 @@
                         this,
                         lastToken,
                         flags,
-                        sourceURL,
                         name,
                         returnType,
                         compileUnit,
@@ -926,7 +874,6 @@
                         this,
                         lastToken,
                         flags,
-                        sourceURL,
                         name,
                         returnType,
                         compileUnit,
@@ -992,7 +939,6 @@
                         this,
                         lastToken,
                         flags,
-                        sourceURL,
                         name,
                         returnType,
                         compileUnit,
@@ -1071,7 +1017,6 @@
                 this,
                 lastToken,
                 flags,
-                sourceURL,
                 name,
                 type,
                 compileUnit,
@@ -1118,7 +1063,6 @@
                         this,
                         lastToken,
                         flags,
-                        sourceURL,
                         name,
                         returnType,
                         compileUnit,
@@ -1174,7 +1118,6 @@
                         this,
                         lastToken,
                         flags,
-                        sourceURL,
                         name,
                         returnType,
                         compileUnit,
--- a/src/jdk/nashorn/internal/parser/AbstractParser.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/parser/AbstractParser.java	Fri Aug 08 12:50:46 2014 +0200
@@ -91,9 +91,6 @@
     /** What should line numbers be counted from? */
     protected final int lineOffset;
 
-    /** //@ sourceURL or //# sourceURL */
-    protected String sourceURL;
-
     /**
      * Construct a parser.
      *
@@ -182,7 +179,7 @@
     // currently only @sourceURL=foo supported
     private void checkDirectiveComment() {
         // if already set, ignore this one
-        if (sourceURL != null) {
+        if (source.getExplicitURL() != null) {
             return;
         }
 
@@ -190,7 +187,7 @@
         final int len = comment.length();
         // 4 characters for directive comment marker //@\s or //#\s
         if (len > 4 && comment.substring(4).startsWith(SOURCE_URL_PREFIX)) {
-            sourceURL = comment.substring(4 + SOURCE_URL_PREFIX.length());
+            source.setExplicitURL(comment.substring(4 + SOURCE_URL_PREFIX.length()));
         }
     }
 
--- a/src/jdk/nashorn/internal/parser/Parser.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/parser/Parser.java	Fri Aug 08 12:50:46 2014 +0200
@@ -477,8 +477,7 @@
                 name,
                 parameters,
                 kind,
-                flags,
-                sourceURL);
+                flags);
 
         lc.push(functionNode);
         // Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the
@@ -702,10 +701,6 @@
 
         script = restoreFunctionNode(script, token); //commit code
         script = script.setBody(lc, script.getBody().setNeedsScope(lc));
-        // user may have directive comment to set sourceURL
-        if (sourceURL != null) {
-            script = script.setSourceURL(lc, sourceURL);
-        }
 
         return script;
     }
--- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Fri Aug 08 12:50:46 2014 +0200
@@ -136,16 +136,16 @@
     }
 
     /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
-    private transient MethodHandle primitiveGetter;
+    transient MethodHandle primitiveGetter;
 
     /** Seed setter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
-    private transient MethodHandle primitiveSetter;
+    transient MethodHandle primitiveSetter;
 
     /** Seed getter for the Object version of this field */
-    private transient MethodHandle objectGetter;
+    transient MethodHandle objectGetter;
 
     /** Seed setter for the Object version of this field */
-    private transient MethodHandle objectSetter;
+    transient MethodHandle objectSetter;
 
     /**
      * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode
@@ -185,10 +185,10 @@
      * @param key    the property key
      * @param flags  the property flags
      * @param slot   spill slot
-     * @param objectGetter
-     * @param objectSetter
-     * @param primitiveGetter
-     * @param primitiveSetter
+     * @param primitiveGetter primitive getter
+     * @param primitiveSetter primitive setter
+     * @param objectGetter    object getter
+     * @param objectSetter    object setter
      */
     protected AccessorProperty(
             final String key,
@@ -255,7 +255,7 @@
     }
 
     /**
-     * Normal ACCESS PROPERTY constructor given a structure glass.
+     * Normal ACCESS PROPERTY constructor given a structure class.
      * Constructor for dual field AccessorPropertys.
      *
      * @param key              property key
@@ -267,6 +267,7 @@
         super(key, flags, slot);
 
         initGetterSetter(structure);
+        initializeType();
     }
 
     private void initGetterSetter(final Class<?> structure) {
@@ -291,8 +292,6 @@
             objectSetter    = gs.objectSetters[slot];
             primitiveSetter = gs.primitiveSetters[slot];
         }
-
-        initializeType();
     }
 
     /**
@@ -412,8 +411,8 @@
         }
      }
 
-     @Override
-     public long getLongValue(final ScriptObject self, final ScriptObject owner) {
+    @Override
+    public long getLongValue(final ScriptObject self, final ScriptObject owner) {
         try {
             return (long)getGetter(long.class).invokeExact((Object)self);
         } catch (final Error | RuntimeException e) {
@@ -531,12 +530,13 @@
 
     @Override
     void initMethodHandles(final Class<?> structure) {
+        // sanity check for structure class
         if (!ScriptObject.class.isAssignableFrom(structure) || !StructureLoader.isStructureClass(structure.getName())) {
             throw new IllegalArgumentException();
         }
-        if (!isSpill()) {
-            initGetterSetter(structure);
-        }
+        // this method is overridden in SpillProperty
+        assert !isSpill();
+        initGetterSetter(structure);
     }
 
     @Override
--- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java	Fri Aug 08 12:50:46 2014 +0200
@@ -92,5 +92,14 @@
      * @param classBytes map of class names to class bytes
      * @param constants constants array
      */
-    public void storeCompiledScript(Source source, String mainClassName, Map<String, byte[]> classBytes, Object[] constants);
+    public void storeScript(String cacheKey, Source source, String mainClassName, Map<String, byte[]> classBytes,
+                            Map<Integer, FunctionInitializer> initializers, Object[] constants, int compilationId);
+
+    /**
+     * Load a previously compiled script
+     * @param source the script source
+     * @param functionKey the function id and signature
+     * @return compiled script data
+     */
+    public StoredScript loadScript(Source source, String functionKey);
 }
--- a/src/jdk/nashorn/internal/runtime/CodeStore.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/CodeStore.java	Fri Aug 08 12:50:46 2014 +0200
@@ -25,6 +25,11 @@
 
 package jdk.nashorn.internal.runtime;
 
+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 java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
@@ -42,21 +47,22 @@
 /**
  * A code cache for persistent caching of compiled scripts.
  */
-final class CodeStore {
+@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;
 
     /**
      * Constructor
-     * @param path directory to store code in
      * @throws IOException
      */
-    public CodeStore(final String path) throws IOException {
-        this(path, DEFAULT_MIN_SIZE);
+    public CodeStore(final Context context, final String path) throws IOException {
+        this(context, path, DEFAULT_MIN_SIZE);
     }
 
     /**
@@ -65,9 +71,20 @@
      * @param minSize minimum file size for caching scripts
      * @throws IOException
      */
-    public CodeStore(final String path, final int minSize) 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);
+    }
+
+    @Override
+    public DebugLogger initLogger(Context context) {
+         return context.getLogger(getClass());
+    }
+
+    @Override
+    public DebugLogger getLogger() {
+        return log;
     }
 
     private static File checkDirectory(final String path) throws IOException {
@@ -77,11 +94,11 @@
                 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);
+                        throw new IOException("Could not create directory: " + dir.getPath());
                     } else if (!dir.isDirectory()) {
-                        throw new IOException("Not a directory: " + dir);
+                        throw new IOException("Not a directory: " + dir.getPath());
                     } else if (!dir.canRead() || !dir.canWrite()) {
-                        throw new IOException("Directory not readable or writable: " + dir);
+                        throw new IOException("Directory not readable or writable: " + dir.getPath());
                     }
                     return dir;
                 }
@@ -91,69 +108,85 @@
         }
     }
 
+    private File getCacheFile(final Source source, final String functionKey) {
+        return new File(dir, source.getDigest() + '-' + functionKey);
+    }
+
+    /**
+     * Generate a string representing the function with {@code functionId} and {@code paramTypes}.
+     * @param functionId function id
+     * @param paramTypes parameter types
+     * @return a string representing the function
+     */
+    public static String getCacheKey(final int functionId, final Type[] paramTypes) {
+        final StringBuilder b = new StringBuilder().append(functionId);
+        if(paramTypes != null && paramTypes.length > 0) {
+            b.append('-');
+            for(final Type t: paramTypes) {
+                b.append(Type.getShortSignatureDescriptor(t));
+            }
+        }
+        return b.toString();
+    }
+
     /**
      * Return a compiled script from the cache, or null if it isn't found.
      *
      * @param source the source
-     * @return the compiled script or null
-     * @throws IOException
-     * @throws ClassNotFoundException
+     * @param functionKey the function key
+     * @return the stored script or null
      */
-    public CompiledScript getScript(final Source source) throws IOException, ClassNotFoundException {
+    public StoredScript loadScript(final Source source, final String functionKey) {
         if (source.getLength() < minSize) {
             return null;
         }
 
-        final File file = new File(dir, source.getDigest());
+        final File file = getCacheFile(source, functionKey);
 
         try {
-            return AccessController.doPrivileged(new PrivilegedExceptionAction<CompiledScript>() {
+            return AccessController.doPrivileged(new PrivilegedExceptionAction<StoredScript>() {
                 @Override
-                public CompiledScript run() throws IOException, ClassNotFoundException {
+                public StoredScript run() throws IOException, ClassNotFoundException {
                     if (!file.exists()) {
                         return null;
                     }
                     try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) {
-                        final CompiledScript compiledScript = (CompiledScript) in.readObject();
-                        compiledScript.setSource(source);
-                        return compiledScript;
+                        final StoredScript storedScript = (StoredScript) in.readObject();
+                        getLogger().info("loaded ", source, "-", functionKey);
+                        return storedScript;
                     }
                 }
             });
         } catch (final PrivilegedActionException e) {
-            final Exception ex = e.getException();
-            if (ex instanceof IOException) {
-                throw  (IOException) ex;
-            } else if (ex instanceof ClassNotFoundException) {
-                throw (ClassNotFoundException) ex;
-            }
-            throw (new RuntimeException(ex));
+            getLogger().warning("failed to load ", source, "-", functionKey, ": ", e.getException());
+            return null;
         }
     }
 
     /**
      * 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
-     * @throws IOException
      */
-    public void putScript(final Source source, final String mainClassName, final Map<String, byte[]> classBytes, final Object[] constants)
-            throws IOException {
+    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;
             }
         }
 
-        final File file = new File(dir, source.getDigest());
-        final CompiledScript script = new CompiledScript(source, mainClassName, classBytes, constants);
+        final File file = getCacheFile(source, functionKey);
+        final StoredScript script = new StoredScript(compilationId, mainClassName, classBytes, initializers, constants);
 
         try {
             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
@@ -162,11 +195,12 @@
                     try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
                         out.writeObject(script);
                     }
+                    getLogger().info("stored ", source, "-", functionKey);
                     return null;
                 }
             });
         } catch (final PrivilegedActionException e) {
-             throw (IOException) e.getException();
+            getLogger().warning("failed to store ", script, "-", functionKey, ": ", e.getException());
         }
     }
 }
--- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Fri Aug 08 12:50:46 2014 +0200
@@ -42,6 +42,7 @@
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.nashorn.internal.codegen.Compiler;
 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
+import jdk.nashorn.internal.codegen.TypeMap;
 import jdk.nashorn.internal.codegen.types.ArrayType;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
@@ -72,6 +73,7 @@
     private MethodHandle constructor;
     private OptimismInfo optimismInfo;
     private final int flags; // from FunctionNode
+    private final MethodType callSiteType;
 
     CompiledFunction(final MethodHandle invoker) {
         this(invoker, null);
@@ -82,19 +84,20 @@
     }
 
     CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) {
-        this(invoker, constructor, 0, DebugLogger.DISABLED_LOGGER);
+        this(invoker, constructor, 0, null, DebugLogger.DISABLED_LOGGER);
     }
 
-    CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final int flags, final DebugLogger log) {
+    CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final int flags, final MethodType callSiteType, final DebugLogger log) {
         this.invoker = invoker;
         this.constructor = constructor;
         this.flags = flags;
+        this.callSiteType = callSiteType;
         this.log = log;
     }
 
     CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData,
-            final Map<Integer, Type> invalidatedProgramPoints, final int flags) {
-        this(invoker, null, flags, functionData.getLogger());
+            final Map<Integer, Type> invalidatedProgramPoints, final MethodType callSiteType, final int flags) {
+        this(invoker, null, flags, callSiteType, functionData.getLogger());
         if ((flags & FunctionNode.IS_DEOPTIMIZABLE) != 0) {
             optimismInfo = new OptimismInfo(functionData, invalidatedProgramPoints);
         } else {
@@ -127,9 +130,9 @@
      * Returns an invoker method handle for this function. Note that the handle is safely composable in
      * the sense that you can compose it with other handles using any combinators even if you can't affect call site
      * invalidation. If this compiled function is non-optimistic, then it returns the same value as
-     * {@link #getInvoker()}. However, if the function is optimistic, then this handle will incur an overhead as it will
-     * add an intermediate internal call site that can relink itself when the function needs to regenerate its code to
-     * always point at the latest generated code version.
+     * {@link #getInvokerOrConstructor(boolean)}. However, if the function is optimistic, then this handle will
+     * incur an overhead as it will add an intermediate internal call site that can relink itself when the function
+     * needs to regenerate its code to always point at the latest generated code version.
      * @return a guaranteed composable invoker method handle for this function.
      */
     MethodHandle createComposableInvoker() {
@@ -165,8 +168,6 @@
      * Compose a constructor from an invoker.
      *
      * @param invoker         invoker
-     * @param needsCallee  do we need to pass a callee
-     *
      * @return the composed constructor
      */
     private static MethodHandle createConstructorFromInvoker(final MethodHandle invoker) {
@@ -427,6 +428,9 @@
     }
 
     boolean matchesCallSite(final MethodType callSiteType, final boolean pickVarArg) {
+        if (callSiteType.equals(this.callSiteType)) {
+            return true;
+        }
         final MethodType type  = type();
         final int fnParamCount = getParamCount(type);
         final boolean isVarArg = fnParamCount == Integer.MAX_VALUE;
@@ -719,6 +723,15 @@
         logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
         final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE);
 
+        if (effectiveOptInfo.data.usePersistentCodeCache()) {
+            final RecompilableScriptFunctionData data = effectiveOptInfo.data;
+            final int functionNodeId = data.getFunctionNodeId();
+            final TypeMap typeMap = data.typeMap(callSiteType);
+            final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
+            final String cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
+            compiler.persistClassInfo(cacheKey, normalFn);
+        }
+
         FunctionNode fn2 = effectiveOptInfo.reparse();
         fn2 = compiler.compile(fn2, CompilationPhases.COMPILE_UPTO_BYTECODE);
         log.info("Done.");
@@ -751,11 +764,11 @@
 
     private MethodHandle restOfHandle(final OptimismInfo info, final FunctionNode restOfFunction, final boolean canBeDeoptimized) {
         assert info != null;
-        assert restOfFunction.getCompileUnit().getUnitClassName().indexOf("restOf") != -1;
+        assert restOfFunction.getCompileUnit().getUnitClassName().contains("restOf");
         final MethodHandle restOf =
                 changeReturnType(
-                        info.data.lookupWithExplicitType(
-                                restOfFunction,
+                        info.data.lookupCodeMethod(
+                                restOfFunction.getCompileUnit().getCode(),
                                 MH.type(restOfFunction.getReturnType().getTypeClass(),
                                         RewriteException.class)),
                         Object.class);
--- a/src/jdk/nashorn/internal/runtime/CompiledFunctions.java	Thu Aug 07 19:19:46 2014 +0530
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,183 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package jdk.nashorn.internal.runtime;
-
-import static jdk.nashorn.internal.lookup.Lookup.MH;
-
-import java.lang.invoke.MethodType;
-import java.util.LinkedList;
-
-/**
- * This is a list of code versions of a function.
- * The list is sorted in ascending order of generic descriptors
- */
-final class CompiledFunctions {
-
-    private final String name;
-    final LinkedList<CompiledFunction> functions = new LinkedList<>();
-
-    CompiledFunctions(final String name) {
-        this.name = name;
-    }
-
-    void add(final CompiledFunction f) {
-        functions.add(f);
-    }
-
-    void addAll(final CompiledFunctions fs) {
-        functions.addAll(fs.functions);
-    }
-
-    boolean isEmpty() {
-        return functions.isEmpty();
-    }
-
-    int size() {
-        return functions.size();
-    }
-
-    @Override
-    public String toString() {
-        return '\'' + name + "' code=" + functions;
-    }
-
-    private static MethodType widen(final MethodType cftype) {
-        final Class<?>[] paramTypes = new Class<?>[cftype.parameterCount()];
-        for (int i = 0; i < cftype.parameterCount(); i++) {
-            paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class;
-        }
-        return MH.type(cftype.returnType(), paramTypes);
-    }
-
-    /**
-     * Used to find an apply to call version that fits this callsite.
-     * We cannot just, as in the normal matcher case, return e.g. (Object, Object, int)
-     * for (Object, Object, int, int, int) or we will destroy the semantics and get
-     * a function that, when padded with undefineds, behaves differently
-     * @param type actual call site type
-     * @return apply to call that perfectly fits this callsite or null if none found
-     */
-    CompiledFunction lookupExactApplyToCall(final MethodType type) {
-        for (final CompiledFunction cf : functions) {
-            if (!cf.isApplyToCall()) {
-                continue;
-            }
-
-            final MethodType cftype = cf.type();
-            if (cftype.parameterCount() != type.parameterCount()) {
-                continue;
-            }
-
-            if (widen(cftype).equals(widen(type))) {
-                return cf;
-            }
-         }
-
-        return null;
-    }
-
-    private CompiledFunction pick(final MethodType callSiteType, final boolean canPickVarArg) {
-        for (final CompiledFunction candidate : functions) {
-            if (candidate.matchesCallSite(callSiteType, false)) {
-                return candidate;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns the compiled function best matching the requested call site method type
-     * @param callSiteType
-     * @param recompilable
-     * @param hasThis
-     * @return
-     */
-    CompiledFunction best(final MethodType callSiteType, final boolean recompilable) {
-        assert callSiteType.parameterCount() >= 2 : callSiteType; // Must have at least (callee, this)
-        assert callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class) : callSiteType; // Callee must be assignable from script function
-
-        if (recompilable) {
-            final CompiledFunction candidate = pick(callSiteType, false);
-            if (candidate != null) {
-                return candidate;
-            }
-            return pick(callSiteType, true); //try vararg last
-        }
-
-        CompiledFunction best = null;
-        for(final CompiledFunction candidate: functions) {
-            if(candidate.betterThanFinal(best, callSiteType)) {
-                best = candidate;
-            }
-        }
-
-        return best;
-    }
-
-    /**
-     * Returns true if functions managed by this {@code CompiledFunctions} require a callee. This method is only safe to
-     * be invoked for a {@code CompiledFunctions} that is not empty. As such, it should only be used from
-     * {@link FinalScriptFunctionData} and not from {@link RecompilableScriptFunctionData}.
-     * @return true if the functions need a callee, false otherwise.
-     */
-    boolean needsCallee() {
-        final boolean needsCallee = functions.getFirst().needsCallee();
-        assert allNeedCallee(needsCallee);
-        return needsCallee;
-    }
-
-    private boolean allNeedCallee(final boolean needCallee) {
-        for (final CompiledFunction inv : functions) {
-            if(inv.needsCallee() != needCallee) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * If this CompiledFunctions object belongs to a {@code FinalScriptFunctionData}, get a method type for a generic
-     * invoker. It will either be a vararg type, if any of the contained functions is vararg, or a generic type of the
-     * arity of the largest arity of all functions.
-     * @return the method type for the generic invoker
-     */
-    MethodType getFinalGenericType() {
-        int max = 0;
-        for(final CompiledFunction fn: functions) {
-            final MethodType t = fn.type();
-            if(ScriptFunctionData.isVarArg(t)) {
-                // 2 for (callee, this, args[])
-                return MethodType.genericMethodType(2, true);
-            }
-            final int paramCount = t.parameterCount() - (ScriptFunctionData.needsCallee(t) ? 1 : 0);
-            if(paramCount > max) {
-                max = paramCount;
-            }
-        }
-        // +1 for callee
-        return MethodType.genericMethodType(max + 1);
-    }
-
-}
--- a/src/jdk/nashorn/internal/runtime/CompiledScript.java	Thu Aug 07 19:19:46 2014 +0530
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.nashorn.internal.runtime;
-
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.Map;
-
-/**
- * Class representing a compiled script.
- */
-final class CompiledScript implements Serializable {
-
-    /** Main class name. */
-    private final String mainClassName;
-
-    /** Map of class names to class bytes. */
-    private final Map<String, byte[]> classBytes;
-
-    /** Constants array. */
-    private final Object[] constants;
-
-    /** The source */
-    private transient Source source;
-
-    private static final long serialVersionUID = 2958227232195298340L;
-
-    /**
-     * Constructor.
-     *
-     * @param mainClassName main class name
-     * @param classBytes map of class names to class bytes
-     * @param constants constants array
-     */
-    CompiledScript(final Source source, final String mainClassName, final Map<String, byte[]> classBytes, final Object[] constants) {
-        this.source = source;
-        this.mainClassName = mainClassName;
-        this.classBytes = classBytes;
-        this.constants = constants;
-    }
-
-    /**
-     * Returns the main class name.
-     * @return the main class name
-     */
-    public String getMainClassName() {
-        return mainClassName;
-    }
-
-    /**
-     * Returns a map of class names to class bytes.
-     * @return map of class bytes
-     */
-    public Map<String, byte[]> getClassBytes() {
-        return classBytes;
-    }
-
-    /**
-     * Returns the constants array.
-     * @return constants array
-     */
-    public Object[] getConstants() {
-        return constants;
-    }
-
-    /**
-     * Returns the source of this cached script.
-     * @return the source
-     */
-    public Source getSource() {
-        return source;
-    }
-
-    /**
-     * Sets the source of this cached script.
-     * @param source the source
-     */
-    void setSource(final Source source) {
-        this.source = source;
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = mainClassName.hashCode();
-        hash = 31 * hash + classBytes.hashCode();
-        hash = 31 * hash + Arrays.hashCode(constants);
-        return hash;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (!(obj instanceof CompiledScript)) {
-            return false;
-        }
-
-        final CompiledScript cs = (CompiledScript) obj;
-        return mainClassName.equals(cs.mainClassName)
-                && classBytes.equals(cs.classBytes)
-                && Arrays.equals(constants, cs.constants);
-    }
-}
--- a/src/jdk/nashorn/internal/runtime/Context.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/Context.java	Fri Aug 08 12:50:46 2014 +0200
@@ -164,10 +164,13 @@
         public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) {
             // do these in parallel, this significantly reduces class installation overhead
             // however - it still means that every thread needs a separate doPrivileged
+            final Global global = currentGlobal.get();
             classes.parallelStream().forEach(
                 new Consumer<Class<?>>() {
                     @Override
                     public void accept(final Class<?> clazz) {
+                        // Global threadlocal may be needed by StructureLoader during in field lookup.
+                        currentGlobal.set(global);
                         try {
                             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
                                 @Override
@@ -210,16 +213,21 @@
         }
 
         @Override
-        public void storeCompiledScript(final Source source, final String mainClassName,
-                                        final Map<String, byte[]> classBytes, final Object[] constants) {
+        public void storeScript(final String classInfoFile, final Source source, final String mainClassName,
+                                final Map<String,byte[]> classBytes, Map<Integer, FunctionInitializer> initializers,
+                                final Object[] constants, final int compilationId) {
             if (context.codeStore != null) {
-                try {
-                    context.codeStore.putScript(source, mainClassName, classBytes, constants);
-                } catch (final IOException e) {
-                    throw new RuntimeException(e);
-                }
+                context.codeStore.storeScript(classInfoFile, source, mainClassName, classBytes, initializers, constants, compilationId);
             }
         }
+
+        @Override
+        public StoredScript loadScript(final Source source, final String functionKey) {
+            if (context.codeStore != null) {
+                return context.codeStore.loadScript(source, functionKey);
+            }
+            return null;
+        }
     }
 
     /** Is Context global debug mode enabled ? */
@@ -447,7 +455,7 @@
         if (env._persistent_cache) {
             try {
                 final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache");
-                codeStore = new CodeStore(cacheDir);
+                codeStore = new CodeStore(this, cacheDir);
             } catch (final IOException e) {
                 throw new RuntimeException("Error initializing code cache", e);
             }
@@ -1080,19 +1088,16 @@
             return script;
         }
 
-        CompiledScript compiledScript = null;
+        StoredScript storedScript = null;
         FunctionNode functionNode = null;
+        final boolean useCodeStore = env._persistent_cache && !env._parse_only && !env._optimistic_types;
+        final String cacheKey = useCodeStore ? CodeStore.getCacheKey(0, null) : null;
 
-        if (!env._parse_only && codeStore != null) {
-            try {
-                compiledScript = codeStore.getScript(source);
-            } catch (IOException | ClassNotFoundException e) {
-                getLogger(Compiler.class).warning("Error loading ", source, " from cache: ", e);
-                // Fall back to normal compilation
-            }
+        if (useCodeStore) {
+            storedScript = codeStore.loadScript(source, cacheKey);
         }
 
-        if (compiledScript == null) {
+        if (storedScript == null) {
             functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse();
 
             if (errors.hasErrors()) {
@@ -1117,7 +1122,7 @@
         final CodeSource   cs     = new CodeSource(url, (CodeSigner[])null);
         final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs);
 
-        if (functionNode != null) {
+        if (storedScript == null) {
             final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
 
             final Compiler compiler = new Compiler(
@@ -1125,12 +1130,14 @@
                     env,
                     installer,
                     source,
-                    functionNode.getSourceURL(),
                     strict | functionNode.isStrict());
 
-            script = compiler.compile(functionNode, phases).getRootClass();
+            final FunctionNode compiledFunction = compiler.compile(functionNode, phases);
+            script = compiledFunction.getRootClass();
+            compiler.persistClassInfo(cacheKey, compiledFunction);
         } else {
-            script = install(compiledScript, installer);
+            Compiler.updateCompilationId(storedScript.getCompilationId());
+            script = install(storedScript, source, installer);
         }
 
         cacheClass(source, script);
@@ -1155,27 +1162,26 @@
         return uniqueScriptId.getAndIncrement();
     }
 
-
     /**
      * Install a previously compiled class from the code cache.
      *
-     * @param compiledScript cached script containing class bytes and constants
+     * @param storedScript cached script containing class bytes and constants
      * @return main script class
      */
-    private static Class<?> install(final CompiledScript compiledScript, final CodeInstaller<ScriptEnvironment> installer) {
+    private static Class<?> install(final StoredScript storedScript, final Source source, final CodeInstaller<ScriptEnvironment> installer) {
 
         final Map<String, Class<?>> installedClasses = new HashMap<>();
-        final Source   source        = compiledScript.getSource();
-        final Object[] constants     = compiledScript.getConstants();
-        final String   rootClassName = compiledScript.getMainClassName();
-        final byte[]   rootByteCode  = compiledScript.getClassBytes().get(rootClassName);
-        final Class<?> rootClass     = installer.install(rootClassName, rootByteCode);
+        final Object[] constants       = storedScript.getConstants();
+        final String   mainClassName   = storedScript.getMainClassName();
+        final byte[]   mainClassBytes  = storedScript.getClassBytes().get(mainClassName);
+        final Class<?> mainClass       = installer.install(mainClassName, mainClassBytes);
+        final Map<Integer, FunctionInitializer> initialzers = storedScript.getInitializers();
 
-        installedClasses.put(rootClassName, rootClass);
+        installedClasses.put(mainClassName, mainClass);
 
-        for (final Map.Entry<String, byte[]> entry : compiledScript.getClassBytes().entrySet()) {
+        for (final Map.Entry<String, byte[]> entry : storedScript.getClassBytes().entrySet()) {
             final String className = entry.getKey();
-            if (className.equals(rootClassName)) {
+            if (className.equals(mainClassName)) {
                 continue;
             }
             final byte[] code = entry.getValue();
@@ -1187,11 +1193,17 @@
 
         for (final Object constant : constants) {
             if (constant instanceof RecompilableScriptFunctionData) {
-                ((RecompilableScriptFunctionData) constant).initTransients(source, installer);
+                final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constant;
+                data.initTransients(source, installer);
+                if (initialzers != null) {
+                    final FunctionInitializer initializer = initialzers.get(data.getFunctionNodeId());
+                    initializer.setCode(installedClasses.get(initializer.getClassName()));
+                    data.initializeCode(initializer);
+                }
             }
         }
 
-        return rootClass;
+        return mainClass;
     }
 
     /**
--- a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java	Fri Aug 08 12:50:46 2014 +0200
@@ -27,6 +27,7 @@
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodType;
+import java.util.List;
 
 /**
  * This is a subclass that represents a script function that may not be regenerated.
@@ -44,10 +45,10 @@
      * @param functions precompiled code
      * @param flags     {@link ScriptFunctionData} flags
      */
-    FinalScriptFunctionData(final String name, final int arity, final CompiledFunctions functions, final int flags) {
+    FinalScriptFunctionData(final String name, final int arity, final List<CompiledFunction> functions, final int flags) {
         super(name, arity, flags);
-        assert !functions.needsCallee();
         code.addAll(functions);
+        assert !needsCallee();
     }
 
     /**
@@ -76,8 +77,19 @@
     }
 
     @Override
-    boolean needsCallee() {
-        return code.needsCallee();
+    protected boolean needsCallee() {
+        final boolean needsCallee = code.getFirst().needsCallee();
+        assert allNeedCallee(needsCallee);
+        return needsCallee;
+    }
+
+    private boolean allNeedCallee(final boolean needCallee) {
+        for (final CompiledFunction inv : code) {
+            if(inv.needsCallee() != needCallee) {
+                return false;
+            }
+        }
+        return true;
     }
 
     @Override
@@ -86,7 +98,20 @@
         // actually correct for lots of built-ins. E.g. ECMAScript 5.1 section 15.5.3.2 prescribes that
         // Script.fromCharCode([char0[, char1[, ...]]]) has a declared arity of 1 even though it's a variable arity
         // method.
-        return code.getFinalGenericType();
+        int max = 0;
+        for(final CompiledFunction fn: code) {
+            final MethodType t = fn.type();
+            if(ScriptFunctionData.isVarArg(t)) {
+                // 2 for (callee, this, args[])
+                return MethodType.genericMethodType(2, true);
+            }
+            final int paramCount = t.parameterCount() - (ScriptFunctionData.needsCallee(t) ? 1 : 0);
+            if(paramCount > max) {
+                max = paramCount;
+            }
+        }
+        // +1 for callee
+        return MethodType.genericMethodType(max + 1);
     }
 
     private void addInvoker(final MethodHandle mh) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/FunctionInitializer.java	Fri Aug 08 12:50:46 2014 +0200
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import jdk.nashorn.internal.codegen.CompileUnit;
+import jdk.nashorn.internal.codegen.FunctionSignature;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.FunctionNode;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.invoke.MethodType;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Class that contains information allowing us to look up a method handle implementing a JavaScript function
+ * from a generated class. This is used both for code coming from codegen and for persistent serialized code.
+ */
+public final class FunctionInitializer implements Serializable {
+
+    private final String className;
+    private final MethodType methodType;
+    private final int flags;
+    private transient Map<Integer, Type> invalidatedProgramPoints;
+    private transient Class<?> code;
+
+    private static final long serialVersionUID = -5420835725902966692L;
+
+    /**
+     * Constructor.
+     *
+     * @param functionNode the function node
+     */
+    public FunctionInitializer(final FunctionNode functionNode) {
+        this(functionNode, null);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param functionNode the function node
+     * @param invalidatedProgramPoints invalidated program points
+     */
+    public FunctionInitializer(final FunctionNode functionNode, final Map<Integer, Type> invalidatedProgramPoints) {
+        this.className  = functionNode.getCompileUnit().getUnitClassName();
+        this.methodType = new FunctionSignature(functionNode).getMethodType();
+        this.flags = functionNode.getFlags();
+        this.invalidatedProgramPoints = invalidatedProgramPoints;
+
+        final CompileUnit cu = functionNode.getCompileUnit();
+        if (cu != null) {
+            this.code = cu.getCode();
+        }
+
+        assert className != null;
+    }
+
+    /**
+     * Returns the name of the class implementing the function.
+     *
+     * @return the class name
+     */
+    public String getClassName() {
+        return className;
+    }
+
+    /**
+     * Returns the type of the method implementing the function.
+     *
+     * @return the method type
+     */
+    public MethodType getMethodType() {
+        return methodType;
+    }
+
+    /**
+     * Returns the function flags.
+     *
+     * @return function flags
+     */
+    public int getFlags() {
+        return flags;
+    }
+
+    /**
+     * Returns the class implementing the function.
+     *
+     * @return the class
+     */
+    public Class<?> getCode() {
+        return code;
+    }
+
+    /**
+     * Set the class implementing the function
+     * @param code the class
+     */
+    public void setCode(Class<?> code) {
+        // Make sure code has not been set and has expected class name
+        if (this.code != null) {
+            throw new IllegalStateException("code already set");
+        }
+        assert className.equals(code.getTypeName().replace('.', '/')) : "unexpected class name";
+        this.code = code;
+    }
+
+    /**
+     * Returns the map of invalidated program points.
+     *
+     * @return invalidated program points
+     */
+    public Map<Integer, Type> getInvalidatedProgramPoints() {
+        return invalidatedProgramPoints;
+    }
+
+    private void writeObject(final ObjectOutputStream out) throws IOException {
+        out.defaultWriteObject();
+        Type.writeTypeMap(invalidatedProgramPoints, out);
+    }
+
+    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        invalidatedProgramPoints = Type.readTypeMap(in);
+    }
+}
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Fri Aug 08 12:50:46 2014 +0200
@@ -32,13 +32,13 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 
 import jdk.internal.dynalink.support.NameCodec;
-import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.Compiler;
 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
 import jdk.nashorn.internal.codegen.CompilerConstants;
@@ -73,11 +73,6 @@
 
     private final String functionName;
 
-    // TODO: try to eliminate the need for this somehow, either by allowing Source to change its name, allowing a
-    // function to internally replace its Source with one of a different name, or storing this additional field in the
-    // Source object.
-    private final String sourceURL;
-
     /** The line number where this function begins. */
     private final int lineNumber;
 
@@ -128,7 +123,6 @@
      * @param allocatorClassName  name of our allocator class, will be looked up dynamically if used as a constructor
      * @param allocatorMap        allocator map to seed instances with, when constructing
      * @param nestedFunctions     nested function map
-     * @param sourceURL           source URL
      * @param externalScopeDepths external scope depths
      * @param internalSymbols     internal symbols to method, defined in its scope
      */
@@ -138,7 +132,6 @@
         final String allocatorClassName,
         final PropertyMap allocatorMap,
         final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
-        final String sourceURL,
         final Map<String, Integer> externalScopeDepths,
         final Set<String> internalSymbols) {
 
@@ -155,7 +148,6 @@
         this.source              = functionNode.getSource();
         this.token               = tokenFor(functionNode);
         this.installer           = installer;
-        this.sourceURL           = sourceURL;
         this.allocatorClassName  = allocatorClassName;
         this.allocatorMap        = allocatorMap;
         this.nestedFunctions     = nestedFunctions;
@@ -366,7 +358,7 @@
         final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, Token.descLength(token), true);
         // Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a
         // single function, extract it from the program.
-        return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName).setSourceURL(null,  sourceURL);
+        return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName);
     }
 
     TypeMap typeMap(final MethodType fnCallSiteType) {
@@ -395,18 +387,18 @@
             final ScriptObject runtimeScope, final Map<Integer, Type> invalidatedProgramPoints,
             final int[] continuationEntryPoints) {
         final TypeMap typeMap = typeMap(actualCallSiteType);
-        final Object typeInformationFile = OptimisticTypesPersistence.getLocationDescriptor(source, functionNodeId, typeMap == null ? null : typeMap.getParameterTypes(functionNodeId));
+        final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
+        final Object typeInformationFile = OptimisticTypesPersistence.getLocationDescriptor(source, functionNodeId, paramTypes);
         final Context context = Context.getContextTrusted();
         return new Compiler(
                 context,
                 context.getEnv(),
                 installer,
                 functionNode.getSource(),  // source
-                functionNode.getSourceURL(),
                 isStrict() | functionNode.isStrict(), // is strict
                 true,       // is on demand
                 this,       // compiledFunction, i.e. this RecompilableScriptFunctionData
-                typeMap(actualCallSiteType), // type map
+                typeMap,    // type map
                 getEffectiveInvalidatedProgramPoints(invalidatedProgramPoints, typeInformationFile), // invalidated program points
                 typeInformationFile,
                 continuationEntryPoints, // continuation entry points
@@ -431,7 +423,7 @@
         return loadedProgramPoints != null ? loadedProgramPoints : new TreeMap<Integer, Type>();
     }
 
-    private TypeSpecializedFunction compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
+    private FunctionInitializer compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope, final boolean persist) {
         // We're creating an empty script object for holding local variables. AssignSymbols will populate it with
         // explicit Undefined values for undefined local variables (see AssignSymbols#defineSymbol() and
         // CompilationEnvironment#declareLocalSymbol()).
@@ -440,21 +432,79 @@
             log.info("Type specialization of '", functionName, "' signature: ", actualCallSiteType);
         }
 
+        final boolean persistentCache = usePersistentCodeCache() && persist;
+        String cacheKey = null;
+        if (persistentCache) {
+            final TypeMap typeMap = typeMap(actualCallSiteType);
+            final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
+            cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
+            final StoredScript script = installer.loadScript(source, cacheKey);
+
+            if (script != null) {
+                Compiler.updateCompilationId(script.getCompilationId());
+                return install(script);
+            }
+        }
+
         final FunctionNode fn = reparse();
         final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope);
+        final FunctionNode compiledFn = compiler.compile(fn, CompilationPhases.COMPILE_ALL);
 
-        final FunctionNode compiledFn = compiler.compile(fn, CompilationPhases.COMPILE_ALL);
-        return new TypeSpecializedFunction(compiledFn, compiler.getInvalidatedProgramPoints());
+        if (persist && !compiledFn.getFlag(FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION)) {
+            compiler.persistClassInfo(cacheKey, compiledFn);
+        }
+        return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints());
     }
 
-    private static class TypeSpecializedFunction {
-        private final FunctionNode fn;
-        private final Map<Integer, Type> invalidatedProgramPoints;
 
-        TypeSpecializedFunction(final FunctionNode fn, final Map<Integer, Type> invalidatedProgramPoints) {
-            this.fn = fn;
-            this.invalidatedProgramPoints = invalidatedProgramPoints;
+    /**
+     * Install this script using the given {@code installer}.
+     *
+     * @param script the compiled script
+     * @return the function initializer
+     */
+    private FunctionInitializer install(final StoredScript script) {
+
+        final Map<String, Class<?>> installedClasses = new HashMap<>();
+        final String   mainClassName   = script.getMainClassName();
+        final byte[]   mainClassBytes  = script.getClassBytes().get(mainClassName);
+
+        final Class<?> mainClass       = installer.install(mainClassName, mainClassBytes);
+
+        installedClasses.put(mainClassName, mainClass);
+
+        for (final Map.Entry<String, byte[]> entry : script.getClassBytes().entrySet()) {
+            final String className = entry.getKey();
+            final byte[] code = entry.getValue();
+
+            if (className.equals(mainClassName)) {
+                continue;
+            }
+
+            installedClasses.put(className, installer.install(className, code));
         }
+
+        final Map<Integer, FunctionInitializer> initializers = script.getInitializers();
+        assert initializers != null;
+        assert initializers.size() == 1;
+        final FunctionInitializer initializer = initializers.values().iterator().next();
+
+        Object[] constants = script.getConstants();
+        for (int i = 0; i < constants.length; i++) {
+            if (constants[i] instanceof RecompilableScriptFunctionData) {
+                // replace deserialized function data with the ones we already have
+                constants[i] = getScriptFunctionData(((RecompilableScriptFunctionData) constants[i]).getFunctionNodeId());
+            }
+        }
+
+        installer.initialize(installedClasses.values(), source, constants);
+        initializer.setCode(installedClasses.get(initializer.getClassName()));
+        return initializer;
+    }
+
+    boolean usePersistentCodeCache() {
+        final ScriptEnvironment env = installer.getOwner();
+        return env._persistent_cache && env._optimistic_types;
     }
 
     private MethodType explicitParams(final MethodType callSiteType) {
@@ -502,61 +552,57 @@
         return f;
     }
 
+    MethodHandle lookup(final FunctionInitializer fnInit) {
+        final MethodType type = fnInit.getMethodType();
+        return lookupCodeMethod(fnInit.getCode(), type);
+    }
+
     MethodHandle lookup(final FunctionNode fn) {
         final MethodType type = new FunctionSignature(fn).getMethodType();
-        log.info("Looking up ", DebugLogger.quote(fn.getName()), " type=", type);
-        return lookupWithExplicitType(fn, new FunctionSignature(fn).getMethodType());
+        return lookupCodeMethod(fn.getCompileUnit().getCode(), type);
     }
 
-    MethodHandle lookupWithExplicitType(final FunctionNode fn, final MethodType targetType) {
-        return lookupCodeMethod(fn.getCompileUnit(), targetType);
-    }
-
-    private MethodHandle lookupCodeMethod(final CompileUnit compileUnit, final MethodType targetType) {
-        return MH.findStatic(LOOKUP, compileUnit.getCode(), functionName, targetType);
+    MethodHandle lookupCodeMethod(final Class<?> code, final MethodType targetType) {
+        log.info("Looking up ", DebugLogger.quote(name), " type=", targetType);
+        return MH.findStatic(LOOKUP, code, functionName, targetType);
     }
 
     /**
      * Initializes this function data with the eagerly generated version of the code. This method can only be invoked
      * by the compiler internals in Nashorn and is public for implementation reasons only. Attempting to invoke it
      * externally will result in an exception.
-     * @param functionNode the functionNode belonging to this data
      */
-    public void initializeCode(final FunctionNode functionNode) {
+    public void initializeCode(final FunctionInitializer initializer) {
         // Since the method is public, we double-check that we aren't invoked with an inappropriate compile unit.
-        if(!(code.isEmpty() && functionNode.getCompileUnit().isInitializing(this, functionNode))) {
-            throw new IllegalStateException(functionNode.getName() + " id=" + functionNode.getId());
+        if(!code.isEmpty()) {
+            throw new IllegalStateException(name);
         }
-        addCode(functionNode);
+        addCode(lookup(initializer), null, null, initializer.getFlags());
     }
 
-    private CompiledFunction addCode(final MethodHandle target, final Map<Integer, Type> invalidatedProgramPoints, final int fnFlags) {
-        final CompiledFunction cfn = new CompiledFunction(target, this, invalidatedProgramPoints, fnFlags);
+    private CompiledFunction addCode(final MethodHandle target, final Map<Integer, Type> invalidatedProgramPoints,
+                                     final MethodType callSiteType, final int fnFlags) {
+        final CompiledFunction cfn = new CompiledFunction(target, this, invalidatedProgramPoints, callSiteType, fnFlags);
         code.add(cfn);
         return cfn;
     }
 
-    private CompiledFunction addCode(final FunctionNode fn) {
-        return addCode(lookup(fn), null, fn.getFlags());
-    }
-
     /**
      * Add code with specific call site type. It will adapt the type of the looked up method handle to fit the call site
      * type. This is necessary because even if we request a specialization that takes an "int" parameter, we might end
      * up getting one that takes a "double" etc. because of internal function logic causes widening (e.g. assignment of
      * a wider value to the parameter variable). However, we use the method handle type for matching subsequent lookups
      * for the same specialization, so we must adapt the handle to the expected type.
-     * @param tfn the function
+     * @param fnInit the function
      * @param callSiteType the call site type
      * @return the compiled function object, with its type matching that of the call site type.
      */
-    private CompiledFunction addCode(final TypeSpecializedFunction tfn, final MethodType callSiteType) {
-        final FunctionNode fn = tfn.fn;
-        if (fn.isVarArg()) {
-            return addCode(fn);
+    private CompiledFunction addCode(final FunctionInitializer fnInit, final MethodType callSiteType) {
+        if (isVariableArity()) {
+            return addCode(lookup(fnInit), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
         }
 
-        final MethodHandle handle = lookup(fn);
+        final MethodHandle handle = lookup(fnInit);
         final MethodType fromType = handle.type();
         MethodType toType = needsCallee(fromType) ? callSiteType.changeParameterType(0, ScriptFunction.class) : callSiteType.dropParameterTypes(0, 1);
         toType = toType.changeReturnType(fromType.returnType());
@@ -581,41 +627,39 @@
             toType = toType.dropParameterTypes(fromCount, toCount);
         }
 
-        return addCode(lookup(fn).asType(toType), tfn.invalidatedProgramPoints, fn.getFlags());
+        return addCode(lookup(fnInit).asType(toType), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
     }
 
 
     @Override
-    CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
-        synchronized (code) {
-            CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
-            if (existingBest == null) {
-                existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
+    synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
+        CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
+        if (existingBest == null) {
+            existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, true), callSiteType);
+        }
+
+        assert existingBest != null;
+        //we are calling a vararg method with real args
+        boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
+
+        //if the best one is an apply to call, it has to match the callsite exactly
+        //or we need to regenerate
+        if (existingBest.isApplyToCall()) {
+            final CompiledFunction best = lookupExactApplyToCall(callSiteType);
+            if (best != null) {
+                return best;
             }
+            applyToCall = true;
+        }
 
-            assert existingBest != null;
-            //we are calling a vararg method with real args
-            boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
+        if (applyToCall) {
+            final FunctionInitializer fnInit = compileTypeSpecialization(callSiteType, runtimeScope, false);
+            if ((fnInit.getFlags() & FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION) != 0) { //did the specialization work
+                existingBest = addCode(fnInit, callSiteType);
+            }
+        }
 
-            //if the best one is an apply to call, it has to match the callsite exactly
-            //or we need to regenerate
-            if (existingBest.isApplyToCall()) {
-                final CompiledFunction best = code.lookupExactApplyToCall(callSiteType);
-                if (best != null) {
-                    return best;
-                }
-                applyToCall = true;
-            }
-
-            if (applyToCall) {
-                final TypeSpecializedFunction tfn = compileTypeSpecialization(callSiteType, runtimeScope);
-                if (tfn.fn.hasOptimisticApplyToCall()) { //did the specialization work
-                    existingBest = addCode(tfn, callSiteType);
-                }
-            }
-
-            return existingBest;
-        }
+        return existingBest;
     }
 
     @Override
@@ -638,6 +682,18 @@
     }
 
     /**
+     * Return the function node id.
+     * @return the function node id
+     */
+    public int getFunctionNodeId() {
+        return functionNodeId;
+    }
+
+    public Source getSource() {
+        return source;
+    }
+
+    /**
      * Return a script function data based on a function id, either this function if
      * the id matches or a nested function based on functionId. This goes down into
      * nested functions until all leaves are exhausted.
--- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Fri Aug 08 12:50:46 2014 +0200
@@ -35,6 +35,9 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+import java.util.LinkedList;
+import java.util.List;
+
 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
 
 
@@ -54,9 +57,10 @@
     /** Name of the function or "" for anonymous functions */
     protected final String name;
 
-    /** All versions of this function that have been generated to code */
-    // TODO: integrate it into ScriptFunctionData; there's not much reason for this to be in its own class.
-    protected transient CompiledFunctions code;
+    /**
+     * A list of code versions of a function sorted in ascending order of generic descriptors.
+     */
+    protected transient LinkedList<CompiledFunction> code = new LinkedList<>();
 
     /** Function flags */
     protected int flags;
@@ -71,7 +75,7 @@
      * multiple threads concurrently, but we still tolerate a race condition in it as all values stored into it are
      * idempotent.
      */
-    private volatile GenericInvokers genericInvokers;
+    private volatile transient GenericInvokers genericInvokers;
 
     private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
 
@@ -108,7 +112,6 @@
      */
     ScriptFunctionData(final String name, final int arity, final int flags) {
         this.name  = name;
-        this.code  = new CompiledFunctions(name);
         this.flags = flags;
         setArity(arity);
     }
@@ -297,6 +300,50 @@
         return lgenericInvokers;
     }
 
+    private static MethodType widen(final MethodType cftype) {
+        final Class<?>[] paramTypes = new Class<?>[cftype.parameterCount()];
+        for (int i = 0; i < cftype.parameterCount(); i++) {
+            paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class;
+        }
+        return MH.type(cftype.returnType(), paramTypes);
+    }
+
+    /**
+     * Used to find an apply to call version that fits this callsite.
+     * We cannot just, as in the normal matcher case, return e.g. (Object, Object, int)
+     * for (Object, Object, int, int, int) or we will destroy the semantics and get
+     * a function that, when padded with undefineds, behaves differently
+     * @param type actual call site type
+     * @return apply to call that perfectly fits this callsite or null if none found
+     */
+    CompiledFunction lookupExactApplyToCall(final MethodType type) {
+        for (final CompiledFunction cf : code) {
+            if (!cf.isApplyToCall()) {
+                continue;
+            }
+
+            final MethodType cftype = cf.type();
+            if (cftype.parameterCount() != type.parameterCount()) {
+                continue;
+            }
+
+            if (widen(cftype).equals(widen(type))) {
+                return cf;
+            }
+        }
+
+        return null;
+    }
+
+    CompiledFunction pickFunction(final MethodType callSiteType, final boolean canPickVarArg) {
+        for (final CompiledFunction candidate : code) {
+            if (candidate.matchesCallSite(callSiteType, canPickVarArg)) {
+                return candidate;
+            }
+        }
+        return null;
+    }
+
     /**
      * Returns the best function for the specified call site type.
      * @param callSiteType The call site type. Call site types are expected to have the form
@@ -307,16 +354,38 @@
      * @return the best function for the specified call site type.
      */
     CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
-        return code.best(callSiteType, isRecompilable());
+        assert callSiteType.parameterCount() >= 2 : callSiteType; // Must have at least (callee, this)
+        assert callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class) : callSiteType; // Callee must be assignable from script function
+
+        if (isRecompilable()) {
+            final CompiledFunction candidate = pickFunction(callSiteType, false);
+            if (candidate != null) {
+                return candidate;
+            }
+            return pickFunction(callSiteType, true); //try vararg last
+        }
+
+        CompiledFunction best = null;
+        for(final CompiledFunction candidate: code) {
+            if(candidate.betterThanFinal(best, callSiteType)) {
+                best = candidate;
+            }
+        }
+
+        return best;
     }
 
+
     abstract boolean isRecompilable();
 
     CompiledFunction getGeneric(final ScriptObject runtimeScope) {
         return getBest(getGenericType(), runtimeScope);
     }
 
-
+    /**
+     * Get a method type for a generic invoker.
+     * @return the method type for the generic invoker
+     */
     abstract MethodType getGenericType();
 
     /**
@@ -352,7 +421,7 @@
         // Clear the callee and this flags
         final int boundFlags = flags & ~NEEDS_CALLEE & ~USES_THIS;
 
-        final CompiledFunctions boundList = new CompiledFunctions(fn.getName());
+        final List<CompiledFunction> boundList = new LinkedList<>();
         final ScriptObject runtimeScope = fn.getScope();
         final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(runtimeScope), getGenericConstructor(runtimeScope));
         boundList.add(bind(bindTarget, fn, self, allArgs));
@@ -805,6 +874,6 @@
 
     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
-        code = new CompiledFunctions(name);
+        code = new LinkedList<>();
     }
 }
--- a/src/jdk/nashorn/internal/runtime/Source.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/Source.java	Fri Aug 08 12:50:46 2014 +0200
@@ -87,6 +87,9 @@
     /** Base64-encoded SHA1 digest of this source object */
     private volatile byte[] digest;
 
+    /** source URL set via //@ sourceURL or //# sourceURL directive */
+    private String explicitURL;
+
     // Do *not* make this public, ever! Trusts the URL and content.
     private Source(final String name, final String base, final Data data) {
         this.name = name;
@@ -597,6 +600,22 @@
     }
 
     /**
+     * Get explicit source URL.
+     * @return URL set vial sourceURL directive
+     */
+    public String getExplicitURL() {
+        return explicitURL;
+    }
+
+    /**
+     * Set explicit source URL.
+     * @param explicitURL URL set via sourceURL directive
+     */
+    public void setExplicitURL(String explicitURL) {
+        this.explicitURL = explicitURL;
+    }
+
+    /**
      * Returns whether this source was submitted via 'eval' call or not.
      *
      * @return true if this source represents code submitted via 'eval'
--- a/src/jdk/nashorn/internal/runtime/SpillProperty.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/SpillProperty.java	Fri Aug 08 12:50:46 2014 +0200
@@ -211,4 +211,12 @@
         return 1;
     }
 
+    @Override
+    void initMethodHandles(Class<?> structure) {
+        final int slot  = getSlot();
+        primitiveGetter = primitiveGetter(slot);
+        primitiveSetter = primitiveSetter(slot);
+        objectGetter    = objectGetter(slot);
+        objectSetter    = objectSetter(slot);
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/StoredScript.java	Fri Aug 08 12:50:46 2014 +0200
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * Class representing a persistent compiled script.
+ */
+public final class StoredScript implements Serializable {
+
+    /** Compilation id */
+    private final int compilationId;
+
+    /** Main class name. */
+    private final String mainClassName;
+
+    /** Map of class names to class bytes. */
+    private final Map<String, byte[]> classBytes;
+
+    /** Constants array. */
+    private final Object[] constants;
+
+    /** Function initializers */
+    private final Map<Integer, FunctionInitializer> initializers;
+
+    private static final long serialVersionUID = 2958227232195298340L;
+
+    /**
+     * Constructor.
+     *
+     * @param mainClassName main class name
+     * @param classBytes map of class names to class bytes
+     * @param constants constants array
+     */
+    public StoredScript(final int compilationId, final String mainClassName, final Map<String, byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers, final Object[] constants) {
+        this.compilationId = compilationId;
+        this.mainClassName = mainClassName;
+        this.classBytes = classBytes;
+        this.constants = constants;
+        this.initializers = initializers;
+    }
+
+    public int getCompilationId() {
+        return compilationId;
+    }
+
+    /**
+     * Returns the main class name.
+     * @return the main class name
+     */
+    public String getMainClassName() {
+        return mainClassName;
+    }
+
+    /**
+     * Returns a map of class names to class bytes.
+     * @return map of class bytes
+     */
+    public Map<String, byte[]> getClassBytes() {
+        return classBytes;
+    }
+
+    /**
+     * Returns the constants array.
+     * @return constants array
+     */
+    public Object[] getConstants() {
+        return constants;
+    }
+
+    Map<Integer, FunctionInitializer> getInitializers() {
+        return initializers;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = mainClassName.hashCode();
+        hash = 31 * hash + classBytes.hashCode();
+        hash = 31 * hash + Arrays.hashCode(constants);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof StoredScript)) {
+            return false;
+        }
+
+        final StoredScript cs = (StoredScript) obj;
+        return mainClassName.equals(cs.mainClassName)
+                && classBytes.equals(cs.classBytes)
+                && Arrays.equals(constants, cs.constants);
+    }
+}
--- a/src/jdk/nashorn/tools/Shell.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/src/jdk/nashorn/tools/Shell.java	Fri Aug 08 12:50:46 2014 +0200
@@ -266,7 +266,6 @@
                        env,
                        null,
                        functionNode.getSource(),
-                       functionNode.getSourceURL(),
                        env._strict | functionNode.isStrict()).
                        compile(functionNode, CompilationPhases.COMPILE_ALL_NO_INSTALL);
             }
--- a/test/script/trusted/JDK-8006529.js	Thu Aug 07 19:19:46 2014 +0530
+++ b/test/script/trusted/JDK-8006529.js	Fri Aug 08 12:50:46 2014 +0200
@@ -120,7 +120,7 @@
 
 var sourceForMethod = Source.class.getMethod("sourceFor", java.lang.String.class, java.lang.String.class)
 var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class)
-var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, String.class, boolean.class);
+var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, boolean.class);
 
 // compile(script) -- compiles a script specified as a string with its
 // source code, returns a jdk.nashorn.internal.ir.FunctionNode object
@@ -134,7 +134,7 @@
     var parser   = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance());
     var func     = parseMethod.invoke(parser);
 
-    var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, null, false);
+    var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, false);
 
     return compileMethod.invoke(compiler, func, phases);
 };
--- a/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java	Thu Aug 07 19:19:46 2014 +0530
+++ b/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java	Fri Aug 08 12:50:46 2014 +0200
@@ -96,7 +96,7 @@
     final String codeCache = "build/nashorn_code_cache";
     final String oldUserDir = System.getProperty("user.dir");
 
-    private static final String[] ENGINE_OPTIONS = new String[]{"--persistent-code-cache"};
+    private static final String[] ENGINE_OPTIONS = new String[]{"--persistent-code-cache", "--optimistic-types=false", "--lazy-compilation=false"};
 
     public void checkCompiledScripts(final DirectoryStream<Path> stream, int numberOfScripts) throws IOException {
         for (final Path file : stream) {