Merge
authorlana
Tue, 01 Jul 2014 14:27:28 -0700
changeset 25251 eb2479579f1e
parent 25031 28dd0c7beb3c (current diff)
parent 25250 b5a4e0ac31d1 (diff)
child 25252 e8bfc909db53
Merge
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/bin/jjsdebug.sh	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 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.
+# 
+# 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.
+#
+
+$JAVA_HOME/bin/jjs -J-Djava.ext.dirs=`dirname $0`/../dist -J-agentlib:jdwp=transport=dt_socket,address=localhost:9009,server=y,suspend=y $*
--- a/nashorn/make/build.xml	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/make/build.xml	Tue Jul 01 14:27:28 2014 -0700
@@ -125,8 +125,7 @@
            encoding="${javac.encoding}"
            includeantruntime="false" fork="true">
       <compilerarg value="-J-Djava.ext.dirs="/>
-      <compilerarg value="-Xlint:unchecked"/>
-      <compilerarg value="-Xlint:deprecation"/>
+      <compilerarg value="-Xlint:all"/>
       <compilerarg value="-XDignore.symbol.file"/>
       <compilerarg value="-Xdiags:verbose"/>
     </javac>
--- a/nashorn/make/nbproject/ide-targets.xml	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/make/nbproject/ide-targets.xml	Tue Jul 01 14:27:28 2014 -0700
@@ -34,7 +34,7 @@
 	    <jvmarg line="-Dnashorn.optimistic"/>
             <jvmarg line="${ext.class.path}"/> 
             <jvmarg line="${run.test.jvmargs}"/>
-            <arg value="../make/str.js"/>
+            <arg value="../samples/test.js"/>
             <jvmarg value="-Xdebug"/>
             <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
         </java>
--- a/nashorn/make/project.properties	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/make/project.properties	Tue Jul 01 14:27:28 2014 -0700
@@ -279,6 +279,7 @@
   -Dfile.encoding=UTF-8 \
   -Duser.language=${run.test.user.language} \
   -Duser.country=${run.test.user.country} \
+  -Dnashorn.typeInfo.cacheDir=${build.dir}${file.separator}test${file.separator}type_info_cache \
   ${jfr.args} \
   -XX:+HeapDumpOnOutOfMemoryError
 
--- a/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java	Tue Jul 01 14:27:28 2014 -0700
@@ -152,7 +152,7 @@
 
     @SuppressWarnings("unused")
     private MethodHandle selectMethod(final Object[] args) throws NoSuchMethodException {
-        final Class<?>[] argTypes = new Class[args.length];
+        final Class<?>[] argTypes = new Class<?>[args.length];
         for(int i = 0; i < argTypes.length; ++i) {
             final Object arg = args[i];
             argTypes[i] = arg == null ? ClassString.NULL_CLASS : arg.getClass();
--- a/nashorn/src/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java	Tue Jul 01 14:27:28 2014 -0700
@@ -111,7 +111,7 @@
         private final TypeBasedGuardingDynamicLinker[] linkers;
         private final List<TypeBasedGuardingDynamicLinker>[] singletonLinkers;
 
-        @SuppressWarnings("unchecked")
+        @SuppressWarnings(value={"unchecked", "rawtypes"})
         ClassToLinker(final TypeBasedGuardingDynamicLinker[] linkers) {
             this.linkers = linkers;
             singletonLinkers = new List[linkers.length];
@@ -120,6 +120,7 @@
             }
         }
 
+        @SuppressWarnings("fallthrough")
         @Override
         protected List<TypeBasedGuardingDynamicLinker> computeValue(final Class<?> clazz) {
             List<TypeBasedGuardingDynamicLinker> list = NO_LINKER;
--- a/nashorn/src/jdk/nashorn/internal/codegen/AssignSymbols.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/AssignSymbols.java	Tue Jul 01 14:27:28 2014 -0700
@@ -209,7 +209,7 @@
                     if (varNode.isFunctionDeclaration()) {
                         symbol.setIsFunctionDeclaration();
                     }
-                    return varNode.setName((IdentNode)ident.setSymbol(symbol));
+                    return varNode.setName(ident.setSymbol(symbol));
                 }
                 return varNode;
             }
@@ -217,7 +217,7 @@
     }
 
     private IdentNode compilerConstantIdentifier(final CompilerConstants cc) {
-        return (IdentNode)createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
+        return createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
     }
 
     /**
@@ -263,7 +263,7 @@
         final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
         assert nameSymbol != null;
 
-        return (VarNode)synthVar.setName((IdentNode)name.setSymbol(nameSymbol)).accept(this);
+        return (VarNode)synthVar.setName(name.setSymbol(nameSymbol)).accept(this);
     }
 
     private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) {
@@ -522,7 +522,7 @@
             final Symbol paramSymbol = body.getExistingSymbol(param.getName());
             assert paramSymbol != null;
             assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
-            newParams.add((IdentNode)param.setSymbol(paramSymbol));
+            newParams.add(param.setSymbol(paramSymbol));
 
             // parameters should not be slots for a function that uses variable arity signature
             if (isVarArg) {
@@ -702,7 +702,7 @@
             // If this is a declared variable or a function parameter, delete always fails (except for globals).
             final String name = ident.getName();
             final Symbol symbol = ident.getSymbol();
-            final boolean failDelete = strictMode || symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel());
+            final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel())));
 
             if (failDelete && symbol.isThis()) {
                 return LiteralNode.newInstance(unaryNode, true).accept(this);
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Jul 01 14:27:28 2014 -0700
@@ -145,6 +145,7 @@
 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 import jdk.nashorn.internal.runtime.RewriteException;
 import jdk.nashorn.internal.runtime.Scope;
+import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -210,6 +211,9 @@
      *  by reflection in class installation */
     private final Compiler compiler;
 
+    /** Is the current code submitted by 'eval' call? */
+    private final boolean evalCode;
+
     /** Call site flags given to the code generator to be used for all generated call sites */
     private final int callSiteFlags;
 
@@ -264,6 +268,7 @@
     CodeGenerator(final Compiler compiler, final int[] continuationEntryPoints) {
         super(new CodeGeneratorLexicalContext());
         this.compiler                = compiler;
+        this.evalCode                = compiler.getSource().isEvalCode();
         this.continuationEntryPoints = continuationEntryPoints;
         this.callSiteFlags           = compiler.getScriptEnvironment()._callsite_flags;
         this.log                     = initLogger(compiler.getContext());
@@ -290,6 +295,14 @@
     }
 
     /**
+     * Are we generating code for 'eval' code?
+     * @return true if currently compiled code is 'eval' code.
+     */
+    boolean isEvalCode() {
+        return evalCode;
+    }
+
+    /**
      * Load an identity node
      *
      * @param identNode an identity node to load
@@ -1084,7 +1097,7 @@
 
         closeBlockVariables(block);
         lc.releaseSlots();
-        assert !method.isReachable() || lc.getUsedSlotCount() == method.getFirstTemp();
+        assert !method.isReachable() || (lc.isFunctionBody() ? 0 : lc.getUsedSlotCount()) == method.getFirstTemp();
 
         return block;
     }
@@ -1277,13 +1290,26 @@
                     int argsCount;
                     @Override
                     void loadStack() {
-                        loadExpressionAsObject(ident); // Type.OBJECT as foo() makes no sense if foo == 3
-                        method.dup();
+                        /**
+                         * We want to load 'eval' to check if it is indeed global builtin eval.
+                         * If this eval call is inside a 'with' statement, dyn:getMethod|getProp|getElem
+                         * would be generated if ident is a "isFunction". But, that would result in a
+                         * bound function from WithObject. We don't want that as bound function as that
+                         * won't be detected as builtin eval. So, we make ident as "not a function" which
+                         * results in "dyn:getProp|getElem|getMethod" being generated and so WithObject
+                         * would return unbounded eval function.
+                         *
+                         * Example:
+                         *
+                         *  var global = this;
+                         *  function func() {
+                         *      with({ eval: global.eval) { eval("var x = 10;") }
+                         *  }
+                         */
+                        loadExpressionAsObject(ident.setIsNotFunction()); // Type.OBJECT as foo() makes no sense if foo == 3
                         globalIsEval();
                         method.ifeq(is_not_eval);
 
-                        // We don't need ScriptFunction object for 'eval'
-                        method.pop();
                         // Load up self (scope).
                         method.loadCompilerConstant(SCOPE);
                         final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
@@ -1303,6 +1329,8 @@
                         method._goto(invoke_direct_eval);
 
                         method.label(is_not_eval);
+                        // load this time but with dyn:getMethod|getProp|getElem
+                        loadExpressionAsObject(ident); // Type.OBJECT as foo() makes no sense if foo == 3
                         // This is some scope 'eval' or global eval replaced by user
                         // but not the built-in ECMAScript 'eval' function call
                         method.loadNull();
@@ -1821,19 +1849,40 @@
         method.storeCompilerConstant(ARGUMENTS);
     }
 
-    /**
-     * Should this code generator skip generating code for inner functions? If lazy compilation is on, or we're
-     * doing an on-demand ("just-in-time") compilation, then we aren't generating code for inner functions.
-     */
-    private boolean compileOutermostOnly() {
-        return compiler.isOnDemandCompilation() || compiler.getScriptEnvironment()._lazy_compilation;
+    private boolean skipFunction(final FunctionNode functionNode) {
+        final ScriptEnvironment env = compiler.getScriptEnvironment();
+        final boolean lazy = env._lazy_compilation;
+        final boolean onDemand = compiler.isOnDemandCompilation();
+
+        // If this is on-demand or lazy compilation, don't compile a nested (not topmost) function.
+        if((onDemand || lazy) && lc.getOutermostFunction() != functionNode) {
+            return true;
+        }
+
+        // If lazy compiling with optimistic types, don't compile the program eagerly either. It will soon be
+        // invalidated anyway. In presence of a class cache, this further means that an obsoleted program version
+        // 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
+        // by just running it up to scope depth calculation, which creates the RSFDs and then this limited codegen).
+        // 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;
     }
 
     @Override
     public boolean enterFunctionNode(final FunctionNode functionNode) {
         final int fnId = functionNode.getId();
 
-        if (compileOutermostOnly() && lc.getOutermostFunction() != functionNode) {
+        if (skipFunction(functionNode)) {
             // In case we are not generating code for the function, we must create or retrieve the function object and
             // load it on the stack here.
             newFunctionObject(functionNode, false);
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Tue Jul 01 14:27:28 2014 -0700
@@ -173,7 +173,18 @@
         @Override
         FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
             final CompileUnit  outermostCompileUnit = compiler.addCompileUnit(0L);
-            final FunctionNode newFunctionNode      = new Splitter(compiler, fn, outermostCompileUnit).split(fn, true);
+
+            FunctionNode newFunctionNode;
+
+            //ensure elementTypes, postsets and presets exist for splitter and arraynodes
+            newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+                @Override
+                public LiteralNode<?> leaveLiteralNode(final LiteralNode<?> literalNode) {
+                    return literalNode.initialize(lc);
+                }
+            });
+
+            newFunctionNode = new Splitter(compiler, newFunctionNode, outermostCompileUnit).split(newFunctionNode, true);
 
             assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
             assert newFunctionNode.isStrict() == compiler.isStrict() : "functionNode.isStrict() != compiler.isStrict() for " + quote(newFunctionNode.getName());
@@ -374,7 +385,7 @@
                             assert newUnit != null;
                             newArrayUnits.add(new ArrayUnit(newUnit, au.getLo(), au.getHi()));
                         }
-                        aln.setUnits(newArrayUnits);
+                        return aln.setUnits(lc, newArrayUnits);
                     }
                     return node;
                 }
@@ -421,7 +432,9 @@
             compiler.getLogger().fine("Starting bytecode generation for ", quote(fn.getName()), " - restOf=", phases.isRestOfCompilation());
             final CodeGenerator codegen = new CodeGenerator(compiler, phases.isRestOfCompilation() ? compiler.getContinuationEntryPoints() : null);
             try {
-                newFunctionNode = (FunctionNode)newFunctionNode.accept(codegen);
+                // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program
+                // in the lazy + optimistic world. See CodeGenerator.skipFunction().
+                newFunctionNode = ((FunctionNode)newFunctionNode.accept(codegen)).setState(null, BYTECODE_GENERATED);
                 codegen.generateScopeCalls();
             } catch (final VerifyError e) {
                 if (senv._verify_code || senv._print_code) {
@@ -489,7 +502,7 @@
             Class<?> rootClass = null;
             long length = 0L;
 
-            final CodeInstaller<?> codeInstaller = compiler.getCodeInstaller();
+            final CodeInstaller<ScriptEnvironment> codeInstaller = compiler.getCodeInstaller();
 
             final Map<String, byte[]> bytecode = compiler.getBytecode();
 
@@ -514,12 +527,10 @@
             final Object[] constants = compiler.getConstantData().toArray();
             codeInstaller.initialize(installedClasses.values(), compiler.getSource(), constants);
 
-            // index recompilable script function datas in the constant pool
-            final Map<RecompilableScriptFunctionData, RecompilableScriptFunctionData> rfns = new IdentityHashMap<>();
+            // initialize transient fields on recompilable script function data
             for (final Object constant: constants) {
                 if (constant instanceof RecompilableScriptFunctionData) {
-                    final RecompilableScriptFunctionData rfn = (RecompilableScriptFunctionData)constant;
-                    rfns.put(rfn, rfn);
+                    ((RecompilableScriptFunctionData)constant).initTransients(compiler.getSource(), codeInstaller);
                 }
             }
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java	Tue Jul 01 14:27:28 2014 -0700
@@ -46,10 +46,10 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 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;
@@ -123,6 +123,11 @@
     private final Map<Integer, Type> invalidatedProgramPoints;
 
     /**
+     * Descriptor of the location where we write the type information after compilation.
+     */
+    private final Object typeInformationFile;
+
+    /**
      * Compile unit name of first compile unit - this prefix will be used for all
      * classes that a compilation generates.
      */
@@ -317,7 +322,7 @@
             final Source source,
             final String sourceURL,
             final boolean isStrict) {
-        this(context, env, installer, source, sourceURL, isStrict, false, null, null, null, null, null);
+        this(context, env, installer, source, sourceURL, isStrict, false, null, null, null, null, null, null);
     }
 
     /**
@@ -333,6 +338,7 @@
      * @param compiledFunction         compiled function, if any
      * @param types                    parameter and return value type information, if any is known
      * @param invalidatedProgramPoints invalidated program points for recompilation
+     * @param typeInformationFile      descriptor of the location where type information is persisted
      * @param continuationEntryPoints  continuation entry points for restof method
      * @param runtimeScope             runtime scope for recompilation type lookup in {@code TypeEvaluator}
      */
@@ -347,6 +353,7 @@
             final RecompilableScriptFunctionData compiledFunction,
             final TypeMap types,
             final Map<Integer, Type> invalidatedProgramPoints,
+            final Object typeInformationFile,
             final int[] continuationEntryPoints,
             final ScriptObject runtimeScope) {
         this.context                  = context;
@@ -363,6 +370,7 @@
         this.compiledFunction         = compiledFunction;
         this.types                    = types;
         this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
+        this.typeInformationFile      = typeInformationFile;
         this.continuationEntryPoints  = continuationEntryPoints == null ? null: continuationEntryPoints.clone();
         this.typeEvaluator            = new TypeEvaluator(this, runtimeScope);
         this.firstCompileUnitName     = firstCompileUnitName();
@@ -457,6 +465,16 @@
         invalidatedProgramPoints.put(programPoint, type);
     }
 
