diff options
author | hannesw <none@none> | 2014-01-07 14:16:23 +0100 |
---|---|---|
committer | hannesw <none@none> | 2014-01-07 14:16:23 +0100 |
commit | 1df080774ae7a4cd3c382e348a9afb32cb94622d (patch) | |
tree | 7c570dec9613035d02632f1e6f4c1f6a3aa025f5 /src | |
parent | d0c52de061c7fc783a96de7f1b7323aa6979ccba (diff) | |
download | nashorn-1df080774ae7a4cd3c382e348a9afb32cb94622d.tar.gz |
8029667: Prototype linking is incorrect
Reviewed-by: jlaskey, sundar
Diffstat (limited to 'src')
-rw-r--r-- | src/jdk/nashorn/internal/runtime/FindProperty.java | 17 | ||||
-rw-r--r-- | src/jdk/nashorn/internal/runtime/ScriptObject.java | 65 | ||||
-rw-r--r-- | src/jdk/nashorn/internal/runtime/SetMethodCreator.java | 8 |
3 files changed, 75 insertions, 15 deletions
diff --git a/src/jdk/nashorn/internal/runtime/FindProperty.java b/src/jdk/nashorn/internal/runtime/FindProperty.java index c14accb7..87a8c748 100644 --- a/src/jdk/nashorn/internal/runtime/FindProperty.java +++ b/src/jdk/nashorn/internal/runtime/FindProperty.java @@ -112,7 +112,7 @@ public final class FindProperty { return property != null && property.hasGetterFunction(prototype) ? self : prototype; } - /** + /** * Return the appropriate receiver for a setter. * @return appropriate receiver */ @@ -172,5 +172,20 @@ public final class FindProperty { property.setObjectValue(getSetterReceiver(), getOwner(), value, strict); } + /** + * Get the number of objects in the prototype chain between the {@code self} and the + * {@code owner} objects. + * @return the prototype chain length + */ + int getProtoChainLength() { + assert self != null; + int length = 0; + for (ScriptObject obj = self; obj != prototype; obj = obj.getProto()) { + assert !(obj instanceof WithObject); + ++length; + } + return length; + } + } diff --git a/src/jdk/nashorn/internal/runtime/ScriptObject.java b/src/jdk/nashorn/internal/runtime/ScriptObject.java index a493f3a2..152681a6 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java @@ -143,6 +143,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr 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); @@ -1712,6 +1714,44 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr } /** + * 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 @@ -1722,7 +1762,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr */ protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) { final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); - if (request.isCallSiteUnstable()) { + if (request.isCallSiteUnstable() || hasWithScope()) { return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator)); } @@ -1748,22 +1788,24 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr 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 = NashornGuards.getMapGuard(getMap()); + final MethodHandle guard = noGuard ? null : 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); + return new GuardedInvocation(methodHandle, guard); } - final ScriptObject prototype = find.getOwner(); - - if (!property.hasGetterFunction(prototype)) { - methodHandle = bindTo(methodHandle, prototype); + if (!property.hasGetterFunction(find.getOwner())) { + // 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, find.getOwner()); } - return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard); + return new GuardedInvocation(methodHandle, noGuard ? null : getMap().getProtoGetSwitchPoint(proto, name), guard); } assert !NashornCallSiteDescriptor.isFastScope(desc); @@ -1833,7 +1875,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr */ protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) { final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); - if (request.isCallSiteUnstable()) { + if (request.isCallSiteUnstable() || hasWithScope()) { return findMegaMorphicSetMethod(desc, name); } @@ -2761,7 +2803,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr 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)) { + 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; } diff --git a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java index 777254d8..7390aadd 100644 --- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java +++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java @@ -151,10 +151,12 @@ final class SetMethodCreator { assert methodHandle != null; assert property != null; - final ScriptObject prototype = find.getOwner(); final MethodHandle boundHandle; - if (!property.hasSetterFunction(prototype) && find.isInherited()) { - boundHandle = ScriptObject.bindTo(methodHandle, prototype); + 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()); } else { boundHandle = methodHandle; } |