keys = new ArrayList<>();
// add string index keys
@@ -403,7 +406,7 @@
}
// add super class properties
- keys.addAll(Arrays.asList(super.getOwnKeys(all)));
+ keys.addAll(Arrays.asList(super.getOwnKeys(all, nonEnumerable)));
return keys.toArray(new String[keys.size()]);
}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/objects/annotations/Optimistic.java
--- a/nashorn/src/jdk/nashorn/internal/objects/annotations/Optimistic.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/objects/annotations/Optimistic.java Mon Aug 11 10:07:15 2014 -0700
@@ -40,7 +40,7 @@
* in NativeArray that takes an int, write
*
*
- * @SpecializedFunction @Optimistic
+ * {@literal @}SpecializedFunction {@literal @}Optimistic
* public static int push(final Object self, final int x, final int programPoint) {
* try {
* //push code assuming that this is an int array
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/parser/AbstractParser.java
--- a/nashorn/src/jdk/nashorn/internal/parser/AbstractParser.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/parser/AbstractParser.java Mon Aug 11 10:07:15 2014 -0700
@@ -91,9 +91,6 @@
/** What should line numbers be counted from? */
protected final int lineOffset;
- /** //@ sourceURL or //# sourceURL */
- protected String sourceURL;
-
/**
* Construct a parser.
*
@@ -182,7 +179,7 @@
// currently only @sourceURL=foo supported
private void checkDirectiveComment() {
// if already set, ignore this one
- if (sourceURL != null) {
+ if (source.getExplicitURL() != null) {
return;
}
@@ -190,7 +187,7 @@
final int len = comment.length();
// 4 characters for directive comment marker //@\s or //#\s
if (len > 4 && comment.substring(4).startsWith(SOURCE_URL_PREFIX)) {
- sourceURL = comment.substring(4 + SOURCE_URL_PREFIX.length());
+ source.setExplicitURL(comment.substring(4 + SOURCE_URL_PREFIX.length()));
}
}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/parser/Parser.java
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java Mon Aug 11 10:07:15 2014 -0700
@@ -477,8 +477,7 @@
name,
parameters,
kind,
- flags,
- sourceURL);
+ flags);
lc.push(functionNode);
// Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the
@@ -702,10 +701,6 @@
script = restoreFunctionNode(script, token); //commit code
script = script.setBody(lc, script.getBody().setNeedsScope(lc));
- // user may have directive comment to set sourceURL
- if (sourceURL != null) {
- script = script.setSourceURL(lc, sourceURL);
- }
return script;
}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Mon Aug 11 10:07:15 2014 -0700
@@ -136,16 +136,16 @@
}
/** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
- private transient MethodHandle primitiveGetter;
+ transient MethodHandle primitiveGetter;
/** Seed setter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
- private transient MethodHandle primitiveSetter;
+ transient MethodHandle primitiveSetter;
/** Seed getter for the Object version of this field */
- private transient MethodHandle objectGetter;
+ transient MethodHandle objectGetter;
/** Seed setter for the Object version of this field */
- private transient MethodHandle objectSetter;
+ transient MethodHandle objectSetter;
/**
* Current type of this object, in object only mode, this is an Object.class. In dual-fields mode
@@ -185,10 +185,10 @@
* @param key the property key
* @param flags the property flags
* @param slot spill slot
- * @param objectGetter
- * @param objectSetter
- * @param primitiveGetter
- * @param primitiveSetter
+ * @param primitiveGetter primitive getter
+ * @param primitiveSetter primitive setter
+ * @param objectGetter object getter
+ * @param objectSetter object setter
*/
protected AccessorProperty(
final String key,
@@ -255,7 +255,7 @@
}
/**
- * Normal ACCESS PROPERTY constructor given a structure glass.
+ * Normal ACCESS PROPERTY constructor given a structure class.
* Constructor for dual field AccessorPropertys.
*
* @param key property key
@@ -267,6 +267,7 @@
super(key, flags, slot);
initGetterSetter(structure);
+ initializeType();
}
private void initGetterSetter(final Class> structure) {
@@ -291,8 +292,6 @@
objectSetter = gs.objectSetters[slot];
primitiveSetter = gs.primitiveSetters[slot];
}
-
- initializeType();
}
/**
@@ -412,8 +411,8 @@
}
}
- @Override
- public long getLongValue(final ScriptObject self, final ScriptObject owner) {
+ @Override
+ public long getLongValue(final ScriptObject self, final ScriptObject owner) {
try {
return (long)getGetter(long.class).invokeExact((Object)self);
} catch (final Error | RuntimeException e) {
@@ -531,12 +530,13 @@
@Override
void initMethodHandles(final Class> structure) {
+ // sanity check for structure class
if (!ScriptObject.class.isAssignableFrom(structure) || !StructureLoader.isStructureClass(structure.getName())) {
throw new IllegalArgumentException();
}
- if (!isSpill()) {
- initGetterSetter(structure);
- }
+ // this method is overridden in SpillProperty
+ assert !isSpill();
+ initGetterSetter(structure);
}
@Override
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/CodeInstaller.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/CodeInstaller.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CodeInstaller.java Mon Aug 11 10:07:15 2014 -0700
@@ -92,5 +92,14 @@
* @param classBytes map of class names to class bytes
* @param constants constants array
*/
- public void storeCompiledScript(Source source, String mainClassName, Map classBytes, Object[] constants);
+ public void storeScript(String cacheKey, Source source, String mainClassName, Map classBytes,
+ Map initializers, Object[] constants, int compilationId);
+
+ /**
+ * Load a previously compiled script
+ * @param source the script source
+ * @param functionKey the function id and signature
+ * @return compiled script data
+ */
+ public StoredScript loadScript(Source source, String functionKey);
}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/CodeStore.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/CodeStore.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CodeStore.java Mon Aug 11 10:07:15 2014 -0700
@@ -25,6 +25,11 @@
package jdk.nashorn.internal.runtime;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.runtime.logging.DebugLogger;
+import jdk.nashorn.internal.runtime.logging.Loggable;
+import jdk.nashorn.internal.runtime.logging.Logger;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -42,21 +47,22 @@
/**
* A code cache for persistent caching of compiled scripts.
*/
-final class CodeStore {
+@Logger(name="codestore")
+final class CodeStore implements Loggable {
private final File dir;
private final int minSize;
+ private final DebugLogger log;
// Default minimum size for storing a compiled script class
private final static int DEFAULT_MIN_SIZE = 1000;
/**
* Constructor
- * @param path directory to store code in
* @throws IOException
*/
- public CodeStore(final String path) throws IOException {
- this(path, DEFAULT_MIN_SIZE);
+ public CodeStore(final Context context, final String path) throws IOException {
+ this(context, path, DEFAULT_MIN_SIZE);
}
/**
@@ -65,9 +71,20 @@
* @param minSize minimum file size for caching scripts
* @throws IOException
*/
- public CodeStore(final String path, final int minSize) throws IOException {
+ public CodeStore(final Context context, final String path, final int minSize) throws IOException {
this.dir = checkDirectory(path);
this.minSize = minSize;
+ this.log = initLogger(context);
+ }
+
+ @Override
+ public DebugLogger initLogger(Context context) {
+ return context.getLogger(getClass());
+ }
+
+ @Override
+ public DebugLogger getLogger() {
+ return log;
}
private static File checkDirectory(final String path) throws IOException {
@@ -77,11 +94,11 @@
public File run() throws IOException {
final File dir = new File(path).getAbsoluteFile();
if (!dir.exists() && !dir.mkdirs()) {
- throw new IOException("Could not create directory: " + dir);
+ throw new IOException("Could not create directory: " + dir.getPath());
} else if (!dir.isDirectory()) {
- throw new IOException("Not a directory: " + dir);
+ throw new IOException("Not a directory: " + dir.getPath());
} else if (!dir.canRead() || !dir.canWrite()) {
- throw new IOException("Directory not readable or writable: " + dir);
+ throw new IOException("Directory not readable or writable: " + dir.getPath());
}
return dir;
}
@@ -91,69 +108,85 @@
}
}
+ private File getCacheFile(final Source source, final String functionKey) {
+ return new File(dir, source.getDigest() + '-' + functionKey);
+ }
+
+ /**
+ * Generate a string representing the function with {@code functionId} and {@code paramTypes}.
+ * @param functionId function id
+ * @param paramTypes parameter types
+ * @return a string representing the function
+ */
+ public static String getCacheKey(final int functionId, final Type[] paramTypes) {
+ final StringBuilder b = new StringBuilder().append(functionId);
+ if(paramTypes != null && paramTypes.length > 0) {
+ b.append('-');
+ for(final Type t: paramTypes) {
+ b.append(Type.getShortSignatureDescriptor(t));
+ }
+ }
+ return b.toString();
+ }
+
/**
* Return a compiled script from the cache, or null if it isn't found.
*
* @param source the source
- * @return the compiled script or null
- * @throws IOException
- * @throws ClassNotFoundException
+ * @param functionKey the function key
+ * @return the stored script or null
*/
- public CompiledScript getScript(final Source source) throws IOException, ClassNotFoundException {
+ public StoredScript loadScript(final Source source, final String functionKey) {
if (source.getLength() < minSize) {
return null;
}
- final File file = new File(dir, source.getDigest());
+ final File file = getCacheFile(source, functionKey);
try {
- return AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction() {
@Override
- public CompiledScript run() throws IOException, ClassNotFoundException {
+ public StoredScript run() throws IOException, ClassNotFoundException {
if (!file.exists()) {
return null;
}
try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) {
- final CompiledScript compiledScript = (CompiledScript) in.readObject();
- compiledScript.setSource(source);
- return compiledScript;
+ final StoredScript storedScript = (StoredScript) in.readObject();
+ getLogger().info("loaded ", source, "-", functionKey);
+ return storedScript;
}
}
});
} catch (final PrivilegedActionException e) {
- final Exception ex = e.getException();
- if (ex instanceof IOException) {
- throw (IOException) ex;
- } else if (ex instanceof ClassNotFoundException) {
- throw (ClassNotFoundException) ex;
- }
- throw (new RuntimeException(ex));
+ getLogger().warning("failed to load ", source, "-", functionKey, ": ", e.getException());
+ return null;
}
}
/**
* Store a compiled script in the cache.
*
+ * @param functionKey the function key
* @param source the source
* @param mainClassName the main class name
* @param classBytes a map of class bytes
* @param constants the constants array
- * @throws IOException
*/
- public void putScript(final Source source, final String mainClassName, final Map classBytes, final Object[] constants)
- throws IOException {
+ public void storeScript(final String functionKey, final Source source, final String mainClassName, final Map classBytes,
+ final Map initializers, final Object[] constants, final int compilationId) {
if (source.getLength() < minSize) {
return;
}
for (final Object constant : constants) {
// Make sure all constant data is serializable
if (! (constant instanceof Serializable)) {
+ getLogger().warning("cannot store ", source, " non serializable constant ", constant);
return;
}
}
- final File file = new File(dir, source.getDigest());
- final CompiledScript script = new CompiledScript(source, mainClassName, classBytes, constants);
+ final File file = getCacheFile(source, functionKey);
+ final StoredScript script = new StoredScript(compilationId, mainClassName, classBytes, initializers, constants);
try {
AccessController.doPrivileged(new PrivilegedExceptionAction() {
@@ -162,11 +195,12 @@
try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
out.writeObject(script);
}
+ getLogger().info("stored ", source, "-", functionKey);
return null;
}
});
} catch (final PrivilegedActionException e) {
- throw (IOException) e.getException();
+ getLogger().warning("failed to store ", script, "-", functionKey, ": ", e.getException());
}
}
}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Mon Aug 11 10:07:15 2014 -0700
@@ -37,9 +37,12 @@
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
+import java.util.function.Supplier;
import java.util.logging.Level;
+import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
+import jdk.nashorn.internal.codegen.TypeMap;
import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
@@ -69,7 +72,8 @@
private MethodHandle invoker;
private MethodHandle constructor;
private OptimismInfo optimismInfo;
- private int flags; // from FunctionNode
+ private final int flags; // from FunctionNode
+ private final MethodType callSiteType;
CompiledFunction(final MethodHandle invoker) {
this(invoker, null);
@@ -80,19 +84,20 @@
}
CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) {
- this(invoker, constructor, DebugLogger.DISABLED_LOGGER);
+ this(invoker, constructor, 0, null, DebugLogger.DISABLED_LOGGER);
}
- CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final DebugLogger log) {
+ CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final int flags, final MethodType callSiteType, final DebugLogger log) {
this.invoker = invoker;
this.constructor = constructor;
+ this.flags = flags;
+ this.callSiteType = callSiteType;
this.log = log;
}
CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData,
- final Map invalidatedProgramPoints, final int flags) {
- this(invoker, null, functionData.getLogger());
- this.flags = flags;
+ final Map invalidatedProgramPoints, final MethodType callSiteType, final int flags) {
+ this(invoker, null, flags, callSiteType, functionData.getLogger());
if ((flags & FunctionNode.IS_DEOPTIMIZABLE) != 0) {
optimismInfo = new OptimismInfo(functionData, invalidatedProgramPoints);
} else {
@@ -125,9 +130,9 @@
* Returns an invoker method handle for this function. Note that the handle is safely composable in
* the sense that you can compose it with other handles using any combinators even if you can't affect call site
* invalidation. If this compiled function is non-optimistic, then it returns the same value as
- * {@link #getInvoker()}. However, if the function is optimistic, then this handle will incur an overhead as it will
- * add an intermediate internal call site that can relink itself when the function needs to regenerate its code to
- * always point at the latest generated code version.
+ * {@link #getInvokerOrConstructor(boolean)}. However, if the function is optimistic, then this handle will
+ * incur an overhead as it will add an intermediate internal call site that can relink itself when the function
+ * needs to regenerate its code to always point at the latest generated code version.
* @return a guaranteed composable invoker method handle for this function.
*/
MethodHandle createComposableInvoker() {
@@ -142,7 +147,7 @@
* all other cases, use {@link #createComposableConstructor()}.
* @return a direct constructor method handle for this function.
*/
- MethodHandle getConstructor() {
+ private MethodHandle getConstructor() {
if (constructor == null) {
constructor = createConstructorFromInvoker(createInvokerForPessimisticCaller());
}
@@ -163,8 +168,6 @@
* Compose a constructor from an invoker.
*
* @param invoker invoker
- * @param needsCallee do we need to pass a callee
- *
* @return the composed constructor
*/
private static MethodHandle createConstructorFromInvoker(final MethodHandle invoker) {
@@ -425,6 +428,9 @@
}
boolean matchesCallSite(final MethodType callSiteType, final boolean pickVarArg) {
+ if (callSiteType.equals(this.callSiteType)) {
+ return true;
+ }
final MethodType type = type();
final int fnParamCount = getParamCount(type);
final boolean isVarArg = fnParamCount == Integer.MAX_VALUE;
@@ -462,17 +468,7 @@
return type.parameterType(paramCount - 1).isArray() ? Integer.MAX_VALUE : paramCount;
}
- /**
- * Returns the switch point embodying the optimistic assumptions in this compiled function. It should be used to
- * guard any linking to the function's invoker or constructor.
- * @return the switch point embodying the optimistic assumptions in this compiled function. Null is returned if the
- * function has no optimistic assumptions.
- */
- SwitchPoint getOptimisticAssumptionsSwitchPoint() {
- return canBeDeoptimized() ? optimismInfo.optimisticAssumptions : null;
- }
-
- boolean canBeDeoptimized() {
+ private boolean canBeDeoptimized() {
return optimismInfo != null;
}
@@ -491,19 +487,73 @@
relinkComposableInvoker(cs, this, isConstructor);
return cs.dynamicInvoker();
}
+
+ private static class HandleAndAssumptions {
+ final MethodHandle handle;
+ final SwitchPoint assumptions;
+
+ HandleAndAssumptions(final MethodHandle handle, final SwitchPoint assumptions) {
+ this.handle = handle;
+ this.assumptions = assumptions;
+ }
+
+ GuardedInvocation createInvocation() {
+ return new GuardedInvocation(handle, assumptions);
+ }
+ }
+
+ /**
+ * Returns a pair of an invocation created with a passed-in supplier and a non-invalidated switch point for
+ * optimistic assumptions (or null for the switch point if the function can not be deoptimized). While the method
+ * makes a best effort to return a non-invalidated switch point (compensating for possible deoptimizing
+ * recompilation happening on another thread) it is still possible that by the time this method returns the
+ * switchpoint has been invalidated by a {@code RewriteException} triggered on another thread for this function.
+ * This is not a problem, though, as these switch points are always used to produce call sites that fall back to
+ * relinking when they are invalidated, and in this case the execution will end up here again. What this method
+ * basically does is minimize such busy-loop relinking while the function is being recompiled on a different thread.
+ * @param invocationSupplier the supplier that constructs the actual invocation method handle; should use the
+ * {@code CompiledFunction} method itself in some capacity.
+ * @return a tuple object containing the method handle as created by the supplier and an optimistic assumptions
+ * switch point that is guaranteed to not have been invalidated before the call to this method (or null if the
+ * function can't be further deoptimized).
+ */
+ private synchronized HandleAndAssumptions getValidOptimisticInvocation(final Supplier invocationSupplier) {
+ for(;;) {
+ final MethodHandle handle = invocationSupplier.get();
+ final SwitchPoint assumptions = canBeDeoptimized() ? optimismInfo.optimisticAssumptions : null;
+ if(assumptions != null && assumptions.hasBeenInvalidated()) {
+ // We can be in a situation where one thread is in the middle of a deoptimizing compilation when we hit
+ // this and thus, it has invalidated the old switch point, but hasn't created the new one yet. Note that
+ // the behavior of invalidating the old switch point before recompilation, and only creating the new one
+ // after recompilation is by design. If we didn't wait here for the recompilation to complete, we would
+ // be busy looping through the fallback path of the invalidated switch point, relinking the call site
+ // again with the same invalidated switch point, invoking the fallback, etc. stealing CPU cycles from
+ // the recompilation task we're dependent on. This can still happen if the switch point gets invalidated
+ // after we grabbed it here, in which case we'll indeed do one busy relink immediately.
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ // Intentionally ignored. There's nothing meaningful we can do if we're interrupted
+ }
+ } else {
+ return new HandleAndAssumptions(handle, assumptions);
+ }
+ }
+ }
+
private static void relinkComposableInvoker(final CallSite cs, final CompiledFunction inv, final boolean constructor) {
- final MethodHandle handle = inv.getInvokerOrConstructor(constructor);
- final SwitchPoint assumptions = inv.getOptimisticAssumptionsSwitchPoint();
+ final HandleAndAssumptions handleAndAssumptions = inv.getValidOptimisticInvocation(new Supplier() {
+ @Override
+ public MethodHandle get() {
+ return inv.getInvokerOrConstructor(constructor);
+ }
+ });
+ final MethodHandle handle = handleAndAssumptions.handle;
+ final SwitchPoint assumptions = handleAndAssumptions.assumptions;
final MethodHandle target;
if(assumptions == null) {
target = handle;
} else {
- // This assertion can obviously fail in a multithreaded environment, as we can be in a situation where
- // one thread is in the middle of a deoptimizing compilation when we hit this and thus, it has invalidated
- // the old switch point, but hasn't created the new one yet. Note that the behavior of invalidating the old
- // switch point before recompilation, and only creating the new one after recompilation is by design.
- // TODO: We need to think about thread safety of CompiledFunction objects.
- assert !assumptions.hasBeenInvalidated();
final MethodHandle relink = MethodHandles.insertArguments(RELINK_COMPOSABLE_INVOKER, 0, cs, inv, constructor);
target = assumptions.guardWithTest(handle, MethodHandles.foldArguments(cs.dynamicInvoker(), relink));
}
@@ -514,7 +564,41 @@
return selectCtor ? getConstructor() : createInvokerForPessimisticCaller();
}
- MethodHandle createInvoker(final Class> callSiteReturnType, final int callerProgramPoint) {
+ /**
+ * Returns a guarded invocation for this function when not invoked as a constructor. The guarded invocation has no
+ * guard but it potentially has an optimistic assumptions switch point. As such, it will probably not be used as a
+ * final guarded invocation, but rather as a holder for an invocation handle and switch point to be decomposed and
+ * reassembled into a different final invocation by the user of this method. Any recompositions should take care to
+ * continue to use the switch point. If that is not possible, use {@link #createComposableInvoker()} instead.
+ * @return a guarded invocation for an ordinary (non-constructor) invocation of this function.
+ */
+ GuardedInvocation createFunctionInvocation(final Class> callSiteReturnType, final int callerProgramPoint) {
+ return getValidOptimisticInvocation(new Supplier() {
+ @Override
+ public MethodHandle get() {
+ return createInvoker(callSiteReturnType, callerProgramPoint);
+ }
+ }).createInvocation();
+ }
+
+ /**
+ * Returns a guarded invocation for this function when invoked as a constructor. The guarded invocation has no guard
+ * but it potentially has an optimistic assumptions switch point. As such, it will probably not be used as a final
+ * guarded invocation, but rather as a holder for an invocation handle and switch point to be decomposed and
+ * reassembled into a different final invocation by the user of this method. Any recompositions should take care to
+ * continue to use the switch point. If that is not possible, use {@link #createComposableConstructor()} instead.
+ * @return a guarded invocation for invocation of this function as a constructor.
+ */
+ GuardedInvocation createConstructorInvocation() {
+ return getValidOptimisticInvocation(new Supplier() {
+ @Override
+ public MethodHandle get() {
+ return getConstructor();
+ }
+ }).createInvocation();
+ }
+
+ private MethodHandle createInvoker(final Class> callSiteReturnType, final int callerProgramPoint) {
final boolean isOptimistic = canBeDeoptimized();
MethodHandle handleRewriteException = isOptimistic ? createRewriteExceptionHandler() : null;
@@ -601,7 +685,7 @@
* @param re the rewrite exception that was raised
* @return the method handle for the rest-of method, for folding composition.
*/
- private MethodHandle handleRewriteException(final OptimismInfo oldOptInfo, final RewriteException re) {
+ private synchronized MethodHandle handleRewriteException(final OptimismInfo oldOptInfo, final RewriteException re) {
if (log.isEnabled()) {
log.info(new RecompilationEvent(Level.INFO, re, re.getReturnValueNonDestructive()), "RewriteException ", re.getMessageShort());
}
@@ -639,6 +723,15 @@
logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE);
+ if (effectiveOptInfo.data.usePersistentCodeCache()) {
+ final RecompilableScriptFunctionData data = effectiveOptInfo.data;
+ final int functionNodeId = data.getFunctionNodeId();
+ final TypeMap typeMap = data.typeMap(callSiteType);
+ final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
+ final String cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
+ compiler.persistClassInfo(cacheKey, normalFn);
+ }
+
FunctionNode fn2 = effectiveOptInfo.reparse();
fn2 = compiler.compile(fn2, CompilationPhases.COMPILE_UPTO_BYTECODE);
log.info("Done.");
@@ -664,17 +757,18 @@
} else {
optimismInfo = null; // If we got to a point where we no longer have optimistic assumptions, let the optimism info go.
}
+ notifyAll();
return restOf;
}
private MethodHandle restOfHandle(final OptimismInfo info, final FunctionNode restOfFunction, final boolean canBeDeoptimized) {
assert info != null;
- assert restOfFunction.getCompileUnit().getUnitClassName().indexOf("restOf") != -1;
+ assert restOfFunction.getCompileUnit().getUnitClassName().contains("restOf");
final MethodHandle restOf =
changeReturnType(
- info.data.lookupWithExplicitType(
- restOfFunction,
+ info.data.lookupCodeMethod(
+ restOfFunction.getCompileUnit().getCode(),
MH.type(restOfFunction.getReturnType().getTypeClass(),
RewriteException.class)),
Object.class);
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java Sun Aug 10 19:39:07 2014 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,183 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package jdk.nashorn.internal.runtime;
-
-import static jdk.nashorn.internal.lookup.Lookup.MH;
-
-import java.lang.invoke.MethodType;
-import java.util.LinkedList;
-
-/**
- * This is a list of code versions of a function.
- * The list is sorted in ascending order of generic descriptors
- */
-final class CompiledFunctions {
-
- private final String name;
- final LinkedList functions = new LinkedList<>();
-
- CompiledFunctions(final String name) {
- this.name = name;
- }
-
- void add(final CompiledFunction f) {
- functions.add(f);
- }
-
- void addAll(final CompiledFunctions fs) {
- functions.addAll(fs.functions);
- }
-
- boolean isEmpty() {
- return functions.isEmpty();
- }
-
- int size() {
- return functions.size();
- }
-
- @Override
- public String toString() {
- return '\'' + name + "' code=" + functions;
- }
-
- private static MethodType widen(final MethodType cftype) {
- final Class>[] paramTypes = new Class>[cftype.parameterCount()];
- for (int i = 0; i < cftype.parameterCount(); i++) {
- paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class;
- }
- return MH.type(cftype.returnType(), paramTypes);
- }
-
- /**
- * Used to find an apply to call version that fits this callsite.
- * We cannot just, as in the normal matcher case, return e.g. (Object, Object, int)
- * for (Object, Object, int, int, int) or we will destroy the semantics and get
- * a function that, when padded with undefineds, behaves differently
- * @param type actual call site type
- * @return apply to call that perfectly fits this callsite or null if none found
- */
- CompiledFunction lookupExactApplyToCall(final MethodType type) {
- for (final CompiledFunction cf : functions) {
- if (!cf.isApplyToCall()) {
- continue;
- }
-
- final MethodType cftype = cf.type();
- if (cftype.parameterCount() != type.parameterCount()) {
- continue;
- }
-
- if (widen(cftype).equals(widen(type))) {
- return cf;
- }
- }
-
- return null;
- }
-
- private CompiledFunction pick(final MethodType callSiteType, final boolean canPickVarArg) {
- for (final CompiledFunction candidate : functions) {
- if (candidate.matchesCallSite(callSiteType, false)) {
- return candidate;
- }
- }
- return null;
- }
-
- /**
- * Returns the compiled function best matching the requested call site method type
- * @param callSiteType
- * @param recompilable
- * @param hasThis
- * @return
- */
- CompiledFunction best(final MethodType callSiteType, final boolean recompilable) {
- assert callSiteType.parameterCount() >= 2 : callSiteType; // Must have at least (callee, this)
- assert callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class) : callSiteType; // Callee must be assignable from script function
-
- if (recompilable) {
- final CompiledFunction candidate = pick(callSiteType, false);
- if (candidate != null) {
- return candidate;
- }
- return pick(callSiteType, true); //try vararg last
- }
-
- CompiledFunction best = null;
- for(final CompiledFunction candidate: functions) {
- if(candidate.betterThanFinal(best, callSiteType)) {
- best = candidate;
- }
- }
-
- return best;
- }
-
- /**
- * Returns true if functions managed by this {@code CompiledFunctions} require a callee. This method is only safe to
- * be invoked for a {@code CompiledFunctions} that is not empty. As such, it should only be used from
- * {@link FinalScriptFunctionData} and not from {@link RecompilableScriptFunctionData}.
- * @return true if the functions need a callee, false otherwise.
- */
- boolean needsCallee() {
- final boolean needsCallee = functions.getFirst().needsCallee();
- assert allNeedCallee(needsCallee);
- return needsCallee;
- }
-
- private boolean allNeedCallee(final boolean needCallee) {
- for (final CompiledFunction inv : functions) {
- if(inv.needsCallee() != needCallee) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * If this CompiledFunctions object belongs to a {@code FinalScriptFunctionData}, get a method type for a generic
- * invoker. It will either be a vararg type, if any of the contained functions is vararg, or a generic type of the
- * arity of the largest arity of all functions.
- * @return the method type for the generic invoker
- */
- MethodType getFinalGenericType() {
- int max = 0;
- for(final CompiledFunction fn: functions) {
- final MethodType t = fn.type();
- if(ScriptFunctionData.isVarArg(t)) {
- // 2 for (callee, this, args[])
- return MethodType.genericMethodType(2, true);
- }
- final int paramCount = t.parameterCount() - (ScriptFunctionData.needsCallee(t) ? 1 : 0);
- if(paramCount > max) {
- max = paramCount;
- }
- }
- // +1 for callee
- return MethodType.genericMethodType(max + 1);
- }
-
-}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/CompiledScript.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledScript.java Sun Aug 10 19:39:07 2014 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.nashorn.internal.runtime;
-
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.Map;
-
-/**
- * Class representing a compiled script.
- */
-final class CompiledScript implements Serializable {
-
- /** Main class name. */
- private final String mainClassName;
-
- /** Map of class names to class bytes. */
- private final Map classBytes;
-
- /** Constants array. */
- private final Object[] constants;
-
- /** The source */
- private transient Source source;
-
- private static final long serialVersionUID = 2958227232195298340L;
-
- /**
- * Constructor.
- *
- * @param mainClassName main class name
- * @param classBytes map of class names to class bytes
- * @param constants constants array
- */
- CompiledScript(final Source source, final String mainClassName, final Map classBytes, final Object[] constants) {
- this.source = source;
- this.mainClassName = mainClassName;
- this.classBytes = classBytes;
- this.constants = constants;
- }
-
- /**
- * Returns the main class name.
- * @return the main class name
- */
- public String getMainClassName() {
- return mainClassName;
- }
-
- /**
- * Returns a map of class names to class bytes.
- * @return map of class bytes
- */
- public Map getClassBytes() {
- return classBytes;
- }
-
- /**
- * Returns the constants array.
- * @return constants array
- */
- public Object[] getConstants() {
- return constants;
- }
-
- /**
- * Returns the source of this cached script.
- * @return the source
- */
- public Source getSource() {
- return source;
- }
-
- /**
- * Sets the source of this cached script.
- * @param source the source
- */
- void setSource(final Source source) {
- this.source = source;
- }
-
- @Override
- public int hashCode() {
- int hash = mainClassName.hashCode();
- hash = 31 * hash + classBytes.hashCode();
- hash = 31 * hash + Arrays.hashCode(constants);
- return hash;
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (obj == this) {
- return true;
- }
- if (!(obj instanceof CompiledScript)) {
- return false;
- }
-
- final CompiledScript cs = (CompiledScript) obj;
- return mainClassName.equals(cs.mainClassName)
- && classBytes.equals(cs.classBytes)
- && Arrays.equals(constants, cs.constants);
- }
-}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/Context.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java Mon Aug 11 10:07:15 2014 -0700
@@ -164,10 +164,13 @@
public void initialize(final Collection> classes, final Source source, final Object[] constants) {
// do these in parallel, this significantly reduces class installation overhead
// however - it still means that every thread needs a separate doPrivileged
+ final Global global = currentGlobal.get();
classes.parallelStream().forEach(
new Consumer>() {
@Override
public void accept(final Class> clazz) {
+ // Global threadlocal may be needed by StructureLoader during in field lookup.
+ currentGlobal.set(global);
try {
AccessController.doPrivileged(new PrivilegedExceptionAction() {
@Override
@@ -210,16 +213,21 @@
}
@Override
- public void storeCompiledScript(final Source source, final String mainClassName,
- final Map classBytes, final Object[] constants) {
+ public void storeScript(final String classInfoFile, final Source source, final String mainClassName,
+ final Map classBytes, Map initializers,
+ final Object[] constants, final int compilationId) {
if (context.codeStore != null) {
- try {
- context.codeStore.putScript(source, mainClassName, classBytes, constants);
- } catch (final IOException e) {
- throw new RuntimeException(e);
- }
+ context.codeStore.storeScript(classInfoFile, source, mainClassName, classBytes, initializers, constants, compilationId);
}
}
+
+ @Override
+ public StoredScript loadScript(final Source source, final String functionKey) {
+ if (context.codeStore != null) {
+ return context.codeStore.loadScript(source, functionKey);
+ }
+ return null;
+ }
}
/** Is Context global debug mode enabled ? */
@@ -447,7 +455,7 @@
if (env._persistent_cache) {
try {
final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache");
- codeStore = new CodeStore(cacheDir);
+ codeStore = new CodeStore(this, cacheDir);
} catch (final IOException e) {
throw new RuntimeException("Error initializing code cache", e);
}
@@ -1080,19 +1088,16 @@
return script;
}
- CompiledScript compiledScript = null;
+ StoredScript storedScript = null;
FunctionNode functionNode = null;
+ final boolean useCodeStore = env._persistent_cache && !env._parse_only && !env._optimistic_types;
+ final String cacheKey = useCodeStore ? CodeStore.getCacheKey(0, null) : null;
- if (!env._parse_only && codeStore != null) {
- try {
- compiledScript = codeStore.getScript(source);
- } catch (IOException | ClassNotFoundException e) {
- getLogger(Compiler.class).warning("Error loading ", source, " from cache: ", e);
- // Fall back to normal compilation
- }
+ if (useCodeStore) {
+ storedScript = codeStore.loadScript(source, cacheKey);
}
- if (compiledScript == null) {
+ if (storedScript == null) {
functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse();
if (errors.hasErrors()) {
@@ -1117,7 +1122,7 @@
final CodeSource cs = new CodeSource(url, (CodeSigner[])null);
final CodeInstaller installer = new ContextCodeInstaller(this, loader, cs);
- if (functionNode != null) {
+ if (storedScript == null) {
final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
final Compiler compiler = new Compiler(
@@ -1125,12 +1130,14 @@
env,
installer,
source,
- functionNode.getSourceURL(),
strict | functionNode.isStrict());
- script = compiler.compile(functionNode, phases).getRootClass();
+ final FunctionNode compiledFunction = compiler.compile(functionNode, phases);
+ script = compiledFunction.getRootClass();
+ compiler.persistClassInfo(cacheKey, compiledFunction);
} else {
- script = install(compiledScript, installer);
+ Compiler.updateCompilationId(storedScript.getCompilationId());
+ script = install(storedScript, source, installer);
}
cacheClass(source, script);
@@ -1155,27 +1162,26 @@
return uniqueScriptId.getAndIncrement();
}
-
/**
* Install a previously compiled class from the code cache.
*
- * @param compiledScript cached script containing class bytes and constants
+ * @param storedScript cached script containing class bytes and constants
* @return main script class
*/
- private static Class> install(final CompiledScript compiledScript, final CodeInstaller installer) {
+ private static Class> install(final StoredScript storedScript, final Source source, final CodeInstaller installer) {
final Map> installedClasses = new HashMap<>();
- final Source source = compiledScript.getSource();
- final Object[] constants = compiledScript.getConstants();
- final String rootClassName = compiledScript.getMainClassName();
- final byte[] rootByteCode = compiledScript.getClassBytes().get(rootClassName);
- final Class> rootClass = installer.install(rootClassName, rootByteCode);
+ final Object[] constants = storedScript.getConstants();
+ final String mainClassName = storedScript.getMainClassName();
+ final byte[] mainClassBytes = storedScript.getClassBytes().get(mainClassName);
+ final Class> mainClass = installer.install(mainClassName, mainClassBytes);
+ final Map initialzers = storedScript.getInitializers();
- installedClasses.put(rootClassName, rootClass);
+ installedClasses.put(mainClassName, mainClass);
- for (final Map.Entry entry : compiledScript.getClassBytes().entrySet()) {
+ for (final Map.Entry entry : storedScript.getClassBytes().entrySet()) {
final String className = entry.getKey();
- if (className.equals(rootClassName)) {
+ if (className.equals(mainClassName)) {
continue;
}
final byte[] code = entry.getValue();
@@ -1187,11 +1193,17 @@
for (final Object constant : constants) {
if (constant instanceof RecompilableScriptFunctionData) {
- ((RecompilableScriptFunctionData) constant).initTransients(source, installer);
+ final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constant;
+ data.initTransients(source, installer);
+ if (initialzers != null) {
+ final FunctionInitializer initializer = initialzers.get(data.getFunctionNodeId());
+ initializer.setCode(installedClasses.get(initializer.getClassName()));
+ data.initializeCode(initializer);
+ }
}
}
- return rootClass;
+ return mainClass;
}
/**
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Mon Aug 11 10:07:15 2014 -0700
@@ -27,6 +27,7 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
+import java.util.List;
/**
* This is a subclass that represents a script function that may not be regenerated.
@@ -44,10 +45,10 @@
* @param functions precompiled code
* @param flags {@link ScriptFunctionData} flags
*/
- FinalScriptFunctionData(final String name, final int arity, final CompiledFunctions functions, final int flags) {
+ FinalScriptFunctionData(final String name, final int arity, final List functions, final int flags) {
super(name, arity, flags);
- assert !functions.needsCallee();
code.addAll(functions);
+ assert !needsCallee();
}
/**
@@ -76,8 +77,19 @@
}
@Override
- boolean needsCallee() {
- return code.needsCallee();
+ protected boolean needsCallee() {
+ final boolean needsCallee = code.getFirst().needsCallee();
+ assert allNeedCallee(needsCallee);
+ return needsCallee;
+ }
+
+ private boolean allNeedCallee(final boolean needCallee) {
+ for (final CompiledFunction inv : code) {
+ if(inv.needsCallee() != needCallee) {
+ return false;
+ }
+ }
+ return true;
}
@Override
@@ -86,7 +98,20 @@
// actually correct for lots of built-ins. E.g. ECMAScript 5.1 section 15.5.3.2 prescribes that
// Script.fromCharCode([char0[, char1[, ...]]]) has a declared arity of 1 even though it's a variable arity
// method.
- return code.getFinalGenericType();
+ int max = 0;
+ for(final CompiledFunction fn: code) {
+ final MethodType t = fn.type();
+ if(ScriptFunctionData.isVarArg(t)) {
+ // 2 for (callee, this, args[])
+ return MethodType.genericMethodType(2, true);
+ }
+ final int paramCount = t.parameterCount() - (ScriptFunctionData.needsCallee(t) ? 1 : 0);
+ if(paramCount > max) {
+ max = paramCount;
+ }
+ }
+ // +1 for callee
+ return MethodType.genericMethodType(max + 1);
}
private void addInvoker(final MethodHandle mh) {
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/FunctionInitializer.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FunctionInitializer.java Mon Aug 11 10:07:15 2014 -0700
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import jdk.nashorn.internal.codegen.CompileUnit;
+import jdk.nashorn.internal.codegen.FunctionSignature;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.FunctionNode;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.invoke.MethodType;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Class that contains information allowing us to look up a method handle implementing a JavaScript function
+ * from a generated class. This is used both for code coming from codegen and for persistent serialized code.
+ */
+public final class FunctionInitializer implements Serializable {
+
+ private final String className;
+ private final MethodType methodType;
+ private final int flags;
+ private transient Map invalidatedProgramPoints;
+ private transient Class> code;
+
+ private static final long serialVersionUID = -5420835725902966692L;
+
+ /**
+ * Constructor.
+ *
+ * @param functionNode the function node
+ */
+ public FunctionInitializer(final FunctionNode functionNode) {
+ this(functionNode, null);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param functionNode the function node
+ * @param invalidatedProgramPoints invalidated program points
+ */
+ public FunctionInitializer(final FunctionNode functionNode, final Map invalidatedProgramPoints) {
+ this.className = functionNode.getCompileUnit().getUnitClassName();
+ this.methodType = new FunctionSignature(functionNode).getMethodType();
+ this.flags = functionNode.getFlags();
+ this.invalidatedProgramPoints = invalidatedProgramPoints;
+
+ final CompileUnit cu = functionNode.getCompileUnit();
+ if (cu != null) {
+ this.code = cu.getCode();
+ }
+
+ assert className != null;
+ }
+
+ /**
+ * Returns the name of the class implementing the function.
+ *
+ * @return the class name
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ * Returns the type of the method implementing the function.
+ *
+ * @return the method type
+ */
+ public MethodType getMethodType() {
+ return methodType;
+ }
+
+ /**
+ * Returns the function flags.
+ *
+ * @return function flags
+ */
+ public int getFlags() {
+ return flags;
+ }
+
+ /**
+ * Returns the class implementing the function.
+ *
+ * @return the class
+ */
+ public Class> getCode() {
+ return code;
+ }
+
+ /**
+ * Set the class implementing the function
+ * @param code the class
+ */
+ public void setCode(Class> code) {
+ // Make sure code has not been set and has expected class name
+ if (this.code != null) {
+ throw new IllegalStateException("code already set");
+ }
+ assert className.equals(code.getTypeName().replace('.', '/')) : "unexpected class name";
+ this.code = code;
+ }
+
+ /**
+ * Returns the map of invalidated program points.
+ *
+ * @return invalidated program points
+ */
+ public Map getInvalidatedProgramPoints() {
+ return invalidatedProgramPoints;
+ }
+
+ private void writeObject(final ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ Type.writeTypeMap(invalidatedProgramPoints, out);
+ }
+
+ private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ invalidatedProgramPoints = Type.readTypeMap(in);
+ }
+}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Mon Aug 11 10:07:15 2014 -0700
@@ -32,13 +32,13 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import jdk.internal.dynalink.support.NameCodec;
-import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.codegen.CompilerConstants;
@@ -73,11 +73,6 @@
private final String functionName;
- // TODO: try to eliminate the need for this somehow, either by allowing Source to change its name, allowing a
- // function to internally replace its Source with one of a different name, or storing this additional field in the
- // Source object.
- private final String sourceURL;
-
/** The line number where this function begins. */
private final int lineNumber;
@@ -128,7 +123,6 @@
* @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor
* @param allocatorMap allocator map to seed instances with, when constructing
* @param nestedFunctions nested function map
- * @param sourceURL source URL
* @param externalScopeDepths external scope depths
* @param internalSymbols internal symbols to method, defined in its scope
*/
@@ -138,7 +132,6 @@
final String allocatorClassName,
final PropertyMap allocatorMap,
final Map nestedFunctions,
- final String sourceURL,
final Map externalScopeDepths,
final Set internalSymbols) {
@@ -155,7 +148,6 @@
this.source = functionNode.getSource();
this.token = tokenFor(functionNode);
this.installer = installer;
- this.sourceURL = sourceURL;
this.allocatorClassName = allocatorClassName;
this.allocatorMap = allocatorMap;
this.nestedFunctions = nestedFunctions;
@@ -366,7 +358,7 @@
final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, Token.descLength(token), true);
// Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a
// single function, extract it from the program.
- return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName).setSourceURL(null, sourceURL);
+ return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName);
}
TypeMap typeMap(final MethodType fnCallSiteType) {
@@ -395,18 +387,18 @@
final ScriptObject runtimeScope, final Map invalidatedProgramPoints,
final int[] continuationEntryPoints) {
final TypeMap typeMap = typeMap(actualCallSiteType);
- final Object typeInformationFile = OptimisticTypesPersistence.getLocationDescriptor(source, functionNodeId, typeMap == null ? null : typeMap.getParameterTypes(functionNodeId));
+ final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
+ final Object typeInformationFile = OptimisticTypesPersistence.getLocationDescriptor(source, functionNodeId, paramTypes);
final Context context = Context.getContextTrusted();
return new Compiler(
context,
context.getEnv(),
installer,
functionNode.getSource(), // source
- functionNode.getSourceURL(),
isStrict() | functionNode.isStrict(), // is strict
true, // is on demand
this, // compiledFunction, i.e. this RecompilableScriptFunctionData
- typeMap(actualCallSiteType), // type map
+ typeMap, // type map
getEffectiveInvalidatedProgramPoints(invalidatedProgramPoints, typeInformationFile), // invalidated program points
typeInformationFile,
continuationEntryPoints, // continuation entry points
@@ -431,7 +423,7 @@
return loadedProgramPoints != null ? loadedProgramPoints : new TreeMap();
}
- private TypeSpecializedFunction compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
+ private FunctionInitializer compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope, final boolean persist) {
// We're creating an empty script object for holding local variables. AssignSymbols will populate it with
// explicit Undefined values for undefined local variables (see AssignSymbols#defineSymbol() and
// CompilationEnvironment#declareLocalSymbol()).
@@ -440,21 +432,79 @@
log.info("Type specialization of '", functionName, "' signature: ", actualCallSiteType);
}
+ final boolean persistentCache = usePersistentCodeCache() && persist;
+ String cacheKey = null;
+ if (persistentCache) {
+ final TypeMap typeMap = typeMap(actualCallSiteType);
+ final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
+ cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
+ final StoredScript script = installer.loadScript(source, cacheKey);
+
+ if (script != null) {
+ Compiler.updateCompilationId(script.getCompilationId());
+ return install(script);
+ }
+ }
+
final FunctionNode fn = reparse();
final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope);
+ final FunctionNode compiledFn = compiler.compile(fn, CompilationPhases.COMPILE_ALL);
- final FunctionNode compiledFn = compiler.compile(fn, CompilationPhases.COMPILE_ALL);
- return new TypeSpecializedFunction(compiledFn, compiler.getInvalidatedProgramPoints());
+ if (persist && !compiledFn.getFlag(FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION)) {
+ compiler.persistClassInfo(cacheKey, compiledFn);
+ }
+ return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints());
}
- private static class TypeSpecializedFunction {
- private final FunctionNode fn;
- private final Map invalidatedProgramPoints;
+
+ /**
+ * Install this script using the given {@code installer}.
+ *
+ * @param script the compiled script
+ * @return the function initializer
+ */
+ private FunctionInitializer install(final StoredScript script) {
+
+ final Map> installedClasses = new HashMap<>();
+ final String mainClassName = script.getMainClassName();
+ final byte[] mainClassBytes = script.getClassBytes().get(mainClassName);
+
+ final Class> mainClass = installer.install(mainClassName, mainClassBytes);
+
+ installedClasses.put(mainClassName, mainClass);
+
+ for (final Map.Entry entry : script.getClassBytes().entrySet()) {
+ final String className = entry.getKey();
+ final byte[] code = entry.getValue();
+
+ if (className.equals(mainClassName)) {
+ continue;
+ }
- TypeSpecializedFunction(final FunctionNode fn, final Map invalidatedProgramPoints) {
- this.fn = fn;
- this.invalidatedProgramPoints = invalidatedProgramPoints;
+ installedClasses.put(className, installer.install(className, code));
}
+
+ final Map initializers = script.getInitializers();
+ assert initializers != null;
+ assert initializers.size() == 1;
+ final FunctionInitializer initializer = initializers.values().iterator().next();
+
+ Object[] constants = script.getConstants();
+ for (int i = 0; i < constants.length; i++) {
+ if (constants[i] instanceof RecompilableScriptFunctionData) {
+ // replace deserialized function data with the ones we already have
+ constants[i] = getScriptFunctionData(((RecompilableScriptFunctionData) constants[i]).getFunctionNodeId());
+ }
+ }
+
+ installer.initialize(installedClasses.values(), source, constants);
+ initializer.setCode(installedClasses.get(initializer.getClassName()));
+ return initializer;
+ }
+
+ boolean usePersistentCodeCache() {
+ final ScriptEnvironment env = installer.getOwner();
+ return env._persistent_cache && env._optimistic_types;
}
private MethodType explicitParams(final MethodType callSiteType) {
@@ -502,61 +552,57 @@
return f;
}
- MethodHandle lookup(final FunctionNode fn) {
- final MethodType type = new FunctionSignature(fn).getMethodType();
- log.info("Looking up ", DebugLogger.quote(fn.getName()), " type=", type);
- return lookupWithExplicitType(fn, new FunctionSignature(fn).getMethodType());
+ MethodHandle lookup(final FunctionInitializer fnInit) {
+ final MethodType type = fnInit.getMethodType();
+ return lookupCodeMethod(fnInit.getCode(), type);
}
- MethodHandle lookupWithExplicitType(final FunctionNode fn, final MethodType targetType) {
- return lookupCodeMethod(fn.getCompileUnit(), targetType);
+ MethodHandle lookup(final FunctionNode fn) {
+ final MethodType type = new FunctionSignature(fn).getMethodType();
+ return lookupCodeMethod(fn.getCompileUnit().getCode(), type);
}
- private MethodHandle lookupCodeMethod(final CompileUnit compileUnit, final MethodType targetType) {
- return MH.findStatic(LOOKUP, compileUnit.getCode(), functionName, targetType);
+ MethodHandle lookupCodeMethod(final Class> code, final MethodType targetType) {
+ log.info("Looking up ", DebugLogger.quote(name), " type=", targetType);
+ return MH.findStatic(LOOKUP, code, functionName, targetType);
}
/**
* Initializes this function data with the eagerly generated version of the code. This method can only be invoked
* by the compiler internals in Nashorn and is public for implementation reasons only. Attempting to invoke it
* externally will result in an exception.
- * @param functionNode the functionNode belonging to this data
*/
- public void initializeCode(final FunctionNode functionNode) {
+ public void initializeCode(final FunctionInitializer initializer) {
// Since the method is public, we double-check that we aren't invoked with an inappropriate compile unit.
- if(!(code.isEmpty() && functionNode.getCompileUnit().isInitializing(this, functionNode))) {
- throw new IllegalStateException(functionNode.getName() + " id=" + functionNode.getId());
+ if(!code.isEmpty()) {
+ throw new IllegalStateException(name);
}
- addCode(functionNode);
+ addCode(lookup(initializer), null, null, initializer.getFlags());
}
- private CompiledFunction addCode(final MethodHandle target, final Map invalidatedProgramPoints, final int fnFlags) {
- final CompiledFunction cfn = new CompiledFunction(target, this, invalidatedProgramPoints, fnFlags);
+ private CompiledFunction addCode(final MethodHandle target, final Map invalidatedProgramPoints,
+ final MethodType callSiteType, final int fnFlags) {
+ final CompiledFunction cfn = new CompiledFunction(target, this, invalidatedProgramPoints, callSiteType, fnFlags);
code.add(cfn);
return cfn;
}
- private CompiledFunction addCode(final FunctionNode fn) {
- return addCode(lookup(fn), null, fn.getFlags());
- }
-
/**
* Add code with specific call site type. It will adapt the type of the looked up method handle to fit the call site
* type. This is necessary because even if we request a specialization that takes an "int" parameter, we might end
* up getting one that takes a "double" etc. because of internal function logic causes widening (e.g. assignment of
* a wider value to the parameter variable). However, we use the method handle type for matching subsequent lookups
* for the same specialization, so we must adapt the handle to the expected type.
- * @param tfn the function
+ * @param fnInit the function
* @param callSiteType the call site type
* @return the compiled function object, with its type matching that of the call site type.
*/
- private CompiledFunction addCode(final TypeSpecializedFunction tfn, final MethodType callSiteType) {
- final FunctionNode fn = tfn.fn;
- if (fn.isVarArg()) {
- return addCode(fn);
+ private CompiledFunction addCode(final FunctionInitializer fnInit, final MethodType callSiteType) {
+ if (isVariableArity()) {
+ return addCode(lookup(fnInit), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
}
- final MethodHandle handle = lookup(fn);
+ final MethodHandle handle = lookup(fnInit);
final MethodType fromType = handle.type();
MethodType toType = needsCallee(fromType) ? callSiteType.changeParameterType(0, ScriptFunction.class) : callSiteType.dropParameterTypes(0, 1);
toType = toType.changeReturnType(fromType.returnType());
@@ -581,41 +627,39 @@
toType = toType.dropParameterTypes(fromCount, toCount);
}
- return addCode(lookup(fn).asType(toType), tfn.invalidatedProgramPoints, fn.getFlags());
+ return addCode(lookup(fnInit).asType(toType), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
}
@Override
- CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
- synchronized (code) {
- CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
- if (existingBest == null) {
- existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
- }
+ synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
+ CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
+ if (existingBest == null) {
+ existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, true), callSiteType);
+ }
- assert existingBest != null;
- //we are calling a vararg method with real args
- boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
+ assert existingBest != null;
+ //we are calling a vararg method with real args
+ boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
- //if the best one is an apply to call, it has to match the callsite exactly
- //or we need to regenerate
- if (existingBest.isApplyToCall()) {
- final CompiledFunction best = code.lookupExactApplyToCall(callSiteType);
- if (best != null) {
- return best;
- }
- applyToCall = true;
+ //if the best one is an apply to call, it has to match the callsite exactly
+ //or we need to regenerate
+ if (existingBest.isApplyToCall()) {
+ final CompiledFunction best = lookupExactApplyToCall(callSiteType);
+ if (best != null) {
+ return best;
}
+ applyToCall = true;
+ }
- if (applyToCall) {
- final TypeSpecializedFunction tfn = compileTypeSpecialization(callSiteType, runtimeScope);
- if (tfn.fn.hasOptimisticApplyToCall()) { //did the specialization work
- existingBest = addCode(tfn, callSiteType);
- }
+ if (applyToCall) {
+ final FunctionInitializer fnInit = compileTypeSpecialization(callSiteType, runtimeScope, false);
+ if ((fnInit.getFlags() & FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION) != 0) { //did the specialization work
+ existingBest = addCode(fnInit, callSiteType);
}
+ }
- return existingBest;
- }
+ return existingBest;
}
@Override
@@ -638,6 +682,18 @@
}
/**
+ * Return the function node id.
+ * @return the function node id
+ */
+ public int getFunctionNodeId() {
+ return functionNodeId;
+ }
+
+ public Source getSource() {
+ return source;
+ }
+
+ /**
* Return a script function data based on a function id, either this function if
* the id matches or a nested function based on functionId. This goes down into
* nested functions until all leaves are exhausted.
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Mon Aug 11 10:07:15 2014 -0700
@@ -423,9 +423,9 @@
* @param constructor constructor
* @return prototype, or null if given constructor is not a ScriptFunction
*/
- public static ScriptObject getPrototype(final Object constructor) {
- if (constructor instanceof ScriptFunction) {
- final Object proto = ((ScriptFunction)constructor).getPrototype();
+ public static ScriptObject getPrototype(final ScriptFunction constructor) {
+ if (constructor != null) {
+ final Object proto = constructor.getPrototype();
if (proto instanceof ScriptObject) {
return (ScriptObject)proto;
}
@@ -465,7 +465,7 @@
final MethodType type = desc.getMethodType();
assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
final CompiledFunction cf = data.getBestConstructor(type, scope);
- final GuardedInvocation bestCtorInv = new GuardedInvocation(cf.getConstructor(), cf.getOptimisticAssumptionsSwitchPoint());
+ final GuardedInvocation bestCtorInv = cf.createConstructorInvocation();
//TODO - ClassCastException
return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this, cf.getFlags()), bestCtorInv.getSwitchPoints(), null);
}
@@ -545,11 +545,7 @@
final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT;
final CompiledFunction cf = data.getBestInvoker(type, scope);
- final GuardedInvocation bestInvoker =
- new GuardedInvocation(
- cf.createInvoker(type.returnType(), programPoint),
- cf.getOptimisticAssumptionsSwitchPoint());
-
+ final GuardedInvocation bestInvoker = cf.createFunctionInvocation(type.returnType(), programPoint);
final MethodHandle callHandle = bestInvoker.getInvocation();
if (data.needsCallee()) {
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Mon Aug 11 10:07:15 2014 -0700
@@ -35,6 +35,9 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.util.LinkedList;
+import java.util.List;
+
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
@@ -54,9 +57,10 @@
/** Name of the function or "" for anonymous functions */
protected final String name;
- /** All versions of this function that have been generated to code */
- // TODO: integrate it into ScriptFunctionData; there's not much reason for this to be in its own class.
- protected transient CompiledFunctions code;
+ /**
+ * A list of code versions of a function sorted in ascending order of generic descriptors.
+ */
+ protected transient LinkedList code = new LinkedList<>();
/** Function flags */
protected int flags;
@@ -71,7 +75,7 @@
* multiple threads concurrently, but we still tolerate a race condition in it as all values stored into it are
* idempotent.
*/
- private volatile GenericInvokers genericInvokers;
+ private volatile transient GenericInvokers genericInvokers;
private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
@@ -108,7 +112,6 @@
*/
ScriptFunctionData(final String name, final int arity, final int flags) {
this.name = name;
- this.code = new CompiledFunctions(name);
this.flags = flags;
setArity(arity);
}
@@ -222,8 +225,7 @@
* and not suddenly a "real" object
*
* @param callSiteType callsite type
- * @return guarded invocation with method handle to best invoker and potentially a switch point guarding optimistic
- * assumptions.
+ * @return compiled function object representing the best invoker.
*/
final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope) {
final CompiledFunction cf = getBest(callSiteType, runtimeScope);
@@ -298,6 +300,50 @@
return lgenericInvokers;
}
+ private static MethodType widen(final MethodType cftype) {
+ final Class>[] paramTypes = new Class>[cftype.parameterCount()];
+ for (int i = 0; i < cftype.parameterCount(); i++) {
+ paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class;
+ }
+ return MH.type(cftype.returnType(), paramTypes);
+ }
+
+ /**
+ * Used to find an apply to call version that fits this callsite.
+ * We cannot just, as in the normal matcher case, return e.g. (Object, Object, int)
+ * for (Object, Object, int, int, int) or we will destroy the semantics and get
+ * a function that, when padded with undefineds, behaves differently
+ * @param type actual call site type
+ * @return apply to call that perfectly fits this callsite or null if none found
+ */
+ CompiledFunction lookupExactApplyToCall(final MethodType type) {
+ for (final CompiledFunction cf : code) {
+ if (!cf.isApplyToCall()) {
+ continue;
+ }
+
+ final MethodType cftype = cf.type();
+ if (cftype.parameterCount() != type.parameterCount()) {
+ continue;
+ }
+
+ if (widen(cftype).equals(widen(type))) {
+ return cf;
+ }
+ }
+
+ return null;
+ }
+
+ CompiledFunction pickFunction(final MethodType callSiteType, final boolean canPickVarArg) {
+ for (final CompiledFunction candidate : code) {
+ if (candidate.matchesCallSite(callSiteType, canPickVarArg)) {
+ return candidate;
+ }
+ }
+ return null;
+ }
+
/**
* Returns the best function for the specified call site type.
* @param callSiteType The call site type. Call site types are expected to have the form
@@ -308,16 +354,38 @@
* @return the best function for the specified call site type.
*/
CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
- return code.best(callSiteType, isRecompilable());
+ assert callSiteType.parameterCount() >= 2 : callSiteType; // Must have at least (callee, this)
+ assert callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class) : callSiteType; // Callee must be assignable from script function
+
+ if (isRecompilable()) {
+ final CompiledFunction candidate = pickFunction(callSiteType, false);
+ if (candidate != null) {
+ return candidate;
+ }
+ return pickFunction(callSiteType, true); //try vararg last
+ }
+
+ CompiledFunction best = null;
+ for(final CompiledFunction candidate: code) {
+ if(candidate.betterThanFinal(best, callSiteType)) {
+ best = candidate;
+ }
+ }
+
+ return best;
}
+
abstract boolean isRecompilable();
CompiledFunction getGeneric(final ScriptObject runtimeScope) {
return getBest(getGenericType(), runtimeScope);
}
-
+ /**
+ * Get a method type for a generic invoker.
+ * @return the method type for the generic invoker
+ */
abstract MethodType getGenericType();
/**
@@ -353,7 +421,7 @@
// Clear the callee and this flags
final int boundFlags = flags & ~NEEDS_CALLEE & ~USES_THIS;
- final CompiledFunctions boundList = new CompiledFunctions(fn.getName());
+ final List boundList = new LinkedList<>();
final ScriptObject runtimeScope = fn.getScope();
final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(runtimeScope), getGenericConstructor(runtimeScope));
boundList.add(bind(bindTarget, fn, self, allArgs));
@@ -806,6 +874,6 @@
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
- code = new CompiledFunctions(name);
+ code = new LinkedList<>();
}
}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Mon Aug 11 10:07:15 2014 -0700
@@ -1324,7 +1324,19 @@
* @param all True if to include non-enumerable keys.
* @return Array of keys.
*/
- public String[] getOwnKeys(final boolean all) {
+ public final String[] getOwnKeys(final boolean all) {
+ return getOwnKeys(all, null);
+ }
+
+ /**
+ * return an array of own property keys associated with the object.
+ *
+ * @param all True if to include non-enumerable keys.
+ * @param nonEnumerable set of non-enumerable properties seen already.Used
+ to filter out shadowed, but enumerable properties from proto children.
+ * @return Array of keys.
+ */
+ protected String[] getOwnKeys(final boolean all, final Set nonEnumerable) {
final List keys = new ArrayList<>();
final PropertyMap selfMap = this.getMap();
@@ -1338,8 +1350,21 @@
}
for (final Property property : selfMap.getProperties()) {
- if (all || property.isEnumerable()) {
- keys.add(property.getKey());
+ final boolean enumerable = property.isEnumerable();
+ final String key = property.getKey();
+ if (all) {
+ keys.add(key);
+ } else if (enumerable) {
+ // either we don't have non-enumerable filter set or filter set
+ // does not contain the current property.
+ if (nonEnumerable == null || !nonEnumerable.contains(key)) {
+ keys.add(key);
+ }
+ } else {
+ // store this non-enumerable property for later proto walk
+ if (nonEnumerable != null) {
+ nonEnumerable.add(key);
+ }
}
}
@@ -2398,8 +2423,9 @@
@Override
protected void init() {
final Set keys = new LinkedHashSet<>();
+ final Set nonEnumerable = new HashSet<>();
for (ScriptObject self = object; self != null; self = self.getProto()) {
- keys.addAll(Arrays.asList(self.getOwnKeys(false)));
+ keys.addAll(Arrays.asList(self.getOwnKeys(false, nonEnumerable)));
}
this.values = keys.toArray(new String[keys.size()]);
}
@@ -2413,8 +2439,9 @@
@Override
protected void init() {
final ArrayList valueList = new ArrayList<>();
+ final Set nonEnumerable = new HashSet<>();
for (ScriptObject self = object; self != null; self = self.getProto()) {
- for (final String key : self.getOwnKeys(false)) {
+ for (final String key : self.getOwnKeys(false, nonEnumerable)) {
valueList.add(self.get(key));
}
}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Mon Aug 11 10:07:15 2014 -0700
@@ -702,6 +702,9 @@
if (x instanceof ScriptObject && y instanceof ScriptObject) {
return x == y;
}
+ if (x instanceof ScriptObjectMirror || y instanceof ScriptObjectMirror) {
+ return ScriptObjectMirror.identical(x, y);
+ }
return equalValues(x, y);
}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/Source.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/Source.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Source.java Mon Aug 11 10:07:15 2014 -0700
@@ -87,6 +87,9 @@
/** Base64-encoded SHA1 digest of this source object */
private volatile byte[] digest;
+ /** source URL set via //@ sourceURL or //# sourceURL directive */
+ private String explicitURL;
+
// Do *not* make this public, ever! Trusts the URL and content.
private Source(final String name, final String base, final Data data) {
this.name = name;
@@ -597,6 +600,22 @@
}
/**
+ * Get explicit source URL.
+ * @return URL set vial sourceURL directive
+ */
+ public String getExplicitURL() {
+ return explicitURL;
+ }
+
+ /**
+ * Set explicit source URL.
+ * @param explicitURL URL set via sourceURL directive
+ */
+ public void setExplicitURL(String explicitURL) {
+ this.explicitURL = explicitURL;
+ }
+
+ /**
* Returns whether this source was submitted via 'eval' call or not.
*
* @return true if this source represents code submitted via 'eval'
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/SpillProperty.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/SpillProperty.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/SpillProperty.java Mon Aug 11 10:07:15 2014 -0700
@@ -211,4 +211,12 @@
return 1;
}
+ @Override
+ void initMethodHandles(Class> structure) {
+ final int slot = getSlot();
+ primitiveGetter = primitiveGetter(slot);
+ primitiveSetter = primitiveSetter(slot);
+ objectGetter = objectGetter(slot);
+ objectSetter = objectSetter(slot);
+ }
}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/StoredScript.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/StoredScript.java Mon Aug 11 10:07:15 2014 -0700
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * Class representing a persistent compiled script.
+ */
+public final class StoredScript implements Serializable {
+
+ /** Compilation id */
+ private final int compilationId;
+
+ /** Main class name. */
+ private final String mainClassName;
+
+ /** Map of class names to class bytes. */
+ private final Map classBytes;
+
+ /** Constants array. */
+ private final Object[] constants;
+
+ /** Function initializers */
+ private final Map initializers;
+
+ private static final long serialVersionUID = 2958227232195298340L;
+
+ /**
+ * Constructor.
+ *
+ * @param mainClassName main class name
+ * @param classBytes map of class names to class bytes
+ * @param constants constants array
+ */
+ public StoredScript(final int compilationId, final String mainClassName, final Map classBytes, final Map initializers, final Object[] constants) {
+ this.compilationId = compilationId;
+ this.mainClassName = mainClassName;
+ this.classBytes = classBytes;
+ this.constants = constants;
+ this.initializers = initializers;
+ }
+
+ public int getCompilationId() {
+ return compilationId;
+ }
+
+ /**
+ * Returns the main class name.
+ * @return the main class name
+ */
+ public String getMainClassName() {
+ return mainClassName;
+ }
+
+ /**
+ * Returns a map of class names to class bytes.
+ * @return map of class bytes
+ */
+ public Map getClassBytes() {
+ return classBytes;
+ }
+
+ /**
+ * Returns the constants array.
+ * @return constants array
+ */
+ public Object[] getConstants() {
+ return constants;
+ }
+
+ Map getInitializers() {
+ return initializers;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = mainClassName.hashCode();
+ hash = 31 * hash + classBytes.hashCode();
+ hash = 31 * hash + Arrays.hashCode(constants);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof StoredScript)) {
+ return false;
+ }
+
+ final StoredScript cs = (StoredScript) obj;
+ return mainClassName.equals(cs.mainClassName)
+ && classBytes.equals(cs.classBytes)
+ && Arrays.equals(constants, cs.constants);
+ }
+}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Mon Aug 11 10:07:15 2014 -0700
@@ -60,6 +60,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import jdk.nashorn.api.scripting.ScriptUtils;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.Label;
@@ -134,10 +135,12 @@
* implemented securely.
*/
final class JavaAdapterBytecodeGenerator {
+ private static final Type SCRIPTUTILS_TYPE = Type.getType(ScriptUtils.class);
private static final Type OBJECT_TYPE = Type.getType(Object.class);
private static final Type CLASS_TYPE = Type.getType(Class.class);
static final String OBJECT_TYPE_NAME = OBJECT_TYPE.getInternalName();
+ static final String SCRIPTUTILS_TYPE_NAME = SCRIPTUTILS_TYPE.getInternalName();
static final String INIT = "";
@@ -172,6 +175,7 @@
private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE);
private static final String GET_CLASS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(CLASS_TYPE);
private static final String EXPORT_RETURN_VALUE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE);
+ private static final String UNWRAP_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE);
private static final String GET_CONVERTER_METHOD_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE, CLASS_TYPE);
private static final String TO_CHAR_PRIMITIVE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.CHAR_TYPE, OBJECT_TYPE);
private static final String TO_STRING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(STRING_TYPE, OBJECT_TYPE);
@@ -927,10 +931,14 @@
invokeValueOf(mv, "Double", 'D');
break;
case Type.ARRAY:
- case Type.OBJECT:
case Type.METHOD:
// Already boxed
break;
+ case Type.OBJECT:
+ if(t.equals(OBJECT_TYPE)) {
+ mv.invokestatic(SCRIPTUTILS_TYPE_NAME, "unwrap", UNWRAP_METHOD_DESCRIPTOR, false);
+ }
+ break;
default:
// Not expecting anything else (e.g. VOID)
assert false;
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java Mon Aug 11 10:07:15 2014 -0700
@@ -47,6 +47,8 @@
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
+import jdk.nashorn.api.scripting.ScriptUtils;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -214,12 +216,12 @@
/**
* Invoked when returning Object from an adapted method to filter out internal Nashorn objects that must not be seen
- * by the callers. Currently only transforms {@code ConsString} into {@code String}.
+ * by the callers. Currently only transforms {@code ConsString} into {@code String} and transforms {@code ScriptObject} into {@code ScriptObjectMirror}.
* @param obj the return value
* @return the filtered return value.
*/
public static Object exportReturnValue(final Object obj) {
- return NashornBeansLinker.exportArgument(obj);
+ return ScriptUtils.wrap(NashornBeansLinker.exportArgument(obj));
}
/**
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/src/jdk/nashorn/tools/Shell.java
--- a/nashorn/src/jdk/nashorn/tools/Shell.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/src/jdk/nashorn/tools/Shell.java Mon Aug 11 10:07:15 2014 -0700
@@ -266,7 +266,6 @@
env,
null,
functionNode.getSource(),
- functionNode.getSourceURL(),
env._strict | functionNode.isStrict()).
compile(functionNode, CompilationPhases.COMPILE_ALL_NO_INSTALL);
}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/test/script/basic/JDK-8046026.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8046026.js Mon Aug 11 10:07:15 2014 -0700
@@ -0,0 +1,49 @@
+/*
+ * 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-8046026: CompiledFunction.relinkComposableInvoker assert is being hit
+ * JDK-8044770: crash with jdk9-dev/nashorn during global object initialization from MT test
+ * JDK-8047770: NPE in deoptimizing recompilation in multithreaded
+ *
+ * @test
+ * @run
+ */
+
+(function() {
+var n = 1 << 25;
+var ThreadLocalRandom = java.util.concurrent.ThreadLocalRandom;
+var m = java.util.stream.IntStream.range(0, n)
+ .parallel() // this is the essence of this test. We must trigger parallel execution
+ .filter(function() {
+ var tlr = ThreadLocalRandom.current();
+
+ var x = tlr.nextDouble(-1.0, 1.0);
+ var y = tlr.nextDouble(-1.0, 1.0);
+
+ return x * x + y * y <= 1.0;
+ })
+ .count();
+var pi = (4.0 * m) / n;
+print(pi.toFixed(2));
+})()
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/test/script/basic/JDK-8046026.js.EXPECTED
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8046026.js.EXPECTED Mon Aug 11 10:07:15 2014 -0700
@@ -0,0 +1,1 @@
+3.14
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/test/script/basic/JDK-8054503.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8054503.js Mon Aug 11 10:07:15 2014 -0700
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+/**
+ * 8054503: test/script/external/test262/test/suite/ch12/12.6/12.6.4/12.6.4-2.js fails with tip
+ *
+ * @test
+ * @run
+ */
+
+function MyFunc() {}
+
+MyFunc.prototype.foo = 42;
+var obj = new MyFunc();
+Object.defineProperty(obj, "foo", {
+ value: "hello",
+ enumerable: false
+});
+
+for (var p in obj) {
+ if (p == "foo") {
+ fail("'foo' is not expected here!");
+ }
+}
+
+for each (var p in obj) {
+ if (p == "hello" || p == 42) {
+ fail("'foo' value is not expected here");
+ }
+}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/test/script/trusted/JDK-8006529.js
--- a/nashorn/test/script/trusted/JDK-8006529.js Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/test/script/trusted/JDK-8006529.js Mon Aug 11 10:07:15 2014 -0700
@@ -120,7 +120,7 @@
var sourceForMethod = Source.class.getMethod("sourceFor", java.lang.String.class, java.lang.String.class)
var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class)
-var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, String.class, boolean.class);
+var CompilerConstructor = Compiler.class.getConstructor(Context.class, ScriptEnvironment.class, CodeInstaller.class, Source.class, boolean.class);
// compile(script) -- compiles a script specified as a string with its
// source code, returns a jdk.nashorn.internal.ir.FunctionNode object
@@ -134,7 +134,7 @@
var parser = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance());
var func = parseMethod.invoke(parser);
- var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, null, false);
+ var compiler = CompilerConstructor.newInstance(ctxt, env, null, source, false);
return compileMethod.invoke(compiler, func, phases);
};
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java
--- a/nashorn/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java Mon Aug 11 10:07:15 2014 -0700
@@ -31,10 +31,12 @@
import static org.testng.Assert.fail;
import java.nio.ByteBuffer;
+import java.util.function.Function;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.script.Bindings;
+import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
@@ -306,4 +308,57 @@
// getMember("obj.foo") - thereby getting null instead of undefined
assertEquals("undefined", engine.eval(TEST_SCRIPT, newGlobal));
}
+
+ public interface MirrorCheckExample {
+ Object test1(Object arg);
+ Object test2(Object arg);
+ boolean compare(Object o1, Object o2);
+ }
+
+ // @bug 8053910: ScriptObjectMirror causing havoc with Invocation interface
+ @Test
+ public void checkMirrorToObject() throws Exception {
+ final ScriptEngineManager engineManager = new ScriptEngineManager();
+ final ScriptEngine engine = engineManager.getEngineByName("nashorn");
+ final Invocable invocable = (Invocable)engine;
+
+ engine.eval("function test1(arg) { return { arg: arg }; }");
+ engine.eval("function test2(arg) { return arg; }");
+ engine.eval("function compare(arg1, arg2) { return arg1 == arg2; }");
+
+ final Map map = new HashMap<>();
+ map.put("option", true);
+
+ final MirrorCheckExample example = invocable.getInterface(MirrorCheckExample.class);
+
+ final Object value1 = invocable.invokeFunction("test1", map);
+ final Object value2 = example.test1(map);
+ final Object value3 = invocable.invokeFunction("test2", value2);
+ final Object value4 = example.test2(value2);
+
+ // check that Object type argument receives a ScriptObjectMirror
+ // when ScriptObject is passed
+ assertEquals(ScriptObjectMirror.class, value1.getClass());
+ assertEquals(ScriptObjectMirror.class, value2.getClass());
+ assertEquals(ScriptObjectMirror.class, value3.getClass());
+ assertEquals(ScriptObjectMirror.class, value4.getClass());
+ assertTrue((boolean)invocable.invokeFunction("compare", value1, value1));
+ assertTrue((boolean)example.compare(value1, value1));
+ assertTrue((boolean)invocable.invokeFunction("compare", value3, value4));
+ assertTrue((boolean)example.compare(value3, value4));
+ }
+
+ // @bug 8053910: ScriptObjectMirror causing havoc with Invocation interface
+ @Test
+ @SuppressWarnings("unchecked")
+ public void mirrorUnwrapInterfaceMethod() throws Exception {
+ final ScriptEngineManager engineManager = new ScriptEngineManager();
+ final ScriptEngine engine = engineManager.getEngineByName("nashorn");
+ final Invocable invocable = (Invocable)engine;
+ engine.eval("function apply(obj) { " +
+ " return obj instanceof Packages.jdk.nashorn.api.scripting.ScriptObjectMirror; " +
+ "}");
+ Function func = invocable.getInterface(Function.class);
+ assertFalse((boolean)func.apply(engine.eval("({ x: 2 })")));
+ }
}
diff -r 62406a5c8345 -r ff5b8d49be95 nashorn/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java
--- a/nashorn/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java Sun Aug 10 19:39:07 2014 -0700
+++ b/nashorn/test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java Mon Aug 11 10:07:15 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"};
+ private static final String[] ENGINE_OPTIONS = new String[]{"--persistent-code-cache", "--optimistic-types=false", "--lazy-compilation=false"};
public void checkCompiledScripts(final DirectoryStream stream, int numberOfScripts) throws IOException {
for (final Path file : stream) {