8062308: Incorrect constant linkage with multiple Globals in a Context
Reviewed-by: lagergren, sundar
This commit is contained in:
parent
ed9bce193a
commit
c2b5d15a9d
@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
@ -41,7 +42,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
@ -54,7 +54,6 @@ import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.runtime.ConsString;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.GlobalConstants;
|
||||
import jdk.nashorn.internal.runtime.GlobalFunctions;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.NativeJavaPackage;
|
||||
@ -438,9 +437,6 @@ public final class Global extends ScriptObject implements Scope {
|
||||
this.scontext = scontext;
|
||||
}
|
||||
|
||||
// global constants for this global - they can be replaced with MethodHandle.constant until invalidated
|
||||
private static AtomicReference<GlobalConstants> gcsInstance = new AtomicReference<>();
|
||||
|
||||
@Override
|
||||
protected Context getContext() {
|
||||
return context;
|
||||
@ -470,11 +466,6 @@ public final class Global extends ScriptObject implements Scope {
|
||||
super(checkAndGetMap(context));
|
||||
this.context = context;
|
||||
this.setIsScope();
|
||||
//we can only share one instance of Global constants between globals, or we consume way too much
|
||||
//memory - this is good enough for most programs
|
||||
while (gcsInstance.get() == null) {
|
||||
gcsInstance.compareAndSet(null, new GlobalConstants(context.getLogger(GlobalConstants.class)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -492,15 +483,6 @@ public final class Global extends ScriptObject implements Scope {
|
||||
return self instanceof Global? (Global)self : instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the global constants map for fields that
|
||||
* can be accessed as MethodHandle.constant
|
||||
* @return constant map
|
||||
*/
|
||||
public static GlobalConstants getConstants() {
|
||||
return gcsInstance.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have a Global instance
|
||||
* @return true if one exists
|
||||
|
@ -33,6 +33,7 @@ import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
import static jdk.nashorn.internal.runtime.Source.sourceFor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
@ -60,6 +61,7 @@ import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
@ -262,6 +264,10 @@ public final class Context {
|
||||
// persistent code store
|
||||
private CodeStore codeStore;
|
||||
|
||||
// A factory for linking global properties as constant method handles. It is created when the first Global
|
||||
// is created, and invalidated forever once the second global is created.
|
||||
private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Get the current global scope
|
||||
* @return the current global scope
|
||||
@ -293,7 +299,10 @@ public final class Context {
|
||||
assert getGlobal() != global;
|
||||
//same code can be cached between globals, then we need to invalidate method handle constants
|
||||
if (global != null) {
|
||||
Global.getConstants().invalidateAll();
|
||||
final GlobalConstants globalConstants = getContext(global).getGlobalConstants();
|
||||
if (globalConstants != null) {
|
||||
globalConstants.invalidateAll();
|
||||
}
|
||||
}
|
||||
currentGlobal.set(global);
|
||||
}
|
||||
@ -528,6 +537,15 @@ public final class Context {
|
||||
return classFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the factory for constant method handles for global properties. The returned factory can be
|
||||
* invalidated if this Context has more than one Global.
|
||||
* @return the factory for constant method handles for global properties.
|
||||
*/
|
||||
GlobalConstants getGlobalConstants() {
|
||||
return globalConstantsRef.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error manager for this context
|
||||
* @return error manger
|
||||
@ -1016,9 +1034,32 @@ public final class Context {
|
||||
* @return the global script object
|
||||
*/
|
||||
public Global newGlobal() {
|
||||
createOrInvalidateGlobalConstants();
|
||||
return new Global(this);
|
||||
}
|
||||
|
||||
private void createOrInvalidateGlobalConstants() {
|
||||
for (;;) {
|
||||
final GlobalConstants currentGlobalConstants = getGlobalConstants();
|
||||
if (currentGlobalConstants != null) {
|
||||
// Subsequent invocation; we're creating our second or later Global. GlobalConstants is not safe to use
|
||||
// with more than one Global, as the constant method handle linkages it creates create a coupling
|
||||
// between the Global and the call sites in the compiled code.
|
||||
currentGlobalConstants.invalidateForever();
|
||||
return;
|
||||
}
|
||||
final GlobalConstants newGlobalConstants = new GlobalConstants(getLogger(GlobalConstants.class));
|
||||
if (globalConstantsRef.compareAndSet(null, newGlobalConstants)) {
|
||||
// First invocation; we're creating the first Global in this Context. Create the GlobalConstants object
|
||||
// for this Context.
|
||||
return;
|
||||
}
|
||||
|
||||
// If we reach here, then we started out as the first invocation, but another concurrent invocation won the
|
||||
// CAS race. We'll just let the loop repeat and invalidate the CAS race winner.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize given global scope object.
|
||||
*
|
||||
@ -1057,12 +1098,19 @@ public final class Context {
|
||||
* @return current global's context
|
||||
*/
|
||||
static Context getContextTrusted() {
|
||||
return ((ScriptObject)Context.getGlobal()).getContext();
|
||||
return getContext(getGlobal());
|
||||
}
|
||||
|
||||
static Context getContextTrustedOrNull() {
|
||||
final Global global = Context.getGlobal();
|
||||
return global == null ? null : ((ScriptObject)global).getContext();
|
||||
return global == null ? null : getContext(global);
|
||||
}
|
||||
|
||||
private static Context getContext(final Global global) {
|
||||
// We can't invoke Global.getContext() directly, as it's a protected override, and Global isn't in our package.
|
||||
// In order to access the method, we must cast it to ScriptObject first (which is in our package) and then let
|
||||
// virtual invocation do its thing.
|
||||
return ((ScriptObject)global).getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,12 +31,14 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.getProgramPoint;
|
||||
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.SwitchPoint;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.DynamicLinker;
|
||||
@ -50,7 +52,7 @@ import jdk.nashorn.internal.runtime.logging.Loggable;
|
||||
import jdk.nashorn.internal.runtime.logging.Logger;
|
||||
|
||||
/**
|
||||
* Each global owns one of these. This is basically table of accessors
|
||||
* Each context owns one of these. This is basically table of accessors
|
||||
* for global properties. A global constant is evaluated to a MethodHandle.constant
|
||||
* for faster access and to avoid walking to proto chain looking for it.
|
||||
*
|
||||
@ -67,12 +69,19 @@ import jdk.nashorn.internal.runtime.logging.Logger;
|
||||
* reregister the switchpoint. Set twice or more - don't try again forever, or we'd
|
||||
* just end up relinking our way into megamorphisism.
|
||||
*
|
||||
* Also it has to be noted that this kind of linking creates a coupling between a Global
|
||||
* and the call sites in compiled code belonging to the Context. For this reason, the
|
||||
* linkage becomes incorrect as soon as the Context has more than one Global. The
|
||||
* {@link #invalidateForever()} is invoked by the Context to invalidate all linkages and
|
||||
* turn off the functionality of this object as soon as the Context's {@link Context#newGlobal()} is invoked
|
||||
* for second time.
|
||||
*
|
||||
* We can extend this to ScriptObjects in general (GLOBAL_ONLY=false), which requires
|
||||
* a receiver guard on the constant getter, but it currently leaks memory and its benefits
|
||||
* have not yet been investigated property.
|
||||
*
|
||||
* As long as all Globals share the same constant instance, we need synchronization
|
||||
* whenever we access the instance.
|
||||
* As long as all Globals in a Context share the same GlobalConstants instance, we need synchronization
|
||||
* whenever we access it.
|
||||
*/
|
||||
@Logger(name="const")
|
||||
public final class GlobalConstants implements Loggable {
|
||||
@ -82,7 +91,7 @@ public final class GlobalConstants implements Loggable {
|
||||
* Script objects require a receiver guard, which is memory intensive, so this is currently
|
||||
* disabled. We might implement a weak reference based approach to this later.
|
||||
*/
|
||||
private static final boolean GLOBAL_ONLY = true;
|
||||
public static final boolean GLOBAL_ONLY = true;
|
||||
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
@ -98,6 +107,8 @@ public final class GlobalConstants implements Loggable {
|
||||
*/
|
||||
private final Map<String, Access> map = new HashMap<>();
|
||||
|
||||
private final AtomicBoolean invalidatedForever = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Constructor - used only by global
|
||||
* @param log logger, or null if none
|
||||
@ -216,10 +227,34 @@ public final class GlobalConstants implements Loggable {
|
||||
* the same class for a new global, but the builtins and global scoped variables
|
||||
* will have changed.
|
||||
*/
|
||||
public synchronized void invalidateAll() {
|
||||
log.info("New global created - invalidating all constant callsites without increasing invocation count.");
|
||||
for (final Access acc : map.values()) {
|
||||
acc.invalidateUncounted();
|
||||
public void invalidateAll() {
|
||||
if (!invalidatedForever.get()) {
|
||||
log.info("New global created - invalidating all constant callsites without increasing invocation count.");
|
||||
synchronized (this) {
|
||||
for (final Access acc : map.values()) {
|
||||
acc.invalidateUncounted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To avoid an expensive global guard "is this the same global", similar to the
|
||||
* receiver guard on the ScriptObject level, we invalidate all getters when the
|
||||
* second Global is created by the Context owning this instance. After this
|
||||
* method is invoked, this GlobalConstants instance will both invalidate all the
|
||||
* switch points it produced, and it will stop handing out new method handles
|
||||
* altogether.
|
||||
*/
|
||||
public void invalidateForever() {
|
||||
if (invalidatedForever.compareAndSet(false, true)) {
|
||||
log.info("New global created - invalidating all constant callsites.");
|
||||
synchronized (this) {
|
||||
for (final Access acc : map.values()) {
|
||||
acc.invalidateForever();
|
||||
}
|
||||
map.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,7 +286,7 @@ public final class GlobalConstants implements Loggable {
|
||||
return obj;
|
||||
}
|
||||
|
||||
private synchronized Access getOrCreateSwitchPoint(final String name) {
|
||||
private Access getOrCreateSwitchPoint(final String name) {
|
||||
Access acc = map.get(name);
|
||||
if (acc != null) {
|
||||
return acc;
|
||||
@ -267,9 +302,13 @@ public final class GlobalConstants implements Loggable {
|
||||
* @param name name of property
|
||||
*/
|
||||
void delete(final String name) {
|
||||
final Access acc = map.get(name);
|
||||
if (acc != null) {
|
||||
acc.invalidateForever();
|
||||
if (!invalidatedForever.get()) {
|
||||
synchronized (this) {
|
||||
final Access acc = map.get(name);
|
||||
if (acc != null) {
|
||||
acc.invalidateForever();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,45 +352,45 @@ public final class GlobalConstants implements Loggable {
|
||||
*
|
||||
* @return null if failed to set up constant linkage
|
||||
*/
|
||||
synchronized GuardedInvocation findSetMethod(final FindProperty find, final ScriptObject receiver, final GuardedInvocation inv, final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
if (GLOBAL_ONLY && !isGlobalSetter(receiver, find)) {
|
||||
GuardedInvocation findSetMethod(final FindProperty find, final ScriptObject receiver, final GuardedInvocation inv, final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
if (invalidatedForever.get() || (GLOBAL_ONLY && !isGlobalSetter(receiver, find))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
|
||||
final Access acc = getOrCreateSwitchPoint(name);
|
||||
synchronized (this) {
|
||||
final Access acc = getOrCreateSwitchPoint(name);
|
||||
|
||||
if (log.isEnabled()) {
|
||||
log.fine("Trying to link constant SETTER ", acc);
|
||||
}
|
||||
|
||||
if (!acc.mayRetry()) {
|
||||
if (log.isEnabled()) {
|
||||
log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
|
||||
log.fine("Trying to link constant SETTER ", acc);
|
||||
}
|
||||
return null;
|
||||
|
||||
if (!acc.mayRetry() || invalidatedForever.get()) {
|
||||
if (log.isEnabled()) {
|
||||
log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (acc.hasBeenInvalidated()) {
|
||||
log.info("New chance for " + acc);
|
||||
acc.newSwitchPoint();
|
||||
}
|
||||
|
||||
assert !acc.hasBeenInvalidated();
|
||||
|
||||
// if we haven't given up on this symbol, add a switchpoint invalidation filter to the receiver parameter
|
||||
final MethodHandle target = inv.getInvocation();
|
||||
final Class<?> receiverType = target.type().parameterType(0);
|
||||
final MethodHandle boundInvalidator = MH.bindTo(INVALIDATE_SP, this);
|
||||
final MethodHandle invalidator = MH.asType(boundInvalidator, boundInvalidator.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
|
||||
final MethodHandle mh = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
|
||||
|
||||
assert inv.getSwitchPoints() == null : Arrays.asList(inv.getSwitchPoints());
|
||||
log.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
|
||||
return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
|
||||
}
|
||||
|
||||
assert acc.mayRetry();
|
||||
|
||||
if (acc.hasBeenInvalidated()) {
|
||||
log.info("New chance for " + acc);
|
||||
acc.newSwitchPoint();
|
||||
}
|
||||
|
||||
assert !acc.hasBeenInvalidated();
|
||||
|
||||
// if we haven't given up on this symbol, add a switchpoint invalidation filter to the receiver parameter
|
||||
final MethodHandle target = inv.getInvocation();
|
||||
final Class<?> receiverType = target.type().parameterType(0);
|
||||
final MethodHandle boundInvalidator = MH.bindTo(INVALIDATE_SP, this);
|
||||
final MethodHandle invalidator = MH.asType(boundInvalidator, boundInvalidator.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
|
||||
final MethodHandle mh = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
|
||||
|
||||
assert inv.getSwitchPoints() == null : Arrays.asList(inv.getSwitchPoints());
|
||||
log.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
|
||||
return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -380,11 +419,11 @@ public final class GlobalConstants implements Loggable {
|
||||
*
|
||||
* @return resulting getter, or null if failed to create constant
|
||||
*/
|
||||
synchronized GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
|
||||
GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
|
||||
// Only use constant getter for fast scope access, because the receiver may change between invocations
|
||||
// for slow-scope and non-scope callsites.
|
||||
// Also return null for user accessor properties as they may have side effects.
|
||||
if (!NashornCallSiteDescriptor.isFastScope(desc)
|
||||
if (invalidatedForever.get() || !NashornCallSiteDescriptor.isFastScope(desc)
|
||||
|| (GLOBAL_ONLY && !find.getOwner().isGlobal())
|
||||
|| find.getProperty() instanceof UserAccessorProperty) {
|
||||
return null;
|
||||
@ -395,51 +434,53 @@ public final class GlobalConstants implements Loggable {
|
||||
final Class<?> retType = desc.getMethodType().returnType();
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
|
||||
final Access acc = getOrCreateSwitchPoint(name);
|
||||
synchronized (this) {
|
||||
final Access acc = getOrCreateSwitchPoint(name);
|
||||
|
||||
log.fine("Starting to look up object value " + name);
|
||||
final Object c = find.getObjectValue();
|
||||
log.fine("Starting to look up object value " + name);
|
||||
final Object c = find.getObjectValue();
|
||||
|
||||
if (log.isEnabled()) {
|
||||
log.fine("Trying to link constant GETTER " + acc + " value = " + c);
|
||||
}
|
||||
|
||||
if (acc.hasBeenInvalidated() || acc.guardFailed()) {
|
||||
if (log.isEnabled()) {
|
||||
log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
|
||||
log.fine("Trying to link constant GETTER " + acc + " value = " + c);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
final MethodHandle cmh = constantGetter(c);
|
||||
if (acc.hasBeenInvalidated() || acc.guardFailed() || invalidatedForever.get()) {
|
||||
if (log.isEnabled()) {
|
||||
log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
MethodHandle mh;
|
||||
MethodHandle guard;
|
||||
final MethodHandle cmh = constantGetter(c);
|
||||
|
||||
if (isOptimistic) {
|
||||
if (JSType.getAccessorTypeIndex(cmh.type().returnType()) <= JSType.getAccessorTypeIndex(retType)) {
|
||||
//widen return type - this is pessimistic, so it will always work
|
||||
mh = MH.asType(cmh, cmh.type().changeReturnType(retType));
|
||||
MethodHandle mh;
|
||||
MethodHandle guard;
|
||||
|
||||
if (isOptimistic) {
|
||||
if (JSType.getAccessorTypeIndex(cmh.type().returnType()) <= JSType.getAccessorTypeIndex(retType)) {
|
||||
//widen return type - this is pessimistic, so it will always work
|
||||
mh = MH.asType(cmh, cmh.type().changeReturnType(retType));
|
||||
} else {
|
||||
//immediately invalidate - we asked for a too wide constant as a narrower one
|
||||
mh = MH.dropArguments(MH.insertArguments(JSType.THROW_UNWARRANTED.methodHandle(), 0, c, programPoint), 0, Object.class);
|
||||
}
|
||||
} else {
|
||||
//immediately invalidate - we asked for a too wide constant as a narrower one
|
||||
mh = MH.dropArguments(MH.insertArguments(JSType.THROW_UNWARRANTED.methodHandle(), 0, c, programPoint), 0, Object.class);
|
||||
//pessimistic return type filter
|
||||
mh = Lookup.filterReturnType(cmh, retType);
|
||||
}
|
||||
} else {
|
||||
//pessimistic return type filter
|
||||
mh = Lookup.filterReturnType(cmh, retType);
|
||||
}
|
||||
|
||||
if (find.getOwner().isGlobal()) {
|
||||
guard = null;
|
||||
} else {
|
||||
guard = MH.insertArguments(RECEIVER_GUARD, 0, acc, receiver);
|
||||
}
|
||||
if (find.getOwner().isGlobal()) {
|
||||
guard = null;
|
||||
} else {
|
||||
guard = MH.insertArguments(RECEIVER_GUARD, 0, acc, receiver);
|
||||
}
|
||||
|
||||
if (log.isEnabled()) {
|
||||
log.info("Linked getter " + quote(name) + " as MethodHandle.constant() -> " + c + " " + acc.getSwitchPoint());
|
||||
mh = MethodHandleFactory.addDebugPrintout(log, Level.FINE, mh, "get const " + acc);
|
||||
}
|
||||
if (log.isEnabled()) {
|
||||
log.info("Linked getter " + quote(name) + " as MethodHandle.constant() -> " + c + " " + acc.getSwitchPoint());
|
||||
mh = MethodHandleFactory.addDebugPrintout(log, Level.FINE, mh, "get const " + acc);
|
||||
}
|
||||
|
||||
return new GuardedInvocation(mh, guard, acc.getSwitchPoint(), null);
|
||||
return new GuardedInvocation(mh, guard, acc.getSwitchPoint(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
|
||||
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
|
||||
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
|
||||
import static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
@ -922,7 +923,10 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
if (property instanceof UserAccessorProperty) {
|
||||
((UserAccessorProperty)property).setAccessors(this, getMap(), null);
|
||||
}
|
||||
Global.getConstants().delete(property.getKey());
|
||||
final GlobalConstants globalConstants = getGlobalConstants();
|
||||
if (globalConstants != null) {
|
||||
globalConstants.delete(property.getKey());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1983,9 +1987,12 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
}
|
||||
}
|
||||
|
||||
final GuardedInvocation cinv = Global.getConstants().findGetMethod(find, this, desc);
|
||||
if (cinv != null) {
|
||||
return cinv;
|
||||
final GlobalConstants globalConstants = getGlobalConstants();
|
||||
if (globalConstants != null) {
|
||||
final GuardedInvocation cinv = globalConstants.findGetMethod(find, this, desc);
|
||||
if (cinv != null) {
|
||||
return cinv;
|
||||
}
|
||||
}
|
||||
|
||||
final Class<?> returnType = desc.getMethodType().returnType();
|
||||
@ -2183,14 +2190,22 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
|
||||
final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name));
|
||||
|
||||
final GuardedInvocation cinv = Global.getConstants().findSetMethod(find, this, inv, desc, request);
|
||||
if (cinv != null) {
|
||||
return cinv;
|
||||
final GlobalConstants globalConstants = getGlobalConstants();
|
||||
if (globalConstants != null) {
|
||||
final GuardedInvocation cinv = globalConstants.findSetMethod(find, this, inv, desc, request);
|
||||
if (cinv != null) {
|
||||
return cinv;
|
||||
}
|
||||
}
|
||||
|
||||
return inv;
|
||||
}
|
||||
|
||||
private GlobalConstants getGlobalConstants() {
|
||||
// Avoid hitting getContext() which might be costly for a non-Global unless needed.
|
||||
return GlobalConstants.GLOBAL_ONLY && !isGlobal() ? null : getContext().getGlobalConstants();
|
||||
}
|
||||
|
||||
private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) {
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
if (NashornCallSiteDescriptor.isStrict(desc)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user