+
+    /**
+     * Returns a copy of this compiler's current mapping of invalidated optimistic program points to their types. The
+     * copy is not live with regard to changes in state in this compiler instance, and is mutable.
+     * @return a copy of this compiler's current mapping of invalidated optimistic program points to their types.
+     */
+    public Map<Integer, Type> getInvalidatedProgramPoints() {
+        return invalidatedProgramPoints == null ? null : new TreeMap<>(invalidatedProgramPoints);
+    }
+
     TypeMap getTypeMap() {
         return types;
     }
@@ -513,6 +531,10 @@
             time += (env.isTimingEnabled() ? phase.getEndTime() - phase.getStartTime() : 0L);
         }
 
+        if(typeInformationFile != null && !phases.isRestOfCompilation()) {
+            OptimisticTypesPersistence.store(typeInformationFile, invalidatedProgramPoints);
+        }
+
         log.unindent();
 
         if (info) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java	Tue Jul 01 14:27:28 2014 -0700
@@ -62,6 +62,8 @@
 
     /** call site flags to be used for invocations */
     private final int callSiteFlags;
+    /** are we creating this field object from 'eval' code? */
+    private final boolean evalCode;
 
     /**
      * Constructor
@@ -88,7 +90,7 @@
     FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples, final boolean isScope, final boolean hasArguments) {
         super(codegen, tuples, isScope, hasArguments);
         this.callSiteFlags = codegen.getCallSiteFlags();
-
+        this.evalCode = codegen.isEvalCode();
         countFields();
         findClass();
     }
@@ -153,7 +155,7 @@
     @Override
     protected PropertyMap makeMap() {
         assert propertyMap == null : "property map already initialized";
-        propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount);
+        propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount, evalCode);
         return propertyMap;
     }
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Tue Jul 01 14:27:28 2014 -0700
@@ -141,7 +141,7 @@
             paramTypeList.add(paramType.getTypeClass());
         }
 
-        this.methodType = MH.type(returnType.getTypeClass(), paramTypeList.toArray(new Class[paramTypes.length]));
+        this.methodType = MH.type(returnType.getTypeClass(), paramTypeList.toArray(new Class<?>[paramTypes.length]));
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java	Tue Jul 01 14:27:28 2014 -0700
@@ -25,10 +25,12 @@
 
 package jdk.nashorn.internal.codegen;
 
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
 import static jdk.nashorn.internal.ir.Expression.isAlwaysFalse;
 import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
 
 import java.util.ArrayDeque;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Deque;
 import java.util.HashSet;
@@ -63,7 +65,6 @@
 import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.LexicalContextNode;
 import jdk.nashorn.internal.ir.LiteralNode;
-import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
 import jdk.nashorn.internal.ir.LocalVariableConversion;
 import jdk.nashorn.internal.ir.LoopNode;
 import jdk.nashorn.internal.ir.Node;
@@ -72,6 +73,7 @@
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.RuntimeNode.Request;
 import jdk.nashorn.internal.ir.SplitNode;
+import jdk.nashorn.internal.ir.Statement;
 import jdk.nashorn.internal.ir.SwitchNode;
 import jdk.nashorn.internal.ir.Symbol;
 import jdk.nashorn.internal.ir.TernaryNode;
@@ -356,6 +358,8 @@
     private boolean reachable = true;
     // Return type of the function
     private Type returnType = Type.UNKNOWN;
+    // Synthetic return node that we must insert at the end of the function if it's end is reachable.
+    private ReturnNode syntheticReturn;
 
     // Topmost current split node (if any)
     private SplitNode topSplit;
@@ -583,7 +587,11 @@
             }
         }
         setCompilerConstantAsObject(functionNode, CompilerConstants.THIS);
-        if(functionNode.needsParentScope()) {
+
+        // TODO: coarse-grained. If we wanted to solve it completely precisely,
+        // we'd also need to push/pop its type when handling WithNode (so that
+        // it can go back to undefined after a 'with' block.
+        if(functionNode.hasScopeBlock() || functionNode.needsParentScope()) {
             setCompilerConstantAsObject(functionNode, CompilerConstants.SCOPE);
         }
         if(functionNode.needsCallee()) {
@@ -841,6 +849,10 @@
 
     @Override
     public boolean enterThrowNode(final ThrowNode throwNode) {
+        if(!reachable) {
+            return false;
+        }
+
         throwNode.getExpression().accept(this);
         jumpToCatchBlock(throwNode);
         doesNotContinueSequentially();
@@ -1027,6 +1039,15 @@
     @Override
     public Node leaveBlock(final Block block) {
         if(lc.isFunctionBody()) {
+            if(reachable) {
+                // reachable==true means we can reach the end of the function without an explicit return statement. We
+                // need to insert a synthetic one then. This logic used to be in Lower.leaveBlock(), but Lower's
+                // reachability analysis (through Terminal.isTerminal() flags) is not precise enough so
+                // Lower$BlockLexicalContext.afterSetStatements will sometimes think the control flow terminates even
+                // when it didn't. Example: function() { switch((z)) { default: {break; } throw x; } }.
+                createSyntheticReturn(block);
+                assert !reachable;
+            }
             // We must calculate the return type here (and not in leaveFunctionNode) as it can affect the liveness of
             // the :return symbol and thus affect conversion type liveness calculations for it.
             calculateReturnType();
@@ -1085,6 +1106,23 @@
             retSymbol.setNeedsSlot(true);
         }
     }
+
+    private void createSyntheticReturn(final Block body) {
+        final FunctionNode functionNode = lc.getCurrentFunction();
+        final long token = functionNode.getToken();
+        final int finish = functionNode.getFinish();
+        final List<Statement> statements = body.getStatements();
+        final int lineNumber = statements.isEmpty() ? functionNode.getLineNumber() : statements.get(statements.size() - 1).getLineNumber();
+        final IdentNode returnExpr;
+        if(functionNode.isProgram()) {
+            returnExpr = new IdentNode(token, finish, RETURN.symbolName()).setSymbol(getCompilerConstantSymbol(functionNode, RETURN));
+        } else {
+            returnExpr = null;
+        }
+        syntheticReturn = new ReturnNode(lineNumber, token, finish, returnExpr);
+        syntheticReturn.accept(this);
+    }
+
     /**
      * Leave a breakable node. If there's a join point associated with its break label (meaning there was at least one
      * break statement to the end of the node), insert the join point into the flow.
@@ -1158,7 +1196,9 @@
                 } else if(binaryNode.isOptimisticUndecidedType()) {
                     // At this point, we can assign a static type to the optimistic binary ADD operator as now we know
                     // the types of its operands.
-                    return binaryNode.setType(Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()));
+                    final Type type = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
+                    // Use Type.CHARSEQUENCE instead of Type.STRING to avoid conversion of ConsStrings to Strings.
+                    return binaryNode.setType(type.equals(Type.STRING) ? Type.CHARSEQUENCE : type);
                 }
                 return binaryNode;
             }
@@ -1174,6 +1214,16 @@
             }
 
             @Override
+            public Node leaveBlock(final Block block) {
+                if(inOuterFunction && syntheticReturn != null && lc.isFunctionBody()) {
+                    final ArrayList<Statement> stmts = new ArrayList<>(block.getStatements());
+                    stmts.add((ReturnNode)syntheticReturn.accept(this));
+                    return block.setStatements(lc, stmts);
+                }
+                return super.leaveBlock(block);
+            }
+
+            @Override
             public Node leaveFunctionNode(final FunctionNode nestedFunctionNode) {
                 inOuterFunction = true;
                 final FunctionNode newNestedFunction = (FunctionNode)nestedFunctionNode.accept(
@@ -1207,10 +1257,10 @@
 
             @Override
             public Node leaveLiteralNode(final LiteralNode<?> literalNode) {
-                if(literalNode instanceof ArrayLiteralNode) {
-                    ((ArrayLiteralNode)literalNode).analyze();
-                }
-                return literalNode;
+                //for e.g. ArrayLiteralNodes the initial types may have been narrowed due to the
+                //introduction of optimistic behavior - hence ensure that all literal nodes are
+                //reinitialized
+                return literalNode.initialize(lc);
             }
 
             @Override
--- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java	Tue Jul 01 14:27:28 2014 -0700
@@ -75,7 +75,6 @@
 import jdk.nashorn.internal.runtime.CodeInstaller;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.logging.DebugLogger;
 import jdk.nashorn.internal.runtime.logging.Loggable;
@@ -160,30 +159,6 @@
     }
 
     @Override
-    public Node leaveBlock(final Block block) {
-        //now we have committed the entire statement list to the block, but we need to truncate
-        //whatever is after the last terminal. block append won't append past it
-
-
-        if (lc.isFunctionBody()) {
-            final FunctionNode currentFunction = lc.getCurrentFunction();
-            final boolean isProgram = currentFunction.isProgram();
-            final Statement last = lc.getLastStatement();
-            final ReturnNode returnNode = new ReturnNode(
-                last == null ? currentFunction.getLineNumber() : last.getLineNumber(), //TODO?
-                currentFunction.getToken(),
-                currentFunction.getFinish(),
-                isProgram ?
-                    compilerConstant(RETURN) :
-                    LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED));
-
-            returnNode.accept(this);
-        }
-
-        return block;
-    }
-
-    @Override
     public boolean enterBreakNode(final BreakNode breakNode) {
         addStatement(breakNode);
         return false;
--- a/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java	Tue Jul 01 14:27:28 2014 -0700
@@ -63,13 +63,13 @@
     /**
      * Constructs a property map based on a set of fields.
      *
-     * @param hasArguments does the created object have an "arguments" property
+     * @param hasArguments  does the created object have an "arguments" property
      * @param fieldCount    Number of fields in use.
-     * @param fieldMaximum Number of fields available.
-     *
+     * @param fieldMaximum  Number of fields available.
+     * @param evalCode      is this property map created for 'eval' code?
      * @return New map populated with accessor properties.
      */
