src/jdk/nashorn/internal/runtime/ScriptObject.java
Print this page
rev 741 : 8029003: setField in ScriptObject is incorrect for non extensible objects
Reviewed-by: lagergren, jlaskey
rev 742 : 8029667: Prototype linking is incorrect
Reviewed-by: jlaskey, sundar
rev 746 : 8031715: Indexed access to java package not working
Reviewed-by: lagergren, hannesw
rev 754 : 8030197: Nashorn: Object.defineProperty() can be lured to change fixed NaN property
Reviewed-by: attila, jlaskey
rev 755 : 8035948: Redesign property listeners for shared classes
Reviewed-by: sundar, lagergren
rev 758 : 8021350: Share script classes between threads/globals within context
Reviewed-by: lagergren, sundar
rev 760 : 8037400: Remove getInitialMap getters and GlobalObject interface
Reviewed-by: lagergren, jlaskey, attila
*** 41,50 ****
--- 41,51 ----
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+ import java.lang.invoke.SwitchPoint;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
*** 63,72 ****
--- 64,74 ----
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.lookup.Lookup;
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;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
*** 86,96 ****
* <li>Modifications of the map include adding/deleting attributes or changing a
* function field value.</li>
* </ul>
*/
! public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess {
/** __proto__ special property name */
public static final String PROTO_PROPERTY_NAME = "__proto__";
/** Search fall back routine name for "no such method" */
static final String NO_SUCH_METHOD_NAME = "__noSuchMethod__";
--- 88,98 ----
* <li>Modifications of the map include adding/deleting attributes or changing a
* function field value.</li>
* </ul>
*/
! public abstract class ScriptObject implements PropertyAccess {
/** __proto__ special property name */
public static final String PROTO_PROPERTY_NAME = "__proto__";
/** Search fall back routine name for "no such method" */
static final String NO_SUCH_METHOD_NAME = "__noSuchMethod__";
*** 105,117 ****
public static final int IS_ARRAY = 0b0000_0010;
/** Per ScriptObject flag - is this an arguments object? */
public static final int IS_ARGUMENTS = 0b0000_0100;
- /** Is this a prototype PropertyMap? */
- public static final int IS_PROTOTYPE = 0b0000_1000;
-
/** Is length property not-writable? */
public static final int IS_LENGTH_NOT_WRITABLE = 0b0001_0000;
/** Spill growth rate - by how many elements does {@link ScriptObject#spill} when full */
public static final int SPILL_RATE = 8;
--- 107,116 ----
*** 131,161 ****
/** Indexed array data. */
private ArrayData arrayData;
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 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);
static final MethodHandle SETSPILLWITHNEW = findOwnMH("setSpillWithNew", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
static final MethodHandle SETSPILLWITHGROW = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class);
private static final MethodHandle TRUNCATINGFILTER = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class);
private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class);
/** Method handle for getting a function argument at a given index. Used from MapCreator */
public static final Call GET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class);
/** Method handle for setting a function argument at a given index. Used from MapCreator */
public static final Call SET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArgument", void.class, int.class, Object.class);
/** Method handle for getting the proto of a ScriptObject */
public static final Call GET_PROTO = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
/** Method handle for setting the proto of a ScriptObject */
! public static final Call SET_PROTO = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class);
/** Method handle for setting the proto of a ScriptObject after checking argument */
public static final Call SET_PROTO_CHECK = virtualCallNoLookup(ScriptObject.class, "setProtoCheck", void.class, Object.class);
/** Method handle for setting the user accessors of a ScriptObject */
--- 130,163 ----
/** Indexed array data. */
private ArrayData arrayData;
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, 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);
static final MethodHandle SETSPILLWITHNEW = findOwnMH("setSpillWithNew", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
static final MethodHandle SETSPILLWITHGROW = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class);
private static final MethodHandle TRUNCATINGFILTER = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class);
private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class);
+ private static final ArrayList<MethodHandle> protoFilters = new ArrayList<>();
+
/** Method handle for getting a function argument at a given index. Used from MapCreator */
public static final Call GET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class);
/** Method handle for setting a function argument at a given index. Used from MapCreator */
public static final Call SET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArgument", void.class, int.class, Object.class);
/** Method handle for getting the proto of a ScriptObject */
public static final Call GET_PROTO = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
/** Method handle for setting the proto of a ScriptObject */
! public static final Call SET_PROTO = virtualCallNoLookup(ScriptObject.class, "setInitialProto", void.class, ScriptObject.class);
/** Method handle for setting the proto of a ScriptObject after checking argument */
public static final Call SET_PROTO_CHECK = virtualCallNoLookup(ScriptObject.class, "setProtoCheck", void.class, Object.class);
/** Method handle for setting the user accessors of a ScriptObject */
*** 197,210 ****
}
this.arrayData = ArrayData.EMPTY_ARRAY;
this.setMap(map == null ? PropertyMap.newMap() : map);
this.proto = proto;
-
- if (proto != null) {
- proto.setIsPrototype();
- }
}
/**
* Copy all properties from the source object with their receiver bound to the source.
* This function was known as mergeMap
--- 199,208 ----
*** 227,238 ****
for (final Property property : properties) {
final String key = property.getKey();
final Property oldProp = newMap.findProperty(key);
if (oldProp == null) {
if (property instanceof UserAccessorProperty) {
final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
! newMap = newMap.addProperty(prop);
} else {
newMap = newMap.addPropertyBind((AccessorProperty)property, source);
}
} else {
// See ECMA section 10.5 Declaration Binding Instantiation
--- 225,237 ----
for (final Property property : properties) {
final String key = property.getKey();
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 {
newMap = newMap.addPropertyBind((AccessorProperty)property, source);
}
} else {
// See ECMA section 10.5 Declaration Binding Instantiation
*** 324,345 ****
* ECMA 8.10.5 ToPropertyDescriptor ( Obj )
*
* @return property descriptor
*/
public final PropertyDescriptor toPropertyDescriptor() {
! final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
final PropertyDescriptor desc;
if (isDataDescriptor()) {
if (has(SET) || has(GET)) {
! throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
}
desc = global.newDataDescriptor(UNDEFINED, false, false, false);
} else if (isAccessorDescriptor()) {
if (has(VALUE) || has(WRITABLE)) {
! throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
}
desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
} else {
desc = global.newGenericDescriptor(false, false);
--- 323,344 ----
* ECMA 8.10.5 ToPropertyDescriptor ( Obj )
*
* @return property descriptor
*/
public final PropertyDescriptor toPropertyDescriptor() {
! final Global global = Context.getGlobal();
final PropertyDescriptor desc;
if (isDataDescriptor()) {
if (has(SET) || has(GET)) {
! throw typeError(global, "inconsistent.property.descriptor");
}
desc = global.newDataDescriptor(UNDEFINED, false, false, false);
} else if (isAccessorDescriptor()) {
if (has(VALUE) || has(WRITABLE)) {
! throw typeError(global, "inconsistent.property.descriptor");
}
desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
} else {
desc = global.newGenericDescriptor(false, false);
*** 354,364 ****
* @param global global scope object
* @param obj object to create property descriptor from
*
* @return property descriptor
*/
! public static PropertyDescriptor toPropertyDescriptor(final ScriptObject global, final Object obj) {
if (obj instanceof ScriptObject) {
return ((ScriptObject)obj).toPropertyDescriptor();
}
throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
--- 353,363 ----
* @param global global scope object
* @param obj object to create property descriptor from
*
* @return property descriptor
*/
! public static PropertyDescriptor toPropertyDescriptor(final Global global, final Object obj) {
if (obj instanceof ScriptObject) {
return ((ScriptObject)obj).toPropertyDescriptor();
}
throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
*** 373,383 ****
* object, or undefined if absent.
*/
public Object getOwnPropertyDescriptor(final String key) {
final Property property = getMap().findProperty(key);
! final GlobalObject global = (GlobalObject)Context.getGlobalTrusted();
if (property != null) {
final ScriptFunction get = property.getGetterFunction(this);
final ScriptFunction set = property.getSetterFunction(this);
--- 372,382 ----
* object, or undefined if absent.
*/
public Object getOwnPropertyDescriptor(final String key) {
final Property property = getMap().findProperty(key);
! final Global global = Context.getGlobal();
if (property != null) {
final ScriptFunction get = property.getGetterFunction(this);
final ScriptFunction set = property.getSetterFunction(this);
*** 438,448 ****
* @param reject is the property extensible - true means new definitions are rejected
*
* @return true if property was successfully defined
*/
public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
! final ScriptObject global = Context.getGlobalTrusted();
final PropertyDescriptor desc = toPropertyDescriptor(global, propertyDesc);
final Object current = getOwnPropertyDescriptor(key);
final String name = JSType.toString(key);
if (current == UNDEFINED) {
--- 437,447 ----
* @param reject is the property extensible - true means new definitions are rejected
*
* @return true if property was successfully defined
*/
public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
! final Global global = Context.getGlobal();
final PropertyDescriptor desc = toPropertyDescriptor(global, propertyDesc);
final Object current = getOwnPropertyDescriptor(key);
final String name = JSType.toString(key);
if (current == UNDEFINED) {
*** 465,475 ****
! newDesc.has(CONFIGURABLE) && ! newDesc.has(ENUMERABLE)) {
// every descriptor field is absent
return true;
}
! if (currentDesc.equals(newDesc)) {
// every descriptor field of the new is same as the current
return true;
}
if (! currentDesc.isConfigurable()) {
--- 464,474 ----
! newDesc.has(CONFIGURABLE) && ! newDesc.has(ENUMERABLE)) {
// every descriptor field is absent
return true;
}
! if (newDesc.hasAndEquals(currentDesc)) {
// every descriptor field of the new is same as the current
return true;
}
if (! currentDesc.isConfigurable()) {
*** 636,646 ****
PropertyDescriptor pdesc = propertyDesc;
final int propFlags = Property.toFlags(pdesc);
if (pdesc.type() == PropertyDescriptor.GENERIC) {
! final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
dDesc.fillFrom((ScriptObject)pdesc);
pdesc = dDesc;
}
--- 635,645 ----
PropertyDescriptor pdesc = propertyDesc;
final int propFlags = Property.toFlags(pdesc);
if (pdesc.type() == PropertyDescriptor.GENERIC) {
! final Global global = Context.getGlobal();
final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
dDesc.fillFrom((ScriptObject)pdesc);
pdesc = dDesc;
}
*** 871,882 ****
// erase old property value and create new user accessor property
erasePropertyValue(oldProperty);
newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
}
- notifyPropertyModified(this, oldProperty, newProperty);
-
return modifyOwnProperty(oldProperty, newProperty);
}
/**
* Modify a property in the object
--- 870,879 ----
*** 979,1009 ****
return ObjectClassGenerator.UNDEFINED_DOUBLE;
}
/**
- * 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.
* @param type method type of function.
* @param bindName null or name to bind to second argument (property not found method.)
*
* @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 methodHandle of value function for call.
*
--- 976,995 ----
return ObjectClassGenerator.UNDEFINED_DOUBLE;
}
/**
* Return methodHandle of value function for call.
*
* @param find data from find property.
* @param type method type of function.
* @param bindName null or name to bind to second argument (property not found method.)
*
* @return value of property as a MethodHandle or null.
*/
protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
! return getCallMethodHandle(find.getObjectValue(), type, bindName);
}
/**
* Return methodHandle of value function for call.
*
*** 1023,1033 ****
* @param property Found property.
*
* @return Value of property.
*/
public final Object getWithProperty(final Property property) {
! return getObjectValue(new FindProperty(this, this, property));
}
/**
* Get a property given a key
*
--- 1009,1019 ----
* @param property Found property.
*
* @return Value of property.
*/
public final Object getWithProperty(final Property property) {
! return new FindProperty(this, this, property).getObjectValue();
}
/**
* Get a property given a key
*
*** 1116,1145 ****
* Set the __proto__ of an object.
* @param newProto new __proto__ to set.
*/
public synchronized final void setProto(final ScriptObject newProto) {
final ScriptObject oldProto = proto;
- map = map.changeProto(oldProto, newProto);
-
- if (newProto != null) {
- newProto.setIsPrototype();
- }
proto = newProto;
! if (isPrototype()) {
! // tell listeners that my __proto__ has been changed
! notifyProtoChanged(this, oldProto, newProto);
!
! if (oldProto != null) {
! oldProto.removePropertyListener(this);
}
!
! if (newProto != null) {
! newProto.addPropertyListener(this);
}
}
}
/**
* Set the __proto__ of an object with checks.
* @param newProto Prototype to set.
--- 1102,1135 ----
* Set the __proto__ of an object.
* @param newProto new __proto__ to set.
*/
public synchronized final void setProto(final ScriptObject newProto) {
final ScriptObject oldProto = proto;
+ if (oldProto != newProto) {
proto = newProto;
! // Let current listeners know that the protototype has changed and set our map
! final PropertyListeners listeners = getMap().getListeners();
! if (listeners != null) {
! listeners.protoChanged();
}
! // Replace our current allocator map with one that is associated with the new prototype.
! setMap(getMap().changeProto(newProto));
}
}
+
+ /**
+ * Set the initial __proto__ of this object. This should be used instead of
+ * {@link #setProto} if it is known that the current property map will not be
+ * used on a new object with any other parent property map, so we can pass over
+ * property map invalidation/evolution.
+ *
+ * @param initialProto the initial __proto__ to set.
+ */
+ public void setInitialProto(final ScriptObject initialProto) {
+ this.proto = initialProto;
}
/**
* Set the __proto__ of an object with checks.
* @param newProto Prototype to set.
*** 1158,1168 ****
}
p = p.getProto();
}
setProto((ScriptObject)newProto);
} else {
! final ScriptObject global = Context.getGlobalTrusted();
final Object newProtoObject = JSType.toScriptObject(global, newProto);
if (newProtoObject instanceof ScriptObject) {
setProto((ScriptObject)newProtoObject);
} else {
--- 1148,1158 ----
}
p = p.getProto();
}
setProto((ScriptObject)newProto);
} else {
! final Global global = Context.getGlobal();
final Object newProtoObject = JSType.toScriptObject(global, newProto);
if (newProtoObject instanceof ScriptObject) {
setProto((ScriptObject)newProtoObject);
} else {
*** 1248,1262 ****
*
* @param typeHint the preferred type hint
* @return the default value
*/
public Object getDefaultValue(final Class<?> typeHint) {
! // We delegate to GlobalObject, as the implementation uses dynamic call sites to invoke object's "toString" and
// "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
// are being executed in a long-running program, we move the code and their associated dynamic call sites
// (Global.TO_STRING and Global.VALUE_OF) into per-context code.
! return ((GlobalObject)Context.getGlobalTrusted()).getDefaultValue(this, typeHint);
}
/**
* Checking whether a script object is an instance of another. Used
* in {@link ScriptFunction} for hasInstance implementation, walks
--- 1238,1252 ----
*
* @param typeHint the preferred type hint
* @return the default value
*/
public Object getDefaultValue(final Class<?> typeHint) {
! // We delegate to Global, as the implementation uses dynamic call sites to invoke object's "toString" and
// "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
// are being executed in a long-running program, we move the code and their associated dynamic call sites
// (Global.TO_STRING and Global.VALUE_OF) into per-context code.
! return Context.getGlobal().getDefaultValue(this, typeHint);
}
/**
* Checking whether a script object is an instance of another. Used
* in {@link ScriptFunction} for hasInstance implementation, walks
*** 1328,1356 ****
public final void setIsArguments() {
flags |= IS_ARGUMENTS;
}
/**
- * Check if this object is a prototype
- *
- * @return {@code true} if is prototype
- */
- public final boolean isPrototype() {
- return (flags & IS_PROTOTYPE) != 0;
- }
-
- /**
- * Flag this object as having a prototype.
- */
- public final void setIsPrototype() {
- if (proto != null && !isPrototype()) {
- proto.addPropertyListener(this);
- }
- flags |= IS_PROTOTYPE;
- }
-
- /**
* Check if this object has non-writable length property
*
* @return {@code true} if 'length' property is non-writable
*/
public final boolean isLengthNotWritable() {
--- 1318,1327 ----
*** 1710,1731 ****
// Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
}
/**
* Find the appropriate GET method for an invoke dynamic call.
*
* @param desc the call site descriptor
* @param request the link request
* @param operator operator for get: getProp, getMethod, getElem etc
*
* @return GuardedInvocation to be invoked at call site.
*/
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
! if (request.isCallSiteUnstable()) {
! return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator));
}
final FindProperty find = findProperty(name, true);
MethodHandle methodHandle;
--- 1681,1740 ----
// Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
}
/**
+ * Test whether this object contains in its prototype chain or is itself a with-object.
+ * @return true if a with-object was found
+ */
+ final boolean hasWithScope() {
+ if (isScope()) {
+ for (ScriptObject obj = this; obj != null; obj = obj.getProto()) {
+ if (obj instanceof WithObject) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add a filter to the first argument of {@code methodHandle} that calls its {@link #getProto()} method
+ * {@code depth} times.
+ * @param methodHandle a method handle
+ * @param depth distance to target prototype
+ * @return the filtered method handle
+ */
+ static MethodHandle addProtoFilter(final MethodHandle methodHandle, final int depth) {
+ if (depth == 0) {
+ return methodHandle;
+ }
+ final int listIndex = depth - 1; // We don't need 0-deep walker
+ MethodHandle filter = listIndex < protoFilters.size() ? protoFilters.get(listIndex) : null;
+
+ if(filter == null) {
+ filter = addProtoFilter(GETPROTO, depth - 1);
+ protoFilters.add(null);
+ protoFilters.set(listIndex, filter);
+ }
+
+ return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0))));
+ }
+
+ /**
* Find the appropriate GET method for an invoke dynamic call.
*
* @param desc the call site descriptor
* @param request the link request
* @param operator operator for get: getProp, getMethod, getElem etc
*
* @return GuardedInvocation to be invoked at call site.
*/
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), isScope() && NashornCallSiteDescriptor.isScope(desc));
}
final FindProperty find = findProperty(name, true);
MethodHandle methodHandle;
*** 1746,1789 ****
final Class<?> returnType = desc.getMethodType().returnType();
final Property property = find.getProperty();
methodHandle = find.getGetter(returnType);
! // getMap() is fine as we have the prototype switchpoint depending on where the property was found
! final MethodHandle guard = NashornGuards.getMapGuard(getMap());
if (methodHandle != null) {
assert methodHandle.type().returnType().equals(returnType);
if (find.isSelf()) {
! return new GuardedInvocation(methodHandle, ObjectClassGenerator.OBJECT_FIELDS_ONLY &&
! NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard);
}
! final ScriptObject prototype = find.getOwner();
!
! if (!property.hasGetterFunction(prototype)) {
! methodHandle = bindTo(methodHandle, prototype);
}
! return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard);
}
assert !NashornCallSiteDescriptor.isFastScope(desc);
! return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
}
! private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
! final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod);
final MethodHandle guard = getScriptObjectGuard(desc.getMethodType());
return new GuardedInvocation(invoker, guard);
}
@SuppressWarnings("unused")
! private Object megamorphicGet(final String key, final boolean isMethod) {
final FindProperty find = findProperty(key, true);
if (find != null) {
! return getObjectValue(find);
}
return isMethod ? getNoSuchMethod(key) : invokeNoSuchProperty(key);
}
--- 1755,1801 ----
final Class<?> returnType = desc.getMethodType().returnType();
final Property property = find.getProperty();
methodHandle = find.getGetter(returnType);
! // 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) {
assert methodHandle.type().returnType().equals(returnType);
if (find.isSelf()) {
! return new GuardedInvocation(methodHandle, guard);
}
! if (!property.hasGetterFunction(owner)) {
! // Add a filter that replaces the self object with the prototype owning the property.
! methodHandle = addProtoFilter(methodHandle, find.getProtoChainLength());
}
! 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 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, final boolean isScope) {
final FindProperty find = findProperty(key, true);
if (find != null) {
! return find.getObjectValue();
! }
! if (isScope) {
! throw referenceError("not.defined", key);
}
return isMethod ? getNoSuchMethod(key) : invokeNoSuchProperty(key);
}
*** 1822,1841 ****
private static MethodHandle getScriptObjectGuard(final MethodType type) {
return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
}
/**
* Find the appropriate SET method for an invoke dynamic call.
*
* @param desc the call site descriptor
* @param request the link request
*
* @return GuardedInvocation to be invoked at call site.
*/
protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
! if (request.isCallSiteUnstable()) {
return findMegaMorphicSetMethod(desc, name);
}
final boolean scope = isScope();
/*
--- 1834,1875 ----
private static MethodHandle getScriptObjectGuard(final MethodType type) {
return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
}
/**
+ * Get a switch point for a property with the given {@code name} that will be invalidated when
+ * the property definition is changed in this object's prototype chain. Returns {@code null} if
+ * the property is defined in this object itself.
+ *
+ * @param name the property name
+ * @param owner the property owner, null if property is not defined
+ * @return a SwitchPoint or null
+ */
+ public final SwitchPoint getProtoSwitchPoint(final String name, final ScriptObject owner) {
+ if (owner == this || getProto() == null) {
+ return null;
+ }
+
+ for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) {
+ ScriptObject parent = obj.getProto();
+ parent.getMap().addListener(name, obj.getMap());
+ }
+
+ return getMap().getSwitchPoint(name);
+ }
+
+ /**
* Find the appropriate SET method for an invoke dynamic call.
*
* @param desc the call site descriptor
* @param request the link request
*
* @return GuardedInvocation to be invoked at call site.
*/
protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
! if (request.isCallSiteUnstable() || hasWithScope()) {
return findMegaMorphicSetMethod(desc, name);
}
final boolean scope = isScope();
/*
*** 1877,1896 ****
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
if (NashornCallSiteDescriptor.isStrict(desc)) {
throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
}
assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
! final PropertyMap myMap = getMap();
! return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap));
}
@SuppressWarnings("unused")
private static void setField(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final Object self, final Object value) throws Throwable {
final ScriptObject obj = (ScriptObject)self;
final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
if (!obj.isExtensible()) {
throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
} else if (obj.compareAndSetMap(oldMap, newMap)) {
setter.invokeExact(self, value);
} else {
obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
}
--- 1911,1931 ----
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
if (NashornCallSiteDescriptor.isStrict(desc)) {
throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
}
assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
! return new GuardedInvocation(Lookup.EMPTY_SETTER, getProtoSwitchPoint(name, null), NashornGuards.getMapGuard(getMap()));
}
@SuppressWarnings("unused")
private static void setField(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final Object self, final Object value) throws Throwable {
final ScriptObject obj = (ScriptObject)self;
final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
if (!obj.isExtensible()) {
+ if (isStrict) {
throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
+ }
} else if (obj.compareAndSetMap(oldMap, newMap)) {
setter.invokeExact(self, value);
} else {
obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
}
*** 1951,1960 ****
--- 1986,2004 ----
} else {
obj.set(desc.getNameToken(2), value, isStrict);
}
}
+ @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));
return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
}
*** 1996,2006 ****
if (find == null) {
return noSuchProperty(desc, request);
}
! final Object value = getObjectValue(find);
if (! (value instanceof ScriptFunction)) {
return createEmptyGetter(desc, name);
}
final ScriptFunction func = (ScriptFunction)value;
--- 2040,2050 ----
if (find == null) {
return noSuchProperty(desc, request);
}
! final Object value = find.getObjectValue();
if (! (value instanceof ScriptFunction)) {
return createEmptyGetter(desc, name);
}
final ScriptFunction func = (ScriptFunction)value;
*** 2022,2032 ****
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
if (find != null) {
! final Object value = getObjectValue(find);
ScriptFunction func = null;
MethodHandle methodHandle = null;
if (value instanceof ScriptFunction) {
func = (ScriptFunction)value;
--- 2066,2076 ----
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
if (find != null) {
! final Object value = find.getObjectValue();
ScriptFunction func = null;
MethodHandle methodHandle = null;
if (value instanceof ScriptFunction) {
func = (ScriptFunction)value;
*** 2036,2066 ****
if (methodHandle != null) {
if (scopeAccess && func.isStrict()) {
methodHandle = bindTo(methodHandle, UNDEFINED);
}
return new GuardedInvocation(methodHandle,
! find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
}
}
if (scopeAccess) {
throw referenceError("not.defined", name);
}
return createEmptyGetter(desc, name);
}
/**
* Invoke fall back if a property is not found.
* @param name Name of property.
* @return Result from call.
*/
! private Object invokeNoSuchProperty(final String name) {
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
if (find != null) {
! final Object func = getObjectValue(find);
if (func instanceof ScriptFunction) {
return ScriptRuntime.apply((ScriptFunction)func, this, name);
}
}
--- 2080,2111 ----
if (methodHandle != null) {
if (scopeAccess && func.isStrict()) {
methodHandle = bindTo(methodHandle, UNDEFINED);
}
return new GuardedInvocation(methodHandle,
! getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()),
getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
}
}
if (scopeAccess) {
throw referenceError("not.defined", name);
}
return createEmptyGetter(desc, name);
}
+
/**
* Invoke fall back if a property is not found.
* @param name Name of property.
* @return Result from call.
*/
! protected Object invokeNoSuchProperty(final String name) {
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
if (find != null) {
! final Object func = find.getObjectValue();
if (func instanceof ScriptFunction) {
return ScriptRuntime.apply((ScriptFunction)func, this, name);
}
}
*** 2078,2097 ****
if (find == null) {
return invokeNoSuchProperty(name);
}
! final Object value = getObjectValue(find);
if (! (value instanceof ScriptFunction)) {
return UNDEFINED;
}
return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name});
}
private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
! return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
}
private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
protected T[] values;
protected final ScriptObject object;
--- 2123,2143 ----
if (find == null) {
return invokeNoSuchProperty(name);
}
! final Object value = find.getObjectValue();
if (! (value instanceof ScriptFunction)) {
return UNDEFINED;
}
return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name});
}
private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
! return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()),
! getProtoSwitchPoint(name, null), NashornGuards.getMapGuard(getMap()));
}
private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
protected T[] values;
protected final ScriptObject object;
*** 2168,2183 ****
int fieldMaximum = getMap().getFieldMaximum();
Property property;
if (fieldCount < fieldMaximum) {
property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount);
- notifyPropertyAdded(this, property);
property = addOwnProperty(property);
} else {
int i = getMap().getSpillLength();
property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
- notifyPropertyAdded(this, property);
property = addOwnProperty(property);
i = property.getSlot();
final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE;
--- 2214,2227 ----
*** 2619,2629 ****
for (ScriptObject object = this; ; ) {
if (object.getMap().containsArrayKeys()) {
final FindProperty find = object.findProperty(key, false, false, this);
if (find != null) {
! return getObjectValue(find);
}
}
if ((object = object.getProto()) == null) {
break;
--- 2663,2673 ----
for (ScriptObject object = this; ; ) {
if (object.getMap().containsArrayKeys()) {
final FindProperty find = object.findProperty(key, false, false, this);
if (find != null) {
! return find.getObjectValue();
}
}
if ((object = object.getProto()) == null) {
break;
*** 2637,2647 ****
}
} else {
final FindProperty find = findProperty(key, true);
if (find != null) {
! return getObjectValue(find);
}
}
return invokeNoSuchProperty(key);
}
--- 2681,2691 ----
}
} else {
final FindProperty find = findProperty(key, true);
if (find != null) {
! return find.getObjectValue();
}
}
return invokeNoSuchProperty(key);
}
*** 2757,2767 ****
* @param value property value
*/
public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
FindProperty f = find;
! if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {
f = null;
}
if (f != null) {
if (!f.getProperty().isWritable()) {
--- 2801,2812 ----
* @param value property value
*/
public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
FindProperty f = find;
! if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty) && !isScope()) {
! // Setting a property should not modify the property in prototype unless this is a scope object.
f = null;
}
if (f != null) {
if (!f.getProperty().isWritable()) {
*** 2777,2787 ****
} else if (!isExtensible()) {
if (strict) {
throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
}
} else {
! spill(key, value);
}
}
private void spill(final String key, final Object value) {
addSpillProperty(key, 0).setObjectValue(this, this, value, false);
--- 2822,2840 ----
} else if (!isExtensible()) {
if (strict) {
throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
}
} else {
! 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);
}
}
private void spill(final String key, final Object value) {
addSpillProperty(key, 0).setObjectValue(this, this, value, false);
*** 3226,3236 ****
}
return false;
}
final Property prop = find.getProperty();
- notifyPropertyDeleted(this, prop);
deleteOwnProperty(prop);
return true;
}
--- 3279,3288 ----