# HG changeset patch # User hannesw # Date 1358860477 -3600 # Node ID 91bd9d2aa2f593724689bb5049f63fa7cad29170 # Parent 893aabe8c800e47ddcb76225dee1daa75a21dd9e 8006570: This-value for non-strict functions should be converted to object Reviewed-by: jlaskey, lagergren, attila diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/objects/Global.java --- a/nashorn/src/jdk/nashorn/internal/objects/Global.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java Tue Jan 22 14:14:37 2013 +0100 @@ -419,6 +419,18 @@ } @Override + public MethodHandle getWrapFilter(Object obj) { + if (obj instanceof String || obj instanceof ConsString) { + return NativeString.WRAPFILTER; + } else if (obj instanceof Number) { + return NativeNumber.WRAPFILTER; + } else if (obj instanceof Boolean) { + return NativeBoolean.WRAPFILTER; + } + throw new IllegalArgumentException("Unsupported primitive value: " + obj); + } + + @Override public GuardedInvocation numberLookup(final NashornCallSiteDescriptor callSite, final Number self) { return NativeNumber.lookupPrimitive(callSite, self); } diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/objects/NativeBoolean.java --- a/nashorn/src/jdk/nashorn/internal/objects/NativeBoolean.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/objects/NativeBoolean.java Tue Jan 22 14:14:37 2013 +0100 @@ -50,7 +50,7 @@ public final class NativeBoolean extends ScriptObject { private final boolean value; - private final static MethodHandle WRAPFILTER = findWrapFilter(); + final static MethodHandle WRAPFILTER = findWrapFilter(); NativeBoolean(final boolean value) { this(value, Global.instance().getBooleanPrototype()); diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/objects/NativeFunction.java --- a/nashorn/src/jdk/nashorn/internal/objects/NativeFunction.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/objects/NativeFunction.java Tue Jan 22 14:14:37 2013 +0100 @@ -67,7 +67,7 @@ } private static Object convertThis(final ScriptFunction func, final Object thiz) { - if (!(thiz instanceof ScriptObject) && !func.isStrict() && !func.isBuiltin()) { + if (!(thiz instanceof ScriptObject) && func.isNonStrictFunction()) { if (thiz == UNDEFINED || thiz == null) { return Global.instance(); } diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/objects/NativeNumber.java --- a/nashorn/src/jdk/nashorn/internal/objects/NativeNumber.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/objects/NativeNumber.java Tue Jan 22 14:14:37 2013 +0100 @@ -57,7 +57,7 @@ @ScriptClass("Number") public final class NativeNumber extends ScriptObject { - private static final MethodHandle WRAPFILTER = findWrapFilter(); + static final MethodHandle WRAPFILTER = findWrapFilter(); /** ECMA 15.7.3.2 largest positive finite value */ @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT, where = Where.CONSTRUCTOR) diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/objects/NativeString.java --- a/nashorn/src/jdk/nashorn/internal/objects/NativeString.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/objects/NativeString.java Tue Jan 22 14:14:37 2013 +0100 @@ -68,7 +68,7 @@ private final CharSequence value; - private static final MethodHandle WRAPFILTER = findWrapFilter(); + static final MethodHandle WRAPFILTER = findWrapFilter(); NativeString(final CharSequence value) { this(value, Global.instance().getStringPrototype()); diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/runtime/GlobalObject.java --- a/nashorn/src/jdk/nashorn/internal/runtime/GlobalObject.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/runtime/GlobalObject.java Tue Jan 22 14:14:37 2013 +0100 @@ -68,6 +68,16 @@ */ public Object wrapAsObject(Object obj); + + /** + * Get a MethodHandle that converts the argument to its JavaScript object representation + * + * @param obj a JavaScript primitive object (String, Number, or Boolean) + * + * @return wrap filter methodhandle + */ + public MethodHandle getWrapFilter(Object obj); + /** * Wrapper for {@link jdk.nashorn.internal.objects.Global#numberLookup(NashornCallSiteDescriptor, Number)} * diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java --- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Tue Jan 22 14:14:37 2013 +0100 @@ -40,6 +40,7 @@ import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.runtime.linker.MethodHandleFactory; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; +import jdk.nashorn.internal.runtime.linker.NashornGuardedInvocation; import jdk.nashorn.internal.runtime.linker.NashornGuards; import jdk.nashorn.internal.runtime.options.Options; import org.dynalang.dynalink.CallSiteDescriptor; @@ -329,6 +330,14 @@ public abstract boolean isBuiltin(); /** + * Is this a non-strict (not built-in) script function? + * @return true if neither strict nor built-in + */ + public boolean isNonStrictFunction() { + return !isStrict() && !isBuiltin(); + } + + /** * Execute this script function. * @param self Target object. * @param arguments Call arguments. @@ -916,7 +925,7 @@ if(NashornCallSiteDescriptor.isScope(desc)) { // (this, callee, args...) => (callee, args...) => (callee, [this], args...) - boundHandle = MH.bindTo(callHandle, isStrict() || isBuiltin()? ScriptRuntime.UNDEFINED : Context.getGlobal()); + boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobal(): ScriptRuntime.UNDEFINED); boundHandle = MH.dropArguments(boundHandle, 1, Object.class); } else { // (this, callee, args...) permute => (callee, this, args...) which is what we get in @@ -934,7 +943,7 @@ final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1)); if(NashornCallSiteDescriptor.isScope(desc)) { - boundHandle = MH.bindTo(callHandle, isStrict() || isBuiltin()? ScriptRuntime.UNDEFINED : Context.getGlobal()); + boundHandle = MH.bindTo(callHandle, isNonStrictFunction()? Context.getGlobal() : ScriptRuntime.UNDEFINED); boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class); } else { boundHandle = MH.dropArguments(callHandle, 0, Object.class); @@ -942,7 +951,7 @@ } boundHandle = pairArguments(boundHandle, type); - return new GuardedInvocation(boundHandle, NashornGuards.getFunctionGuard(this)); + return new NashornGuardedInvocation(boundHandle, null, NashornGuards.getFunctionGuard(this), isNonStrictFunction()); } /** diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java --- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Tue Jan 22 14:14:37 2013 +0100 @@ -1643,16 +1643,14 @@ // getMap() is fine as we have the prototype switchpoint depending on where the property was found final MethodHandle guard = NashornGuards.getMapGuard(getMap()); - int invokeFlags = 0; - if (methodHandle != null) { assert methodHandle.type().returnType().equals(returnType); final ScriptFunction getter = find.getGetterFunction(); - invokeFlags = (getter != null && getter.isStrict()) ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0; + final boolean nonStrict = getter != null && getter.isNonStrictFunction(); if (find.isSelf()) { return new NashornGuardedInvocation(methodHandle, null, ObjectClassGenerator.OBJECT_FIELDS_ONLY && NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard, - invokeFlags); + nonStrict); } final ScriptObject prototype = find.getOwner(); @@ -1660,11 +1658,11 @@ if (!property.hasGetterFunction()) { methodHandle = bindTo(methodHandle, prototype); } - return new NashornGuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(name), guard, invokeFlags); + return new NashornGuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(name), guard, nonStrict); } assert !NashornCallSiteDescriptor.isFastScope(desc); - return new NashornGuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(name), guard, invokeFlags); + return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(name), guard); } private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) { @@ -1855,7 +1853,6 @@ final Class valueClass = callType.parameterType(2); MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class); - methodHandle = MH.asType(methodHandle, methodHandle.type().changeParameterType(0, Object.class)); methodHandle = MH.insertArguments(methodHandle, 3, isStrict); return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType)); diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java --- a/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Tue Jan 22 14:14:37 2013 +0100 @@ -89,21 +89,19 @@ private class SetMethod { private final MethodHandle methodHandle; private final Property property; - private final int invokeFlags; + private final boolean nonStrict; /** * Creates a new lookup result. * @param methodHandle the actual method handle * @param property the property object. Can be null in case we're creating a new property in the global object. - * @param invokeFlags flags for the invocation. Normally either 0, or - * {@link NashornCallSiteDescriptor#CALLSITE_STRICT} when an existing property with a strict function for a - * property setter is discovered. + * @param nonStrict True if an existing property with a non-strict function as property setter is discovered. */ - SetMethod(final MethodHandle methodHandle, final Property property, final int invokeFlags) { + SetMethod(final MethodHandle methodHandle, final Property property, final boolean nonStrict) { assert methodHandle != null; this.methodHandle = methodHandle; this.property = property; - this.invokeFlags = invokeFlags; + this.nonStrict = nonStrict; } /** @@ -111,7 +109,7 @@ * @return the composed guarded invocation that represents the dynamic setter method for the property. */ GuardedInvocation createGuardedInvocation() { - return new NashornGuardedInvocation(methodHandle, null, getGuard(), invokeFlags); + return new NashornGuardedInvocation(methodHandle, null, getGuard(), nonStrict); } private MethodHandle getGuard() { @@ -164,20 +162,17 @@ } else { boundHandle = methodHandle; } - return new SetMethod(boundHandle, property, getExistingPropertySetterInvokeFlags()); + return new SetMethod(boundHandle, property, getExistingSetterNonStrictFlag()); } - private int getExistingPropertySetterInvokeFlags() { + private boolean getExistingSetterNonStrictFlag() { final ScriptFunction setter = find.getSetterFunction(); - if (setter != null && setter.isStrict()) { - return NashornCallSiteDescriptor.CALLSITE_STRICT; - } - return 0; + return setter != null && setter.isNonStrictFunction(); } private SetMethod createGlobalPropertySetter() { final ScriptObject global = Context.getGlobal(); - return new SetMethod(ScriptObject.bindTo(global.addSpill(getName()), global), null, 0); + return new SetMethod(ScriptObject.bindTo(global.addSpill(getName()), global), null, false); } private SetMethod createNewPropertySetter() { @@ -197,7 +192,7 @@ final int nextSpill = getMap().getSpillLength(); final Property property = createSpillProperty(nextSpill); - return new SetMethod(createSpillMethodHandle(nextSpill, property), property, 0); + return new SetMethod(createSpillMethodHandle(nextSpill, property), property, false); } private Property createSpillProperty(final int nextSpill) { @@ -227,7 +222,7 @@ final Property property = new SpillProperty(getName(), 0, nextEmbed, ScriptObject.GET_EMBED[nextEmbed], ScriptObject.SET_EMBED[nextEmbed]); //TODO specfields final MethodHandle methodHandle = MH.insertArguments(ScriptObject.SETEMBED, 0, desc, getMap(), getNewMap(property), property.getSetter(Object.class, getMap()), nextEmbed); - return new SetMethod(methodHandle, property, 0); + return new SetMethod(methodHandle, property, false); } private PropertyMap getNewMap(Property property) { diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuardedInvocation.java --- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuardedInvocation.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuardedInvocation.java Tue Jan 22 14:14:37 2013 +0100 @@ -33,7 +33,8 @@ * Guarded invocation with Nashorn specific bits. */ public class NashornGuardedInvocation extends GuardedInvocation { - private final int flags; + + private final boolean nonStrict; /** * Constructor @@ -41,28 +42,28 @@ * @param invocation invocation target * @param switchPoint SwitchPoint that will, when invalidated, require relinking callsite, null if no SwitchPoint * @param guard guard that will, when failed, require relinking callsite, null if no guard - * @param flags callsite flags + * @param nonStrict non-strict invocation target flag */ - public NashornGuardedInvocation(final MethodHandle invocation, final SwitchPoint switchPoint, final MethodHandle guard, final int flags) { + public NashornGuardedInvocation(final MethodHandle invocation, final SwitchPoint switchPoint, final MethodHandle guard, final boolean nonStrict) { super(invocation, switchPoint, guard); - this.flags = flags; + this.nonStrict = nonStrict; } /** - * Is this invocation created from a callsite with strict semantics - * @return true if strict + * Is the target of this invocation a non-strict function? + * @return true if invocation target is non-strict */ - public boolean isStrict() { - return (flags & NashornCallSiteDescriptor.CALLSITE_STRICT) != 0; + public boolean isNonStrict() { + return nonStrict; } /** - * Check whether a GuardedInvocation is created from a callsite with strict semantics + * Check whether the target of this invocation is a non-strict script function. * @param inv guarded invocation - * @return true if strict + * @return true if invocation target is non-strict */ - public static boolean isStrict(final GuardedInvocation inv) { - return (inv instanceof NashornGuardedInvocation)? ((NashornGuardedInvocation)inv).isStrict() : false; + public static boolean isNonStrict(final GuardedInvocation inv) { + return inv instanceof NashornGuardedInvocation && ((NashornGuardedInvocation)inv).isNonStrict(); } } diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java --- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java Tue Jan 22 14:14:37 2013 +0100 @@ -29,6 +29,10 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; + +import jdk.nashorn.internal.runtime.ConsString; +import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.GlobalObject; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.Undefined; @@ -71,9 +75,23 @@ return null; } - final GuardedInvocation inv; + GuardedInvocation inv; if (self instanceof ScriptObject) { inv = ((ScriptObject)self).lookup(desc, request.isCallSiteUnstable()); + + if (self instanceof ScriptFunction && "call".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) { + + // Add toObject wrapper for non-object self argument to non-strict functions + assert request.getArguments().length >= 2 : "No self argument in script function callsite"; + Object thisObject = request.getArguments()[1]; + // Add wrapper filter for primitive this-object on non-strict functions + if (NashornGuardedInvocation.isNonStrict(inv) && isPrimitive(thisObject)) { + MethodHandle wrapFilter = ((GlobalObject) Context.getGlobal()).getWrapFilter(thisObject); + MethodHandle invocation = MH.filterArguments(inv.getInvocation(), 1, + MH.asType(wrapFilter, wrapFilter.type().changeReturnType(Object.class))); + inv = new GuardedInvocation(invocation, inv.getSwitchPoint(), inv.getGuard()); + } + } } else if (self instanceof Undefined) { inv = Undefined.lookup(desc); } else { @@ -135,6 +153,13 @@ JavaAdapterFactory.isAutoConvertibleFromFunction(clazz); } + private static boolean isPrimitive(Object obj) { + return obj instanceof Boolean || + obj instanceof Number || + obj instanceof String || + obj instanceof ConsString; + } + @Override public Comparison compareConversion(final Class sourceType, final Class targetType1, final Class targetType2) { if(ScriptObject.class.isAssignableFrom(sourceType)) { diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java --- a/nashorn/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Mon Jan 21 21:17:38 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Tue Jan 22 14:14:37 2013 +0100 @@ -94,11 +94,11 @@ final GuardedInvocation link = wrappedReceiver.lookup(desc); if (link != null) { MethodHandle method = link.getInvocation(); - if (!NashornGuardedInvocation.isStrict(link)) { + final Class receiverType = method.type().parameterType(0); + if (receiverType != Object.class || NashornGuardedInvocation.isNonStrict(link)) { final MethodType wrapType = wrapFilter.type(); - final Class methodReceiverType = method.type().parameterType(0); - assert methodReceiverType.isAssignableFrom(wrapType.returnType()); - method = MH.filterArguments(method, 0, MH.asType(wrapFilter, wrapType.changeReturnType(methodReceiverType))); + assert receiverType.isAssignableFrom(wrapType.returnType()); + method = MH.filterArguments(method, 0, MH.asType(wrapFilter, wrapType.changeReturnType(receiverType))); } return new GuardedInvocation(method, guard, link.getSwitchPoint()); } diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/test/script/basic/JDK-8006570.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8006570.js Tue Jan 22 14:14:37 2013 +0100 @@ -0,0 +1,60 @@ +/* + * 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-8006570 : this-value for non-strict functions should be converted to object + * + * @test + * @run + */ + +var strict, nonstrict; + +nonstrict = Object.prototype.nonstrict = function nonstrict() { + print(typeof this, this instanceof Object); +}; + +(function() { + "use strict"; + strict = Object.prototype.strict = function strict() { + print(typeof this, this instanceof Object); + }; +})(); + +"foo".nonstrict(); +(1).nonstrict(); +true.nonstrict(); +nonstrict(); +nonstrict.call(null); +nonstrict.call("foo"); +nonstrict.call(1); +nonstrict.call(true); + +"foo".strict(); +(1).strict(); +true.strict(); +strict(); +strict.call(null); +strict.call("foo"); +strict.call(1); +strict.call(true); \ No newline at end of file diff -r 893aabe8c800 -r 91bd9d2aa2f5 nashorn/test/script/basic/JDK-8006570.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8006570.js.EXPECTED Tue Jan 22 14:14:37 2013 +0100 @@ -0,0 +1,16 @@ +object true +object true +object true +object true +object true +object true +object true +object true +string false +number false +boolean false +undefined false +object false +string false +number false +boolean false