-    PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) {
+    PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum, final boolean evalCode) {
         final List<Property> properties = new ArrayList<>();
         assert tuples != null;
 
@@ -79,7 +79,7 @@
             final Class<?> initialType = tuple.getValueType();
 
             if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
-                final int      flags    = getPropertyFlags(symbol, hasArguments);
+                final int      flags    = getPropertyFlags(symbol, hasArguments, evalCode);
                 final Property property = new AccessorProperty(
                         key,
                         flags,
@@ -104,7 +104,7 @@
 
             //TODO initial type is object here no matter what. Is that right?
             if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
-                final int flags = getPropertyFlags(symbol, hasArguments);
+                final int flags = getPropertyFlags(symbol, hasArguments, false);
                 properties.add(
                         new SpillProperty(
                                 key,
@@ -124,7 +124,7 @@
      *
      * @return flags to use for fields
      */
-    static int getPropertyFlags(final Symbol symbol, final boolean hasArguments) {
+    static int getPropertyFlags(final Symbol symbol, final boolean hasArguments, final boolean evalCode) {
         int flags = 0;
 
         if (symbol.isParam()) {
@@ -135,7 +135,13 @@
             flags |= Property.HAS_ARGUMENTS;
         }
 
-        if (symbol.isScope()) {
+        // See ECMA 5.1 10.5 Declaration Binding Instantiation.
+        // Step 2  If code is eval code, then let configurableBindings
+        // be true else let configurableBindings be false.
+        // We have to make vars, functions declared in 'eval' code
+        // configurable. But vars, functions from any other code is
+        // not configurable.
+        if (symbol.isScope() && !evalCode) {
             flags |= Property.NOT_CONFIGURABLE;
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,345 @@
+/*
+ * 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.codegen;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.MessageDigest;
+import java.security.PrivilegedAction;
+import java.sql.Date;
+import java.text.SimpleDateFormat;
+import java.util.Base64;
+import java.util.Map;
+import java.util.TreeMap;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+import jdk.nashorn.internal.runtime.Source;
+import jdk.nashorn.internal.runtime.logging.DebugLogger;
+import jdk.nashorn.internal.runtime.options.Options;
+
+/**
+ * Static utility that encapsulates persistence of decompilation information for functions. Normally, the type info
+ * persistence feature is enabled and operates in an operating-system specific per-user cache directory. You can
+ * override the directory by specifying it in the {@code nashorn.typeInfo.cacheDir} directory. Also, you can disable the
+ * type info persistence altogether by specifying the {@code nashorn.typeInfo.disabled} system property.
+ */
+public final class OptimisticTypesPersistence {
+    private static final File cacheDir = createCacheDir();
+    // In-process locks to make sure we don't have a cross-thread race condition manipulating any file.
+    private static final Object[] locks = cacheDir == null ? null : createLockArray();
+
+    // Only report one read/write error every minute
+    private static final long ERROR_REPORT_THRESHOLD = 60000L;
+
+    private static volatile long lastReportedError;
+
+    /**
+     * Retrieves an opaque descriptor for the persistence location for a given function. It should be passed to
+     * {@link #load(Object)} and {@link #store(Object, Map)} methods.
+     * @param source the source where the function comes from
+     * @param functionId the unique ID number of the function within the source
+     * @param paramTypes the types of the function parameters (as persistence is per parameter type specialization).
+     * @return an opaque descriptor for the persistence location. Can be null if persistence is disabled.
+     */
+    public static Object getLocationDescriptor(final Source source, final int functionId, final Type[] paramTypes) {
+        if(cacheDir == null) {
+            return null;
+        }
+        final StringBuilder b = new StringBuilder(48);
+        // Base64-encode the digest of the source, and append the function id.
+        b.append(Base64.getUrlEncoder().encodeToString(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.
+        if(paramTypes != null && paramTypes.length > 0) {
+            b.append('-');
+            for(final Type t: paramTypes) {
+                b.append(t.getBytecodeStackType());
+            }
+        }
+        return new LocationDescriptor(new File(cacheDir, b.toString()));
+    }
+
+    private static final class LocationDescriptor {
+        private final File file;
+
+        LocationDescriptor(final File file) {
+            this.file = file;
+        }
+    }
+
+
+    /**
+     * Stores the map of optimistic types for a given function.
+     * @param locationDescriptor the opaque persistence location descriptor, retrieved by calling
+     * {@link #getLocationDescriptor(Source, int, Type[])}.
+     * @param optimisticTypes the map of optimistic types.
+     */
+    @SuppressWarnings("resource")
+    public static void store(final Object locationDescriptor, final Map<Integer, Type> optimisticTypes) {
+        if(locationDescriptor == null) {
+            return;
+        }
+        final File file = ((LocationDescriptor)locationDescriptor).file;
+        AccessController.doPrivileged(new PrivilegedAction<Void>() {
+            @Override
+            public Void run() {
+                synchronized(getFileLock(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(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);
+                        }
+                        dout.flush();
+                    } catch(final Exception e) {
+                        reportError("write", file, e);
+                    }
+                }
+                return null;
+            }
+        });
+    }
+
+    /**
+     * Loads the map of optimistic types for a given function.
+     * @param locationDescriptor the opaque persistence location descriptor, retrieved by calling
+     * {@link #getLocationDescriptor(Source, int, Type[])}.
+     * @return the map of optimistic types, or null if persisted type information could not be retrieved.
+     */
+    @SuppressWarnings("resource")
+    public static Map<Integer, Type> load(final Object locationDescriptor) {
+        if (locationDescriptor == null) {
+            return null;
+        }
+        final File file = ((LocationDescriptor)locationDescriptor).file;
+
+        return AccessController.doPrivileged(new PrivilegedAction<Map<Integer, Type>>() {
+            @Override
+            public Map<Integer, Type> run() {
+                try {
+                    if(!file.isFile()) {
+                        return null;
+                    }
+                    synchronized(getFileLock(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;
+                        }
+                    }
+                } catch (final Exception e) {
+                    reportError("read", file, e);
+                    return null;
+                }
+            }
+        });
+    }
+
+    private static void reportError(final String msg, final File file, final Exception e) {
+        final long now = System.currentTimeMillis();
+        if(now - lastReportedError > ERROR_REPORT_THRESHOLD) {
+            getLogger().warning(String.format("Failed to %s %s", msg, file), e);
+            lastReportedError = now;
+        }
+    }
+
+    private static File createCacheDir() {
+        if(Options.getBooleanProperty("nashorn.typeInfo.disabled")) {
+            return null;
+        }
+        try {
+            return createCacheDirPrivileged();
+        } catch(final Exception e) {
+            getLogger().warning("Failed to create cache dir", e);
+            return null;
+        }
+    }
+
+    private static File createCacheDirPrivileged() {
+        return AccessController.doPrivileged(new PrivilegedAction<File>() {
+            @Override
+            public File run() {
+                final String explicitDir = System.getProperty("nashorn.typeInfo.cacheDir");
+                final File dir;
+                if(explicitDir != null) {
+                    dir = new File(explicitDir);
+                } else {
+                    // When no directory is explicitly specified, get an operating system specific cache directory,
+                    // and create "com.oracle.java.NashornTypeInfo" in it.
+                    dir = new File(getCacheDirBase(), "com.oracle.java.NashornTypeInfo");
+                }
+                final String versionDirName;
+                try {
+                    versionDirName = getVersionDirName();
+                } catch(Exception e) {
+                    getLogger().warning("Failed to calculate version dir name", e);
+                    return null;
+                }
+                final File versionDir = new File(dir, versionDirName);
+                versionDir.mkdirs();
+                if(versionDir.isDirectory()) {
+                    getLogger().info("Optimistic type persistence directory is " + versionDir);
+                    return versionDir;
+                }
+                getLogger().warning("Could not create optimistic type persistence directory " + versionDir);
+                return null;
+            }
+        });
+    }
+
+    /**
+     * Returns an operating system specific root directory for cache files.
+     * @return an operating system specific root directory for cache files.
+     */
+    private static File getCacheDirBase() {
+        final String os = System.getProperty("os.name", "generic");
+        if("Mac OS X".equals(os)) {
+            // Mac OS X stores caches in ~/Library/Caches
+            return new File(new File(System.getProperty("user.home"), "Library"), "Caches");
+        } else if(os.startsWith("Windows")) {
+            // On Windows, temp directory is the best approximation of a cache directory, as its contents persist across
+            // reboots and various cleanup utilities know about it. java.io.tmpdir normally points to a user-specific
+            // temp directory, %HOME%\LocalSettings\Temp.
+            return new File(System.getProperty("java.io.tmpdir"));
+        } else {
+            // In all other cases we're presumably dealing with a UNIX flavor (Linux, Solaris, etc.); "~/.cache"
+            return new File(System.getProperty("user.home"), ".cache");
+        }
+    }
+
+    /**
+     * In order to ensure that changes in Nashorn code don't cause corruption in the data, we'll create a
+     * per-code-version directory. Normally, this will create the SHA-1 digest of the nashorn.jar. In case the classpath
+     * for nashorn is local directory (e.g. during development), this will create the string "dev-" followed by the
+     * timestamp of the most recent .class file.
+     * @return
+     */
+    private static String getVersionDirName() throws Exception {
+        final URL url = OptimisticTypesPersistence.class.getResource("");
+        final String protocol = url.getProtocol();
+        if(protocol.equals("jar")) {
+            // Normal deployment: nashorn.jar
+            final String jarUrlFile = url.getFile();
+            final String filePath = jarUrlFile.substring(0, jarUrlFile.indexOf('!'));
+            final URL file = new URL(filePath);
+            try (final InputStream in = file.openStream()) {
+                final byte[] buf = new byte[128*1024];
+                final MessageDigest digest = MessageDigest.getInstance("SHA-1");
+                for(;;) {
+                    final int l = in.read(buf);
+                    if(l == -1) {
+                        return Base64.getUrlEncoder().encodeToString(digest.digest());
+                    }
+                    digest.update(buf, 0, l);
+                }
+            }
+        } else if(protocol.equals("file")) {
+            // Development
+            final String fileStr = url.getFile();
+            final String className = OptimisticTypesPersistence.class.getName();
+            final int packageNameLen = className.lastIndexOf('.');
+            final String dirStr = fileStr.substring(0, fileStr.length() - packageNameLen - 1);
+            final File dir = new File(dirStr);
+            return "dev-" + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(getLastModifiedClassFile(dir, 0L)));
+        } else {
+            throw new AssertionError();
+        }
+    }
+
+    private static long getLastModifiedClassFile(final File dir, final long max) {
+        long currentMax = max;
+        for(File f: dir.listFiles()) {
+            if(f.getName().endsWith(".class")) {
+                final long lastModified = f.lastModified();
+                if(lastModified > currentMax) {
+                    currentMax = lastModified;
+                }
+            } else if(f.isDirectory()) {
+                final long lastModified = getLastModifiedClassFile(f, currentMax);
+                if(lastModified > currentMax) {
+                    currentMax = lastModified;
+                }
+            }
+        }
+        return currentMax;
+    }
+
+    private static Object[] createLockArray() {
+        final Object[] lockArray = new Object[Runtime.getRuntime().availableProcessors() * 2];
+        for(int i = 0; i < lockArray.length; ++i) {
+            lockArray[i] = new Object();
+        }
+        return lockArray;
+    }
+
+    private static Object getFileLock(final File file) {
+        return locks[(file.hashCode() & Integer.MAX_VALUE) % locks.length];
+    }
+
+    private static DebugLogger getLogger() {
+        try {
+            return Context.getContext().getLogger(RecompilableScriptFunctionData.class);
+        } catch (final Exception e) {
+            e.printStackTrace();
+            return DebugLogger.DISABLED_LOGGER;
+        }
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java	Tue Jul 01 14:27:28 2014 -0700
@@ -307,7 +307,7 @@
                 units.add(new ArrayUnit(unit, lo, postsets.length));
             }
 
-            arrayLiteralNode.setUnits(units);
+            return arrayLiteralNode.setUnits(lc, units);
         }
 
         return literal;
--- a/nashorn/src/jdk/nashorn/internal/codegen/TypeEvaluator.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/TypeEvaluator.java	Tue Jul 01 14:27:28 2014 -0700
@@ -25,6 +25,10 @@
 
 package jdk.nashorn.internal.codegen;
 
+import static jdk.nashorn.internal.runtime.Property.NOT_CONFIGURABLE;
+import static jdk.nashorn.internal.runtime.Property.NOT_ENUMERABLE;
+import static jdk.nashorn.internal.runtime.Property.NOT_WRITABLE;
+
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.AccessNode;
 import jdk.nashorn.internal.ir.Expression;
@@ -43,8 +47,8 @@
  * Used during recompilation.
  */
 final class TypeEvaluator {
-    final Compiler compiler;
-    final ScriptObject runtimeScope;
+    private final Compiler compiler;
+    private final ScriptObject runtimeScope;
 
     TypeEvaluator(final Compiler compiler, final ScriptObject runtimeScope) {
         this.compiler = compiler;
@@ -123,7 +127,7 @@
                     " scope="+runtimeScope;
 
         if (runtimeScope.findProperty(symbolName, false) == null) {
-            runtimeScope.set(symbolName, ScriptRuntime.UNDEFINED, true);
+            runtimeScope.addOwnProperty(symbolName, NOT_WRITABLE | NOT_ENUMERABLE | NOT_CONFIGURABLE, ScriptRuntime.UNDEFINED);
         }
     }
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/TypeMap.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/TypeMap.java	Tue Jul 01 14:27:28 2014 -0700
@@ -29,6 +29,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.runtime.ScriptFunction;
@@ -61,6 +62,20 @@
         this.needsCallee = needsCallee;
     }
 
+    /**
+     * Returns the array of parameter types for a particular function node
+     * @param functionNodeId the ID of the function node
+     * @return an array of parameter types
+     * @throws NoSuchElementException if the type map has no mapping for the requested function
+     */
+    public Type[] getParameterTypes(final int functionNodeId) {
+        final Type[] paramTypes = paramTypeMap.get(functionNodeId);
+        if (paramTypes == null) {
+            throw new NoSuchElementException(Integer.toString(functionNodeId));
+        }
+        return paramTypes.clone();
+    }
+
     MethodType getCallSiteType(final FunctionNode functionNode) {
         final Type[] types = paramTypeMap.get(functionNode.getId());
         if (types == null) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java	Tue Jul 01 14:27:28 2014 -0700
@@ -173,7 +173,6 @@
         if (functionNode == topFunction) {
             // the function being weighted; descend into its statements
             return true;
-//            functionNode.visitStatements(this);
         }
         // just a reference to inner function from outer function
         weight += FUNC_EXPR_WEIGHT;
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/ObjectType.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/ObjectType.java	Tue Jul 01 14:27:28 2014 -0700
@@ -171,6 +171,8 @@
             invokestatic(method, JSType.TO_BOOLEAN);
         } else if (to.isString()) {
             invokestatic(method, JSType.TO_PRIMITIVE_TO_STRING);
+        } else if (to.isCharSequence()) {
+            invokestatic(method, JSType.TO_PRIMITIVE_TO_CHARSEQUENCE);
         } else {
             throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString);
         }
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java	Tue Jul 01 14:27:28 2014 -0700
@@ -417,6 +417,15 @@
     }
 
     /**
+     * Determines whether a type is a CHARSEQUENCE type used internally strings
+     *
+     * @return true if CharSequence (internal string) type, false otherwise
+     */
+    public boolean isCharSequence() {
+        return this.equals(Type.CHARSEQUENCE);
+    }
+
+    /**
      * Determine if two types are equivalent, i.e. need no conversion
      *
      * @param type the second type to check
@@ -800,6 +809,13 @@
     public static final Type STRING = putInCache(new ObjectType(String.class));
 
     /**
+     * This is the CharSequence singleton used to represent JS strings internally
+     * (either a {@code java.lang.String} or {@code jdk.nashorn.internal.runtime.ConsString}.
+     */
+    public static final Type CHARSEQUENCE = putInCache(new ObjectType(CharSequence.class));
+
+
+    /**
      * This is the object singleton, used for all object types
      */
     public static final Type OBJECT = putInCache(new ObjectType());
--- a/nashorn/src/jdk/nashorn/internal/ir/Block.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Block.java	Tue Jul 01 14:27:28 2014 -0700
@@ -41,7 +41,7 @@
  * IR representation for a list of statements.
  */
 @Immutable
-public class Block extends Node implements BreakableNode, Flags<Block> {
+public class Block extends Node implements BreakableNode, Terminal, Flags<Block> {
     /** List of statements */
     protected final List<Statement> statements;
 
@@ -231,6 +231,11 @@
         return flags;
     }
 
+    /**
+     * Is this a terminal block, i.e. does it end control flow like ending with a throw or return?
+     *
+     * @return true if this node statement is terminal
+     */
     @Override
     public boolean isTerminal() {
         return getFlag(IS_TERMINAL);
--- a/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java	Tue Jul 01 14:27:28 2014 -0700
@@ -36,7 +36,7 @@
  * Case nodes are not BreakableNodes, but the SwitchNode is
  */
 @Immutable
-public final class CaseNode extends Node implements JoinPredecessor, Labels {
+public final class CaseNode extends Node implements JoinPredecessor, Labels, Terminal {
     /** Test expression. */
     private final Expression test;
 
@@ -77,6 +77,11 @@
         this.conversion = conversion;
     }
 
+    /**
+     * Is this a terminal case node, i.e. does it end control flow like having a throw or return?
+     *
+     * @return true if this node statement is terminal
+     */
     @Override
     public boolean isTerminal() {
         return body.isTerminal();
--- a/nashorn/src/jdk/nashorn/internal/ir/ExpressionStatement.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/ExpressionStatement.java	Tue Jul 01 14:27:28 2014 -0700
@@ -57,11 +57,6 @@
     }
 
     @Override
-    public boolean isTerminal() {
-        return expression.isTerminal();
-    }
-
-    @Override
     public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
         if (visitor.enterExpressionStatement(this)) {
             return visitor.leaveExpressionStatement(setExpression((Expression)expression.accept(visitor)));
--- a/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Tue Jul 01 14:27:28 2014 -0700
@@ -110,7 +110,7 @@
      * @return a temporary identifier for the symbol.
      */
     public static IdentNode createInternalIdentifier(final Symbol symbol) {
-        return (IdentNode)new IdentNode(Token.toDesc(TokenType.IDENT, 0, 0), 0, symbol.getName()).setSymbol(symbol);
+        return new IdentNode(Token.toDesc(TokenType.IDENT, 0, 0), 0, symbol.getName()).setSymbol(symbol);
     }
 
     @Override
@@ -180,7 +180,7 @@
      * @param symbol the symbol
      * @return new node
      */
-    public Expression setSymbol(final Symbol symbol) {
+    public IdentNode setSymbol(final Symbol symbol) {
         if (this.symbol == symbol) {
             return this;
         }
@@ -280,6 +280,17 @@
         return new IdentNode(this, name, type, flags | FUNCTION, programPoint, conversion);
     }
 
+    /**
+     * Mark this node as not being the callee operand of a {@link CallNode}.
+     * @return an ident node identical to this one in all aspects except with its function flag unset.
+     */
+    public IdentNode setIsNotFunction() {
+        if (! isFunction()) {
+            return this;
+        }
+        return new IdentNode(this, name, type, flags & ~FUNCTION, programPoint, conversion);
+    }
+
     @Override
     public int getProgramPoint() {
         return programPoint;
--- a/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java	Tue Jul 01 14:27:28 2014 -0700
@@ -29,6 +29,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Function;
+
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.types.ArrayType;
 import jdk.nashorn.internal.codegen.types.Type;
@@ -87,6 +88,17 @@
     }
 
     /**
+     * Initialization setter, if required for immutable state. This is used for
+     * things like ArrayLiteralNodes that need to carry state for the splitter.
+     * Default implementation is just a nop.
+     * @param lc lexical context
+     * @return new literal node with initialized state, or same if nothing changed
+     */
+    public LiteralNode<?> initialize(final LexicalContext lc) {
+        return this;
+    }
+
+    /**
      * Check if the literal value is null
      * @return true if literal value is null
      */
@@ -573,24 +585,26 @@
     /**
      * Array literal node class.
      */
+    @Immutable
     public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode {
 
         /** Array element type. */
-        private Type elementType;
+        private final Type elementType;
 
         /** Preset constant array. */
-        private Object presets;
+        private final Object presets;
 
         /** Indices of array elements requiring computed post sets. */
-        private int[] postsets;
+        private final int[] postsets;
 
-        private List<ArrayUnit> units;
+        /** Sub units with indexes ranges, in which to split up code generation, for large literals */
+        private final List<ArrayUnit> units;
 
         /**
          * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can
          * be split if they are too large, for bytecode generation reasons
          */
-        public static class ArrayUnit {
+        public static final class ArrayUnit {
             /** Compile unit associated with the postsets range. */
             private final CompileUnit compileUnit;
 
@@ -634,6 +648,150 @@
             }
         }
 
+        private static final class ArrayLiteralInitializer {
+
+            static ArrayLiteralNode initialize(final ArrayLiteralNode node) {
+                final Type elementType = computeElementType(node.value, node.elementType);
+                final int[] postsets = computePostsets(node.value);
+                final Object presets = computePresets(node.value, elementType, postsets);
+                return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.units);
+            }
+
+            private static Type computeElementType(final Expression[] value, final Type elementType) {
+                Type widestElementType = Type.INT;
+
+                for (final Expression elem : value) {
+                    if (elem == null) {
+                        widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number
+                        break;
+                    }
+
+                    final Type type = elem.getType().isUnknown() ? Type.OBJECT : elem.getType();
+                    if (type.isBoolean()) {
+                        //TODO fix this with explicit boolean types
+                        widestElementType = widestElementType.widest(Type.OBJECT);
+                        break;
+                    }
+
+                    widestElementType = widestElementType.widest(type);
+                    if (widestElementType.isObject()) {
+                        break;
+                    }
+                }
+                return widestElementType;
+            }
+
+            private static int[] computePostsets(final Expression[] value) {
+                final int[] computed = new int[value.length];
+                int nComputed = 0;
+
+                for (int i = 0; i < value.length; i++) {
+                    final Expression element = value[i];
+                    if (element == null || objectAsConstant(element) == POSTSET_MARKER) {
+                        computed[nComputed++] = i;
+                    }
+                }
+                return Arrays.copyOf(computed, nComputed);
+            }
+
+            private static boolean setArrayElement(final int[] array, final int i, final Object n) {
+                if (n instanceof Number) {
+                    array[i] = ((Number)n).intValue();
+                    return true;
+                }
+                return false;
+            }
+
+            private static boolean setArrayElement(final long[] array, final int i, final Object n) {
+                if (n instanceof Number) {
+                    array[i] = ((Number)n).longValue();
+                    return true;
+                }
+                return false;
+            }
+
+            private static boolean setArrayElement(final double[] array, final int i, final Object n) {
+                if (n instanceof Number) {
+                    array[i] = ((Number)n).doubleValue();
+                    return true;
+                }
+                return false;
+            }
+
+            private static int[] presetIntArray(final Expression[] value, final int[] postsets) {
+                final int[] array = new int[value.length];
+                int nComputed = 0;
+                for (int i = 0; i < value.length; i++) {
+                    if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
+                        assert postsets[nComputed++] == i;
+                    }
+                }
+                assert postsets.length == nComputed;
+                return array;
+            }
+
+            private static long[] presetLongArray(final Expression[] value, final int[] postsets) {
+                final long[] array = new long[value.length];
+                int nComputed = 0;
+                for (int i = 0; i < value.length; i++) {
+                    if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
+                        assert postsets[nComputed++] == i;
+                    }
+                }
+                assert postsets.length == nComputed;
+                return array;
+            }
+
+            private static double[] presetDoubleArray(final Expression[] value, final int[] postsets) {
+                final double[] array = new double[value.length];
+                int nComputed = 0;
+                for (int i = 0; i < value.length; i++) {
+                    if (!setArrayElement(array, i, objectAsConstant(value[i]))) {
+                        assert postsets[nComputed++] == i;
+                    }
+                }
+                assert postsets.length == nComputed;
+                return array;
+            }
+
+            private static Object[] presetObjectArray(final Expression[] value, final int[] postsets) {
+                final Object[] array = new Object[value.length];
+                int nComputed = 0;
+
+                for (int i = 0; i < value.length; i++) {
+                    final Node node = value[i];
+
+                    if (node == null) {
+                        assert postsets[nComputed++] == i;
+                        continue;
+                    }
+                    final Object element = objectAsConstant(node);
+
+                    if (element != POSTSET_MARKER) {
+                        array[i] = element;
+                    } else {
+                        assert postsets[nComputed++] == i;
+                    }
+                }
+
+                assert postsets.length == nComputed;
+                return array;
+            }
+
+            static Object computePresets(final Expression[] value, final Type elementType, final int[] postsets) {
+                assert !elementType.isUnknown();
+                if (elementType.isInteger()) {
+                    return presetIntArray(value, postsets);
+                } else if (elementType.isLong()) {
+                    return presetLongArray(value, postsets);
+                } else if (elementType.isNumeric()) {
+                    return presetDoubleArray(value, postsets);
+                } else {
+                    return presetObjectArray(value, postsets);
+                }
+            }
+        }
+
         /**
          * Constructor
          *
@@ -644,136 +802,21 @@
         protected ArrayLiteralNode(final long token, final int finish, final Expression[] value) {
             super(Token.recast(token, TokenType.ARRAY), finish, value);
             this.elementType = Type.UNKNOWN;
+            this.presets     = null;
+            this.postsets    = null;
+            this.units       = null;
         }
 
         /**
          * Copy constructor
          * @param node source array literal node
          */
-        private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value) {
+        private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<ArrayUnit> units) {
             super(node, value);
-            this.elementType = node.elementType;
-            this.presets     = node.presets;
-            this.postsets    = node.postsets;
-            this.units       = node.units;
-        }
-
-        /**
-         * Compute things like widest element type needed. Internal use from compiler only
-         */
-        public void analyze() {
-            assert elementType.isUnknown();
-            elementType = getNarrowestElementType(value);
-        }
-
-        private int[] presetIntArray() {
-            final int[] array = new int[value.length];
-            int nComputed = 0;
-
-            for (int i = 0; i < value.length; i++) {
-                final Object element = objectAsConstant(value[i]);
-
-                if (element instanceof Number) {
-                    array[i] = ((Number)element).intValue();
-                } else {
-                    assert getPostsets()[nComputed++] == i;
-                }
-            }
-
-            assert getPostsets().length == nComputed;
-            return array;
-        }
-
-        private long[] presetLongArray() {
-            final long[] array = new long[value.length];
-            int nComputed = 0;
-
-            for (int i = 0; i < value.length; i++) {
-                final Object element = objectAsConstant(value[i]);
-
-                if (element instanceof Number) {
-                    array[i] = ((Number)element).longValue();
-                } else {
-                    assert getPostsets()[nComputed++] == i;
-                }
-            }
-
-            assert getPostsets().length == nComputed;
-            return array;
-        }
-
-        private double[] presetNumberArray() {
-            final double[] array = new double[value.length];
-            int nComputed = 0;
-
-            for (int i = 0; i < value.length; i++) {
-                final Object element = objectAsConstant(value[i]);
-
-                if (element instanceof Number) {
-                    array[i] = ((Number)element).doubleValue();
-                } else {
-                    assert getPostsets()[nComputed++] == i;
-                }
-            }
-
-            assert getPostsets().length == nComputed;
-            return array;
-        }
-
-        private Object[] presetObjectArray() {
-            final Object[] array = new Object[value.length];
-            int nComputed = 0;
-
-            for (int i = 0; i < value.length; i++) {
-                final Node node = value[i];
-
-                if (node == null) {
-                    assert getPostsets()[nComputed++] == i;
-                } else {
-                    final Object element = objectAsConstant(node);
-
-                    if (element != POSTSET_MARKER) {
-                        array[i] = element;
-                    } else {
-                        assert getPostsets()[nComputed++] == i;
-                    }
-                }
-            }
-
-            assert getPostsets().length == nComputed;
-            return array;
-        }
-
-        /**
-         * Returns the narrowest element type that is wide enough to represent all the expressions in the array.
-         * @param elementExpressions the array of expressions
-         * @return the narrowest element type that is wide enough to represent all the expressions in the array.
-         */
-        private static Type getNarrowestElementType(final Expression[] elementExpressions) {
-            Type widestElementType = Type.INT;
-            for (final Expression element : elementExpressions) {
-                if (element == null) {
-                    widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number
-                    break;
-                }
-
-                Type elementType = element.getType();
-                if (elementType.isUnknown()) {
-                    elementType = Type.OBJECT;
-                }
-
-                if (elementType.isBoolean()) {
-                    widestElementType = widestElementType.widest(Type.OBJECT);
-                    break;
-                }
-
-                widestElementType = widestElementType.widest(elementType);
-
-                if (widestElementType.isObject()) {
-                    break;
-                }
-            }
-            return widestElementType;
+            this.elementType = elementType;
+            this.postsets    = postsets;
+            this.presets     = presets;
+            this.units       = units;
         }
 
         @Override
@@ -782,6 +825,19 @@
         }
 
         /**
+         * Setter that initializes all code generation meta data for an
+         * ArrayLiteralNode. This acts a setter, so the return value may
+         * return a new node and must be handled
+         *
+         * @param lc lexical context
+         * @return new array literal node with postsets, presets and element types initialized
+         */
+        @Override
+        public ArrayLiteralNode initialize(final LexicalContext lc) {
+            return Node.replaceInLexicalContext(lc, this, ArrayLiteralInitializer.initialize(this));
+        }
+
+        /**
          * Get the array element type as Java format, e.g. [I
          * @return array element type
          */
@@ -811,7 +867,7 @@
          * @return element type
          */
         public Type getElementType() {
-            assert !elementType.isUnknown();
+            assert !elementType.isUnknown() : this + " has elementType=unknown";
             return elementType;
         }
 
@@ -821,19 +877,20 @@
          * @return post set indices
          */
         public int[] getPostsets() {
-            if(postsets == null) {
-                final int[] computed = new int[value.length];
-                int nComputed = 0;
+            assert postsets != null : this + " elementType=" + elementType + " has no postsets";
+            return postsets;
+        }
 
-                for (int i = 0; i < value.length; i++) {
-                    final Expression element = value[i];
-                    if(element == null || objectAsConstant(element) == POSTSET_MARKER) {
-                        computed[nComputed++] = i;
-                    }
-                }
-                postsets = Arrays.copyOf(computed, nComputed);
+        private boolean presetsMatchElementType() {
+            if (elementType == Type.INT) {
+                return presets instanceof int[];
+            } else if (elementType == Type.LONG) {
+                return presets instanceof long[];
+            } else if (elementType == Type.NUMBER) {
+                return presets instanceof double[];
+            } else {
+                return presets instanceof Object[];
             }
-            return postsets;
         }
 
         /**
@@ -841,18 +898,7 @@
          * @return presets array, always returns an array type
          */
         public Object getPresets() {
-            if(presets == null) {
-                final Type type = getElementType();
-                if (type.isInteger()) {
-                    presets = presetIntArray();
-                } else if (type.isLong()) {
-                    presets = presetLongArray();
-                } else if (type.isNumeric()) {
-                    presets = presetNumberArray();
-                } else {
-                    presets = presetObjectArray();
-                }
-            }
+            assert presets != null && presetsMatchElementType() : this + " doesn't have presets, or invalid preset type: " + presets;
             return presets;
         }
 
@@ -867,11 +913,16 @@
 
         /**
          * Set the ArrayUnits that make up this ArrayLiteral
+         * @param lc lexical context
          * @see ArrayUnit
          * @param units list of array units
+         * @return new or changed arrayliteralnode
          */
-        public void setUnits(final List<ArrayUnit> units) {
-            this.units = units;
+        public ArrayLiteralNode setUnits(final LexicalContext lc, final List<ArrayUnit> units) {
+            if (this.units == units) {
+                return this;
+            }
+            return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units));
         }
 
         @Override
@@ -889,8 +940,15 @@
             return this;
         }
 
+        private ArrayLiteralNode setValue(final LexicalContext lc, final Expression[] value) {
+            if (this.value == value) {
+                return this;
+            }
+            return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units));
+        }
+
         private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) {
-            return (ArrayLiteralNode)lc.replace(this, new ArrayLiteralNode(this, value.toArray(new Expression[value.size()])));
+            return setValue(lc, value.toArray(new Expression[value.size()]));
         }
 
         @Override
--- a/nashorn/src/jdk/nashorn/internal/ir/Node.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Node.java	Tue Jul 01 14:27:28 2014 -0700
@@ -144,15 +144,6 @@
     public abstract void toString(final StringBuilder sb, final boolean printType);
 
     /**
-     * Check if this node has terminal flags, i.e. ends or breaks control flow
-     *
-     * @return true if terminal
-     */
-    public boolean hasTerminalFlags() {
-        return isTerminal() || hasGoto();
-    }
-
-    /**
      * Get the finish position for this node in the source string
      * @return finish
      */
@@ -169,15 +160,6 @@
     }
 
     /**
-     * Check if this function repositions control flow with goto like
-     * semantics, for example {@link BreakNode} or a {@link ForNode} with no test
-     * @return true if node has goto semantics
-     */
-    public boolean hasGoto() {
-        return false;
-    }
-
-    /**
      * Get start position for node
      * @return start position
      */
@@ -249,16 +231,6 @@
         return token;
     }
 
-    /**
-     * Is this a terminal Node, i.e. does it end control flow like a throw or return
-     * expression does?
-     *
-     * @return true if this node is terminal
-     */
-    public boolean isTerminal() {
-        return false;
-    }
-
     //on change, we have to replace the entire list, that's we can't simple do ListIterator.set
     static <T extends Node> List<T> accept(final NodeVisitor<? extends LexicalContext> visitor, final Class<T> clazz, final List<T> list) {
         boolean changed = false;
--- a/nashorn/src/jdk/nashorn/internal/ir/Statement.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Statement.java	Tue Jul 01 14:27:28 2014 -0700
@@ -30,7 +30,7 @@
  * made up of statements. The only node subclass that needs to keep token and
  * location information is the Statement
  */
-public abstract class Statement extends Node {
+public abstract class Statement extends Node implements Terminal {
 
     private final int lineNumber;
 
@@ -77,4 +77,32 @@
         return lineNumber;
     }
 
+    /**
+     * Is this a terminal statement, i.e. does it end control flow like a throw or return?
+     *
+     * @return true if this node statement is terminal
+     */
+    @Override
+    public boolean isTerminal() {
+        return false;
+    }
+
+    /**
+     * Check if this statement repositions control flow with goto like
+     * semantics, for example {@link BreakNode} or a {@link ForNode} with no test
+     * @return true if statement has goto semantics
+     */
+    public boolean hasGoto() {
+        return false;
+    }
+
+    /**
+     * Check if this statement has terminal flags, i.e. ends or breaks control flow
+     *
+     * @return true if has terminal flags
+     */
+    public final boolean hasTerminalFlags() {
+        return isTerminal() || hasGoto();
+    }
 }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/Terminal.java	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,37 @@
+/*
+ * 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.ir;
+
+/**
+ * Interface for AST nodes that can have a flag determining if they can terminate function control flow.
+ */
+public interface Terminal {
+    /**
+     * Returns true if this AST node is (or contains) a statement that terminates function control flow.
+     * @return true if this AST node is (or contains) a statement that terminates function control flow.
+     */
+    public boolean isTerminal();
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Tue Jul 01 14:27:28 2014 -0700
@@ -38,7 +38,9 @@
 import jdk.nashorn.internal.ir.Expression;
 import jdk.nashorn.internal.ir.IdentNode;
 import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.Statement;
 import jdk.nashorn.internal.ir.Symbol;
+import jdk.nashorn.internal.ir.Terminal;
 import jdk.nashorn.internal.ir.TernaryNode;
 import jdk.nashorn.internal.ir.annotations.Ignore;
 import jdk.nashorn.internal.ir.annotations.Reference;
@@ -144,11 +146,11 @@
 
         String status = "";
 
-        if (node.isTerminal()) {
+        if (node instanceof Terminal && ((Terminal)node).isTerminal()) {
             status += " Terminal";
         }
 
-        if (node.hasGoto()) {
+        if (node instanceof Statement && ((Statement)node).hasGoto()) {
             status += " Goto ";
         }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/NashornClassReader.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/NashornClassReader.java	Tue Jul 01 14:27:28 2014 -0700
@@ -443,7 +443,7 @@
     @Override
     protected Label readLabel(final int offset, final Label[] labels) {
         final Label label = super.readLabel(offset, labels);
-        label.info = (int)offset;
+        label.info = offset;
         return label;
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Tue Jul 01 14:27:28 2014 -0700
@@ -182,9 +182,9 @@
 
         final List<Statement> statements = block.getStatements();
 
-        for (final Node statement : statements) {
-            if (printLineNumbers && (statement instanceof Statement)) {
-                final int lineNumber = ((Statement)statement).getLineNumber();
+        for (final Statement statement : statements) {
+            if (printLineNumbers) {
+                final int lineNumber = statement.getLineNumber();
                 sb.append('\n');
                 if (lineNumber != lastLineNumber) {
                     indent();
@@ -196,10 +196,6 @@
 
             statement.accept(this);
 
-            if (statement instanceof FunctionNode) {
-                continue;
-            }
-
             int  lastIndex = sb.length() - 1;
             char lastChar  = sb.charAt(lastIndex);
             while (Character.isWhitespace(lastChar) && lastIndex >= 0) {
--- a/nashorn/src/jdk/nashorn/internal/objects/Global.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java	Tue Jul 01 14:27:28 2014 -0700
@@ -883,7 +883,7 @@
         final Global global = Global.instance();
         final ScriptObject scope = self instanceof ScriptObject ? (ScriptObject)self : global;
 
-        return global.getContext().eval(scope, str.toString(), callThis, location, Boolean.TRUE.equals(strict));
+        return global.getContext().eval(scope, str.toString(), callThis, location, Boolean.TRUE.equals(strict), true);
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java	Tue Jul 01 14:27:28 2014 -0700
@@ -909,6 +909,7 @@
         sb.append(n);
     }
 
+    @SuppressWarnings("fallthrough")
     private static String toStringImpl(final Object self, final int format) {
         final NativeDate nd = getNativeDate(self);
 
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java	Tue Jul 01 14:27:28 2014 -0700
@@ -716,6 +716,32 @@
         return target;
     }
 
+    /*
+     * Binds the source mirror object's properties to the target object. Binding
+     * properties allows two-way read/write for the properties of the source object.
+     * All inherited, enumerable properties are also bound. This method is used to
+     * to make 'with' statement work with ScriptObjectMirror as scope object.
+     *
+     * @param target the target object to which the source object's properties are bound
+     * @param source the source object whose properties are bound to the target
+     * @return the target object after property binding
+     */
+    public static Object bindAllProperties(final ScriptObject target, final ScriptObjectMirror source) {
+        final Set<String> keys = source.keySet();
+        // make accessor properties using dynamic invoker getters and setters
+        final AccessorProperty[] props = new AccessorProperty[keys.size()];
+        int idx = 0;
+        for (String name : keys) {
+            final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, MIRROR_GETTER_TYPE);
+            final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, MIRROR_SETTER_TYPE);
+            props[idx] = AccessorProperty.create(name, 0, getter, setter);
+            idx++;
+        }
+
+        target.addBoundProperties(source, props);
+        return target;
+    }
+
     private static void bindBeanProperties(final ScriptObject targetObj, final Object source,
             final Collection<String> readablePropertyNames, final Collection<String> writablePropertyNames,
             final Collection<String> methodNames) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Tue Jul 01 14:27:28 2014 -0700
@@ -89,11 +89,12 @@
         this.log = log;
     }
 
-    CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData, final int flags) {
+    CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData,
+            final Map<Integer, Type> invalidatedProgramPoints, final int flags) {
         this(invoker, null, functionData.getLogger());
         this.flags = flags;
         if ((flags & FunctionNode.IS_DEOPTIMIZABLE) != 0) {
-            optimismInfo = new OptimismInfo(functionData);
+            optimismInfo = new OptimismInfo(functionData, invalidatedProgramPoints);
         } else {
             optimismInfo = null;
         }
@@ -691,13 +692,14 @@
     private static class OptimismInfo {
         // TODO: this is pointing to its owning ScriptFunctionData. Re-evaluate if that's okay.
         private final RecompilableScriptFunctionData data;
-        private final Map<Integer, Type> invalidatedProgramPoints = new TreeMap<>();
+        private final Map<Integer, Type> invalidatedProgramPoints;
         private SwitchPoint optimisticAssumptions;
         private final DebugLogger log;
 
-        OptimismInfo(final RecompilableScriptFunctionData data) {
+        OptimismInfo(final RecompilableScriptFunctionData data, final Map<Integer, Type> invalidatedProgramPoints) {
             this.data = data;
             this.log  = data.getLogger();
+            this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new TreeMap<Integer, Type>() : invalidatedProgramPoints;
             newOptimisticAssumptions();
         }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/ConsString.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ConsString.java	Tue Jul 01 14:27:28 2014 -0700
@@ -36,8 +36,12 @@
 public final class ConsString implements CharSequence {
 
     private CharSequence left, right;
-    final private int length;
-    private boolean flat = false;
+    private final int length;
+    private volatile int state = STATE_NEW;
+
+    private final static int STATE_NEW       =  0;
+    private final static int STATE_THRESHOLD =  2;
+    private final static int STATE_FLATTENED = -1;
 
     /**
      * Constructor
@@ -53,11 +57,14 @@
         this.left = left;
         this.right = right;
         length = left.length() + right.length();
+        if (length < 0) {
+            throw new IllegalArgumentException("too big concatenated String");
+        }
     }
 
     @Override
     public String toString() {
-        return (String) flattened();
+        return (String) flattened(true);
     }
 
     @Override
@@ -67,22 +74,31 @@
 
     @Override
     public char charAt(final int index) {
-        return flattened().charAt(index);
+        return flattened(true).charAt(index);
     }
 
     @Override
     public CharSequence subSequence(final int start, final int end) {
-        return flattened().subSequence(start, end);
+        return flattened(true).subSequence(start, end);
     }
 
-    private CharSequence flattened() {
-        if (!flat) {
-            flatten();
+    /**
+     * Returns the components of this ConsString as a {@code CharSequence} array with two elements.
+     * The elements will be either {@code Strings} or other {@code ConsStrings}.
+     * @return CharSequence array of length 2
+     */
+    public synchronized CharSequence[] getComponents() {
+        return new CharSequence[] { left, right };
+    }
+
+    private CharSequence flattened(boolean flattenNested) {
+        if (state != STATE_FLATTENED) {
+            flatten(flattenNested);
         }
         return left;
     }
 
-    private void flatten() {
+    private synchronized void flatten(boolean flattenNested) {
         // We use iterative traversal as recursion may exceed the stack size limit.
         final char[] chars = new char[length];
         int pos = length;
@@ -97,8 +113,14 @@
         do {
             if (cs instanceof ConsString) {
                 final ConsString cons = (ConsString) cs;
-                stack.addFirst(cons.left);
-                cs = cons.right;
+                // Count the times a cons-string is traversed as part of other cons-strings being flattened.
+                // If it crosses a threshold we flatten the nested cons-string internally.
+                if (cons.state == STATE_FLATTENED || (flattenNested && ++cons.state >= STATE_THRESHOLD)) {
+                    cs = cons.flattened(false);
+                } else {
+                    stack.addFirst(cons.left);
+                    cs = cons.right;
+                }
             } else {
                 final String str = (String) cs;
                 pos -= str.length();
@@ -109,7 +131,7 @@
 
         left = new String(chars);
         right = "";
-        flat = true;
+        state = STATE_FLATTENED;
     }
 
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Tue Jul 01 14:27:28 2014 -0700
@@ -444,15 +444,11 @@
         }
 
         if (env._persistent_cache) {
-            if (env._lazy_compilation || env._optimistic_types) {
-                getErr().println("Can not use persistent class caching with lazy compilation or optimistic compilation.");
-            } else {
-                try {
-                    final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache");
-                    codeStore = new CodeStore(cacheDir);
-                } catch (final IOException e) {
-                    throw new RuntimeException("Error initializing code cache", e);
-                }
+            try {
+                final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache");
+                codeStore = new CodeStore(cacheDir);
+            } catch (final IOException e) {
+                throw new RuntimeException("Error initializing code cache", e);
             }
         }
 
@@ -560,12 +556,29 @@
      * @param callThis     "this" to be passed to the evaluated code
      * @param location     location of the eval call
      * @param strict       is this {@code eval} call from a strict mode code?
+     * @return the return value of the {@code eval}
+     */
+    public Object eval(final ScriptObject initialScope, final String string,
+            final Object callThis, final Object location, final boolean strict) {
+        return eval(initialScope, string, callThis, location, strict, false);
+    }
+
+    /**
+     * Entry point for {@code eval}
+     *
+     * @param initialScope The scope of this eval call
+     * @param string       Evaluated code as a String
+     * @param callThis     "this" to be passed to the evaluated code
+     * @param location     location of the eval call
+     * @param strict       is this {@code eval} call from a strict mode code?
+     * @param evalCall     is this called from "eval" builtin?
      *
      * @return the return value of the {@code eval}
      */
-    public Object eval(final ScriptObject initialScope, final String string, final Object callThis, final Object location, final boolean strict) {
+    public Object eval(final ScriptObject initialScope, final String string,
+            final Object callThis, final Object location, final boolean strict, final boolean evalCall) {
         final String  file       = location == UNDEFINED || location == null ? "<eval>" : location.toString();
-        final Source  source     = sourceFor(file, string);
+        final Source  source     = sourceFor(file, string, evalCall);
         final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval?
         final Global  global = Context.getGlobal();
         ScriptObject scope = initialScope;
@@ -1162,7 +1175,7 @@
 
         for (final Object constant : constants) {
             if (constant instanceof RecompilableScriptFunctionData) {
-                ((RecompilableScriptFunctionData) constant).setCodeAndSource(installedClasses, source);
+                ((RecompilableScriptFunctionData) constant).initTransients(source, installer);
             }
         }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSType.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSType.java	Tue Jul 01 14:27:28 2014 -0700
@@ -130,6 +130,9 @@
     /** Combined call to toPrimitive followed by toString. */
     public static final Call TO_PRIMITIVE_TO_STRING = staticCall(JSTYPE_LOOKUP, JSType.class, "toPrimitiveToString", String.class, Object.class);
 
+    /** Combined call to toPrimitive followed by toCharSequence. */
+    public static final Call TO_PRIMITIVE_TO_CHARSEQUENCE = staticCall(JSTYPE_LOOKUP, JSType.class, "toPrimitiveToCharSequence", CharSequence.class, Object.class);
+
     /** Throw an unwarranted optimism exception */
     public static final Call THROW_UNWARRANTED = staticCall(JSTYPE_LOOKUP, JSType.class, "throwUnwarrantedOptimismException", Object.class, Object.class, int.class);
 
@@ -488,6 +491,16 @@
     }
 
     /**
+     * Like {@link #toPrimitiveToString(Object)}, but avoids conversion of ConsString to String.
+     *
+     * @param obj  an object
+     * @return the CharSequence form of the primitive form of the object
+     */
+    public static CharSequence toPrimitiveToCharSequence(final Object obj) {
+        return toCharSequence(toPrimitive(obj));
+    }
+
+    /**
      * JavaScript compliant conversion of number to boolean
      *
      * @param num a number
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Tue Jul 01 14:27:28 2014 -0700
@@ -28,7 +28,6 @@
 import static jdk.nashorn.internal.lookup.Lookup.MH;
 
 import java.io.IOException;
-import java.io.Serializable;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
@@ -36,12 +35,14 @@
 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;
 import jdk.nashorn.internal.codegen.FunctionSignature;
+import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
 import jdk.nashorn.internal.codegen.TypeMap;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
@@ -54,7 +55,6 @@
 import jdk.nashorn.internal.runtime.logging.DebugLogger;
 import jdk.nashorn.internal.runtime.logging.Loggable;
 import jdk.nashorn.internal.runtime.logging.Logger;
-import jdk.nashorn.internal.scripts.JS;
 
 /**
  * This is a subclass that represents a script function that may be regenerated,
@@ -83,9 +83,6 @@
     /** Source from which FunctionNode was parsed. */
     private transient Source source;
 
-    /** Allows us to retrieve the method handle for this function once the code is compiled */
-    private MethodLocator methodLocator;
-
     /** Token of this function within the source. */
     private final long token;
 
@@ -237,15 +234,18 @@
     }
 
     /**
-     * Setter for code and source
+     * Initialize transient fields on deserialized instances
      *
-     * @param code   map of code, class name to class
      * @param source source
+     * @param installer code installer
      */
-    public void setCodeAndSource(final Map<String, Class<?>> code, final Source source) {
-        this.source = source;
-        if (methodLocator != null) {
-            methodLocator.setClass(code.get(methodLocator.getClassName()));
+    public void initTransients(final Source source, final CodeInstaller<ScriptEnvironment> installer) {
+        if (this.source == null && this.installer == null) {
+            this.source = source;
+            this.installer = installer;
+        } else if (this.source != source || this.installer != installer) {
+            // Existing values must be same as those passed as parameters
+            throw new IllegalArgumentException();
         }
     }
 
@@ -390,7 +390,11 @@
         return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null);
     }
 
-    Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType, final ScriptObject runtimeScope, final Map<Integer, Type> ipp, final int[] cep) {
+    Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType,
+            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 Context context = Context.getContextTrusted();
         return new Compiler(
                 context,
@@ -402,12 +406,31 @@
                 true,       // is on demand
                 this,       // compiledFunction, i.e. this RecompilableScriptFunctionData
                 typeMap(actualCallSiteType), // type map
-                ipp, // invalidated program points
-                cep, // continuation entry points
+                getEffectiveInvalidatedProgramPoints(invalidatedProgramPoints, typeInformationFile), // invalidated program points
+                typeInformationFile,
+                continuationEntryPoints, // continuation entry points
                 runtimeScope); // runtime scope
     }
 
-    private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
+    /**
+     * If the function being compiled already has its own invalidated program points map, use it. Otherwise, attempt to
+     * load invalidated program points map from the persistent type info cache.
+     * @param invalidatedProgramPoints the function's current invalidated program points map. Null if the function
+     * doesn't have it.
+     * @param typeInformationFile the object describing the location of the persisted type information.
+     * @return either the existing map, or a loaded map from the persistent type info cache, or a new empty map if
+     * neither an existing map or a persistent cached type info is available.
+     */
+    private static Map<Integer, Type> getEffectiveInvalidatedProgramPoints(
+            final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile) {
+        if(invalidatedProgramPoints != null) {
+            return invalidatedProgramPoints;
+        }
+        final Map<Integer, Type> loadedProgramPoints = OptimisticTypesPersistence.load(typeInformationFile);
+        return loadedProgramPoints != null ? loadedProgramPoints : new TreeMap<Integer, Type>();
+    }
+
+    private TypeSpecializedFunction compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
         // 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()).
@@ -417,7 +440,20 @@
         }
 
         final FunctionNode fn = reparse();
-        return getCompiler(fn, actualCallSiteType, runtimeScope).compile(fn, CompilationPhases.COMPILE_ALL);
+        final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope);
+
+        final FunctionNode compiledFn = compiler.compile(fn, CompilationPhases.COMPILE_ALL);
+        return new TypeSpecializedFunction(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;
+        }
     }
 
     private MethodType explicitParams(final MethodType callSiteType) {
@@ -491,17 +527,16 @@
             throw new IllegalStateException(functionNode.getName() + " id=" + functionNode.getId());
         }
         addCode(functionNode);
-        methodLocator = new MethodLocator(functionNode);
     }
 
-    private CompiledFunction addCode(final MethodHandle target, final int fnFlags) {
-        final CompiledFunction cfn = new CompiledFunction(target, this, fnFlags);
+    private CompiledFunction addCode(final MethodHandle target, final Map<Integer, Type> invalidatedProgramPoints, final int fnFlags) {
+        final CompiledFunction cfn = new CompiledFunction(target, this, invalidatedProgramPoints, fnFlags);
         code.add(cfn);
         return cfn;
     }
 
     private CompiledFunction addCode(final FunctionNode fn) {
-        return addCode(lookup(fn), fn.getFlags());
+        return addCode(lookup(fn), null, fn.getFlags());
     }
 
     /**
@@ -510,11 +545,12 @@
      * 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 fn the function
+     * @param tfn 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 FunctionNode fn, final MethodType callSiteType) {
+    private CompiledFunction addCode(final TypeSpecializedFunction tfn, final MethodType callSiteType) {
+        final FunctionNode fn = tfn.fn;
         if (fn.isVarArg()) {
             return addCode(fn);
         }
@@ -544,7 +580,7 @@
             toType = toType.dropParameterTypes(fromCount, toCount);
         }
 
-        return addCode(lookup(fn).asType(toType), fn.getFlags());
+        return addCode(lookup(fn).asType(toType), tfn.invalidatedProgramPoints, fn.getFlags());
     }
 
 
@@ -553,12 +589,7 @@
         synchronized (code) {
             CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
             if (existingBest == null) {
-                if(code.isEmpty() && methodLocator != null) {
-                    // This is a deserialized object, reconnect from method handle
-                    existingBest = addCode(methodLocator.getMethodHandle(), methodLocator.getFunctionFlags());
-                } else {
-                    existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
-                }
+                existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
             }
 
             assert existingBest != null;
@@ -576,9 +607,9 @@
             }
 
             if (applyToCall) {
-                final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope);
-                if (fn.hasOptimisticApplyToCall()) { //did the specialization work
-                    existingBest = addCode(fn, callSiteType);
+                final TypeSpecializedFunction tfn = compileTypeSpecialization(callSiteType, runtimeScope);
+                if (tfn.fn.hasOptimisticApplyToCall()) { //did the specialization work
+                    existingBest = addCode(tfn, callSiteType);
                 }
             }
 
@@ -670,48 +701,6 @@
         return true;
     }
 
-    /**
-     * Helper class that allows us to retrieve the method handle for this function once it has been generated.
-     */
-    private static class MethodLocator implements Serializable {
-        private transient Class<?> clazz;
-        private final String className;
-        private final String methodName;
-        private final MethodType methodType;
-        private final int functionFlags;
-
-        private static final long serialVersionUID = -5420835725902966692L;
-
-        MethodLocator(final FunctionNode functionNode) {
-            this.className  = functionNode.getCompileUnit().getUnitClassName();
-            this.methodName = functionNode.getName();
-            this.methodType = new FunctionSignature(functionNode).getMethodType();
-            this.functionFlags = functionNode.getFlags();
-
-            assert className != null;
-            assert methodName != null;
-        }
-
-        void setClass(final Class<?> clazz) {
-            if (!JS.class.isAssignableFrom(clazz)) {
-                throw new IllegalArgumentException();
-            }
-            this.clazz = clazz;
-        }
-
-        String getClassName() {
-            return className;
-        }
-
-        MethodHandle getMethodHandle() {
-            return MH.findStatic(LOOKUP, clazz, methodName, methodType);
-        }
-
-        int getFunctionFlags() {
-            return functionFlags;
-        }
-    }
-
     private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
         createLogger();
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Tue Jul 01 14:27:28 2014 -0700
@@ -35,6 +35,7 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.SwitchPoint;
+import java.util.Collections;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.LinkRequest;
@@ -593,6 +594,12 @@
     private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) {
         final MethodType descType = desc.getMethodType();
         final int paramCount = descType.parameterCount();
+        if(descType.parameterType(paramCount - 1).isArray()) {
+            // This is vararg invocation of apply or call. This can normally only happen when we do a recursive
+            // invocation of createApplyOrCallCall (because we're doing apply-of-apply). In this case, create delegate
+            // linkage by unpacking the vararg invocation and use pairArguments to introduce the necessary spreader.
+            return createVarArgApplyOrCallCall(isApply, desc, request, args);
+        }
 
         final boolean passesThis = paramCount > 2;
         final boolean passesArgs = paramCount > 3;
@@ -647,6 +654,7 @@
                     System.arraycopy(args, 3, tmp, 0, tmp.length);
                     appliedArgs[2] = NativeFunction.toApplyArgs(tmp);
                 } else {
+                    assert !isApply;
                     System.arraycopy(args, 3, appliedArgs, 2, args.length - 3);
                 }
             } else if (isFailedApplyToCall) {
@@ -705,8 +713,7 @@
         }
         final MethodType guardType = guard.type();
 
-        // Original function guard will expect the invoked function in parameter position 0, but we're passing it in
-        // position 1.
+        // We need to account for the dropped (apply|call) function argument.
         guard = MH.dropArguments(guard, 0, descType.parameterType(0));
         // Take the "isApplyFunction" guard, and bind it to this function.
         MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this);
@@ -718,7 +725,72 @@
         return appliedInvocation.replaceMethods(inv, guard);
     }
 
-     private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) {
+    /*
+     * This method is used for linking nested apply. Specialized apply and call linking will create a variable arity
+     * call site for an apply call; when createApplyOrCallCall sees a linking request for apply or call with
+     * Nashorn-style variable arity call site (last argument type is Object[]) it'll delegate to this method.
+     * This method converts the link request from a vararg to a non-vararg one (unpacks the array), then delegates back
+     * to createApplyOrCallCall (with which it is thus mutually recursive), and adds appropriate argument spreaders to
+     * invocation and the guard of whatever createApplyOrCallCall returned to adapt it back into a variable arity
+     * invocation. It basically reduces the problem of vararg call site linking of apply and call back to the (already
+     * solved by createApplyOrCallCall) non-vararg call site linking.
+     */
+    private GuardedInvocation createVarArgApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc,
+            final LinkRequest request, final Object[] args) {
+        final MethodType descType = desc.getMethodType();
+        final int paramCount = descType.parameterCount();
+        final Object[] varArgs = (Object[])args[paramCount - 1];
+        // -1 'cause we're not passing the vararg array itself
+        final int copiedArgCount = args.length - 1;
+        int varArgCount = varArgs.length;
+
+        // Spread arguments for the delegate createApplyOrCallCall invocation.
+        final Object[] spreadArgs = new Object[copiedArgCount + varArgCount];
+        System.arraycopy(args, 0, spreadArgs, 0, copiedArgCount);
+        System.arraycopy(varArgs, 0, spreadArgs, copiedArgCount, varArgCount);
+
+        // Spread call site descriptor for the delegate createApplyOrCallCall invocation. We drop vararg array and
+        // replace it with a list of Object.class.
+        final MethodType spreadType = descType.dropParameterTypes(paramCount - 1, paramCount).appendParameterTypes(
+                Collections.<Class<?>>nCopies(varArgCount, Object.class));
+        final CallSiteDescriptor spreadDesc = desc.changeMethodType(spreadType);
+
+        // Delegate back to createApplyOrCallCall with the spread (that is, reverted to non-vararg) request/
+        final LinkRequest spreadRequest = request.replaceArguments(spreadDesc, spreadArgs);
+        final GuardedInvocation spreadInvocation = createApplyOrCallCall(isApply, spreadDesc, spreadRequest, spreadArgs);
+
+        // Add spreader combinators to returned invocation and guard.
+        return spreadInvocation.replaceMethods(
+                // Use standard ScriptObject.pairArguments on the invocation
+                pairArguments(spreadInvocation.getInvocation(), descType),
+                // Use our specialized spreadGuardArguments on the guard (see below).
+                spreadGuardArguments(spreadInvocation.getGuard(), descType));
+    }
+
+    private static MethodHandle spreadGuardArguments(final MethodHandle guard, final MethodType descType) {
+        final MethodType guardType = guard.type();
+        final int guardParamCount = guardType.parameterCount();
+        final int descParamCount = descType.parameterCount();
+        final int spreadCount = guardParamCount - descParamCount + 1;
+        if (spreadCount <= 0) {
+            // Guard doesn't dip into the varargs
+            return guard;
+        }
+
+        final MethodHandle arrayConvertingGuard;
+        // If the last parameter type of the guard is an array, then it is already itself a guard for a vararg apply
+        // invocation. We must filter the last argument with toApplyArgs otherwise deeper levels of nesting will fail
+        // with ClassCastException of NativeArray to Object[].
+        if(guardType.parameterType(guardParamCount - 1).isArray()) {
+            arrayConvertingGuard = MH.filterArguments(guard, guardParamCount - 1, NativeFunction.TO_APPLY_ARGS);
+        } else {
+            arrayConvertingGuard = guard;
+        }
+
+        return ScriptObject.adaptHandleToVarArgCallSite(arrayConvertingGuard, descParamCount);
+    }
+
+    private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) {
          final MethodHandle bound;
          if(fn instanceof ScriptFunction && ((ScriptFunction)fn).needsWrappedThis()) {
              bound = MH.filterArguments(mh, 1, SCRIPTFUNCTION_GLOBALFILTER);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Tue Jul 01 14:27:28 2014 -0700
@@ -691,6 +691,7 @@
         assert isValidArrayIndex(index) : "invalid array index";
         final long longIndex = ArrayIndex.toLongIndex(index);
         doesNotHaveEnsureDelete(longIndex, getArray().length(), false);
+        setArray(getArray().ensure(longIndex));
         setArray(getArray().set(index, value, false));
     }
 
@@ -2499,18 +2500,7 @@
         }
 
         if (isCallerVarArg) {
-            final int spreadArgs = parameterCount - callCount + 1;
-            return MH.filterArguments(
-                MH.asSpreader(
-                    methodHandle,
-                    Object[].class,
-                    spreadArgs),
-                callCount - 1,
-                MH.insertArguments(
-                    TRUNCATINGFILTER,
-                    0,
-                    spreadArgs)
-                );
+            return adaptHandleToVarArgCallSite(methodHandle, callCount);
         }
 
         if (callCount < parameterCount) {
@@ -2541,6 +2531,21 @@
         return methodHandle;
     }
 
+    static MethodHandle adaptHandleToVarArgCallSite(final MethodHandle mh, final int callSiteParamCount) {
+        final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1;
+        return MH.filterArguments(
+            MH.asSpreader(
+            mh,
+            Object[].class,
+            spreadArgs),
+            callSiteParamCount - 1,
+            MH.insertArguments(
+                TRUNCATINGFILTER,
+                0,
+                spreadArgs)
+            );
+    }
+
     @SuppressWarnings("unused")
     private static Object[] truncatingFilter(final int n, final Object[] array) {
         final int length = array == null ? 0 : array.length;
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Tue Jul 01 14:27:28 2014 -0700
@@ -27,6 +27,7 @@
 
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
+import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
 import static jdk.nashorn.internal.runtime.ECMAErrors.syntaxError;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
@@ -49,6 +50,7 @@
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
 import jdk.nashorn.internal.ir.debug.JSONWriter;
 import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.objects.NativeObject;
 import jdk.nashorn.internal.parser.Lexer;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
 
@@ -478,9 +480,21 @@
             throw typeError(global, "cant.apply.with.to.null");
         }
 
-        final Object wrappedExpr = JSType.toScriptObject(global, expression);
-        if (wrappedExpr instanceof ScriptObject) {
-            return new WithObject(scope, (ScriptObject)wrappedExpr);
+        if (expression instanceof ScriptObjectMirror) {
+            final Object unwrapped = ScriptObjectMirror.unwrap(expression, global);
+            if (unwrapped instanceof ScriptObject) {
+                return new WithObject(scope, (ScriptObject)unwrapped);
+            } else {
+                // foreign ScriptObjectMirror
+                ScriptObject exprObj = global.newObject();
+                NativeObject.bindAllProperties(exprObj, (ScriptObjectMirror)expression);
+                return new WithObject(scope, exprObj);
+            }
+        } else {
+            final Object wrappedExpr = JSType.toScriptObject(global, expression);
+            if (wrappedExpr instanceof ScriptObject) {
+                return new WithObject(scope, (ScriptObject)wrappedExpr);
+            }
         }
 
         throw typeError(global, "cant.apply.with.to.non.scriptobject");
@@ -518,7 +532,11 @@
 
         if (xPrim instanceof String || yPrim instanceof String
                 || xPrim instanceof ConsString || yPrim instanceof ConsString) {
-            return new ConsString(JSType.toCharSequence(xPrim), JSType.toCharSequence(yPrim));
+            try {
+                return new ConsString(JSType.toCharSequence(xPrim), JSType.toCharSequence(yPrim));
+            } catch (final IllegalArgumentException iae) {
+                throw rangeError(iae, "concat.string.too.big");
+            }
         }
 
         return JSType.toNumber(xPrim) + JSType.toNumber(yPrim);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java	Tue Jul 01 14:27:28 2014 -0700
@@ -107,8 +107,8 @@
 
         if (file instanceof File) {
             f = (File)file;
-        } else if (file instanceof String) {
-            f = new java.io.File((String)file);
+        } else if (file instanceof String || file instanceof ConsString) {
+            f = new java.io.File(((CharSequence)file).toString());
         }
 
         if (f == null || !f.isFile()) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/Source.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Source.java	Tue Jul 01 14:27:28 2014 -0700
@@ -142,29 +142,34 @@
         long lastModified();
 
         char[] array();
+
+        boolean isEvalCode();
     }
 
     private static class RawData implements Data {
         private final char[] array;
+        private final boolean evalCode;
         private int hash;
 
-        private RawData(final char[] array) {
+        private RawData(final char[] array, final boolean evalCode) {
             this.array = Objects.requireNonNull(array);
+            this.evalCode = evalCode;
         }
 
-        private RawData(final String source) {
+        private RawData(final String source, final boolean evalCode) {
             this.array = Objects.requireNonNull(source).toCharArray();
+            this.evalCode = evalCode;
         }
 
         private RawData(final Reader reader) throws IOException {
-            this(readFully(reader));
+            this(readFully(reader), false);
         }
 
         @Override
         public int hashCode() {
             int h = hash;
             if (h == 0) {
-                h = hash = Arrays.hashCode(array);
+                h = hash = Arrays.hashCode(array) ^ (evalCode? 1 : 0);
             }
             return h;
         }
@@ -175,7 +180,8 @@
                 return true;
             }
             if (obj instanceof RawData) {
-                return Arrays.equals(array, ((RawData)obj).array);
+                final RawData other = (RawData)obj;
+                return Arrays.equals(array, other.array) && evalCode == other.evalCode;
             }
             return false;
         }
@@ -206,6 +212,10 @@
         }
 
 
+        @Override
+        public boolean isEvalCode() {
+            return evalCode;
+        }
     }
 
     private static class URLData implements Data {
@@ -287,10 +297,16 @@
             return array;
         }
 
+        @Override
+        public boolean isEvalCode() {
+            return false;
+        }
+
         boolean isDeferred() {
             return array == null;
         }
 
+        @SuppressWarnings("try")
         protected void checkPermissionAndClose() throws IOException {
             try (InputStream in = url.openStream()) {
                 // empty
@@ -373,11 +389,23 @@
      *
      * @param name    source name
      * @param content contents as char array
+     * @param isEval does this represent code from 'eval' call?
+     * @return source instance
+     */
+    public static Source sourceFor(final String name, final char[] content, final boolean isEval) {
+        return new Source(name, baseName(name), new RawData(content, isEval));
+    }
+
+    /**
+     * Returns a Source instance
+     *
+     * @param name    source name
+     * @param content contents as char array
      *
      * @return source instance
      */
     public static Source sourceFor(final String name, final char[] content) {
-        return new Source(name, baseName(name), new RawData(content));
+        return sourceFor(name, content, false);
     }
 
     /**
@@ -385,11 +413,22 @@
      *
      * @param name    source name
      * @param content contents as string
+     * @param isEval does this represent code from 'eval' call?
+     * @return source instance
+     */
+    public static Source sourceFor(final String name, final String content, final boolean isEval) {
+        return new Source(name, baseName(name), new RawData(content, isEval));
+    }
+
+    /**
+     * Returns a Source instance
      *
+     * @param name    source name
+     * @param content contents as string
      * @return source instance
      */
     public static Source sourceFor(final String name, final String content) {
-        return new Source(name, baseName(name), new RawData(content));
+        return sourceFor(name, content, false);
     }
 
     /**
@@ -555,6 +594,15 @@
     }
 
     /**
+     * Returns whether this source was submitted via 'eval' call or not.
+     *
+     * @return true if this source represents code submitted via 'eval'
+     */
+    public boolean isEvalCode() {
+        return data.isEvalCode();
+    }
+
+    /**
      * Find the beginning of the line containing position.
      * @param position Index to offending token.
      * @return Index of first character of line.
--- a/nashorn/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Tue Jul 01 14:27:28 2014 -0700
@@ -201,7 +201,7 @@
 
     @Override
     public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
-        return userAccessorGetter(getAccessors((owner != null) ? owner : (ScriptObject)self), self);
+        return userAccessorGetter(getAccessors((owner != null) ? owner : self), self);
     }
 
     @Override
@@ -221,7 +221,7 @@
 
     @Override
     public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
-        userAccessorSetter(getAccessors((owner != null) ? owner : (ScriptObject)self), strict ? getKey() : null, self, value);
+        userAccessorSetter(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value);
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java	Tue Jul 01 14:27:28 2014 -0700
@@ -31,6 +31,8 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.SwitchPoint;
+import jdk.nashorn.api.scripting.AbstractJSObject;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.LinkRequest;
@@ -312,7 +314,22 @@
 
     @SuppressWarnings("unused")
     private static Object bindToExpression(final Object fn, final Object receiver) {
-        return fn instanceof ScriptFunction ? bindToExpression((ScriptFunction) fn, receiver) : fn;
+        if (fn instanceof ScriptFunction) {
+            return bindToExpression((ScriptFunction) fn, receiver);
+        } else if (fn instanceof ScriptObjectMirror) {
+            final ScriptObjectMirror mirror = (ScriptObjectMirror)fn;
+            if (mirror.isFunction()) {
+                // We need to make sure correct 'this' is used for calls with Ident call
+                // expressions. We do so here using an AbstractJSObject instance.
+                return new AbstractJSObject() {
+                    public Object call(final Object thiz, final Object... args) {
+                        return mirror.call(withFilterExpression(receiver), args);
+                    }
+                };
+            }
+        }
+
+        return fn;
     }
 
     private static Object bindToExpression(final ScriptFunction fn, final Object receiver) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java	Tue Jul 01 14:27:28 2014 -0700
@@ -150,7 +150,7 @@
 
     @Override
     public Object getObject(final int index) {
-        return (int)(0x0ff & buf.get(index));
+        return 0x0ff & buf.get(index);
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/InvokeByName.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/InvokeByName.java	Tue Jul 01 14:27:28 2014 -0700
@@ -90,7 +90,7 @@
         if(plength == 0) {
             finalPtypes = new Class<?>[] { Object.class, targetClass };
         } else {
-            finalPtypes = new Class[plength + 2];
+            finalPtypes = new Class<?>[plength + 2];
             finalPtypes[0] = Object.class;
             finalPtypes[1] = targetClass;
             System.arraycopy(ptypes, 0, finalPtypes, 2, plength);
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Tue Jul 01 14:27:28 2014 -0700
@@ -151,6 +151,7 @@
 range.error.invalid.radix=radix argument must be in [2, 36]
 range.error.invalid.date=Invalid Date
 range.error.too.many.errors=Script contains too many errors: {0} errors
+range.error.concat.string.too.big=Concatenated String is too big
 
 reference.error.not.defined="{0}" is not defined
 reference.error.cant.be.used.as.lhs="{0}" can not be used as the left-hand side of assignment
--- a/nashorn/test/script/basic/JDK-8030182_2.js.EXPECTED	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/test/script/basic/JDK-8030182_2.js.EXPECTED	Tue Jul 01 14:27:28 2014 -0700
@@ -1,3 +1,3 @@
 ReferenceError: "g" is not defined
-	at <program> (test/script/basic/JDK-8030182_2.js#42:4<eval>@0:-1)
+	at <program> (test/script/basic/JDK-8030182_2.js#42:4<eval>@1:-1)
 	at <program> (test/script/basic/JDK-8030182_2.js:42)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8046013.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8046013: TypeError: Cannot apply "with" to non script object
+ *
+ * @test
+ * @run
+ */
+
+var obj = loadWithNewGlobal({
+    script: "({ f: 33 })",
+    name: "test"
+});
+
+with (obj) {
+   print("f = " + f);
+}
+
+var obj2 = loadWithNewGlobal({
+    script: "var obj = Object.create({ foo: 42 }); obj.bar = 'hello'; obj",
+    name: "test2"
+});
+
+with (obj2) {
+    print("foo = " + foo);
+    print("bar = " + bar);
+}
+
+var obj3 = loadWithNewGlobal({
+    script: "({ f: 33, func: function() { print('this.f =', this.f); } })",
+    name: "test"
+});
+
+with(obj3) {
+    func();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8046013.js.EXPECTED	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,4 @@
+f = 33
+foo = 42
+bar = hello
+this.f = 33
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8046905.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ * 
+ * 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.
+ */
+
+/**
+ * JDK-8046905: apply on apply is broken
+ *
+ * @test
+ * @run
+ */
+
+var apply = Function.prototype.apply;
+var call = Function.prototype.call;
+var sort = Array.prototype.sort;
+var join = Array.prototype.join;
+
+// Running three times so that we test an already linked call site too:
+// i==0: linking initially with assumed optimistic returned type int.
+// i==1: linking after deoptimization with returned type Object.
+// i==2: re-running code linked in previous iteration. This will 
+//       properly exercise the guards too.
+print("1 level of apply")
+for(i = 0; i < 3; ++i) {
+    print(sort.apply([4,3,2,1]))
+}
+print("2 levels of apply")
+for(i = 0; i < 3; ++i) {
+    print(apply.apply(sort,[[4,3,2,1]]))
+}
+print("3 levels of apply")
+for(i = 0; i < 3; ++i) {
+    print(apply.apply(apply,[sort,[[4,3,2,1]]]))
+}
+print("4 levels of apply")
+for(i = 0; i < 3; ++i) {
+    print(apply.apply(apply,[apply,[sort,[[4,3,2,1]]]]))
+}
+print("5 levels of apply")
+for(i = 0; i < 3; ++i) {
+    print(apply.apply(apply,[apply,[apply,[sort,[[4,3,2,1]]]]]))
+}
+print("Many levels of apply!")
+for(i = 0; i < 3; ++i) {
+    print(apply.apply(apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[apply,[sort,[[4,3,2,1]]]]]]]]]]]]]]]]]]]]]]))
+}
+
+print("different invocations that'll trigger relinking")
+var invocation = [sort,[[4,3,2,1]]];
+for(i = 0; i < 4; ++i) {
+    print(apply.apply(apply,[apply,invocation]))
+    // First change after i==1, so it relinks an otherwise stable linkage
+    if(i == 1) {
+	invocation = [sort,[[8,7,6,5]]];
+    } else if(i == 2) {
+        invocation = [join,[[8,7,6,5],["-"]]];
+    }
+}
+
+print("Many levels of call!")
+for(i = 0; i < 3; ++i) {
+    print(call.call(call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,call,sort,[4,3,2,1]))
+}
+
+print("call apply call apply call... a lot");
+for(i = 0; i < 3; ++i) {
+    print(apply.call(call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [sort, [4,3,2,1]]]]]]]))
+}
+
+print("apply call apply call apply... a lot");
+for(i = 0; i < 3; ++i) {
+    print(call.apply(apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, apply, [call, sort, [[4,3,2,1]]]]]]]]]))
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8046905.js.EXPECTED	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,41 @@
+1 level of apply
+1,2,3,4
+1,2,3,4
+1,2,3,4
+2 levels of apply
+1,2,3,4
+1,2,3,4
+1,2,3,4
+3 levels of apply
+1,2,3,4
+1,2,3,4
+1,2,3,4
+4 levels of apply
+1,2,3,4
+1,2,3,4
+1,2,3,4
+5 levels of apply
+1,2,3,4
+1,2,3,4
+1,2,3,4
+Many levels of apply!
+1,2,3,4
+1,2,3,4
+1,2,3,4
+different invocations that'll trigger relinking
+1,2,3,4
+1,2,3,4
+5,6,7,8
+8-7-6-5
+Many levels of call!
+1,2,3,4
+1,2,3,4
+1,2,3,4
+call apply call apply call... a lot
+1,2,3,4
+1,2,3,4
+1,2,3,4
+apply call apply call apply... a lot
+1,2,3,4
+1,2,3,4
+1,2,3,4
--- a/nashorn/test/script/basic/JDK-8047057.js	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/test/script/basic/JDK-8047057.js	Tue Jul 01 14:27:28 2014 -0700
@@ -29,8 +29,7 @@
  */
 
 // commented out makeFuncAndCall calls are still result in crash
