aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhannesw <none@none>2014-01-07 14:16:23 +0100
committerhannesw <none@none>2014-01-07 14:16:23 +0100
commit1df080774ae7a4cd3c382e348a9afb32cb94622d (patch)
tree7c570dec9613035d02632f1e6f4c1f6a3aa025f5
parentd0c52de061c7fc783a96de7f1b7323aa6979ccba (diff)
downloadnashorn-1df080774ae7a4cd3c382e348a9afb32cb94622d.tar.gz
8029667: Prototype linking is incorrect
Reviewed-by: jlaskey, sundar
-rw-r--r--src/jdk/nashorn/internal/runtime/FindProperty.java17
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptObject.java65
-rw-r--r--src/jdk/nashorn/internal/runtime/SetMethodCreator.java8
-rw-r--r--test/script/basic/JDK-8029667.js91
-rw-r--r--test/script/basic/JDK-8029667.js.EXPECTED8
5 files changed, 174 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;
}
diff --git a/test/script/basic/JDK-8029667.js b/test/script/basic/JDK-8029667.js
new file mode 100644
index 00000000..c0c2d156
--- /dev/null
+++ b/test/script/basic/JDK-8029667.js
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8029667: Prototype linking is incorrect
+ *
+ * @test
+ * @run
+ */
+
+function f(x) {
+ return (function inner() {
+ var y; (function dummy() { return y })() // force own scope for the inner function
+ with({}) { // 'with' block turns off fast scopes
+ return x
+ }
+ })();
+}
+print(f(1));
+print(f(2));
+
+function g(x) {
+ (function inner() {
+ var y; (function dummy() { return y })() // force own scope for the inner function
+ with({}) { // 'with' block turns off fast scopes
+ // Test setter as well as getter
+ x = x + 2;
+ }
+ })();
+ print(x);
+}
+
+g(1);
+g(2);
+
+var withScopes = [{ func: function() { print("called 1");} }, { func: function() { print("called 2");} }];
+
+for(var i in withScopes) {
+ with (withScopes[i]) {
+ var main = function() {
+ var frame; // <---- this local variable caused scope to be not set properly prior to fix
+
+ function callFunc() {
+ frame = func();
+ }
+
+ callFunc();
+ }
+ }
+ main();
+}
+
+for(var i in withScopes) {
+ with (withScopes[i]) {
+ var main = function() {
+ var frame; // <---- this local variable caused scope to be not set properly prior to fix
+
+ function callFunc() {
+ frame = func = i;
+ }
+
+ callFunc();
+ }
+ }
+ main();
+}
+
+print(withScopes[0].func);
+print(withScopes[1].func);
+
+
diff --git a/test/script/basic/JDK-8029667.js.EXPECTED b/test/script/basic/JDK-8029667.js.EXPECTED
new file mode 100644
index 00000000..8aa78efe
--- /dev/null
+++ b/test/script/basic/JDK-8029667.js.EXPECTED
@@ -0,0 +1,8 @@
+1
+2
+3
+4
+called 1
+called 2
+0
+1