8021350: Share script classes between threads/globals within context
Reviewed-by: lagergren, sundar
--- a/nashorn/make/build.xml Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/make/build.xml Wed Mar 12 11:26:00 2014 +0100
@@ -253,6 +253,10 @@
<fileset dir="${test.src.dir}/jdk/nashorn/internal/runtime/resources"/>
</copy>
+ <copy todir="${build.test.classes.dir}/jdk/nashorn/api/scripting/resources">
+ <fileset dir="${test.src.dir}/jdk/nashorn/api/scripting/resources"/>
+ </copy>
+
<!-- tests that check nashorn internals and internal API -->
<jar jarfile="${nashorn.internal.tests.jar}">
<fileset dir="${build.test.classes.dir}" excludes="**/api/**"/>
--- a/nashorn/make/project.properties Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/make/project.properties Wed Mar 12 11:26:00 2014 +0100
@@ -206,7 +206,7 @@
# test262 test frameworks
test262-test-sys-prop.test.js.framework=\
- --class-cache-size=0 \
+ --class-cache-size=10 \
--no-java \
--no-typed-arrays \
-timezone=PST \
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Wed Mar 12 11:26:00 2014 +0100
@@ -375,10 +375,11 @@
* @return Symbol for given name or null for redefinition.
*/
private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) {
- int flags = symbolFlags;
- Symbol symbol = findSymbol(block, name); // Locate symbol.
+ int flags = symbolFlags;
+ Symbol symbol = findSymbol(block, name); // Locate symbol.
+ boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL;
- if ((flags & KINDMASK) == IS_GLOBAL) {
+ if (isGlobal) {
flags |= IS_SCOPE;
}
@@ -414,6 +415,8 @@
// Determine where to create it.
if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
symbolBlock = block; //internal vars are always defined in the block closest to them
+ } else if (isGlobal) {
+ symbolBlock = lc.getOutermostFunction().getBody();
} else {
symbolBlock = lc.getFunctionBody(function);
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Mar 12 11:26:00 2014 +0100
@@ -685,7 +685,7 @@
private void scopeCall(final IdentNode node, final int flags) {
load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3
// ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
- method.loadNull(); //the 'this'
+ method.loadUndefined(Type.OBJECT); //the 'this' object
method.dynamicCall(callNodeType, 2 + loadArgs(args), flags);
}
@@ -818,7 +818,7 @@
protected boolean enterDefault(final Node node) {
// Load up function.
load(function, Type.OBJECT); //TODO, e.g. booleans can be used as functions
- method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE
+ method.loadUndefined(Type.OBJECT); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE
method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE);
return false;
--- a/nashorn/src/jdk/nashorn/internal/codegen/SharedScopeCall.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/codegen/SharedScopeCall.java Wed Mar 12 11:26:00 2014 +0100
@@ -156,7 +156,7 @@
if (isCall) {
method.convert(Type.OBJECT);
// ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
- method.loadNull();
+ method.loadUndefined(Type.OBJECT);
int slot = 2;
for (final Type type : paramTypes) {
method.load(type, slot++);
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Wed Mar 12 11:26:00 2014 +0100
@@ -164,11 +164,11 @@
public static final int HAS_EVAL = 1 << 5;
/** Does a nested function contain eval? If it does, then all variables in this function might be get/set by it. */
- public static final int HAS_NESTED_EVAL = 1 << 6;
+ public static final int HAS_NESTED_EVAL = 1 << 6;
/** Does this function have any blocks that create a scope? This is used to determine if the function needs to
* have a local variable slot for the scope symbol. */
- public static final int HAS_SCOPE_BLOCK = 1 << 7;
+ public static final int HAS_SCOPE_BLOCK = 1 << 7;
/**
* Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
@@ -197,6 +197,9 @@
/** Can this function be specialized? */
public static final int CAN_SPECIALIZE = 1 << 14;
+ /** Does this function use the "this" keyword? */
+ public static final int USES_THIS = 1 << 15;
+
/** Does this function or any nested functions contain an eval? */
private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL;
@@ -591,6 +594,15 @@
}
/**
+ * Return {@code true} if this function makes use of the {@code this} object.
+ *
+ * @return true if function uses {@code this} object
+ */
+ public boolean usesThis() {
+ return getFlag(USES_THIS);
+ }
+
+ /**
* Get the identifier for this function, this is its symbol.
* @return the identifier as an IdentityNode
*/
--- a/nashorn/src/jdk/nashorn/internal/objects/Global.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java Wed Mar 12 11:26:00 2014 +0100
@@ -33,11 +33,8 @@
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.util.Arrays;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
@@ -59,10 +56,10 @@
import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
+import jdk.nashorn.internal.runtime.ScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.ScriptingFunctions;
-import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.InvokeByName;
@@ -373,9 +370,6 @@
// Flag to indicate that a split method issued a return statement
private int splitState = -1;
- // class cache
- private ClassCache classCache;
-
// Used to store the last RegExp result to support deprecated RegExp constructor properties
private RegExpResult lastRegExpResult;
@@ -426,11 +420,6 @@
super(checkAndGetMap(context));
this.context = context;
this.setIsScope();
-
- final int cacheSize = context.getEnv()._class_cache_size;
- if (cacheSize > 0) {
- classCache = new ClassCache(cacheSize);
- }
}
/**
@@ -488,7 +477,7 @@
@Override
public ScriptFunction newScriptFunction(final String name, final MethodHandle handle, final ScriptObject scope, final boolean strict) {
- return new ScriptFunctionImpl(name, handle, scope, null, strict, false, true);
+ return new ScriptFunctionImpl(name, handle, scope, null, strict ? ScriptFunctionData.IS_STRICT_CONSTRUCTOR : ScriptFunctionData.IS_CONSTRUCTOR);
}
@Override
@@ -664,62 +653,6 @@
}
- /**
- * Cache for compiled script classes.
- */
- @SuppressWarnings("serial")
- private static class ClassCache extends LinkedHashMap<Source, ClassReference> {
- private final int size;
- private final ReferenceQueue<Class<?>> queue;
-
- ClassCache(int size) {
- super(size, 0.75f, true);
- this.size = size;
- this.queue = new ReferenceQueue<>();
- }
-
- void cache(final Source source, final Class<?> clazz) {
- put(source, new ClassReference(clazz, queue, source));
- }
-
- @Override
- protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) {
- return size() > size;
- }
-
- @Override
- public ClassReference get(Object key) {
- for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) {
- remove(ref.source);
- }
- return super.get(key);
- }
-
- }
-
- private static class ClassReference extends SoftReference<Class<?>> {
- private final Source source;
-
- ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) {
- super(clazz, queue);
- this.source = source;
- }
- }
-
- // Class cache management
- @Override
- public Class<?> findCachedClass(final Source source) {
- assert classCache != null : "Class cache used without being initialized";
- ClassReference ref = classCache.get(source);
- return ref != null ? ref.get() : null;
- }
-
- @Override
- public void cacheClass(final Source source, final Class<?> clazz) {
- assert classCache != null : "Class cache used without being initialized";
- classCache.cache(source, clazz);
- }
-
private static <T> T getLazilyCreatedValue(final Object key, final Callable<T> creator, final Map<Object, T> map) {
final T obj = map.get(key);
if (obj != null) {
@@ -1838,7 +1771,7 @@
anon.deleteOwnProperty(anon.getMap().findProperty("prototype"));
// use "getter" so that [[ThrowTypeError]] function's arity is 0 - as specified in step 10 of section 13.2.3
- this.typeErrorThrower = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_GETTER, null, null, false, false, false);
+ this.typeErrorThrower = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_GETTER, null, null, 0);
typeErrorThrower.setPrototype(UNDEFINED);
// Non-constructor built-in functions do not have "prototype" property
typeErrorThrower.deleteOwnProperty(typeErrorThrower.getMap().findProperty("prototype"));
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeBoolean.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeBoolean.java Wed Mar 12 11:26:00 2014 +0100
@@ -30,6 +30,7 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.objects.annotations.Attribute;
@@ -50,7 +51,10 @@
public final class NativeBoolean extends ScriptObject {
private final boolean value;
- final static MethodHandle WRAPFILTER = findWrapFilter();
+ // Method handle to create an object wrapper for a primitive boolean
+ private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeBoolean.class, Object.class));
+ // Method handle to retrieve the Boolean prototype object
+ private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
// initialized by nasgen
private static PropertyMap $nasgenmap$;
@@ -164,7 +168,7 @@
* @return Link to be invoked at call site.
*/
public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) {
- return PrimitiveLookup.lookupPrimitive(request, Boolean.class, new NativeBoolean((Boolean)receiver), WRAPFILTER);
+ return PrimitiveLookup.lookupPrimitive(request, Boolean.class, new NativeBoolean((Boolean)receiver), WRAPFILTER, PROTOFILTER);
}
/**
@@ -178,7 +182,12 @@
return new NativeBoolean((Boolean)receiver);
}
- private static MethodHandle findWrapFilter() {
- return MH.findStatic(MethodHandles.lookup(), NativeBoolean.class, "wrapFilter", MH.type(NativeBoolean.class, Object.class));
+ @SuppressWarnings("unused")
+ private static Object protoFilter(final Object object) {
+ return Global.instance().getBooleanPrototype();
+ }
+
+ private static MethodHandle findOwnMH(final String name, final MethodType type) {
+ return MH.findStatic(MethodHandles.lookup(), NativeBoolean.class, name, type);
}
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Wed Mar 12 11:26:00 2014 +0100
@@ -622,7 +622,7 @@
case "getMethod":
final FindProperty find = adaptee.findProperty(__call__, true);
if (find != null) {
- final Object value = getObjectValue(find);
+ final Object value = find.getObjectValue();
if (value instanceof ScriptFunction) {
final ScriptFunctionImpl func = (ScriptFunctionImpl)value;
// TODO: It's a shame we need to produce a function bound to this and name, when we'd only need it bound
@@ -691,7 +691,7 @@
final MethodType type = desc.getMethodType();
if (findData != null) {
final String name = desc.getNameTokenCount() > 2 ? desc.getNameToken(2) : null;
- final Object value = getObjectValue(findData);
+ final Object value = findData.getObjectValue();
if (value instanceof ScriptFunction) {
final ScriptFunction func = (ScriptFunction)value;
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeNumber.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeNumber.java Wed Mar 12 11:26:00 2014 +0100
@@ -34,6 +34,7 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
import java.text.NumberFormat;
import java.util.Locale;
import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -57,7 +58,10 @@
@ScriptClass("Number")
public final class NativeNumber extends ScriptObject {
- static final MethodHandle WRAPFILTER = findWrapFilter();
+ // Method handle to create an object wrapper for a primitive number
+ private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeNumber.class, Object.class));
+ // Method handle to retrieve the Number prototype object
+ private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
/** ECMA 15.7.3.2 largest positive finite value */
@Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT, where = Where.CONSTRUCTOR)
@@ -322,7 +326,7 @@
* @return Link to be invoked at call site.
*/
public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) {
- return PrimitiveLookup.lookupPrimitive(request, Number.class, new NativeNumber(((Number)receiver).doubleValue()), WRAPFILTER);
+ return PrimitiveLookup.lookupPrimitive(request, Number.class, new NativeNumber(((Number)receiver).doubleValue()), WRAPFILTER, PROTOFILTER);
}
@SuppressWarnings("unused")
@@ -330,6 +334,11 @@
return new NativeNumber(((Number)receiver).doubleValue());
}
+ @SuppressWarnings("unused")
+ private static Object protoFilter(final Object object) {
+ return Global.instance().getNumberPrototype();
+ }
+
private static double getNumberValue(final Object self) {
if (self instanceof Number) {
return ((Number)self).doubleValue();
@@ -378,7 +387,7 @@
return str;
}
- private static MethodHandle findWrapFilter() {
- return MH.findStatic(MethodHandles.lookup(), NativeNumber.class, "wrapFilter", MH.type(NativeNumber.class, Object.class));
+ private static MethodHandle findOwnMH(final String name, final MethodType type) {
+ return MH.findStatic(MethodHandles.lookup(), NativeNumber.class, name, type);
}
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeString.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeString.java Wed Mar 12 11:26:00 2014 +0100
@@ -32,6 +32,7 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
@@ -69,7 +70,10 @@
private final CharSequence value;
- static final MethodHandle WRAPFILTER = findWrapFilter();
+ // Method handle to create an object wrapper for a primitive string
+ private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class));
+ // Method handle to retrieve the String prototype object
+ private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
// initialized by nasgen
private static PropertyMap $nasgenmap$;
@@ -1199,7 +1203,7 @@
*/
public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) {
final MethodHandle guard = NashornGuards.getInstanceOf2Guard(String.class, ConsString.class);
- return PrimitiveLookup.lookupPrimitive(request, guard, new NativeString((CharSequence)receiver), WRAPFILTER);
+ return PrimitiveLookup.lookupPrimitive(request, guard, new NativeString((CharSequence)receiver), WRAPFILTER, PROTOFILTER);
}
@SuppressWarnings("unused")
@@ -1207,6 +1211,11 @@
return new NativeString((CharSequence)receiver);
}
+ @SuppressWarnings("unused")
+ private static Object protoFilter(final Object object) {
+ return Global.instance().getStringPrototype();
+ }
+
private static CharSequence getCharSequence(final Object self) {
if (self instanceof String || self instanceof ConsString) {
return (CharSequence)self;
@@ -1254,7 +1263,7 @@
return key >= 0 && key < value.length();
}
- private static MethodHandle findWrapFilter() {
- return MH.findStatic(MethodHandles.lookup(), NativeString.class, "wrapFilter", MH.type(NativeString.class, Object.class));
+ private static MethodHandle findOwnMH(final String name, final MethodType type) {
+ return MH.findStatic(MethodHandles.lookup(), NativeString.class, name, type);
}
}
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Wed Mar 12 11:26:00 2014 +0100
@@ -75,7 +75,7 @@
private static final Object LAZY_PROTOTYPE = new Object();
private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs, final Global global) {
- super(name, invokeHandle, getInitialMap(), null, specs, false, true, true);
+ super(name, invokeHandle, getInitialMap(), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR);
init(global);
}
@@ -92,7 +92,7 @@
}
private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs, final Global global) {
- super(name, invokeHandle, map.addAll(getInitialMap()), null, specs, false, true, true);
+ super(name, invokeHandle, map.addAll(getInitialMap()), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR);
init(global);
}
@@ -109,8 +109,8 @@
this(name, invokeHandle, map, specs, Global.instance());
}
- private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor, final Global global) {
- super(name, methodHandle, getMap(global, isStrict), scope, specs, isStrict, isBuiltin, isConstructor);
+ private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final int flags, final Global global) {
+ super(name, methodHandle, getMap(global, isStrict(flags)), scope, specs, flags);
init(global);
}
@@ -121,12 +121,10 @@
* @param methodHandle handle for invocation
* @param scope scope object
* @param specs specialized versions of this method, if available, null otherwise
- * @param isStrict are we in strict mode
- * @param isBuiltin is this a built-in function
- * @param isConstructor can the function be used as a constructor (most can; some built-ins are restricted).
+ * @param flags {@link ScriptFunctionData} flags
*/
- ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
- this(name, methodHandle, scope, specs, isStrict, isBuiltin, isConstructor, Global.instance());
+ ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final int flags) {
+ this(name, methodHandle, scope, specs, flags, Global.instance());
}
private ScriptFunctionImpl(final RecompilableScriptFunctionData data, final ScriptObject scope, final Global global) {
@@ -173,6 +171,10 @@
return newMap;
}
+ private static boolean isStrict(final int flags) {
+ return (flags & ScriptFunctionData.IS_STRICT) != 0;
+ }
+
// Choose the map based on strict mode!
private static PropertyMap getMap(final Global global, final boolean strict) {
return strict ? getInitialStrictMap() : getInitialMap();
@@ -211,7 +213,7 @@
* @return new ScriptFunction
*/
static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) {
- final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, false, true, false);
+ final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, ScriptFunctionData.IS_BUILTIN);
func.setPrototype(UNDEFINED);
// Non-constructor built-in functions do not have "prototype" property
func.deleteOwnProperty(func.getMap().findProperty("prototype"));
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java Wed Mar 12 11:26:00 2014 +0100
@@ -1799,6 +1799,7 @@
case THIS:
final String name = type.getName();
next();
+ lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_THIS);
return new IdentNode(primaryToken, finish, name);
case IDENT:
final IdentNode ident = getIdent();
--- a/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Wed Mar 12 11:26:00 2014 +0100
@@ -141,10 +141,12 @@
private Class<?> currentType;
/**
- * Delegate constructor. This is used when adding properties to the Global scope, which
- * is necessary for outermost levels in a script (the ScriptObject is represented by
- * a JO-prefixed ScriptObject class, but the properties need to be in the Global scope
- * and are thus rebound with that as receiver
+ * Delegate constructor for bound properties. This is used for properties created by
+ * {@link ScriptRuntime#mergeScope} and the Nashorn {@code Object.bindProperties} method.
+ * The former is used to add a script's defined globals to the current global scope while
+ * still storing them in a JO-prefixed ScriptObject class.
+ *
+ * <p>All properties created by this constructor have the {@link #IS_BOUND} flag set.</p>
*
* @param property accessor property to rebind
* @param delegate delegate object to rebind receiver to
@@ -157,6 +159,8 @@
this.objectGetter = bindTo(property.ensureObjectGetter(), delegate);
this.objectSetter = bindTo(property.ensureObjectSetter(), delegate);
+ // Properties created this way are bound to a delegate
+ this.flags |= IS_BOUND;
setCurrentType(property.getCurrentType());
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java Wed Mar 12 11:26:00 2014 +0100
@@ -36,6 +36,8 @@
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
@@ -46,6 +48,7 @@
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import jdk.internal.org.objectweb.asm.ClassReader;
@@ -155,6 +158,9 @@
private static final ThreadLocal<ScriptObject> currentGlobal = new ThreadLocal<>();
+ // class cache
+ private ClassCache classCache;
+
/**
* Get the current global scope
* @return the current global scope
@@ -348,6 +354,11 @@
this.classPathLoader = null;
}
+ final int cacheSize = env._class_cache_size;
+ if (cacheSize > 0) {
+ classCache = new ClassCache(cacheSize);
+ }
+
// print version info if asked.
if (env._version) {
getErr().println("nashorn " + Version.version());
@@ -659,7 +670,7 @@
public static void checkPackageAccess(final String pkgName) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
- checkPackageAccess(sm, pkgName.endsWith(".")? pkgName : pkgName + ".");
+ checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + ".");
}
}
@@ -925,16 +936,10 @@
// start with no errors, no warnings.
errMan.reset();
- GlobalObject global = null;
- Class<?> script;
-
- if (env._class_cache_size > 0) {
- global = (GlobalObject)Context.getGlobalTrusted();
- script = global.findCachedClass(source);
- if (script != null) {
- Compiler.LOG.fine("Code cache hit for ", source, " avoiding recompile.");
- return script;
- }
+ Class<?> script = findCachedClass(source);
+ if (script != null) {
+ Compiler.LOG.fine("Code cache hit for ", source, " avoiding recompile.");
+ return script;
}
final FunctionNode functionNode = new Parser(env, source, errMan, strict).parse();
@@ -963,10 +968,7 @@
final FunctionNode newFunctionNode = compiler.compile(functionNode);
script = compiler.install(newFunctionNode);
-
- if (global != null) {
- global.cacheClass(source, script);
- }
+ cacheClass(source, script);
return script;
}
@@ -988,4 +990,60 @@
private long getUniqueScriptId() {
return uniqueScriptId.getAndIncrement();
}
+
+ /**
+ * Cache for compiled script classes.
+ */
+ @SuppressWarnings("serial")
+ private static class ClassCache extends LinkedHashMap<Source, ClassReference> {
+ private final int size;
+ private final ReferenceQueue<Class<?>> queue;
+
+ ClassCache(int size) {
+ super(size, 0.75f, true);
+ this.size = size;
+ this.queue = new ReferenceQueue<>();
+ }
+
+ void cache(final Source source, final Class<?> clazz) {
+ put(source, new ClassReference(clazz, queue, source));
+ }
+
+ @Override
+ protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) {
+ return size() > size;
+ }
+
+ @Override
+ public ClassReference get(Object key) {
+ for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) {
+ remove(ref.source);
+ }
+ return super.get(key);
+ }
+
+ }
+
+ private static class ClassReference extends SoftReference<Class<?>> {
+ private final Source source;
+
+ ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) {
+ super(clazz, queue);
+ this.source = source;
+ }
+ }
+
+ // Class cache management
+ private Class<?> findCachedClass(final Source source) {
+ ClassReference ref = classCache == null ? null : classCache.get(source);
+ return ref != null ? ref.get() : null;
+ }
+
+ private void cacheClass(final Source source, final Class<?> clazz) {
+ if (classCache != null) {
+ classCache.cache(source, clazz);
+ }
+ }
+
+
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Wed Mar 12 11:26:00 2014 +0100
@@ -38,31 +38,27 @@
/**
* Constructor - used for bind
*
- * @param name name
- * @param arity arity
- * @param functions precompiled code
- * @param isStrict strict
- * @param isBuiltin builtin
- * @param isConstructor constructor
+ * @param name name
+ * @param arity arity
+ * @param functions precompiled code
+ * @param flags {@link ScriptFunctionData} flags
*/
- FinalScriptFunctionData(final String name, int arity, CompiledFunctions functions, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
- super(name, arity, isStrict, isBuiltin, isConstructor);
+ FinalScriptFunctionData(final String name, final int arity, final CompiledFunctions functions, final int flags) {
+ super(name, arity, flags);
code.addAll(functions);
}
/**
- * Constructor - used from ScriptFunction. This assumes that we have code alraedy for the
+ * Constructor - used from ScriptFunction. This assumes that we have code already for the
* method (typically a native method) and possibly specializations.
*
- * @param name name
- * @param mh method handle for generic version of method
- * @param specs specializations
- * @param isStrict strict
- * @param isBuiltin builtin
- * @param isConstructor constructor
+ * @param name name
+ * @param mh method handle for generic version of method
+ * @param specs specializations
+ * @param flags {@link ScriptFunctionData} flags
*/
- FinalScriptFunctionData(final String name, final MethodHandle mh, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
- super(name, arity(mh), isStrict, isBuiltin, isConstructor);
+ FinalScriptFunctionData(final String name, final MethodHandle mh, final MethodHandle[] specs, final int flags) {
+ super(name, arity(mh), flags);
addInvoker(mh);
if (specs != null) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/GlobalObject.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/GlobalObject.java Wed Mar 12 11:26:00 2014 +0100
@@ -211,22 +211,6 @@
public Object getDefaultValue(ScriptObject sobj, Class<?> typeHint);
/**
- * Find the compiled Class for the given script source, if available
- *
- * @param source Source object of the script
- * @return compiled Class object or null
- */
- public Class<?> findCachedClass(Source source);
-
- /**
- * Put the Source associated Class object in the Source-to-Class cache
- *
- * @param source Source of the script
- * @param clazz compiled Class object for the source
- */
- public void cacheClass(Source source, Class<?> clazz);
-
- /**
* Get cached InvokeByName object for the given key
* @param key key to be associated with InvokeByName object
* @param creator if InvokeByName is absent 'creator' is called to make one (lazy init)
--- a/nashorn/src/jdk/nashorn/internal/runtime/Property.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Property.java Wed Mar 12 11:26:00 2014 +0100
@@ -84,9 +84,13 @@
/** Can this property be undefined? */
public static final int CAN_BE_UNDEFINED = 1 << 8;
- /* Is this a function declaration property ? */
+ /** Is this a function declaration property ? */
public static final int IS_FUNCTION_DECLARATION = 1 << 9;
+ /** Is this property bound to a receiver? This means get/set operations will be delegated to
+ * a statically defined object instead of the object passed as callsite parameter. */
+ public static final int IS_BOUND = 1 << 10;
+
/** Property key. */
private final String key;
@@ -252,6 +256,16 @@
}
/**
+ * Is this property bound to a receiver? If this method returns {@code true} get and set operations
+ * will be delegated to a statically bound object instead of the object passed as parameter.
+ *
+ * @return true if this is a bound property
+ */
+ public boolean isBound() {
+ return (flags & IS_BOUND) == IS_BOUND;
+ }
+
+ /**
* Does this property use any slots in the spill array described in
* {@link Property#isSpill}? In that case how many. Currently a property
* only uses max one spill slot, but this may change in future representations
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Mar 12 11:26:00 2014 +0100
@@ -103,9 +103,7 @@
public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, final String allocatorClassName, final PropertyMap allocatorMap) {
super(functionName(functionNode),
functionNode.getParameters().size(),
- functionNode.isStrict(),
- false,
- true);
+ getFlags(functionNode));
this.functionNode = functionNode;
this.source = functionNode.getSource();
@@ -129,10 +127,11 @@
final StringBuilder sb = new StringBuilder();
if (source != null) {
- sb.append(source.getName())
- .append(':')
- .append(functionNode.getLineNumber())
- .append(' ');
+ sb.append(source.getName());
+ if (functionNode != null) {
+ sb.append(':').append(functionNode.getLineNumber());
+ }
+ sb.append(' ');
}
return sb.toString() + super.toString();
@@ -159,6 +158,20 @@
return Token.toDesc(TokenType.FUNCTION, position, length);
}
+ private static int getFlags(final FunctionNode functionNode) {
+ int flags = IS_CONSTRUCTOR;
+ if (functionNode.isStrict()) {
+ flags |= IS_STRICT;
+ }
+ if (functionNode.needsCallee()) {
+ flags |= NEEDS_CALLEE;
+ }
+ if (functionNode.usesThis() || functionNode.hasEval()) {
+ flags |= USES_THIS;
+ }
+ return flags;
+ }
+
@Override
ScriptObject allocate(final PropertyMap map) {
try {
@@ -182,41 +195,42 @@
return allocatorMap;
}
+
+ @Override
+ protected void ensureCompiled() {
+ if (functionNode != null && functionNode.isLazy()) {
+ Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
+ final Compiler compiler = new Compiler(installer);
+ functionNode = compiler.compile(functionNode);
+ assert !functionNode.isLazy();
+ compiler.install(functionNode);
+ flags = getFlags(functionNode);
+ }
+ }
+
@Override
protected synchronized void ensureCodeGenerated() {
- if (!code.isEmpty()) {
- return; // nothing to do, we have code, at least some.
- }
+ if (!code.isEmpty()) {
+ return; // nothing to do, we have code, at least some.
+ }
- if (functionNode.isLazy()) {
- Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
- final Compiler compiler = new Compiler(installer);
- functionNode = compiler.compile(functionNode);
- assert !functionNode.isLazy();
- compiler.install(functionNode);
+ ensureCompiled();
+
+ /*
+ * We can't get to this program point unless we have bytecode, either from
+ * eager compilation or from running a lazy compile on the lines above
+ */
- /*
- * We don't need to update any flags - varArgs and needsCallee are instrincic
- * in the function world we need to get a destination node from the compile instead
- * and replace it with our function node. TODO
- */
- }
+ assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
+
+ // code exists - look it up and add it into the automatically sorted invoker list
+ addCode(functionNode);
- /*
- * We can't get to this program point unless we have bytecode, either from
- * eager compilation or from running a lazy compile on the lines above
- */
-
- assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
-
- // code exists - look it up and add it into the automatically sorted invoker list
- addCode(functionNode);
-
- if (! functionNode.canSpecialize()) {
- // allow GC to claim IR stuff that is not needed anymore
- functionNode = null;
- installer = null;
- }
+ if (! functionNode.canSpecialize()) {
+ // allow GC to claim IR stuff that is not needed anymore
+ functionNode = null;
+ installer = null;
+ }
}
private MethodHandle addCode(final FunctionNode fn) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Wed Mar 12 11:26:00 2014 +0100
@@ -66,6 +66,8 @@
private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
+ private static final MethodHandle GLOBALFILTER = findOwnMH("globalFilter", Object.class, Object.class);
+
/** method handle to scope getter for this ScriptFunction */
public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
@@ -91,9 +93,7 @@
* @param map property map
* @param scope scope
* @param specs specialized version of this function - other method handles
- * @param strict is this a strict mode function?
- * @param builtin is this a built in function?
- * @param isConstructor is this a constructor?
+ * @param flags {@link ScriptFunctionData} flags
*/
protected ScriptFunction(
final String name,
@@ -101,11 +101,9 @@
final PropertyMap map,
final ScriptObject scope,
final MethodHandle[] specs,
- final boolean strict,
- final boolean builtin,
- final boolean isConstructor) {
+ final int flags) {
- this(new FinalScriptFunctionData(name, methodHandle, specs, strict, builtin, isConstructor), map, scope);
+ this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope);
}
/**
@@ -480,6 +478,13 @@
return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(obj);
}
+
+ @SuppressWarnings("unused")
+ private static Object globalFilter(final Object object) {
+ // replace whatever we get with the current global object
+ return Context.getGlobalTrusted();
+ }
+
/**
* dyn:call call site signature: (callee, thiz, [args...])
* generated method signature: (callee, thiz, [args...])
@@ -495,11 +500,11 @@
@Override
protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final MethodType type = desc.getMethodType();
+ final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc);
if (request.isCallSiteUnstable()) {
- // (this, callee, args...) => (this, callee, args[])
- final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class,
- type.parameterCount() - 2);
+ // (callee, this, args...) => (callee, this, args[])
+ final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, type.parameterCount() - 2);
// If call site is statically typed to take a ScriptFunction, we don't need a guard, otherwise we need a
// generic "is this a ScriptFunction?" guard.
@@ -510,17 +515,12 @@
MethodHandle boundHandle;
MethodHandle guard = null;
- final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc);
-
if (data.needsCallee()) {
final MethodHandle callHandle = getBestInvoker(type, request.getArguments());
- if (scopeCall) {
+ if (scopeCall && needsWrappedThis()) {
// Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
- // (callee, this, args...) => (callee, args...)
- boundHandle = MH.insertArguments(callHandle, 1, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
- // (callee, args...) => (callee, [this], args...)
- boundHandle = MH.dropArguments(boundHandle, 1, Object.class);
-
+ // (callee, this, args...) => (callee, [this], args...)
+ boundHandle = MH.filterArguments(callHandle, 1, GLOBALFILTER);
} else {
// It's already (callee, this, args...), just what we need
boundHandle = callHandle;
@@ -531,12 +531,12 @@
// NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the
// current lookup as its "this" so it can do security-sensitive creation of adapter classes.
boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc.getLookup()), 0, Object.class, Object.class);
- } else if (scopeCall) {
+ } else if (scopeCall && needsWrappedThis()) {
// Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
- // (this, args...) => (args...)
- boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
- // (args...) => ([callee], [this], args...)
- boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class);
+ // (this, args...) => ([this], args...)
+ boundHandle = MH.filterArguments(callHandle, 0, GLOBALFILTER);
+ // ([this], args...) => ([callee], [this], args...)
+ boundHandle = MH.dropArguments(boundHandle, 0, Object.class);
} else {
// (this, args...) => ([callee], this, args...)
boundHandle = MH.dropArguments(callHandle, 0, Object.class);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Wed Mar 12 11:26:00 2014 +0100
@@ -47,33 +47,44 @@
/** All versions of this function that have been generated to code */
protected final CompiledFunctions code;
- private int arity;
-
- private final boolean isStrict;
+ /** Function flags */
+ protected int flags;
- private final boolean isBuiltin;
-
- private final boolean isConstructor;
+ private int arity;
private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
+ /** Is this a strict mode function? */
+ public static final int IS_STRICT = 1 << 0;
+ /** Is this a built-in function? */
+ public static final int IS_BUILTIN = 1 << 1;
+ /** Is this a constructor function? */
+ public static final int IS_CONSTRUCTOR = 1 << 2;
+ /** Does this function expect a callee argument? */
+ public static final int NEEDS_CALLEE = 1 << 3;
+ /** Does this function make use of the this-object argument? */
+ public static final int USES_THIS = 1 << 4;
+
+ /** Flag for strict or built-in functions */
+ public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN;
+ /** Flag for built-in constructors */
+ public static final int IS_BUILTIN_CONSTRUCTOR = IS_BUILTIN | IS_CONSTRUCTOR;
+ /** Flag for strict constructors */
+ public static final int IS_STRICT_CONSTRUCTOR = IS_STRICT | IS_CONSTRUCTOR;
+
/**
* Constructor
*
* @param name script function name
* @param arity arity
- * @param isStrict is the function strict
- * @param isBuiltin is the function built in
- * @param isConstructor is the function a constructor
+ * @param flags the function flags
*/
- ScriptFunctionData(final String name, final int arity, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
- this.name = name;
- this.arity = arity;
- this.code = new CompiledFunctions();
- this.isStrict = isStrict;
- this.isBuiltin = isBuiltin;
- this.isConstructor = isConstructor;
+ ScriptFunctionData(final String name, final int arity, final int flags) {
+ this.name = name;
+ this.arity = arity;
+ this.code = new CompiledFunctions();
+ this.flags = flags;
}
final int getArity() {
@@ -105,21 +116,21 @@
* @return true if strict, false otherwise
*/
public boolean isStrict() {
- return isStrict;
+ return (flags & IS_STRICT) != 0;
}
boolean isBuiltin() {
- return isBuiltin;
+ return (flags & IS_BUILTIN) != 0;
}
boolean isConstructor() {
- return isConstructor;
+ return (flags & IS_CONSTRUCTOR) != 0;
}
boolean needsCallee() {
- // we don't know if we need a callee or not unless we are generated
- ensureCodeGenerated();
- return code.needsCallee();
+ // we don't know if we need a callee or not unless code has been compiled
+ ensureCompiled();
+ return (flags & NEEDS_CALLEE) != 0;
}
/**
@@ -128,7 +139,7 @@
* @return true if this argument must be an object
*/
boolean needsWrappedThis() {
- return !isStrict && !isBuiltin;
+ return (flags & USES_THIS) != 0 && (flags & IS_STRICT_OR_BUILTIN) == 0;
}
String toSource() {
@@ -202,6 +213,15 @@
}
/**
+ * If we can have lazy code generation, this is a hook to ensure that the code has been compiled.
+ * This does not guarantee the code been installed in this {@code ScriptFunctionData} instance;
+ * use {@link #ensureCodeGenerated()} to install the actual method handles.
+ */
+ protected void ensureCompiled() {
+ //empty
+ }
+
+ /**
* Return a generic Object/Object invoker for this method. It will ensure code
* is generated, get the most generic of all versions of this function and adapt it
* to Objects.
@@ -259,6 +279,8 @@
final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
final int length = args == null ? 0 : args.length;
+ // Clear the callee and this flags
+ final int boundFlags = flags & ~NEEDS_CALLEE & ~USES_THIS;
CompiledFunctions boundList = new CompiledFunctions();
if (code.size() == 1) {
@@ -273,8 +295,7 @@
boundList.add(bind(inv, fn, self, allArgs));
}
- ScriptFunctionData boundData = new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, isStrict(), isBuiltin(), isConstructor());
- return boundData;
+ return new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, boundFlags);
}
/**
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Mar 12 11:26:00 2014 +0100
@@ -66,6 +66,7 @@
import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
import jdk.nashorn.internal.objects.DataPropertyDescriptor;
+import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
@@ -131,7 +132,8 @@
static final MethodHandle GETPROTO = findOwnMH("getProto", ScriptObject.class);
static final MethodHandle SETPROTOCHECK = findOwnMH("setProtoCheck", void.class, Object.class);
- static final MethodHandle MEGAMORPHIC_GET = findOwnMH("megamorphicGet", Object.class, String.class, boolean.class);
+ static final MethodHandle MEGAMORPHIC_GET = findOwnMH("megamorphicGet", Object.class, String.class, boolean.class, boolean.class);
+ static final MethodHandle GLOBALFILTER = findOwnMH("globalFilter", Object.class, Object.class);
static final MethodHandle SETFIELD = findOwnMH("setField", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
static final MethodHandle SETSPILL = findOwnMH("setSpill", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
@@ -225,6 +227,7 @@
final Property oldProp = newMap.findProperty(key);
if (oldProp == null) {
if (property instanceof UserAccessorProperty) {
+ // Note: we copy accessor functions to this object which is semantically different from binding.
final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
newMap = newMap.addPropertyNoHistory(prop);
} else {
@@ -975,17 +978,6 @@
}
/**
- * Get the object value of a property
- *
- * @param find {@link FindProperty} lookup result
- *
- * @return the value of the property
- */
- protected static Object getObjectValue(final FindProperty find) {
- return find.getObjectValue();
- }
-
- /**
* Return methodHandle of value function for call.
*
* @param find data from find property.
@@ -995,7 +987,7 @@
* @return value of property as a MethodHandle or null.
*/
protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
- return getCallMethodHandle(getObjectValue(find), type, bindName);
+ return getCallMethodHandle(find.getObjectValue(), type, bindName);
}
/**
@@ -1019,7 +1011,7 @@
* @return Value of property.
*/
public final Object getWithProperty(final Property property) {
- return getObjectValue(new FindProperty(this, this, property));
+ return new FindProperty(this, this, property).getObjectValue();
}
/**
@@ -1740,7 +1732,7 @@
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
if (request.isCallSiteUnstable() || hasWithScope()) {
- return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator));
+ return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator), isScope() && NashornCallSiteDescriptor.isScope(desc));
}
final FindProperty find = findProperty(name, true);
@@ -1765,9 +1757,8 @@
final Property property = find.getProperty();
methodHandle = find.getGetter(returnType);
- final boolean noGuard = ObjectClassGenerator.OBJECT_FIELDS_ONLY && NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType();
- // getMap() is fine as we have the prototype switchpoint depending on where the property was found
- final MethodHandle guard = noGuard ? null : NashornGuards.getMapGuard(getMap());
+ // Get the appropriate guard for this callsite and property.
+ final MethodHandle guard = NashornGuards.getGuard(this, property, desc);
final ScriptObject owner = find.getOwner();
if (methodHandle != null) {
@@ -1777,31 +1768,32 @@
}
if (!property.hasGetterFunction(owner)) {
- // If not a scope bind to actual prototype as changing prototype will change the property map.
- // For scopes we install a filter that replaces the self object with the prototype owning the property.
- methodHandle = isScope() ?
- addProtoFilter(methodHandle, find.getProtoChainLength()) :
- bindTo(methodHandle, owner);
+ // Add a filter that replaces the self object with the prototype owning the property.
+ methodHandle = addProtoFilter(methodHandle, find.getProtoChainLength());
}
- return new GuardedInvocation(methodHandle, noGuard ? null : getProtoSwitchPoint(name, owner), guard);
+ return new GuardedInvocation(methodHandle, guard == null ? null : getProtoSwitchPoint(name, owner), guard);
}
assert !NashornCallSiteDescriptor.isFastScope(desc);
return new GuardedInvocation(Lookup.emptyGetter(returnType), getProtoSwitchPoint(name, owner), guard);
}
- private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
- final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod);
+ private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name,
+ final boolean isMethod, final boolean isScope) {
+ final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod, isScope);
final MethodHandle guard = getScriptObjectGuard(desc.getMethodType());
return new GuardedInvocation(invoker, guard);
}
@SuppressWarnings("unused")
- private Object megamorphicGet(final String key, final boolean isMethod) {
+ private Object megamorphicGet(final String key, final boolean isMethod, final boolean isScope) {
final FindProperty find = findProperty(key, true);
if (find != null) {
- return getObjectValue(find);
+ return find.getObjectValue();
+ }
+ if (isScope) {
+ throw referenceError("not.defined", key);
}
return isMethod ? getNoSuchMethod(key) : invokeNoSuchProperty(key);
@@ -1996,6 +1988,15 @@
}
}
+ @SuppressWarnings("unused")
+ private static Object globalFilter(final Object object) {
+ ScriptObject sobj = (ScriptObject) object;
+ while (sobj != null && !(sobj instanceof Global)) {
+ sobj = sobj.getProto();
+ }
+ return sobj;
+ }
+
private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
@@ -2041,7 +2042,7 @@
return noSuchProperty(desc, request);
}
- final Object value = getObjectValue(find);
+ final Object value = find.getObjectValue();
if (! (value instanceof ScriptFunction)) {
return createEmptyGetter(desc, name);
}
@@ -2067,7 +2068,7 @@
final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
if (find != null) {
- final Object value = getObjectValue(find);
+ final Object value = find.getObjectValue();
ScriptFunction func = null;
MethodHandle methodHandle = null;
@@ -2102,7 +2103,7 @@
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
if (find != null) {
- final Object func = getObjectValue(find);
+ final Object func = find.getObjectValue();
if (func instanceof ScriptFunction) {
return ScriptRuntime.apply((ScriptFunction)func, this, name);
@@ -2124,7 +2125,7 @@
return invokeNoSuchProperty(name);
}
- final Object value = getObjectValue(find);
+ final Object value = find.getObjectValue();
if (! (value instanceof ScriptFunction)) {
return UNDEFINED;
}
@@ -2664,7 +2665,7 @@
final FindProperty find = object.findProperty(key, false, false, this);
if (find != null) {
- return getObjectValue(find);
+ return find.getObjectValue();
}
}
@@ -2682,7 +2683,7 @@
final FindProperty find = findProperty(key, true);
if (find != null) {
- return getObjectValue(find);
+ return find.getObjectValue();
}
}
@@ -2823,7 +2824,15 @@
throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
}
} else {
- spill(key, value);
+ ScriptObject sobj = this;
+ // undefined scope properties are set in the global object.
+ if (isScope()) {
+ while (sobj != null && !(sobj instanceof Global)) {
+ sobj = sobj.getProto();
+ }
+ assert sobj != null : "no parent global object in scope";
+ }
+ sobj.spill(key, value);
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Wed Mar 12 11:26:00 2014 +0100
@@ -31,7 +31,6 @@
import java.lang.invoke.MethodHandle;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
-import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.linker.NashornGuards;
@@ -104,21 +103,9 @@
* @return the composed guarded invocation that represents the dynamic setter method for the property.
*/
GuardedInvocation createGuardedInvocation() {
- return new GuardedInvocation(methodHandle, getGuard());
- }
-
- private MethodHandle getGuard() {
- return needsNoGuard() ? null : NashornGuards.getMapGuard(getMap());
+ return new GuardedInvocation(methodHandle, NashornGuards.getGuard(sobj, property, desc));
}
- private boolean needsNoGuard() {
- return NashornCallSiteDescriptor.isFastScope(desc) &&
- (ObjectClassGenerator.OBJECT_FIELDS_ONLY || isPropertyTypeStable());
- }
-
- private boolean isPropertyTypeStable() {
- return property == null || !property.canChangeType();
- }
}
private SetMethod createSetMethod() {
@@ -153,10 +140,7 @@
final MethodHandle boundHandle;
if (!property.hasSetterFunction(find.getOwner()) && find.isInherited()) {
- // Bind or add prototype filter depending on whether this is a scope object.
- boundHandle = sobj.isScope() ?
- ScriptObject.addProtoFilter(methodHandle, find.getProtoChainLength()):
- ScriptObject.bindTo(methodHandle, find.getOwner());
+ boundHandle = ScriptObject.addProtoFilter(methodHandle, find.getProtoChainLength());
} else {
boundHandle = methodHandle;
}
@@ -165,7 +149,7 @@
private SetMethod createGlobalPropertySetter() {
final ScriptObject global = Context.getGlobalTrusted();
- return new SetMethod(ScriptObject.bindTo(global.addSpill(getName()), global), null);
+ return new SetMethod(MH.filterArguments(global.addSpill(getName()), 0, ScriptObject.GLOBALFILTER), null);
}
private SetMethod createNewPropertySetter() {
--- a/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java Wed Mar 12 11:26:00 2014 +0100
@@ -88,6 +88,11 @@
@Override
public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
+ if (request.isCallSiteUnstable()) {
+ // Fall back to megamorphic invocation which performs a complete lookup each time without further relinking.
+ return super.lookup(desc, request);
+ }
+
// With scopes can never be observed outside of Nashorn code, so all call sites that can address it will of
// necessity have a Nashorn descriptor - it is safe to cast.
final NashornCallSiteDescriptor ndesc = (NashornCallSiteDescriptor)desc;
@@ -265,7 +270,7 @@
}
private static MethodHandle filter(final MethodHandle mh, final MethodHandle filter) {
- return MH.filterArguments(mh, 0, filter);
+ return MH.filterArguments(mh, 0, filter.asType(filter.type().changeReturnType(mh.type().parameterType(0))));
}
/**
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java Wed Mar 12 11:26:00 2014 +0100
@@ -29,6 +29,11 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.ref.WeakReference;
+import jdk.internal.dynalink.CallSiteDescriptor;
+import jdk.nashorn.internal.codegen.ObjectClassGenerator;
+import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -40,6 +45,7 @@
private static final MethodHandle IS_SCRIPTOBJECT = findOwnMH("isScriptObject", boolean.class, Object.class);
private static final MethodHandle IS_SCRIPTFUNCTION = findOwnMH("isScriptFunction", boolean.class, Object.class);
private static final MethodHandle IS_MAP = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class);
+ private static final MethodHandle SAME_OBJECT = findOwnMH("sameObject", boolean.class, Object.class, WeakReference.class);
private static final MethodHandle IS_INSTANCEOF_2 = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class);
// don't create me!
@@ -75,6 +81,55 @@
}
/**
+ * Determine whether the given callsite needs a guard.
+ * @param property the property, or null
+ * @param desc the callsite descriptor
+ * @return true if a guard should be used for this callsite
+ */
+ static boolean needsGuard(final Property property, final CallSiteDescriptor desc) {
+ return property == null || property.isConfigurable()
+ || property.isBound() || !ObjectClassGenerator.OBJECT_FIELDS_ONLY
+ || !NashornCallSiteDescriptor.isFastScope(desc) || property.canChangeType();
+ }
+
+ /**
+ * Get the guard for a property access. This returns an identity guard for non-configurable global properties
+ * and a map guard for everything else.
+ *
+ * @param sobj the first object in the prototype chain
+ * @param property the property
+ * @param desc the callsite descriptor
+ * @return method handle for guard
+ */
+ public static MethodHandle getGuard(final ScriptObject sobj, final Property property, final CallSiteDescriptor desc) {
+ if (!needsGuard(property, desc)) {
+ return null;
+ }
+ if (NashornCallSiteDescriptor.isScope(desc)) {
+ if (property != null && property.isBound()) {
+ // This is a declared top level variables in main script or eval, use identity guard.
+ return getIdentityGuard(sobj);
+ }
+ if (!(sobj instanceof Global) && (property == null || property.isConfigurable())) {
+ // Undeclared variables in nested evals need stronger guards
+ return combineGuards(getIdentityGuard(sobj), getMapGuard(sobj.getMap()));
+ }
+ }
+ return getMapGuard(sobj.getMap());
+ }
+
+
+ /**
+ * Get a guard that checks referential identity of the current object.
+ *
+ * @param sobj the self object
+ * @return true if same self object instance
+ */
+ public static MethodHandle getIdentityGuard(final ScriptObject sobj) {
+ return MH.insertArguments(SAME_OBJECT, 1, new WeakReference<>(sobj));
+ }
+
+ /**
* Get a guard that checks if in item is an instance of either of two classes.
*
* @param class1 the first class
@@ -112,6 +167,11 @@
}
@SuppressWarnings("unused")
+ private static boolean sameObject(final Object self, final WeakReference<ScriptObject> ref) {
+ return self == ref.get();
+ }
+
+ @SuppressWarnings("unused")
private static boolean isInstanceOf2(final Object self, final Class<?> class1, final Class<?> class2) {
return class1.isInstance(self) || class2.isInstance(self);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Wed Mar 12 11:26:00 2014 +0100
@@ -35,6 +35,7 @@
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.internal.dynalink.support.Guards;
import jdk.nashorn.internal.lookup.Lookup;
+import jdk.nashorn.internal.runtime.FindProperty;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
@@ -61,8 +62,9 @@
* type {@code receiverClass}.
*/
public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Class<?> receiverClass,
- final ScriptObject wrappedReceiver, final MethodHandle wrapFilter) {
- return lookupPrimitive(request, Guards.getInstanceOfGuard(receiverClass), wrappedReceiver, wrapFilter);
+ final ScriptObject wrappedReceiver, final MethodHandle wrapFilter,
+ final MethodHandle protoFilter) {
+ return lookupPrimitive(request, Guards.getInstanceOfGuard(receiverClass), wrappedReceiver, wrapFilter, protoFilter);
}
/**
@@ -79,7 +81,8 @@
* type (that is implied by both {@code guard} and {@code wrappedReceiver}).
*/
public static GuardedInvocation lookupPrimitive(final LinkRequest request, final MethodHandle guard,
- final ScriptObject wrappedReceiver, final MethodHandle wrapFilter) {
+ final ScriptObject wrappedReceiver, final MethodHandle wrapFilter,
+ final MethodHandle protoFilter) {
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
if ("setProp".equals(operator) || "setElem".equals(operator)) {
@@ -93,9 +96,23 @@
if(desc.getNameTokenCount() > 2) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
- if(wrappedReceiver.findProperty(name, true) == null) {
+ final FindProperty find = wrappedReceiver.findProperty(name, true);
+ if(find == null) {
// Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it.
return null;
+ } else if (find.isInherited() && !find.getProperty().hasGetterFunction(find.getOwner())) {
+ // If property is found in the prototype object bind the method handle directly to
+ // the proto filter instead of going through wrapper instantiation below.
+ final ScriptObject proto = wrappedReceiver.getProto();
+ final GuardedInvocation link = proto.lookup(desc, request);
+
+ if (link != null) {
+ final MethodHandle invocation = link.getInvocation();
+ final MethodHandle adaptedInvocation = MH.asType(invocation, invocation.type().changeParameterType(0, Object.class));
+ final MethodHandle method = MH.filterArguments(adaptedInvocation, 0, protoFilter);
+ final MethodHandle protoGuard = MH.filterArguments(link.getGuard(), 0, protoFilter);
+ return new GuardedInvocation(method, NashornGuards.combineGuards(guard, protoGuard));
+ }
}
}
final GuardedInvocation link = wrappedReceiver.lookup(desc, request);
--- a/nashorn/test/src/jdk/nashorn/api/scripting/ScopeTest.java Fri Mar 07 10:59:02 2014 -0800
+++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScopeTest.java Wed Mar 12 11:26:00 2014 +0100
@@ -245,4 +245,320 @@
sb.put("x", "newX");
assertTrue(e.eval("x", ctx).equals("newX"));
}
+
+ /**
+ * Test multi-threaded access to defined global variables for shared script classes with multiple globals.
+ */
+ @Test
+ public static void multiThreadedVarTest() throws ScriptException, InterruptedException {
+ final ScriptEngineManager m = new ScriptEngineManager();
+ final ScriptEngine e = m.getEngineByName("nashorn");
+ final Bindings b = e.createBindings();
+ final ScriptContext origContext = e.getContext();
+ final ScriptContext newCtxt = new SimpleScriptContext();
+ newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
+ final String sharedScript = "foo";
+
+ assertEquals(e.eval("var foo = 'original context';", origContext), null);
+ assertEquals(e.eval("var foo = 'new context';", newCtxt), null);
+
+ final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
+ final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000));
+ t1.start();
+ t2.start();
+ t1.join();
+ t2.join();
+
+ assertEquals(e.eval("var foo = 'newer context';", newCtxt), null);
+ final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
+ final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000));
+
+ t3.start();
+ t4.start();
+ t3.join();
+ t4.join();
+
+ assertEquals(e.eval(sharedScript), "original context");
+ assertEquals(e.eval(sharedScript, newCtxt), "newer context");
+ }
+
+ /**
+ * Test multi-threaded access to undefined global variables for shared script classes with multiple globals.
+ */
+ @Test
+ public static void multiThreadedGlobalTest() throws ScriptException, InterruptedException {
+ final ScriptEngineManager m = new ScriptEngineManager();
+ final ScriptEngine e = m.getEngineByName("nashorn");
+ final Bindings b = e.createBindings();
+ final ScriptContext origContext = e.getContext();
+ final ScriptContext newCtxt = new SimpleScriptContext();
+ newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
+
+ assertEquals(e.eval("foo = 'original context';", origContext), "original context");
+ assertEquals(e.eval("foo = 'new context';", newCtxt), "new context");
+ final String sharedScript = "foo";
+
+ final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
+ final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000));
+ t1.start();
+ t2.start();
+ t1.join();
+ t2.join();
+
+ Object obj3 = e.eval("delete foo; foo = 'newer context';", newCtxt);
+ assertEquals(obj3, "newer context");
+ final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
+ final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000));
+
+ t3.start();
+ t4.start();
+ t3.join();
+ t4.join();
+
+ Assert.assertEquals(e.eval(sharedScript), "original context");
+ Assert.assertEquals(e.eval(sharedScript, newCtxt), "newer context");
+ }
+
+ /**
+ * Test multi-threaded access using the postfix ++ operator for shared script classes with multiple globals.
+ */
+ @Test
+ public static void multiThreadedIncTest() throws ScriptException, InterruptedException {
+ final ScriptEngineManager m = new ScriptEngineManager();
+ final ScriptEngine e = m.getEngineByName("nashorn");
+ final Bindings b = e.createBindings();
+ final ScriptContext origContext = e.getContext();
+ final ScriptContext newCtxt = new SimpleScriptContext();
+ newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
+
+ assertEquals(e.eval("var x = 0;", origContext), null);
+ assertEquals(e.eval("var x = 2;", newCtxt), null);
+ final String sharedScript = "x++;";
+
+ final Thread t1 = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ for (int i = 0; i < 1000; i++) {
+ assertEquals(e.eval(sharedScript, origContext), (double)i);
+ }
+ } catch (ScriptException se) {
+ fail(se.toString());
+ }
+ }
+ });
+ final Thread t2 = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ for (int i = 2; i < 1000; i++) {
+ assertEquals(e.eval(sharedScript, newCtxt), (double)i);
+ }
+ } catch (ScriptException se) {
+ fail(se.toString());
+ }
+ }
+ });
+ t1.start();
+ t2.start();
+ t1.join();
+ t2.join();
+ }
+
+ /**
+ * Test multi-threaded access to primitive prototype properties for shared script classes with multiple globals.
+ */
+ @Test
+ public static void multiThreadedPrimitiveTest() throws ScriptException, InterruptedException {
+ final ScriptEngineManager m = new ScriptEngineManager();
+ final ScriptEngine e = m.getEngineByName("nashorn");
+ final Bindings b = e.createBindings();
+ final ScriptContext origContext = e.getContext();
+ final ScriptContext newCtxt = new SimpleScriptContext();
+ newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
+
+ Object obj1 = e.eval("String.prototype.foo = 'original context';", origContext);
+ Object obj2 = e.eval("String.prototype.foo = 'new context';", newCtxt);
+ assertEquals(obj1, "original context");
+ assertEquals(obj2, "new context");
+ final String sharedScript = "''.foo";
+
+ final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
+ final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000));
+ t1.start();
+ t2.start();
+ t1.join();
+ t2.join();
+
+ Object obj3 = e.eval("delete String.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt);
+ assertEquals(obj3, "newer context");
+ final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
+ final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000));
+
+ t3.start();
+ t4.start();
+ t3.join();
+ t4.join();
+
+ Assert.assertEquals(e.eval(sharedScript), "original context");
+ Assert.assertEquals(e.eval(sharedScript, newCtxt), "newer context");
+ }
+
+ /**
+ * Test multi-threaded scope function invocation for shared script classes with multiple globals.
+ */
+ @Test
+ public static void multiThreadedFunctionTest() throws ScriptException, InterruptedException {
+ final ScriptEngineManager m = new ScriptEngineManager();
+ final ScriptEngine e = m.getEngineByName("nashorn");
+ final Bindings b = e.createBindings();
+ final ScriptContext origContext = e.getContext();
+ final ScriptContext newCtxt = new SimpleScriptContext();
+ newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
+
+ e.eval(new URLReader(ScopeTest.class.getResource("resources/func.js")), origContext);
+ assertEquals(origContext.getAttribute("scopeVar"), 1);
+ assertEquals(e.eval("scopeTest()"), 1);
+
+ e.eval(new URLReader(ScopeTest.class.getResource("resources/func.js")), newCtxt);
+ assertEquals(newCtxt.getAttribute("scopeVar"), 1);
+ assertEquals(e.eval("scopeTest();", newCtxt), 1);
+
+ assertEquals(e.eval("scopeVar = 3;", newCtxt), 3);
+ assertEquals(newCtxt.getAttribute("scopeVar"), 3);
+
+
+ final Thread t1 = new Thread(new ScriptRunner(e, origContext, "scopeTest()", 1, 1000));
+ final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, "scopeTest()", 3, 1000));
+
+ t1.start();
+ t2.start();
+ t1.join();
+ t2.join();
+
+ }
+
+ /**
+ * Test multi-threaded access to global getters and setters for shared script classes with multiple globals.
+ */
+ @Test
+ public static void getterSetterTest() throws ScriptException, InterruptedException {
+ final ScriptEngineManager m = new ScriptEngineManager();
+ final ScriptEngine e = m.getEngineByName("nashorn");
+ final Bindings b = e.createBindings();
+ final ScriptContext origContext = e.getContext();
+ final ScriptContext newCtxt = new SimpleScriptContext();
+ newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
+ final String sharedScript = "accessor1";
+
+ e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), origContext);
+ assertEquals(e.eval("accessor1 = 1;"), 1);
+ assertEquals(e.eval(sharedScript), 1);
+
+ e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), newCtxt);
+ assertEquals(e.eval("accessor1 = 2;", newCtxt), 2);
+ assertEquals(e.eval(sharedScript, newCtxt), 2);
+
+
+ final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, 1, 1000));
+ final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, 2, 1000));
+
+ t1.start();
+ t2.start();
+ t1.join();
+ t2.join();
+
+ assertEquals(e.eval(sharedScript), 1);
+ assertEquals(e.eval(sharedScript, newCtxt), 2);
+ assertEquals(e.eval("v"), 1);
+ assertEquals(e.eval("v", newCtxt), 2);
+ }
+
+ /**
+ * Test multi-threaded access to global getters and setters for shared script classes with multiple globals.
+ */
+ @Test
+ public static void getterSetter2Test() throws ScriptException, InterruptedException {
+ final ScriptEngineManager m = new ScriptEngineManager();
+ final ScriptEngine e = m.getEngineByName("nashorn");
+ final Bindings b = e.createBindings();
+ final ScriptContext origContext = e.getContext();
+ final ScriptContext newCtxt = new SimpleScriptContext();
+ newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
+ final String sharedScript = "accessor2";
+
+ e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), origContext);
+ assertEquals(e.eval("accessor2 = 1;"), 1);
+ assertEquals(e.eval(sharedScript), 1);
+
+ e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), newCtxt);
+ assertEquals(e.eval("accessor2 = 2;", newCtxt), 2);
+ assertEquals(e.eval(sharedScript, newCtxt), 2);
+
+
+ final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, 1, 1000));
+ final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, 2, 1000));
+
+ t1.start();
+ t2.start();
+ t1.join();
+ t2.join();
+
+ assertEquals(e.eval(sharedScript), 1);
+ assertEquals(e.eval(sharedScript, newCtxt), 2);
+ assertEquals(e.eval("x"), 1);
+ assertEquals(e.eval("x", newCtxt), 2);
+ }
+
+ /**
+ * Test "slow" scopes involving {@code with} and {@code eval} statements for shared script classes with multiple globals.
+ */
+ @Test
+ public static void testSlowScope() throws ScriptException, InterruptedException {
+ final ScriptEngineManager m = new ScriptEngineManager();
+ final ScriptEngine e = m.getEngineByName("nashorn");
+
+ for (int i = 0; i < 100; i++) {
+ final Bindings b = e.createBindings();
+ final ScriptContext ctxt = new SimpleScriptContext();
+ ctxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
+
+ e.eval(new URLReader(ScopeTest.class.getResource("resources/witheval.js")), ctxt);
+ assertEquals(e.eval("a", ctxt), 1);
+ assertEquals(b.get("a"), 1);
+ assertEquals(e.eval("b", ctxt), 3);
+ assertEquals(b.get("b"), 3);
+ assertEquals(e.eval("c", ctxt), 10);
+ assertEquals(b.get("c"), 10);
+ }
+ }
+
+ private static class ScriptRunner implements Runnable {
+
+ final ScriptEngine engine;
+ final ScriptContext context;
+ final String source;
+ final Object expected;
+ final int iterations;
+
+ ScriptRunner(final ScriptEngine engine, final ScriptContext context, final String source, final Object expected, final int iterations) {
+ this.engine = engine;
+ this.context = context;
+ this.source = source;
+ this.expected = expected;
+ this.iterations = iterations;
+ }
+
+ @Override
+ public void run() {
+ try {
+ for (int i = 0; i < iterations; i++) {
+ assertEquals(engine.eval(source, context), expected);
+ }
+ } catch (ScriptException se) {
+ throw new RuntimeException(se);
+ }
+ }
+ }
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/src/jdk/nashorn/api/scripting/resources/func.js Wed Mar 12 11:26:00 2014 +0100
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+// This script is loaded from jdk.nashorn.api.scripting.ScopeTest to test script class sharing and reuse.
+
+var scopeVar = 1;
+var global = this;
+undefGlobal = this;
+
+function scopeTest() {
+ if (this !== global) {
+ throw new Error("this !== global");
+ }
+ if (this !== undefGlobal) {
+ throw new Error("this !== undefinedGlobal")
+ }
+ return scopeVar;
+}
+
+scopeTest();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/src/jdk/nashorn/api/scripting/resources/gettersetter.js Wed Mar 12 11:26:00 2014 +0100
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+// This script is loaded from jdk.nashorn.api.scripting.ScopeTest to test script class sharing and reuse.
+
+var v;
+
+Object.defineProperty(this, "accessor1", {
+ get: function() { return v; },
+ set: function(n) { v = n; }
+});
+
+Object.defineProperty(this, "accessor2", {
+ get: function() { return x; },
+ set: function(n) { x = n; }
+});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/src/jdk/nashorn/api/scripting/resources/witheval.js Wed Mar 12 11:26:00 2014 +0100
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+// This script is loaded from jdk.nashorn.api.scripting.ScopeTest to test script class sharing and reuse.
+
+var a;
+
+function outer(p, e) {
+ eval(e);
+ with(p) {
+ function inner() {
+ a = 1;
+ c = 10;
+ if (a !== 1) {
+ throw new Error("a !== 1");
+ }
+ if (b !== 3) {
+ throw new Error("b !== 3");
+ }
+ if (c !== 10) {
+ throw new Error("c !== 10");
+ }
+ }
+ inner();
+ }
+}
+
+outer({}, "b = 3;");
+
+if (a !== 1) {
+ throw new Error("a !== 1");
+}
+if (b !== 3) {
+ throw new Error("b !== 3");
+}
+if (c !== 10) {
+ throw new Error("c !== 10");
+}