-// Tests commented with //** fail only within test framework.
-// Pass fine with standalone "jjs" mode.
+// Tests commented with //** fail only when assertions are turned on
 
 function makeFuncAndCall(code) {
     Function(code)();
@@ -46,30 +45,31 @@
     }
 }
 
-// makeFuncAndCall("switch(0) { default: {break;} return }");
-// makeFuncAndCall("L: { { break L; } return; }");
+makeFuncAndCall("switch(0) { default: {break;} return }");
+makeFuncAndCall("L: { { break L; } return; }");
 makeFuncAndCall("L: { while(0) break L; return; }");
 makeFuncExpectError("L: {while(0) break L; return [](); }", TypeError);
 // makeFuncAndCall("do with({}) break ; while(0);");
 makeFuncAndCall("while(0) with({}) continue ;");
-//** makeFuncAndCall("eval([]);");
-//** makeFuncAndCall("try{} finally{[]}");
+makeFuncAndCall("eval([]);");
+makeFuncAndCall("try{} finally{[]}");
 makeFuncAndCall("try { } catch(x if 1) { try { } catch(x2) { } }");
 makeFuncAndCall("try { } catch(x if 1) { try { return; } catch(x2) { { } } }");
 makeFuncAndCall("Error() * (false)[-0]--");
 makeFuncAndCall("try { var x = 1, x = null; } finally { }");
 makeFuncAndCall("try { var x = {}, x = []; } catch(x3) { }");
