diff options
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/WithObject.java')
-rw-r--r-- | src/jdk/nashorn/internal/runtime/WithObject.java | 113 |
1 files changed, 87 insertions, 26 deletions
diff --git a/src/jdk/nashorn/internal/runtime/WithObject.java b/src/jdk/nashorn/internal/runtime/WithObject.java index dc48d727..20510dfd 100644 --- a/src/jdk/nashorn/internal/runtime/WithObject.java +++ b/src/jdk/nashorn/internal/runtime/WithObject.java @@ -35,6 +35,8 @@ import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.support.CallSiteDescriptorFactory; +import jdk.nashorn.api.scripting.AbstractJSObject; +import jdk.nashorn.api.scripting.ScriptObjectMirror; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; import jdk.nashorn.internal.runtime.linker.NashornGuards; @@ -64,7 +66,6 @@ public final class WithObject extends ScriptObject implements Scope { this.expression = expression; } - /** * Delete a property based on a key. * @param key Any valid JavaScript value. @@ -98,11 +99,11 @@ public final class WithObject extends ScriptObject implements Scope { final NashornCallSiteDescriptor ndesc = (NashornCallSiteDescriptor)desc; FindProperty find = null; GuardedInvocation link = null; - ScriptObject self = null; + ScriptObject self; final boolean isNamedOperation; final String name; - if(desc.getNameTokenCount() > 2) { + if (desc.getNameTokenCount() > 2) { isNamedOperation = true; name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); } else { @@ -117,7 +118,6 @@ public final class WithObject extends ScriptObject implements Scope { if (find != null) { link = self.lookup(desc, request); - if (link != null) { return fixExpressionCallSite(ndesc, link); } @@ -193,18 +193,33 @@ public final class WithObject extends ScriptObject implements Scope { * * @param key Property key. * @param deep Whether the search should look up proto chain. - * @param stopOnNonScope should a deep search stop on the first non-scope object? * @param start the object on which the lookup was originally initiated * * @return FindPropertyData or null if not found. */ @Override - FindProperty findProperty(final String key, final boolean deep, final boolean stopOnNonScope, final ScriptObject start) { - final FindProperty exprProperty = expression.findProperty(key, deep, stopOnNonScope, start); + FindProperty findProperty(final String key, final boolean deep, final ScriptObject start) { + // We call findProperty on 'expression' with 'expression' itself as start parameter. + // This way in ScriptObject.setObject we can tell the property is from a 'with' expression + // (as opposed from another non-scope object in the proto chain such as Object.prototype). + final FindProperty exprProperty = expression.findProperty(key, true, expression); if (exprProperty != null) { return exprProperty; } - return super.findProperty(key, deep, stopOnNonScope, start); + return super.findProperty(key, deep, start); + } + + @Override + protected Object invokeNoSuchProperty(final String name, final int programPoint) { + FindProperty find = expression.findProperty(NO_SUCH_PROPERTY_NAME, true); + if (find != null) { + final Object func = find.getObjectValue(); + if (func instanceof ScriptFunction) { + return ScriptRuntime.apply((ScriptFunction)func, expression, name); + } + } + + return getProto().invokeNoSuchProperty(name, programPoint); } @Override @@ -242,35 +257,65 @@ public final class WithObject extends ScriptObject implements Scope { private static GuardedInvocation fixExpressionCallSite(final NashornCallSiteDescriptor desc, final GuardedInvocation link) { // If it's not a getMethod, just add an expression filter that converts WithObject in "this" position to its // expression. - if(!"getMethod".equals(desc.getFirstOperator())) { + if (!"getMethod".equals(desc.getFirstOperator())) { return fixReceiverType(link, WITHEXPRESSIONFILTER).filterArguments(0, WITHEXPRESSIONFILTER); } - final MethodHandle linkInvocation = link.getInvocation(); - final MethodType linkType = linkInvocation.type(); - final boolean linkReturnsFunction = ScriptFunction.class.isAssignableFrom(linkType.returnType()); + final MethodHandle linkInvocation = link.getInvocation(); + final MethodType linkType = linkInvocation.type(); + final boolean linkReturnsFunction = ScriptFunction.class.isAssignableFrom(linkType.returnType()); + return link.replaceMethods( // Make sure getMethod will bind the script functions it receives to WithObject.expression - MH.foldArguments(linkReturnsFunction ? BIND_TO_EXPRESSION_FN : BIND_TO_EXPRESSION_OBJ, - filter(linkInvocation.asType(linkType.changeReturnType( - linkReturnsFunction ? ScriptFunction.class : Object.class)), WITHEXPRESSIONFILTER)), - // No clever things for the guard -- it is still identically filtered. - filterGuard(link, WITHEXPRESSIONFILTER)); + MH.foldArguments( + linkReturnsFunction ? + BIND_TO_EXPRESSION_FN : + BIND_TO_EXPRESSION_OBJ, + filterReceiver( + linkInvocation.asType( + linkType.changeReturnType( + linkReturnsFunction ? + ScriptFunction.class : + Object.class). + changeParameterType( + 0, + Object.class)), + WITHEXPRESSIONFILTER)), + filterGuardReceiver(link, WITHEXPRESSIONFILTER)); + // No clever things for the guard -- it is still identically filtered. + } private GuardedInvocation fixScopeCallSite(final GuardedInvocation link, final String name, final ScriptObject owner) { - final GuardedInvocation newLink = fixReceiverType(link, WITHSCOPEFILTER); - return link.replaceMethods(filter(newLink.getInvocation(), WITHSCOPEFILTER), - NashornGuards.combineGuards(expressionGuard(name, owner), filterGuard(newLink, WITHSCOPEFILTER))); + final GuardedInvocation newLink = fixReceiverType(link, WITHSCOPEFILTER); + final MethodHandle expressionGuard = expressionGuard(name, owner); + final MethodHandle filterGuardReceiver = filterGuardReceiver(newLink, WITHSCOPEFILTER); + return link.replaceMethods( + filterReceiver( + newLink.getInvocation(), + WITHSCOPEFILTER), + NashornGuards.combineGuards( + expressionGuard, + filterGuardReceiver)); } - private static MethodHandle filterGuard(final GuardedInvocation link, final MethodHandle filter) { + private static MethodHandle filterGuardReceiver(final GuardedInvocation link, final MethodHandle receiverFilter) { final MethodHandle test = link.getGuard(); - return test == null ? null : filter(test, filter); + if (test == null) { + return null; + } + + final Class<?> receiverType = test.type().parameterType(0); + final MethodHandle filter = MH.asType(receiverFilter, + receiverFilter.type().changeParameterType(0, receiverType). + changeReturnType(receiverType)); + + return filterReceiver(test, filter); } - private static MethodHandle filter(final MethodHandle mh, final MethodHandle filter) { - return MH.filterArguments(mh, 0, filter.asType(filter.type().changeReturnType(mh.type().parameterType(0)))); + private static MethodHandle filterReceiver(final MethodHandle mh, final MethodHandle receiverFilter) { + //With expression filter == receiverFilter, i.e. receiver is cast to withobject and its expression returned + return MH.filterArguments(mh, 0, receiverFilter.asType(receiverFilter.type().changeReturnType(mh.type().parameterType(0)))); } /** @@ -284,11 +329,27 @@ public final class WithObject extends ScriptObject implements Scope { @SuppressWarnings("unused") private static Object bindToExpression(final Object fn, final Object receiver) { - return fn instanceof ScriptFunction ? bindToExpression((ScriptFunction) fn, receiver) : fn; + if (fn instanceof ScriptFunction) { + return bindToExpression((ScriptFunction) fn, receiver); + } else if (fn instanceof ScriptObjectMirror) { + final ScriptObjectMirror mirror = (ScriptObjectMirror)fn; + if (mirror.isFunction()) { + // We need to make sure correct 'this' is used for calls with Ident call + // expressions. We do so here using an AbstractJSObject instance. + return new AbstractJSObject() { + @Override + public Object call(final Object thiz, final Object... args) { + return mirror.call(withFilterExpression(receiver), args); + } + }; + } + } + + return fn; } private static Object bindToExpression(final ScriptFunction fn, final Object receiver) { - return fn.makeBoundFunction(withFilterExpression(receiver), new Object[0]); + return fn.makeBoundFunction(withFilterExpression(receiver), ScriptRuntime.EMPTY_ARRAY); } private MethodHandle expressionGuard(final String name, final ScriptObject owner) { |