-//** makeFuncAndCall("[delete this]");
+makeFuncAndCall("[delete this]");
 // makeFuncAndCall("if(eval('', eval('', function() {}))) { }");
 // makeFuncAndCall("if(eval('', eval('', function() {}))) { }");
 // makeFuncAndCall("eval(\"[,,];\", [11,12,13,14].some)");
 // makeFuncAndCall("eval(\"1.2e3\", ({})[ /x/ ])");
-// makeFuncAndCall("eval(\"x4\", x3);");
+makeFuncExpectError("eval(\"x4\", x3);", ReferenceError);
 makeFuncAndCall("with({5.0000000000000000000000: String()}){(false); }");
 makeFuncAndCall("try { var x = undefined, x = 5.0000000000000000000000; } catch(x) { x = undefined; }");
 makeFuncAndCall("(function (x){ x %= this}(false))");
-// makeFuncAndCall("eval.apply.apply(function(){ eval('') })");
+makeFuncAndCall("eval.apply.apply(function(){ eval('') })");
 makeFuncAndCall("(false % !this) && 0");
 makeFuncAndCall("with({8: 'fafafa'.replace()}){ }");
 makeFuncAndCall("(function (x) '' )(true)");
 makeFuncExpectError("new eval(function(){})", TypeError);
+//** makeFuncAndCall('eval("23", ({})[/x/])');
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047078.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8047078: ArrayLiteral mutability caused trouble in optimistic types
+ *
+ * @test
+ * @run
+ */
+
+function makeFuncAndCall(code) {
+    Function(code)();
+}
+
+makeFuncAndCall("eval([]);");
+makeFuncAndCall("eval([1]);");
+makeFuncAndCall("eval([1,2,3,,4]);");
+makeFuncAndCall("try{} finally{[]}");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047357.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ * 
+ * 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.
+ */
+
+/**
+ * JDK-8047357: More precise synthetic return + unreachable throw
+ *
+ * @test
+ * @run
+ */
+
+print((function() { switch(0) { default: {var x; break ; } throw x; } })());
+print((function() { switch(0) { default: {break;} return; } })());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047357.js.EXPECTED	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,2 @@
+undefined
+undefined
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047359.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8047359: large string size RangeError should be thrown rather than reporting negative length
+ *
+ * @test
+ * @run
+ */
+
+try {
+    var s = " "; for (var i=0;i<31;++i) s+=s; s.length;
+    throw new Error("should have thrown RangeError!");
+} catch (e) {
+    if (! (e instanceof RangeError)) {
+        fail("RangeError expected, got " + e);
+    }
+}
+
+try {
+    var s = " "; for (var i=0;i<31;++i) s+=s;
+    throw new Error("should have thrown RangeError!");
+} catch (e) {
+    if (! (e instanceof RangeError)) {
+        fail("RangeError expected, got " + e);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047369.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8047369: Add regression tests for passing test cases of JDK-8024971
+ * 
+ * @test
+ * @run
+ * @option -scripting
+ */
+
+function makeFuncAndCall(code) {
+    Function(code)();
+}
+
+function makeFuncExpectError(code, ErrorType) {
+    try {
+        makeFuncAndCall(code);
+    } catch (e) {
+        if (! (e instanceof ErrorType)) {
+            fail(ErrorType.name + " expected, got " + e);
+        }
+    }
+}
+
+function evalExpectError(code, ErrorType) {
+    try {
+        eval(code)();
+    } catch (e) {
+        if (! (e instanceof ErrorType)) {
+            fail(ErrorType.name + " expected, got " + e);
+        }
+    }
+}
+
+function evalExpectValue(code, value) {
+    if (eval(code) != value) {
+        fail("Expected " + value + " with eval of " + code);
+    }
+}
+
+makeFuncAndCall("for(x.x in 0) {}");
+// bug JDK-8047357
+// makeFuncAndCall("switch((null >> x3)) { default: {var x; break ; }\nthrow x; }");
+makeFuncExpectError("switch(x) { case 8: break; case false: }", ReferenceError);
+makeFuncAndCall("try { return true; } finally { return false; } ");
+makeFuncAndCall("({ get 1e81(){} })");
+makeFuncAndCall("{var x, x3;try { return 0; } finally { return 3/0; } }");
+makeFuncExpectError("with(x ? 1e81 : (x2.constructor = 0.1)) {}", ReferenceError);
+makeFuncAndCall("while(x-=1) {var x=0; }");
+makeFuncAndCall("while((x-=false) && 0) { var x = this; }");
+makeFuncAndCall("/*infloop*/while(x4-=x) var x, x4 = x1;");
+makeFuncAndCall("/*infloop*/L:while(x+=null) { this;var x = /x/g ; }");
+makeFuncAndCall("while((x1|=0.1) && 0) { var x1 = -0, functional; }");
+makeFuncAndCall("with({}) return (eval(\"arguments\"));");
+
+evalExpectValue(<<CODE
+    var s = "(function() { return y })()";
+    (function() { 
+        with({ y:1 })
+            eval(s)
+    })();
+    (function() { 
+        with({ 
+            get y() { return "get"; }
+        }) 
+        return eval(s)
+    })();
+CODE, "get");
+
+// bug JDK-8047359
+// evalExpectValue("s = ' '; for (var i=0;i<31;++i) s+=s; s.length", RangeError);
+
+evalExpectValue(<<CODE
+    function f(o) {
+        var eval=0; 
+        with({
+            get eval() { return o.eval }
+        }) 
+        return eval("1+2");
+    } 
+    f(this);
+CODE, 3)
+
+evalExpectValue(<<CODE
+    function f() {
+        var a=1,e=2;
+        try { 
+            throw 3
+        } catch(e) {
+            return + function g(){return eval('a+e')}()
+        }
+    }
+    f();
+CODE, 4);
+
+//evalExpectValue(
+// "function f(){var a=1; with({get a(){return false}}) return a}; f()", false);
+
+evalExpectError("function public() {\"use strict\"}", SyntaxError);
+evalExpectError("function f(public) {\"use strict\"}", SyntaxError);
+evalExpectError("function f() { switch(x) {} } f()", ReferenceError);
+
+// bug JDK-8047364
+// makeFuncAndCall("L1:try { return } finally { break L1 }");
+
+evalExpectValue(<<CODE
+    function f() { 
+        function g() { return 0 }
+        function g() { return 1 } 
+        function g$1() { return 2 }
+        return g$1()
+    }
+
+    f();
+CODE, 2);
+
+evalExpectValue(<<CODE
+    function f() {
+        function g() {return 0 }
+        var h = function g() { return 1 };
+        function g$1() { return 2 }; 
+        return h()
+    }
+
+    f()
+CODE, 1);
+
+evalExpectValue(<<CODE
+    function f() {
+        var obj = { get ":"() {} }
+        var desc = Object.getOwnPropertyDescriptor(obj, ":")
+        return desc.get.name
+    }
+
+    f()
+CODE, ":");
+
+evalExpectValue(<<CODE
+    function f() {
+        var obj = { set ":"(a) {} };
+        var desc = Object.getOwnPropertyDescriptor(obj, ":");
+        return desc.set;
+    }
+
+    f()
+CODE, "set \":\"(a) {}");
+
+// bug JDK-8047366
+// evalExpectValue("(1000000000000000128).toString()", "1000000000000000100");
+// evalExpectValue("(1000000000000000128).toFixed().toString()", "1000000000000000128");
+
+try {
+    Function("-", { 
+        toString: function() {
+            throw "err"
+        }
+    })();
+} catch (e) {
+    if (e != "err") {
+        fail("Expected 'err' here, got " + e);
+    }
+}
+evalExpectError("function f() { switch(x) {} } f()", ReferenceError);
+Array.prototype.splice.call(Java.type("java.util.HashMap"))
+Array.prototype.slice.call(Java.type("java.util.HashMap"))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047371.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ * 
+ * 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.
+ */
+
+/**
+ * JDK-8047371: local variable declaration in TypeEvaluator should use ScriptObject.addOwnProperty instead of .set
+ *
+ * @test
+ * @run
+ */
+
+print((function(){ var a=1; with({ get a() { return false } }) return a })());
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047371.js.EXPECTED	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,1 @@
+false
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047728.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8047728: (function(x){var o={x:0}; with(o){delete x} return o.x})() evaluates to 0 instead of undefined
+ *
+ * @test
+ * @run
+ */
+
+function func(x) {
+    var o = {x:0}; 
+    with(o){
+       delete x;
+    }
+    return o.x
+}
+
+if (typeof func() != 'undefined') {
+    fail("expected undefined from 'func' call");
+}
+
+function func2() {
+    var x;
+    var o = {x:0}; 
+    with(o){
+       delete x;
+    }
+    return o.x
+}
+
+if (typeof func2() != 'undefined') {
+    fail("expected undefined from 'func2' call");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047959.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8047959: bindings created for declarations in eval code are not mutable
+ *
+ * @test
+ * @run
+ */
+
+eval("var x=10;");
+print('delete x? ' + delete x);
+print('typeof x = ' + typeof x);
+
+eval("function f() {}");
+print('delete f? ' + delete f);
+print('typeof f = ' + typeof f);
+
+var foo = 223;
+print('delete foo? ' + delete foo);
+print('typeof foo = ' + typeof foo);
+
+function func() {}
+print('delete func? ' + delete func);
+print('typeof func = ' + typeof func);
+
+eval("var foo = 33;");
+print("delete foo? " + delete foo);
+print("typeof foo? " + typeof foo);
+print("foo = " + foo);
+
+var x = "global"; 
+(function(){
+    eval("var x='local'");
+    print("x in function = "+ x);
+    print("delete x? = " + delete x);
+    print("x after delete = " + x);
+})();
+print("x = " + x); 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047959.js.EXPECTED	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,15 @@
+delete x? false
+typeof x = number
+delete f? true
+typeof f = undefined
+delete foo? false
+typeof foo = number
+delete func? false
+typeof func = function
+delete foo? false
+typeof foo? number
+foo = 33
+x in function = local
+delete x? = true
+x after delete = global
+x = global
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048071.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8048071: eval within 'with' statement does not use correct scope if with scope expression has a copy of eval
+ *
+ * @test
+ * @run
+ */
+
+function func() {
+   var x = 1;
+   with ({ eval: this.eval }) {
+      eval("var x = 23");
+   }
+
+   return x;
+}
+
+print(func());
+print("typeof x? " + typeof x);
+
+print((function(global){
+    var x = 1;
+    with(global) {
+        eval("eval('var x=0')");
+    }
+    return x;
+})(this));
+print("typeof x? " + typeof x);
+
+print((function(global){
+   var x = 1;
+   with({eval:  global.eval}) {
+       eval("eval('var x=0')");
+   }
+   return x;
+})(this));
+print("typeof x? " + typeof x);
+
+// not-builtin eval cases
+
+(function () {
+   function eval(str) {
+      print("local eval called: " + str);
+      print(this);
+   }
+
+   with({}) {
+     eval("hello");
+   }
+})();
+
+(function () {
+   with({
+    eval:function(str) {
+       print("with's eval called: " + str);
+       print("this = " + this);
+       print("this.foo = " + this.foo);
+    },
+    foo: 42
+   }) {
+     eval("hello")
+   }
+})();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048071.js.EXPECTED	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,11 @@
+23
+typeof x? undefined
+0
+typeof x? undefined
+0
+typeof x? undefined
+local eval called: hello
+[object global]
+with's eval called: hello
+this = [object Object]
+this.foo = 42
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048079_1.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ * 
+ * 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.
+ */
+
+/**
+ * JDK-8048079: Persistent code store is broken after optimistic types merge
+ *
+ * @test
+ * @run
+ * @option -pcc
+ * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
+ * @fork
+ */
+
+load(__DIR__ + 'prototype.js');
+load(__DIR__ + 'yui.js');
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048079_1.js.EXPECTED	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,3 @@
+parsed and compiled ok prototype.js
+parsed and compiled ok yui-min.js
+parsed and compiled ok yui.js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048079_2.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ * 
+ * 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.
+ */
+
+/**
+ * JDK-8048079: Persistent code store is broken after optimistic types merge
+ *
+ * @test
+ * @run
+ * @option -pcc
+ * @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
+ * @fork
+ */
+
+load(__DIR__ + 'prototype.js');
+load(__DIR__ + 'yui.js');
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048079_2.js.EXPECTED	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,3 @@
+parsed and compiled ok prototype.js
+parsed and compiled ok yui-min.js
+parsed and compiled ok yui.js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048505.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/**
+ * Read fully parameter test
+ *
+ * @test
+ * @option -scripting
+ * @run
+ */
+
+var str = __FILE__;
+var first = readFully(str);
+print(typeof str);
+
+var str2 = __FILE__.substring(0,5);
+var str3 = __FILE__.substring(5);
+print(typeof str2);
+print(typeof str3);
+
+var cons = str2 + str3;
+print(typeof cons);
+
+var second = readFully(cons);
+
+var f = new java.io.File(str);
+print(typeof f);
+var third = readFully(f);
+
+print(first.length() == second.length());
+print(first.length() == third.length());
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048505.js.EXPECTED	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,7 @@
+string
+string
+string
+string
+object
+true
+true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048586.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ * 
+ * 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.
+ */
+
+/**
+ * JDK-8048586: String concatenation with optimistic types is slow
+ *
+ * @test
+ * @run
+ */
+
+var body = '';
+
+for (var i = 0; i < 1024 * 1024; i++) {
+  body += 'hello world\n';
+}
+
+body = '';
+
+for (var i = 0; i < 1024 * 1024; i++) {
+  body = body + 'hello world\n';
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8048718.js	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8048718: JSON.parse('{"0":0, "64":0}') throws ArrayindexOutOfBoundsException
+ *
+ * @test
+ * @run
+ */
+
+var obj = JSON.parse('{"0":0, "64":0}');
+if ("1" in obj) {
+    fail("found element at index 1");
+}
+
+if ("63" in obj) {
+    fail("found element at index 63");
+}
+
+if (obj[0] != 0) {
+    fail("expected obj[0] to be 0");
+}
+
+if (obj[64] != 0) {
+    fail("expected obj[64] to be 0");
+}
+
+for (var i in obj) {
+    if (i != "0" && i != "64") {
+        fail("invalid property " + i);
+    }
+}
--- a/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java	Tue Jul 01 14:27:28 2014 -0700
@@ -593,6 +593,20 @@
         }
     }
 
+    // @bug 8046013: TypeError: Cannot apply "with" to non script object
+    @Test
+    public void withOnMirrorTest() throws ScriptException {
+        final ScriptEngineManager m = new ScriptEngineManager();
+        final ScriptEngine e = m.getEngineByName("nashorn");
+
+        final Object obj = e.eval("({ foo: 'hello'})");
+        final Object[] arr = new Object[1];
+        arr[0] = obj;
+        e.put("arr", arr);
+        final Object res = e.eval("var res; with(arr[0]) { res = foo; }; res");
+        assertEquals(res, "hello");
+    }
+
     private static void checkProperty(final ScriptEngine e, final String name)
         throws ScriptException {
         final String value = System.getProperty(name);
--- a/nashorn/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java	Wed Jul 05 19:47:10 2017 +0200
+++ b/nashorn/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java	Tue Jul 01 14:27:28 2014 -0700
@@ -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", "--optimistic-types=false", "--lazy-compilation=false"};
+    private static final String[] ENGINE_OPTIONS = new String[]{"--persistent-code-cache"};
 
     public void checkCompiledScripts(final DirectoryStream<Path> stream, int numberOfScripts) throws IOException {
         for (final Path file : stream) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/src/jdk/nashorn/internal/runtime/ConsStringTest.java	Tue Jul 01 14:27:28 2014 -0700
@@ -0,0 +1,135 @@
+/*
+ * 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 static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for JSType methods.
+ *
+ * @test
+ * @run testng jdk.nashorn.internal.runtime.ConsStringTest
+ */
+public class ConsStringTest {
+
+    /**
+     * Test toString conversion
+     */
+    @Test
+    public void testConsStringToString() {
+        final ConsString cs1 = new ConsString("b", "c");
+        final ConsString cs2 = new ConsString("d", "e");
+        final ConsString cs3 = new ConsString(cs1, cs2);
+        final ConsString cs4 = new ConsString(cs3, "f");
+        final ConsString cs5 = new ConsString("a", cs4);
+        assertEquals(cs5.toString(), "abcdef");
+        assertEquals(cs4.toString(), "bcdef");
+        assertEquals(cs3.toString(), "bcde");
+        assertEquals(cs2.toString(), "de");
+        assertEquals(cs1.toString(), "bc");
+        // ConsStrings should be flattened now
+        assertEquals(cs1.getComponents()[0], "bc");
+        assertEquals(cs1.getComponents()[1], "");
+        assertEquals(cs2.getComponents()[0], "de");
+        assertEquals(cs2.getComponents()[1], "");
+        assertEquals(cs3.getComponents()[0], "bcde");
+        assertEquals(cs3.getComponents()[1], "");
+        assertEquals(cs4.getComponents()[0], "bcdef");
+        assertEquals(cs4.getComponents()[1], "");
+        assertEquals(cs5.getComponents()[0], "abcdef");
+        assertEquals(cs5.getComponents()[1], "");
+    }
+
+    /**
+     * Test charAt
+     */
+    @Test
+    public void testConsStringCharAt() {
+        final ConsString cs1 = new ConsString("b", "c");
+        final ConsString cs2 = new ConsString("d", "e");
+        final ConsString cs3 = new ConsString(cs1, cs2);
+        final ConsString cs4 = new ConsString(cs3, "f");
+        final ConsString cs5 = new ConsString("a", cs4);
+        assertEquals(cs1.charAt(1), 'c');
+        assertEquals(cs2.charAt(0), 'd');
+        assertEquals(cs3.charAt(3), 'e');
+        assertEquals(cs4.charAt(1), 'c');
+        assertEquals(cs5.charAt(2), 'c');
+        // ConsStrings should be flattened now
+        assertEquals(cs1.getComponents()[0], "bc");
+        assertEquals(cs1.getComponents()[1], "");
+        assertEquals(cs2.getComponents()[0], "de");
+        assertEquals(cs2.getComponents()[1], "");
+        assertEquals(cs3.getComponents()[0], "bcde");
+        assertEquals(cs3.getComponents()[1], "");
+        assertEquals(cs4.getComponents()[0], "bcdef");
+        assertEquals(cs4.getComponents()[1], "");
+        assertEquals(cs5.getComponents()[0], "abcdef");
+        assertEquals(cs5.getComponents()[1], "");
+    }
+
+
+    /**
+     * Test flattening of top-level and internal ConsStrings
+     */
+    @Test
+    public void testConsStringFlattening() {
+        final ConsString cs1 = new ConsString("b", "c");
+        final ConsString cs2 = new ConsString("d", "e");
+        final ConsString cs3 = new ConsString(cs1, cs2);
+        final ConsString cs4 = new ConsString(cs3, "f");
+
+        final ConsString cs5 = new ConsString("a", cs4);
+        // top-level ConsString should not yet be flattened
+        assert(cs5.getComponents()[0] == "a");
+        assert(cs5.getComponents()[1] == cs4);
+        assertEquals(cs5.toString(), "abcdef");
+        // top-level ConsString should be flattened
+        assertEquals(cs5.getComponents()[0], "abcdef");
+        assertEquals(cs5.getComponents()[1], "");
+        // internal ConsString should not yet be flattened after first traversal
+        assertEquals(cs4.getComponents()[0], cs3);
+        assertEquals(cs4.getComponents()[1], "f");
+
+        final ConsString cs6 = new ConsString("a", cs4);
+        // top-level ConsString should not yet be flattened
+        assertEquals(cs6.getComponents()[0], "a");
+        assertEquals(cs6.getComponents()[1], cs4);
+        assertEquals(cs6.toString(), "abcdef");
+        // top-level ConsString should be flattened
+        assertEquals(cs6.getComponents()[0], "abcdef");
+        assertEquals(cs6.getComponents()[1], "");
+        // internal ConsString should have been flattened after second traversal
+        assertEquals(cs4.getComponents()[0], "bcdef");
+        assertEquals(cs4.getComponents()[1], "");
+    }
+}