8039746: Transform applies to calls wherever possible, for ScriptFunctions and JSObjects.
Reviewed-by: hannesw, attila, sundar, jlaskey
--- a/nashorn/src/jdk/internal/dynalink/linker/GuardedInvocation.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/internal/dynalink/linker/GuardedInvocation.java Fri Apr 11 16:52:14 2014 +0200
@@ -110,7 +110,7 @@
private final MethodHandle invocation;
private final MethodHandle guard;
private final Class<? extends Throwable> exception;
- private final SwitchPoint switchPoint;
+ private final SwitchPoint[] switchPoints;
/**
* Creates a new guarded invocation. This invocation is unconditional as it has no invalidations.
@@ -118,8 +118,8 @@
* @param invocation the method handle representing the invocation. Must not be null.
* @throws NullPointerException if invocation is null.
*/
- public GuardedInvocation(MethodHandle invocation) {
- this(invocation, null, null, null);
+ public GuardedInvocation(final MethodHandle invocation) {
+ this(invocation, null, (SwitchPoint)null, null);
}
/**
@@ -131,8 +131,8 @@
* an unconditional invocation, although that is unusual.
* @throws NullPointerException if invocation is null.
*/
- public GuardedInvocation(MethodHandle invocation, MethodHandle guard) {
- this(invocation, guard, null, null);
+ public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard) {
+ this(invocation, guard, (SwitchPoint)null, null);
}
/**
@@ -142,7 +142,7 @@
* @param switchPoint the optional switch point that can be used to invalidate this linkage.
* @throws NullPointerException if invocation is null.
*/
- public GuardedInvocation(MethodHandle invocation, SwitchPoint switchPoint) {
+ public GuardedInvocation(final MethodHandle invocation, final SwitchPoint switchPoint) {
this(invocation, null, switchPoint, null);
}
@@ -156,7 +156,7 @@
* @param switchPoint the optional switch point that can be used to invalidate this linkage.
* @throws NullPointerException if invocation is null.
*/
- public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint) {
+ public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint switchPoint) {
this(invocation, guard, switchPoint, null);
}
@@ -172,11 +172,31 @@
* invalidates the linkage.
* @throws NullPointerException if invocation is null.
*/
- public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint, Class<? extends Throwable> exception) {
+ public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint switchPoint, final Class<? extends Throwable> exception) {
invocation.getClass(); // NPE check
this.invocation = invocation;
this.guard = guard;
- this.switchPoint = switchPoint;
+ this.switchPoints = switchPoint == null ? null : new SwitchPoint[] { switchPoint };
+ this.exception = exception;
+ }
+
+ /**
+ * Creates a new guarded invocation
+ *
+ * @param invocation the method handle representing the invocation. Must not be null.
+ * @param guard the method handle representing the guard. Must have the same method type as the invocation, except
+ * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
+ * and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
+ * @param switchPoints the optional switch points that can be used to invalidate this linkage.
+ * @param exception the optional exception type that is expected to be thrown by the invocation and that also
+ * invalidates the linkage.
+ * @throws NullPointerException if invocation is null.
+ */
+ public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint[] switchPoints, final Class<? extends Throwable> exception) {
+ invocation.getClass(); // NPE check
+ this.invocation = invocation;
+ this.guard = guard;
+ this.switchPoints = switchPoints;
this.exception = exception;
}
@@ -203,8 +223,8 @@
*
* @return the switch point that can be used to invalidate the invocation handle. Can be null.
*/
- public SwitchPoint getSwitchPoint() {
- return switchPoint;
+ public SwitchPoint[] getSwitchPoints() {
+ return switchPoints == null ? null : switchPoints.clone();
}
/**
@@ -221,7 +241,15 @@
* @return true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
*/
public boolean hasBeenInvalidated() {
- return switchPoint != null && switchPoint.hasBeenInvalidated();
+ if (switchPoints == null) {
+ return false;
+ }
+ for (final SwitchPoint sp : switchPoints) {
+ if (sp.hasBeenInvalidated()) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -231,9 +259,9 @@
* @param type the asserted type
* @throws WrongMethodTypeException if the invocation and the guard are not of the expected method type.
*/
- public void assertType(MethodType type) {
+ public void assertType(final MethodType type) {
assertType(invocation, type);
- if(guard != null) {
+ if (guard != null) {
assertType(guard, type.changeReturnType(Boolean.TYPE));
}
}
@@ -245,12 +273,34 @@
* @param newGuard the new guard
* @return a new guarded invocation with the replaced methods and the same switch point as this invocation.
*/
- public GuardedInvocation replaceMethods(MethodHandle newInvocation, MethodHandle newGuard) {
- return new GuardedInvocation(newInvocation, newGuard, switchPoint, exception);
+ public GuardedInvocation replaceMethods(final MethodHandle newInvocation, final MethodHandle newGuard) {
+ return new GuardedInvocation(newInvocation, newGuard, switchPoints, exception);
}
- private GuardedInvocation replaceMethodsOrThis(MethodHandle newInvocation, MethodHandle newGuard) {
- if(newInvocation == invocation && newGuard == guard) {
+ /**
+ * Add a switchpoint to this guarded invocation
+ * @param newSwitchPoint new switchpoint, or null for nop
+ * @return new guarded invocation with the extra switchpoint
+ */
+ public GuardedInvocation addSwitchPoint(final SwitchPoint newSwitchPoint) {
+ if (newSwitchPoint == null) {
+ return this;
+ }
+
+ final SwitchPoint[] newSwitchPoints;
+ if (switchPoints != null) {
+ newSwitchPoints = new SwitchPoint[switchPoints.length + 1];
+ System.arraycopy(switchPoints, 0, newSwitchPoints, 0, switchPoints.length);
+ newSwitchPoints[switchPoints.length] = newSwitchPoint;
+ } else {
+ newSwitchPoints = new SwitchPoint[] { newSwitchPoint };
+ }
+
+ return new GuardedInvocation(invocation, guard, newSwitchPoints, exception);
+ }
+
+ private GuardedInvocation replaceMethodsOrThis(final MethodHandle newInvocation, final MethodHandle newGuard) {
+ if (newInvocation == invocation && newGuard == guard) {
return this;
}
return replaceMethods(newInvocation, newGuard);
@@ -263,7 +313,7 @@
* @param newType the new type of the invocation.
* @return a guarded invocation with the new type applied to it.
*/
- public GuardedInvocation asType(MethodType newType) {
+ public GuardedInvocation asType(final MethodType newType) {
return replaceMethodsOrThis(invocation.asType(newType), guard == null ? null : Guards.asType(guard, newType));
}
@@ -275,7 +325,7 @@
* @param newType the new type of the invocation.
* @return a guarded invocation with the new type applied to it.
*/
- public GuardedInvocation asType(LinkerServices linkerServices, MethodType newType) {
+ public GuardedInvocation asType(final LinkerServices linkerServices, final MethodType newType) {
return replaceMethodsOrThis(linkerServices.asType(invocation, newType), guard == null ? null :
Guards.asType(linkerServices, guard, newType));
}
@@ -289,7 +339,7 @@
* @param newType the new type of the invocation.
* @return a guarded invocation with the new type applied to it.
*/
- public GuardedInvocation asTypeSafeReturn(LinkerServices linkerServices, MethodType newType) {
+ public GuardedInvocation asTypeSafeReturn(final LinkerServices linkerServices, final MethodType newType) {
return replaceMethodsOrThis(linkerServices.asTypeLosslessReturn(invocation, newType), guard == null ? null :
Guards.asType(linkerServices, guard, newType));
}
@@ -301,7 +351,7 @@
* @param desc a call descriptor whose method type is adapted.
* @return a guarded invocation with the new type applied to it.
*/
- public GuardedInvocation asType(CallSiteDescriptor desc) {
+ public GuardedInvocation asType(final CallSiteDescriptor desc) {
return asType(desc.getMethodType());
}
@@ -311,7 +361,7 @@
* @param filters the argument filters
* @return a filtered invocation
*/
- public GuardedInvocation filterArguments(int pos, MethodHandle... filters) {
+ public GuardedInvocation filterArguments(final int pos, final MethodHandle... filters) {
return replaceMethods(MethodHandles.filterArguments(invocation, pos, filters), guard == null ? null :
MethodHandles.filterArguments(guard, pos, filters));
}
@@ -322,7 +372,7 @@
* @param valueTypes the types of the values being dropped
* @return an invocation that drops arguments
*/
- public GuardedInvocation dropArguments(int pos, List<Class<?>> valueTypes) {
+ public GuardedInvocation dropArguments(final int pos, final List<Class<?>> valueTypes) {
return replaceMethods(MethodHandles.dropArguments(invocation, pos, valueTypes), guard == null ? null :
MethodHandles.dropArguments(guard, pos, valueTypes));
}
@@ -333,7 +383,7 @@
* @param valueTypes the types of the values being dropped
* @return an invocation that drops arguments
*/
- public GuardedInvocation dropArguments(int pos, Class<?>... valueTypes) {
+ public GuardedInvocation dropArguments(final int pos, final Class<?>... valueTypes) {
return replaceMethods(MethodHandles.dropArguments(invocation, pos, valueTypes), guard == null ? null :
MethodHandles.dropArguments(guard, pos, valueTypes));
}
@@ -344,7 +394,7 @@
* @param fallback the fallback method handle in case switchpoint is invalidated or guard returns false.
* @return a composite method handle.
*/
- public MethodHandle compose(MethodHandle fallback) {
+ public MethodHandle compose(final MethodHandle fallback) {
return compose(fallback, fallback, fallback);
}
@@ -355,7 +405,7 @@
* @param catchFallback the fallback method in case the exception handler triggers
* @return a composite method handle.
*/
- public MethodHandle compose(MethodHandle guardFallback, MethodHandle switchpointFallback, MethodHandle catchFallback) {
+ public MethodHandle compose(final MethodHandle guardFallback, final MethodHandle switchpointFallback, final MethodHandle catchFallback) {
final MethodHandle guarded =
guard == null ?
invocation :
@@ -375,24 +425,26 @@
0,
exception));
- final MethodHandle spGuarded =
- switchPoint == null ?
- catchGuarded :
- switchPoint.guardWithTest(
- catchGuarded,
- switchpointFallback);
+ if (switchPoints == null) {
+ return catchGuarded;
+ }
+
+ MethodHandle spGuarded = catchGuarded;
+ for (final SwitchPoint sp : switchPoints) {
+ spGuarded = sp.guardWithTest(spGuarded, switchpointFallback);
+ }
return spGuarded;
}
private static MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
- if(USE_FAST_REWRITE) {
+ if (USE_FAST_REWRITE) {
return CatchExceptionCombinator.catchException(target, exType, handler);
}
return MethodHandles.catchException(target, exType, handler);
}
- private static void assertType(MethodHandle mh, MethodType type) {
+ private static void assertType(final MethodHandle mh, final MethodType type) {
if(!mh.type().equals(type)) {
throw new WrongMethodTypeException("Expected type: " + type + " actual type: " + mh.type());
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2010, 2014, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.nashorn.internal.codegen;
+
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
+import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX;
+
+import java.lang.invoke.MethodType;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import jdk.nashorn.internal.ir.AccessNode;
+import jdk.nashorn.internal.ir.CallNode;
+import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.runtime.DebugLogger;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+
+/**
+ * An optimization that attempts to turn applies into calls. This pattern
+ * is very common for fake class instance creation, and apply
+ * introduces expensive args collection and boxing
+ *
+ * <pre>
+ * {@code
+ * var Class = {
+ * create: function() {
+ * return function() { //vararg
+ * this.initialize.apply(this, arguments);
+ * }
+ * }
+ * };
+ *
+ * Color = Class.create();
+ *
+ * Color.prototype = {
+ * red: 0, green: 0, blue: 0,
+ * initialize: function(r,g,b) {
+ * this.red = r;
+ * this.green = g;
+ * this.blue = b;
+ * }
+ * }
+ *
+ * new Color(17, 47, 11);
+ * }
+ * </pre>
+ */
+
+public class ApplySpecialization {
+
+ private final RecompilableScriptFunctionData data;
+
+ private FunctionNode functionNode;
+
+ private final MethodType actualCallSiteType;
+
+ private static final DebugLogger LOG = new DebugLogger("apply2call");
+
+ private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
+
+ private boolean changed;
+
+ private boolean finished;
+
+ private final boolean isRestOf;
+
+ /**
+ * Return the apply to call specialization logger
+ * @return the logger
+ */
+ public static DebugLogger getLogger() {
+ return LOG;
+ }
+
+ /**
+ * Apply specialization optimization. Try to explode arguments and call
+ * applies as calls if they just pass on the "arguments" array and
+ * "arguments" doesn't escape.
+ *
+ * @param data recompilable script function data, which contains e.g. needs callee information
+ * @param functionNode functionNode
+ * @param actualCallSiteType actual call site type that we use (not Object[] varargs)
+ * @param isRestOf is this a restof method
+ */
+ public ApplySpecialization(final RecompilableScriptFunctionData data, final FunctionNode functionNode, final MethodType actualCallSiteType, final boolean isRestOf) {
+ this.data = data;
+ this.functionNode = functionNode;
+ this.actualCallSiteType = actualCallSiteType;
+ this.isRestOf = isRestOf;
+ }
+
+ /**
+ * Return the function node, possibly after transformation
+ * @return function node
+ */
+ public FunctionNode getFunctionNode() {
+ return functionNode;
+ }
+
+ /**
+ * Arguments may only be used as args to the apply. Everything else is disqualified
+ * @return true if arguments escape
+ */
+ private boolean argumentsEscape() {
+
+ final Deque<Set<Expression>> stack = new ArrayDeque<>();
+ //ensure that arguments is only passed as arg to apply
+ try {
+ functionNode = (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ private boolean isCurrentArg(final Expression expr) {
+ return !stack.isEmpty() && stack.peek().contains(expr);
+ }
+
+ private boolean isArguments(final Expression expr) {
+ return expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName());
+ }
+
+ @Override
+ public Node leaveIdentNode(final IdentNode identNode) {
+ if (ARGUMENTS.equals(identNode.getName()) && !isCurrentArg(identNode)) {
+ throw new UnsupportedOperationException();
+ }
+ return identNode;
+ }
+
+ @Override
+ public boolean enterCallNode(final CallNode callNode) {
+ final Set<Expression> callArgs = new HashSet<>();
+ if (isApply(callNode)) {
+ final List<Expression> argList = callNode.getArgs();
+ if (argList.size() != 2 || !isArguments(argList.get(argList.size() - 1))) {
+ throw new UnsupportedOperationException();
+ }
+ callArgs.addAll(callNode.getArgs());
+ }
+ stack.push(callArgs);
+ return true;
+ }
+
+ @Override
+ public Node leaveCallNode(final CallNode callNode) {
+ stack.pop();
+ return callNode;
+ }
+ });
+ } catch (final UnsupportedOperationException e) {
+ LOG.fine("'arguments' escapes, is not used in standard call dispatch, or is reassigned in '" + functionNode.getName() + "'. Aborting");
+ return true; //bad
+ }
+
+ return false;
+ }
+
+ private boolean finish() {
+ finished = true;
+ return changed;
+ }
+
+ /**
+ * Try to do the apply to call transformation
+ * @return true if successful, false otherwise
+ */
+ public boolean transform() {
+ if (finished) {
+ throw new AssertionError("Can't apply transform twice");
+ }
+
+ changed = false;
+
+ if (!Global.instance().isSpecialNameValid("apply")) {
+ LOG.fine("Apply transform disabled: apply/call overridden");
+ assert !Global.instance().isSpecialNameValid("call") : "call and apply should have the same SwitchPoint";
+ return finish();
+ }
+
+ //eval can do anything to escape arguments so that is not ok
+ if (functionNode.hasEval()) {
+ return finish();
+ }
+
+ if (argumentsEscape()) {
+ return finish();
+ }
+
+ int start = 0;
+
+ if (data.needsCallee()) {
+ start++;
+ }
+
+ start++; //we always uses this
+
+ final List<IdentNode> newParams = new ArrayList<>();
+ for (int i = start; i < actualCallSiteType.parameterCount(); i++) {
+ newParams.add(new IdentNode(functionNode.getToken(), functionNode.getFinish(), EXPLODED_ARGUMENT_PREFIX.symbolName() + (i - start)));
+ }
+
+ // expand arguments
+ // this function has to be guarded with call and apply being builtins
+ functionNode = (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ @Override
+ public Node leaveCallNode(final CallNode callNode) {
+ //apply needs to be a global symbol or we don't allow it
+
+ if (isApply(callNode)) {
+ final List<Expression> newArgs = new ArrayList<>();
+ for (final Expression arg : callNode.getArgs()) {
+ if (arg instanceof IdentNode && ARGUMENTS.equals(((IdentNode)arg).getName())) {
+ newArgs.addAll(newParams);
+ } else {
+ newArgs.add(arg);
+ }
+ }
+
+ changed = true;
+
+ final CallNode newCallNode = callNode.setArgs(newArgs).setIsApplyToCall();
+ LOG.fine("Transformed " + callNode + " from apply to call => " + newCallNode + " in '" + functionNode.getName() + "'");
+ return newCallNode;
+ }
+
+ return callNode;
+ }
+ });
+
+ if (changed) {
+ functionNode = functionNode.clearFlag(null, FunctionNode.USES_ARGUMENTS).
+ setFlag(null, FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION).
+ setParameters(null, newParams);
+ }
+
+ LOG.info("Successfully specialized apply to call in '" + functionNode.getName() + "' id=" + functionNode.getId() + " signature=" + actualCallSiteType + " isRestOf=" + isRestOf);
+ return finish();
+ }
+
+ private static boolean isApply(final Node node) {
+ if (node instanceof AccessNode) {
+ return isApply(((AccessNode)node).getProperty());
+ }
+ return node instanceof IdentNode && "apply".equals(((IdentNode)node).getName());
+ }
+
+ private static boolean isApply(final CallNode callNode) {
+ final Expression f = callNode.getFunction();
+ return f instanceof AccessNode && isApply(f);
+ }
+
+}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Fri Apr 11 16:52:14 2014 +0200
@@ -1,4 +1,5 @@
/*
+
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -588,8 +589,7 @@
final int optimisticFlag = lc.hasOptimisticAssumptions() ? FunctionNode.IS_OPTIMISTIC : 0;
newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR).setFlag(lc, optimisticFlag);
-
- popLocals();
+ popLocalsFunction();
if (!env.isOnDemandCompilation() && newFunctionNode.isProgram()) {
newFunctionNode = newFunctionNode.setBody(lc, newFunctionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
@@ -2147,6 +2147,10 @@
localUses.pop();
}
+ private void popLocalsFunction() {
+ popLocals();
+ }
+
private boolean isLocalDef(final String name) {
return localDefs.peek().contains(name);
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Apr 11 16:52:14 2014 +0200
@@ -51,6 +51,7 @@
import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_APPLY_TO_CALL;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
@@ -809,7 +810,11 @@
}
private int loadArgs(final List<Expression> args) {
- return loadArgs(args, null, false, args.size());
+ return loadArgs(args, args.size());
+ }
+
+ private int loadArgs(final List<Expression> args, final int argCount) {
+ return loadArgs(args, null, false, argCount);
}
private int loadArgs(final List<Expression> args, final String signature, final boolean isVarArg, final int argCount) {
@@ -990,6 +995,11 @@
@Override
public boolean enterAccessNode(final AccessNode node) {
+ //check if this is an apply to call node. only real applies, that haven't been
+ //shadowed from their way to the global scope counts
+
+ //call nodes have program points.
+
new OptimisticOperation() {
int argCount;
@Override
@@ -999,14 +1009,14 @@
// NOTE: not using a nested OptimisticOperation on this dynamicGet, as we expect to get back
// a callable object. Nobody in their right mind would optimistically type this call site.
assert !node.isOptimistic();
- method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true);
+ final int flags = getCallSiteFlags() | (callNode.isApplyToCall() ? CALLSITE_APPLY_TO_CALL : 0);
+ method.dynamicGet(node.getType(), node.getProperty().getName(), flags, true);
method.swap();
argCount = loadArgs(args);
}
@Override
void consumeStack() {
- final int flags = getCallSiteFlagsOptimistic(callNode);
- dynamicCall(method, callNode, callNodeType, 2 + argCount, flags);
+ dynamicCall(method, callNode, callNodeType, 2 + argCount, getCallSiteFlagsOptimistic(callNode) | (callNode.isApplyToCall() ? CALLSITE_APPLY_TO_CALL : 0));
}
}.emit(callNode);
@@ -1034,7 +1044,6 @@
final int flags = getCallSiteFlagsOptimistic(callNode);
//assert callNodeType.equals(callee.getReturnType()) : callNodeType + " != " + callee.getReturnType();
dynamicCall(method, callNode, callNodeType, 2 + argsCount, flags);
- //assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
}
}.emit(callNode);
method.convert(callNodeType);
@@ -1149,7 +1158,7 @@
private static MethodEmitter dynamicCall(final MethodEmitter method, final Expression expr, final Type desiredType, final int argCount, final int flags) {
final int finalFlags = maybeRemoveOptimisticFlags(desiredType, flags);
- if(isOptimistic(finalFlags)) {
+ if (isOptimistic(finalFlags)) {
return method.dynamicCall(getOptimisticCoercedType(desiredType, expr), argCount, finalFlags).convert(desiredType);
}
return method.dynamicCall(desiredType, argCount, finalFlags);
@@ -2024,16 +2033,9 @@
//make sure that undefined has not been overridden or scoped as a local var
//between us and global
- final CompilationEnvironment env = compiler.getCompilationEnvironment();
- RecompilableScriptFunctionData data = env.getScriptFunctionData(lc.getCurrentFunction().getId());
- final RecompilableScriptFunctionData program = compiler.getCompilationEnvironment().getProgram();
- assert data != null;
-
- while (data != program) {
- if (data.hasInternalSymbol("undefined")) {
- return false;
- }
- data = data.getParent();
+ final CompilationEnvironment env = compiler.getCompilationEnvironment();
+ if (!env.isGlobalSymbol(lc.getCurrentFunction(), "undefined")) {
+ return false;
}
load(expr);
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java Fri Apr 11 16:52:14 2014 +0200
@@ -481,18 +481,15 @@
if (compiledFunction == null) {
return null;
}
- RecompilableScriptFunctionData program = compiledFunction;
- while (true) {
- final RecompilableScriptFunctionData parent = program.getParent();
- if (parent == null) {
- return program;
- }
- program = parent;
- }
+ return compiledFunction.getProgram();
}
RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
}
+ boolean isGlobalSymbol(final FunctionNode functionNode, final String name) {
+ final RecompilableScriptFunctionData data = getScriptFunctionData(functionNode.getId());
+ return data.isGlobalSymbol(functionNode, name);
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Fri Apr 11 16:52:14 2014 +0200
@@ -244,7 +244,7 @@
public FunctionNode compile(final String className, final FunctionNode functionNode) throws CompilationException {
try {
return compileInternal(className, functionNode);
- } catch(final AssertionError e) {
+ } catch (final AssertionError e) {
throw new AssertionError("Assertion failure compiling " + functionNode.getSource(), e);
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java Fri Apr 11 16:52:14 2014 +0200
@@ -111,6 +111,9 @@
/** the internal arguments object, when necessary (not visible to scripts, can't be reassigned). */
ARGUMENTS(":arguments", ScriptObject.class),
+ /** prefix for apply-to-call exploded arguments */
+ EXPLODED_ARGUMENT_PREFIX(":xarg"),
+
/** prefix for iterators for for (x in ...) */
ITERATOR_PREFIX(":i", Iterator.class),
--- a/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Fri Apr 11 16:52:14 2014 +0200
@@ -36,6 +36,7 @@
import java.util.Iterator;
import java.util.List;
+
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.Context;
@@ -143,6 +144,9 @@
} else {
putSlot(method, ArrayIndex.toLongIndex(index), tuple);
}
+
+ //this is a nop of tuple.key isn't e.g. "apply" or another special name
+ method.invalidateSpecialName(tuple.key);
}
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java Fri Apr 11 16:52:14 2014 +0200
@@ -78,6 +78,7 @@
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
+
import jdk.internal.dynalink.support.NameCodec;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
@@ -94,6 +95,7 @@
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.Symbol;
+import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.ArgumentSetter;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
@@ -168,6 +170,9 @@
/** Bootstrap for array populators */
private static final Handle POPULATE_ARRAY_BOOTSTRAP = new Handle(H_INVOKESTATIC, RewriteException.BOOTSTRAP.className(), RewriteException.BOOTSTRAP.name(), RewriteException.BOOTSTRAP.descriptor());
+ /** Bootstrap for global name invalidation */
+ private static final Handle INVALIDATE_NAME_BOOTSTRAP = new Handle(H_INVOKESTATIC, Global.BOOTSTRAP.className(), Global.BOOTSTRAP.name(), Global.BOOTSTRAP.descriptor());
+
/**
* Constructor - internal use from ClassEmitter only
* @see ClassEmitter#method
@@ -509,7 +514,6 @@
stack.markLocalLoad(l0);
pushType(p1);
stack.markLocalLoad(l1);
- debug("after ", p0, p1);
return this;
}
@@ -1886,6 +1890,15 @@
return descriptor;
}
+ MethodEmitter invalidateSpecialName(final String name) {
+ //this is a nop if the global hasn't registered this as a special name - we can just ignore it
+ if (Global.instance().isSpecialName(name)) {
+ debug("dynamic_invalidate_name", "name=", name);
+ method.visitInvokeDynamicInsn(name, "()V", INVALIDATE_NAME_BOOTSTRAP);
+ }
+ return this;
+ }
+
/**
* Generate a dynamic new
*
@@ -1923,6 +1936,7 @@
}
MethodEmitter dynamicArrayPopulatorCall(final int argCount, final int startIndex) {
+ debug("populate_array", "args=", argCount, "startIndex=", startIndex);
final String signature = getDynamicSignature(Type.OBJECT_ARRAY, argCount);
method.visitInvokeDynamicInsn("populateArray", signature, POPULATE_ARRAY_BOOTSTRAP, startIndex);
pushType(Type.OBJECT_ARRAY);
--- a/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java Fri Apr 11 16:52:14 2014 +0200
@@ -26,8 +26,8 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
+import java.util.List;
-import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
--- a/nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java Fri Apr 11 16:52:14 2014 +0200
@@ -28,9 +28,11 @@
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
+
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.runtime.JSType;
@@ -75,6 +77,9 @@
final String key = tuple.key;
final Expression value = tuple.value;
+ //this is a nop of tuple.key isn't e.g. "apply" or another special name
+ method.invalidateSpecialName(tuple.key);
+
if (value != null) {
final Object constantValue = LiteralNode.objectAsConstant(value);
if (constantValue == LiteralNode.POSTSET_MARKER) {
--- a/nashorn/src/jdk/nashorn/internal/ir/Block.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Block.java Fri Apr 11 16:52:14 2014 +0200
@@ -243,6 +243,11 @@
}
@Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
public boolean isTerminal() {
return getFlag(IS_TERMINAL);
}
--- a/nashorn/src/jdk/nashorn/internal/ir/CallNode.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/CallNode.java Fri Apr 11 16:52:14 2014 +0200
@@ -47,10 +47,13 @@
private final List<Expression> args;
/** Is this a "new" operation */
- public static final int IS_NEW = 1 << 0;
+ private static final int IS_NEW = 1 << 0;
/** Is the callsite type for this call optimistic rather than based on statically known coercion semantics */
- public static final int OPTIMISTIC = 1 << 1;
+ private static final int IS_OPTIMISTIC = 1 << 1;
+
+ /** Can this be a Function.call? */
+ private static final int IS_APPLY_TO_CALL = 1 << 2;
private final int flags;
@@ -188,7 +191,7 @@
}
@Override
- public Optimistic setType(TemporarySymbols ts, Type optimisticType) {
+ public Optimistic setType(final TemporarySymbols ts, final Type optimisticType) {
if (this.optimisticType == optimisticType) {
return this;
}
@@ -214,7 +217,7 @@
setThis((IdentNode)evalArgs.getThis().accept(visitor))));
// Theoretically, we'd need to instead pass lc to every setter and do a replacement on each. In practice,
// setType from TypeOverride can't accept a lc, and we don't necessarily want to go there now.
- if(this != newCallNode) {
+ if (this != newCallNode) {
return Node.replaceInLexicalContext(lc, this, newCallNode);
}
}
@@ -225,7 +228,12 @@
@Override
public void toString(final StringBuilder sb) {
Node.optimisticType(this, sb);
- function.toString(sb);
+
+ final StringBuilder fsb = new StringBuilder();
+ function.toString(fsb);
+ if (isApplyToCall()) {
+ sb.append(fsb.toString().replace("apply", "[apply => call]"));
+ }
sb.append('(');
@@ -255,8 +263,9 @@
/**
* Reset the arguments for the call
* @param args new arguments list
+ * @return new callnode, or same if unchanged
*/
- private CallNode setArgs(final List<Expression> args) {
+ public CallNode setArgs(final List<Expression> args) {
if (this.args == args) {
return this;
}
@@ -294,6 +303,23 @@
}
/**
+ * Is this an apply call that we optimistically should try to turn into
+ * a call instead
+ * @return true if apply to call
+ */
+ public boolean isApplyToCall() {
+ return (flags & IS_APPLY_TO_CALL) != 0;
+ }
+
+ /**
+ * Flag this call node as one that tries to call call instead of apply
+ * @return new call node with changed flags, if not already flagged as apply to call, then the same node
+ */
+ public CallNode setIsApplyToCall() {
+ return setFlags(flags | IS_APPLY_TO_CALL);
+ }
+
+ /**
* Return the function expression that this call invokes
* @return the function
*/
@@ -318,7 +344,7 @@
* @return true if this a new operation
*/
public boolean isNew() {
- return (flags & IS_NEW) == IS_NEW;
+ return (flags & IS_NEW) != 0;
}
/**
@@ -326,7 +352,7 @@
* @return same node or new one on state change
*/
public CallNode setIsNew() {
- return setFlags(IS_NEW);
+ return setFlags(flags | IS_NEW);
}
private CallNode setFlags(final int flags) {
@@ -366,7 +392,7 @@
@Override
public boolean isOptimistic() {
- return (flags & OPTIMISTIC) == OPTIMISTIC;
+ return (flags & IS_OPTIMISTIC) != 0;
}
@Override
@@ -374,6 +400,6 @@
if (isOptimistic() == isOptimistic) {
return this;
}
- return new CallNode(this, function, args, isOptimistic ? (flags | OPTIMISTIC) : (flags & ~OPTIMISTIC), optimisticType, evalArgs, programPoint);
+ return new CallNode(this, function, args, isOptimistic ? (flags | IS_OPTIMISTIC) : (flags & ~IS_OPTIMISTIC), optimisticType, evalArgs, programPoint);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/Flags.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Flags.java Fri Apr 11 16:52:14 2014 +0200
@@ -37,6 +37,12 @@
public interface Flags<T extends LexicalContextNode> {
/**
+ * Get all flags of a LexicalContextNode
+ * @return flags
+ */
+ public int getFlags();
+
+ /**
* Check if a flag is set in a lexical context node
* @param flag flag to check
* @return flags
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Fri Apr 11 16:52:14 2014 +0200
@@ -192,21 +192,24 @@
/** Does this function have optimistic expressions? */
public static final int IS_OPTIMISTIC = 1 << 12;
+ /** Are we vararg, but do we just pass the arguments along to apply or call */
+ public static final int HAS_APPLY_TO_CALL_SPECIALIZATION = 1 << 13;
+
/** Does this function explicitly use the {@link CompilerConstants#RETURN} symbol? Some functions are known to
* always use the return symbol, namely a function that is a program (as it must track its last executed expression
* statement's value) as well as functions that are split (to communicate return values from inner to outer
* partitions). Other functions normally don't use the return symbol (so we optimize away its slot), except in some
* very special cases, e.g. when containing a return statement in a finally block. These special cases set this
* flag. */
- public static final int USES_RETURN_SYMBOL = 1 << 13;
+ public static final int USES_RETURN_SYMBOL = 1 << 14;
/**
* Is this function the top-level program?
*/
- public static final int IS_PROGRAM = 1 << 14;
+ public static final int IS_PROGRAM = 1 << 15;
/** Does this function use the "this" keyword? */
- public static final int USES_THIS = 1 << 15;
+ public static final int USES_THIS = 1 << 16;
/** Does this function or any nested functions contain an eval? */
private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL;
@@ -477,6 +480,11 @@
}
@Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
public boolean getFlag(final int flag) {
return (flags & flag) != 0;
}
@@ -509,7 +517,7 @@
/**
* Returns true if the function is optimistic
- * @return True if this function is optimistic
+ * @return true if this function is optimistic
*/
public boolean isOptimistic() {
return getFlag(IS_OPTIMISTIC);
@@ -569,6 +577,15 @@
return getFlag(USES_THIS);
}
+
+ /**
+ * Return true if function contains an apply to call transform
+ * @return true if this function has transformed apply to call
+ */
+ public boolean hasOptimisticApplyToCall() {
+ return getFlag(HAS_APPLY_TO_CALL_SPECIALIZATION);
+ }
+
/**
* Get the identifier for this function, this is its symbol.
* @return the identifier as an IdentityNode
--- a/nashorn/src/jdk/nashorn/internal/objects/Global.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java Fri Apr 11 16:52:14 2014 +0200
@@ -25,22 +25,30 @@
package jdk.nashorn.internal.objects;
+import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.SwitchPoint;
import java.lang.reflect.Field;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.nashorn.internal.codegen.ApplySpecialization;
+import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Property;
@@ -75,6 +83,33 @@
private final InvokeByName TO_STRING = new InvokeByName("toString", ScriptObject.class);
private final InvokeByName VALUE_OF = new InvokeByName("valueOf", ScriptObject.class);
+ /**
+ * Optimistic builtin names that require switchpoint invalidation
+ * upon assignment. Overly conservative, but works for now, to avoid
+ * any complicated scope checks and especially heavy weight guards
+ * like
+ *
+ * <pre>
+ * {@code
+ * public boolean setterGuard(final Object receiver) {
+ * final Global global = Global.instance();
+ * final ScriptObject sobj = global.getFunctionPrototype();
+ * final Object apply = sobj.get("apply");
+ * return apply == receiver;
+ * }
+ *
+ * }
+ * </pre>
+ *
+ * Naturally, checking for builting classes like NativeFunction is cheaper,
+ * it's when you start adding property checks for said builtins you have
+ * problems with guard speed.
+ */
+ public final Map<String, SwitchPoint> optimisticFunctionMap;
+
+ /** Name invalidator for things like call/apply */
+ public static final Call BOOTSTRAP = staticCall(MethodHandles.lookup(), Global.class, "invalidateNameBootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
+
/** ECMA 15.1.2.2 parseInt (string , radix) */
@Property(attributes = Attribute.NOT_ENUMERABLE)
public Object parseInt;
@@ -379,12 +414,15 @@
// Used to store the last RegExp result to support deprecated RegExp constructor properties
private RegExpResult lastRegExpResult;
- private static final MethodHandle EVAL = findOwnMH("eval", Object.class, Object.class, Object.class);
- private static final MethodHandle PRINT = findOwnMH("print", Object.class, Object.class, Object[].class);
- private static final MethodHandle PRINTLN = findOwnMH("println", Object.class, Object.class, Object[].class);
- private static final MethodHandle LOAD = findOwnMH("load", Object.class, Object.class, Object.class);
- private static final MethodHandle LOADWITHNEWGLOBAL = findOwnMH("loadWithNewGlobal", Object.class, Object.class, Object[].class);
- private static final MethodHandle EXIT = findOwnMH("exit", Object.class, Object.class, Object.class);
+ private static final MethodHandle EVAL = findOwnMH_S("eval", Object.class, Object.class, Object.class);
+ private static final MethodHandle PRINT = findOwnMH_S("print", Object.class, Object.class, Object[].class);
+ private static final MethodHandle PRINTLN = findOwnMH_S("println", Object.class, Object.class, Object[].class);
+ private static final MethodHandle LOAD = findOwnMH_S("load", Object.class, Object.class, Object.class);
+ private static final MethodHandle LOADWITHNEWGLOBAL = findOwnMH_S("loadWithNewGlobal", Object.class, Object.class, Object[].class);
+ private static final MethodHandle EXIT = findOwnMH_S("exit", Object.class, Object.class, Object.class);
+
+ /** Invalidate a reserved name, such as "apply" or "call" if assigned */
+ public MethodHandle INVALIDATE_RESERVED_NAME = MH.bindTo(findOwnMH_V("invalidateReservedName", void.class, String.class), this);
// initialized by nasgen
private static PropertyMap $nasgenmap$;
@@ -426,6 +464,7 @@
super(checkAndGetMap(context));
this.context = context;
this.setIsScope();
+ this.optimisticFunctionMap = new HashMap<>();
GlobalConstants.instance().invalidateAll();
}
@@ -1886,7 +1925,7 @@
// to play with object references carefully!!
private void initFunctionAndObject() {
// First-n-foremost is Function
- this.builtinFunction = (ScriptFunction)initConstructor("Function");
+ this.builtinFunction = (ScriptFunction)initConstructor("Function");
// create global anonymous function
final ScriptFunction anon = ScriptFunctionImpl.newAnonymousFunction(this);
@@ -1946,7 +1985,15 @@
}
}
+ //make sure apply and call have the same invalidation switchpoint
+ final SwitchPoint sp = new SwitchPoint();
+ optimisticFunctionMap.put("apply", sp);
+ optimisticFunctionMap.put("call", sp);
+ getFunctionPrototype().getProperty("apply").setChangeCallback(sp);
+ getFunctionPrototype().getProperty("call").setChangeCallback(sp);
+
properties = getObjectPrototype().getMap().getProperties();
+
for (final jdk.nashorn.internal.runtime.Property property : properties) {
final Object key = property.getKey();
final Object value = ObjectPrototype.get(key);
@@ -1965,7 +2012,11 @@
}
}
- private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
+ private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
+ return MH.findVirtual(MethodHandles.lookup(), Global.class, name, MH.type(rtype, types));
+ }
+
+ private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findStatic(MethodHandles.lookup(), Global.class, name, MH.type(rtype, types));
}
@@ -1982,4 +2033,59 @@
return true;
}
+ /**
+ * Check if there is a switchpoint for a reserved name. If there
+ * is, it must be invalidated upon properties with this name
+ * @param name property name
+ * @return switchpoint for invalidating this property, or null if not registered
+ */
+ public SwitchPoint getChangeCallback(final String name) {
+ return optimisticFunctionMap.get(name);
+ }
+
+ /**
+ * Is this a special name, that might be subject to invalidation
+ * on write, such as "apply" or "call"
+ * @param name name to check
+ * @return true if special name
+ */
+ public boolean isSpecialName(final String name) {
+ return getChangeCallback(name) != null;
+ }
+
+ /**
+ * Check if a reserved property name is invalidated
+ * @param name property name
+ * @return true if someone has written to it since Global was instantiated
+ */
+ public boolean isSpecialNameValid(final String name) {
+ final SwitchPoint sp = getChangeCallback(name);
+ return sp != null && !sp.hasBeenInvalidated();
+ }
+
+ /**
+ * Tag a reserved name as invalidated - used when someone writes
+ * to a property with this name - overly conservative, but link time
+ * is too late to apply e.g. apply->call specialization
+ * @param name property name
+ */
+ public void invalidateReservedName(final String name) {
+ final SwitchPoint sp = getChangeCallback(name);
+ if (sp != null) {
+ ApplySpecialization.getLogger().info("Overwrote special name '" + name +"' - invalidating switchpoint");
+ SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
+ }
+ }
+
+ /**
+ * Bootstrapper for invalidating a builtin name
+ * @param lookup lookup
+ * @param name name to invalidate
+ * @param type methodhandle type
+ * @return callsite for invalidator
+ */
+ public static CallSite invalidateNameBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type) {
+ final MethodHandle target = MH.insertArguments(Global.instance().INVALIDATE_RESERVED_NAME, 0, name);
+ return new ConstantCallSite(target);
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeFunction.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeFunction.java Fri Apr 11 16:52:14 2014 +0200
@@ -97,7 +97,7 @@
public static Object apply(final Object self, final Object thiz, final Object array) {
checkCallable(self);
- Object[] args = toApplyArgs(array);
+ final Object[] args = toApplyArgs(array);
if (self instanceof ScriptFunction) {
return ScriptRuntime.apply((ScriptFunction)self, thiz, args);
@@ -177,7 +177,7 @@
public static Object call(final Object self, final Object... args) {
checkCallable(self);
- Object thiz = (args.length == 0) ? UNDEFINED : args[0];
+ final Object thiz = (args.length == 0) ? UNDEFINED : args[0];
Object[] arguments;
if (args.length > 1) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Fri Apr 11 16:52:14 2014 +0200
@@ -578,7 +578,7 @@
}
@Override
- protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
+ protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
return findHook(desc, __new__, false);
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java Fri Apr 11 16:52:14 2014 +0200
@@ -765,7 +765,7 @@
} catch(final Throwable t) {
throw new RuntimeException(t);
}
- assert inv.getSwitchPoint() == null; // Linkers in Dynalink's beans package don't use switchpoints.
+ assert inv.getSwitchPoints() == null; // Linkers in Dynalink's beans package don't use switchpoints.
// We discard the guard, as all method handles will be bound to a specific object.
return inv.getInvocation();
}
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Fri Apr 11 16:52:14 2014 +0200
@@ -30,6 +30,7 @@
import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
+
import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.Property;
@@ -226,7 +227,7 @@
* @return a function with the specified self and parameters bound.
*/
@Override
- protected ScriptFunction makeBoundFunction(Object self, Object[] args) {
+ protected ScriptFunction makeBoundFunction(final Object self, final Object[] args) {
return super.makeBoundFunction(self, args);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Fri Apr 11 16:52:14 2014 +0200
@@ -41,12 +41,14 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.invoke.SwitchPoint;
import java.util.logging.Level;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.lookup.MethodHandleFactory;
+import jdk.nashorn.internal.objects.Global;
/**
* An AccessorProperty is the most generic property type. An AccessorProperty is
@@ -60,6 +62,8 @@
private static final DebugLogger LOG = ObjectClassGenerator.getLogger();
+ private static final MethodHandle INVALIDATE_SP = findOwnMH("invalidateSwitchPoint", Object.class, Object.class, SwitchPoint.class, String.class);
+
/**
* Properties in different maps for the same structure class will share their field getters and setters. This could
* be further extended to other method handles that are looked up in the AccessorProperty constructor, but right now
@@ -168,7 +172,6 @@
this.primitiveSetter = bindTo(property.primitiveSetter, delegate);
this.objectGetter = bindTo(property.objectGetter, delegate);
this.objectSetter = bindTo(property.objectSetter, delegate);
-
property.GETTER_CACHE = new MethodHandle[NOOF_TYPES];
// Properties created this way are bound to a delegate
setCurrentType(property.getCurrentType());
@@ -601,6 +604,13 @@
return sobj;
}
+ @SuppressWarnings("unused")
+ private static Object invalidateSwitchPoint(final Object obj, final SwitchPoint sp, final String key) {
+ LOG.info("Field change callback for " + key + " triggered: " + sp);
+ SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
+ return obj;
+ }
+
private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter);
mh = debug(mh, getCurrentType(), type, "set");
@@ -637,11 +647,40 @@
mh = generateSetter(forType, type);
}
+ /**
+ * Check if this is a special global name that requires switchpoint invalidation
+ */
+ final SwitchPoint ccb = getChangeCallback();
+ if (ccb != null && ccb != NO_CHANGE_CALLBACK) {
+ mh = MH.filterArguments(mh, 0, MH.insertArguments(INVALIDATE_SP, 1, changeCallback, getKey()));
+ }
+
assert mh.type().returnType() == void.class;
return mh;
}
+ private static final SwitchPoint NO_CHANGE_CALLBACK = new SwitchPoint();
+
+ /**
+ * Get the change callback for this property
+ * @return switchpoint that is invalidated when property changes
+ */
+ protected SwitchPoint getChangeCallback() {
+ if (changeCallback == null) {
+ try {
+ changeCallback = Global.instance().getChangeCallback(getKey());
+ } catch (final NullPointerException e) {
+ assert !"apply".equals(getKey()) && !"call".equals(getKey());
+ //empty
+ }
+ if (changeCallback == null) {
+ changeCallback = NO_CHANGE_CALLBACK;
+ }
+ }
+ return changeCallback;
+ }
+
@Override
public final boolean canChangeType() {
if (OBJECT_FIELDS_ONLY) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Fri Apr 11 16:52:14 2014 +0200
@@ -72,6 +72,7 @@
private MethodHandle invoker;
private MethodHandle constructor;
private OptimismInfo optimismInfo;
+ private int flags; // from FunctionNode
CompiledFunction(final MethodHandle invoker) {
this.invoker = invoker;
@@ -87,15 +88,24 @@
this.constructor = constructor;
}
- CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData, final boolean isOptimistic) {
+ CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData, final int flags) {
this(invoker);
- if(isOptimistic) {
+ this.flags = flags;
+ if ((flags & FunctionNode.IS_OPTIMISTIC) != 0) {
optimismInfo = new OptimismInfo(functionData);
} else {
optimismInfo = null;
}
}
+ int getFlags() {
+ return flags;
+ }
+
+ boolean isVarArg() {
+ return isVarArgsType(invoker.type());
+ }
+
@Override
public String toString() {
return "<callSiteType=" + invoker.type() + " invoker=" + invoker + " ctor=" + constructor + " weight=" + weight() + ">";
@@ -127,13 +137,17 @@
* @return a direct constructor method handle for this function.
*/
MethodHandle getConstructor() {
- if(constructor == null) {
+ if (constructor == null) {
constructor = createConstructorFromInvoker(createInvokerForPessimisticCaller());
}
return constructor;
}
+ MethodHandle getInvoker() {
+ return invoker;
+ }
+
/**
* Creates a version of the invoker intended for a pessimistic caller (return type is Object, no caller optimistic
* program point available).
@@ -248,7 +262,7 @@
return weight;
}
- private static boolean isVarArgsType(final MethodType type) {
+ static boolean isVarArgsType(final MethodType type) {
assert type.parameterCount() >= 1 : type;
return type.parameterType(type.parameterCount() - 1) == Object[].class;
}
@@ -405,20 +419,20 @@
return paramTypes[i];
}
assert isVarArg;
- return ((ArrayType)(paramTypes[paramTypes.length - 1])).getElementType();
+ return ((ArrayType)paramTypes[paramTypes.length - 1]).getElementType();
}
- boolean matchesCallSite(final MethodType callSiteType) {
- if(!ScriptEnvironment.globalOptimistic()) {
+ boolean matchesCallSite(final MethodType callSiteType, final boolean pickVarArg) {
+ if (!ScriptEnvironment.globalOptimistic()) {
// Without optimistic recompilation, always choose the first eagerly compiled version.
return true;
}
- final MethodType type = type();
+ final MethodType type = type();
final int fnParamCount = getParamCount(type);
final boolean isVarArg = fnParamCount == Integer.MAX_VALUE;
- if(isVarArg) {
- return true;
+ if (isVarArg) {
+ return pickVarArg;
}
final int csParamCount = getParamCount(callSiteType);
@@ -618,7 +632,7 @@
final Type previousFailedType = invalidatedProgramPoints.put(e.getProgramPoint(), retType);
if (previousFailedType != null && !previousFailedType.narrowerThan(retType)) {
final StackTraceElement[] stack = e.getStackTrace();
- final String functionId = stack.length == 0 ? data.getName() : (stack[0].getClassName() + "." + stack[0].getMethodName());
+ final String functionId = stack.length == 0 ? data.getName() : stack[0].getClassName() + "." + stack[0].getMethodName();
LOG.info("RewriteException for an already invalidated program point ", e.getProgramPoint(), " in ", functionId, ". This is okay for a recursive function invocation, but a bug otherwise.");
return null;
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java Fri Apr 11 16:52:14 2014 +0200
@@ -40,11 +40,11 @@
this.name = name;
}
- void add(CompiledFunction f) {
+ void add(final CompiledFunction f) {
functions.add(f);
}
- void addAll(CompiledFunctions fs) {
+ void addAll(final CompiledFunctions fs) {
functions.addAll(fs.functions);
}
@@ -61,6 +61,15 @@
return '\'' + name + "' code=" + functions;
}
+ private CompiledFunction pick(final MethodType callSiteType, final boolean canPickVarArg) {
+ for (final CompiledFunction candidate : functions) {
+ if (candidate.matchesCallSite(callSiteType, false)) {
+ return candidate;
+ }
+ }
+ return null;
+ }
+
/**
* Returns the compiled function best matching the requested call site method type
* @param callSiteType
@@ -73,12 +82,11 @@
assert callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class) : callSiteType; // Callee must be assignable from script function
if (recompilable) {
- for (final CompiledFunction candidate: functions) {
- if(candidate.matchesCallSite(callSiteType)) {
- return candidate;
- }
+ final CompiledFunction candidate = pick(callSiteType, false);
+ if (candidate != null) {
+ return candidate;
}
- return null;
+ return pick(callSiteType, true); //try vararg last
}
CompiledFunction best = null;
@@ -98,7 +106,7 @@
* @return true if the functions need a callee, false otherwise.
*/
boolean needsCallee() {
- boolean needsCallee = functions.getFirst().needsCallee();
+ final boolean needsCallee = functions.getFirst().needsCallee();
assert allNeedCallee(needsCallee);
return needsCallee;
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/GlobalConstants.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/GlobalConstants.java Fri Apr 11 16:52:14 2014 +0200
@@ -32,6 +32,7 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.SwitchPoint;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
@@ -333,7 +334,7 @@
final MethodHandle invalidator = MH.asType(INVALIDATE_SP, INVALIDATE_SP.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
final MethodHandle mh = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
- assert inv.getSwitchPoint() == null : inv.getSwitchPoint();
+ assert inv.getSwitchPoints() == null : Arrays.asList(inv.getSwitchPoints());
LOG.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Fri Apr 11 16:52:14 2014 +0200
@@ -137,12 +137,12 @@
}
@Override
- protected GuardedInvocation findNewMethod(CallSiteDescriptor desc) {
+ protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
return createClassNotFoundInvocation(desc);
}
@Override
- protected GuardedInvocation findCallMethod(CallSiteDescriptor desc, LinkRequest request) {
+ protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
return createClassNotFoundInvocation(desc);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/Property.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Property.java Fri Apr 11 16:52:14 2014 +0200
@@ -30,7 +30,9 @@
import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
import java.lang.invoke.MethodHandle;
+import java.lang.invoke.SwitchPoint;
import java.util.Objects;
+
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
/**
@@ -98,6 +100,8 @@
/** Property field number or spill slot. */
private final int slot;
+ protected SwitchPoint changeCallback;
+
/**
* Constructor
*
@@ -118,9 +122,10 @@
* @param property source property
*/
Property(final Property property, final int flags) {
- this.key = property.key;
- this.slot = property.slot;
- this.flags = flags;
+ this.key = property.key;
+ this.slot = property.slot;
+ this.changeCallback = property.changeCallback;
+ this.flags = flags;
}
/**
@@ -168,6 +173,10 @@
return propFlags;
}
+ public final void setChangeCallback(final SwitchPoint sp) {
+ this.changeCallback = sp;
+ }
+
/**
* Property flag utility method for {@link PropertyDescriptor}. Get the property flags
* conforming to any Property using this PropertyDescriptor
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Fri Apr 11 16:52:14 2014 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, 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
@@ -40,6 +40,7 @@
import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.CompilationEnvironment;
import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
+import jdk.nashorn.internal.codegen.ApplySpecialization;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
@@ -238,7 +239,7 @@
public String toStringVerbose() {
final StringBuilder sb = new StringBuilder();
- sb.append("fid=").append(functionNodeId).append(' ');
+ sb.append("fnId=").append(functionNodeId).append(' ');
if (source != null) {
sb.append(source.getName())
@@ -250,6 +251,11 @@
return sb.toString() + super.toString();
}
+ @Override
+ public String getFunctionName() {
+ return functionName;
+ }
+
private static String functionName(final FunctionNode fn) {
if (fn.isAnonymous()) {
return "";
@@ -323,7 +329,7 @@
functionNodeId - (isProgram ? 0 : 1),
lineNumber - 1); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive
- if(isAnonymous) {
+ if (isAnonymous) {
parser.setFunctionName(functionName);
}
final FunctionNode program = parser.parse(scriptName, descPosition, Token.descLength(token), true);
@@ -367,13 +373,19 @@
final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet() + "$restOf";
FunctionNode fn = reparse(scriptName);
+ final ApplyToCallTransform tr = new ApplyToCallTransform(fn, fnCallSiteType, true);
+ fn = tr.transform();
+ final int newArity = tr.arity;
+
final Compiler compiler = new Compiler(
new CompilationEnvironment(
CompilationPhases.EAGER.makeOptimistic(),
isStrict(),
this,
runtimeScope,
- isVariableArity() ? null : new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType)),
+ isVariableArity() && !tr.transformed ?
+ null :
+ new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType, newArity)),
invalidatedProgramPoints,
continuationEntryPoints,
true
@@ -381,11 +393,11 @@
installer);
fn = compiler.compile(scriptName, fn);
-
compiler.install(fn);
// look up the rest of method
- return lookupWithExplicitType(fn, MethodType.methodType(fn.getReturnType().getTypeClass(), RewriteException.class));
+ final MethodHandle mh = lookupWithExplicitType(fn, MethodType.methodType(fn.getReturnType().getTypeClass(), RewriteException.class));
+ return mh;
}
private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
@@ -399,8 +411,13 @@
if (LOG.isEnabled()) {
LOG.info(reason, " of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
}
+
FunctionNode fn = reparse(scriptName);
+ final ApplyToCallTransform tr = new ApplyToCallTransform(fn, actualCallSiteType, false);
+ fn = tr.transform();
+ final int newArity = tr.arity;
+
final CompilationPhases phases = CompilationPhases.EAGER;
final Compiler compiler = new Compiler(
new CompilationEnvironment(
@@ -408,11 +425,11 @@
isStrict(),
this,
runtimeScope,
- fnCallSiteType == null || isVariableArity() ?
+ fnCallSiteType == null || isVariableArity() && !tr.transformed ?
null :
new ParamTypeMap(
functionNodeId,
- explicitParams(fnCallSiteType)),
+ explicitParams(fnCallSiteType, newArity)),
invalidatedProgramPoints,
true),
installer);
@@ -424,10 +441,7 @@
return fn;
}
- private MethodType explicitParams(final MethodType callSiteType) {
- assert !isVariableArity(); // Should not be invoked for varargs
- final int arity = getArity();
-
+ private static MethodType explicitParams(final MethodType callSiteType, final int arity) {
final MethodType noCalleeThisType = callSiteType.dropParameterTypes(0, 2); // (callee, this) is always in call site type
final int callSiteParamCount = noCalleeThisType.parameterCount();
@@ -436,9 +450,9 @@
final int minParams = Math.min(callSiteParamCount, arity);
final Class<?>[] paramTypes = noCalleeThisType.parameterArray();
boolean changed = false;
- for(int i = 0; i < minParams; ++i) {
+ for (int i = 0; i < minParams; ++i) {
final Class<?> paramType = paramTypes[i];
- if(!(paramType.isPrimitive() || paramType == Object.class)) {
+ if (!(paramType.isPrimitive() || paramType == Object.class)) {
paramTypes[i] = Object.class;
changed = true;
}
@@ -446,9 +460,9 @@
final MethodType generalized = changed ? MethodType.methodType(noCalleeThisType.returnType(), paramTypes) : noCalleeThisType;
// Match arity
- if(callSiteParamCount < arity) {
+ if (callSiteParamCount < arity) {
return generalized.appendParameterTypes(Collections.<Class<?>>nCopies(arity - callSiteParamCount, Object.class));
- } else if(callSiteParamCount > arity) {
+ } else if (callSiteParamCount > arity) {
return generalized.dropParameterTypes(arity, callSiteParamCount);
} else {
return generalized;
@@ -467,7 +481,7 @@
assert fns.size() == 1 : "got back more than one method in recompilation";
final FunctionNode f = fns.iterator().next();
assert f.getId() == functionNodeId;
- if(!isDeclared && f.isDeclared()) {
+ if (!isDeclared && f.isDeclared()) {
return f.clearFlag(null, FunctionNode.IS_DECLARED);
}
return f;
@@ -499,14 +513,14 @@
addCode(functionNode);
}
- private CompiledFunction addCode(final MethodHandle target, final boolean isOptimistic) {
- final CompiledFunction cfn = new CompiledFunction(target, this, isOptimistic);
+ private CompiledFunction addCode(final MethodHandle target, final int fnFlags) {
+ final CompiledFunction cfn = new CompiledFunction(target, this, fnFlags);
code.add(cfn);
return cfn;
}
private CompiledFunction addCode(final FunctionNode fn) {
- return addCode(lookup(fn), fn.isOptimistic());
+ return addCode(lookup(fn), fn.getFlags());
}
/**
@@ -520,7 +534,7 @@
* @return the compiled function object, with its type matching that of the call site type.
*/
private CompiledFunction addCode(final FunctionNode fn, final MethodType callSiteType) {
- if(fn.isVarArg()) {
+ if (fn.isVarArg()) {
return addCode(fn);
}
@@ -549,15 +563,31 @@
toType = toType.dropParameterTypes(fromCount, toCount);
}
- return addCode(lookup(fn).asType(toType), fn.isOptimistic());
+ return addCode(lookup(fn).asType(toType), fn.getFlags());
}
@Override
CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
synchronized (code) {
final CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
- // TODO: what if callSiteType is vararg?
- return existingBest != null ? existingBest : addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
+ if (existingBest != null) {
+ /*
+ * If callsite type isn't vararg and our best is vararg, generate a specialization
+ * we DO have a generic version, which means that we know which ones of the applies
+ * were actual applies
+ */
+ if (existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType)) {
+ //System.err.println("Looking in code for best " + callSiteType + " " + existingBest + " code=" + code);
+ final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope);
+ if (fn.hasOptimisticApplyToCall()) { //did the specialization work
+ final CompiledFunction cf = addCode(fn, callSiteType);
+ assert !cf.isVarArg();
+ return cf;
+ }
+ }
+ return existingBest;
+ }
+ return addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
}
}
@@ -567,7 +597,7 @@
}
@Override
- boolean needsCallee() {
+ public boolean needsCallee() {
return needsCallee;
}
@@ -606,4 +636,73 @@
}
return null;
}
+
+ /**
+ * Get the uppermost parent, the program, for this data
+ * @return program
+ */
+ public RecompilableScriptFunctionData getProgram() {
+ RecompilableScriptFunctionData program = this;
+ while (true) {
+ final RecompilableScriptFunctionData p = program.getParent();
+ if (p == null) {
+ return program;
+ }
+ program = p;
+ }
+ }
+
+ /**
+ * Check whether a certain name is a global symbol, i.e. only exists as defined
+ * in outermost scope and not shadowed by being parameter or assignment in inner
+ * scopes
+ *
+ * @param functionNode function node to check
+ * @param symbolName symbol name
+ * @return true if global symbol
+ */
+ public boolean isGlobalSymbol(final FunctionNode functionNode, final String symbolName) {
+ RecompilableScriptFunctionData data = getScriptFunctionData(functionNode.getId());
+ assert data != null;
+ final RecompilableScriptFunctionData program = getProgram();
+
+ while (data != program) {
+ if (data.hasInternalSymbol(symbolName)) {
+ return false;
+ }
+ data = data.getParent();
+ }
+
+ return true;
+ }
+
+ /**
+ * Helper class for transforming apply calls to calls
+ */
+ private class ApplyToCallTransform {
+ private final MethodType actualCallSiteType;
+ private final boolean isRestOf;
+ private int arity;
+ private FunctionNode functionNode;
+ private boolean transformed;
+
+ ApplyToCallTransform(final FunctionNode functionNode, final MethodType actualCallSiteType, final boolean isRestOf) {
+ this.functionNode = functionNode;
+ this.actualCallSiteType = actualCallSiteType;
+ this.arity = getArity();
+ this.isRestOf = isRestOf;
+ }
+
+ FunctionNode transform() {
+ if (isVariableArity()) {
+ final ApplySpecialization spec = new ApplySpecialization(RecompilableScriptFunctionData.this, functionNode, actualCallSiteType, isRestOf);
+ if (spec.transform()) {
+ functionNode = spec.getFunctionNode();
+ arity = functionNode.getParameters().size();
+ transformed = true;
+ }
+ }
+ return functionNode;
+ }
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Fri Apr 11 16:52:14 2014 +0200
@@ -294,9 +294,6 @@
if (kv.hasValue("objects")) {
callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_VALUES;
}
- if (kv.hasValue("scope")) {
- callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_SCOPE;
- }
}
this._callsite_flags = callSiteFlags;
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Fri Apr 11 16:52:14 2014 +0200
@@ -34,11 +34,14 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.lang.invoke.SwitchPoint;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.support.Guards;
+import jdk.nashorn.internal.codegen.ApplySpecialization;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
+import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.objects.NativeFunction;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
@@ -324,17 +327,6 @@
public abstract ScriptFunction makeSynchronizedFunction(Object sync);
/**
- * Return the most appropriate invocation for the specified call site type. If specializations are possible, it will
- * strive to return an efficient specialization.
- * @param callSiteType the call site type; can be as specific as needed.
- * @return a guarded invocation with invoke method handle and potentially a switch point guarding optimistic
- * assumptions.
- */
- private GuardedInvocation getBestInvoker(final MethodType callSiteType, final int callerProgramPoint) {
- return data.getBestInvoker(callSiteType, callerProgramPoint, scope);
- }
-
- /**
* Return the invoke handle bound to a given ScriptObject self reference.
* If callee parameter is required result is rebound to this.
*
@@ -468,12 +460,13 @@
}
@Override
- protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
+ protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final MethodType type = desc.getMethodType();
assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
- final GuardedInvocation bestCtorInv = data.getBestConstructor(type, scope);
+ final CompiledFunction cf = data.getBestConstructor(type, scope);
+ final GuardedInvocation bestCtorInv = new GuardedInvocation(cf.getConstructor(), cf.getOptimisticAssumptionsSwitchPoint());
//TODO - ClassCastException
- return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this), bestCtorInv.getSwitchPoint());
+ return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this, cf.getFlags()), bestCtorInv.getSwitchPoints(), null);
}
@SuppressWarnings("unused")
@@ -512,6 +505,7 @@
final String name = getName();
final boolean isUnstable = request.isCallSiteUnstable();
final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc);
+
final boolean isCall = !scopeCall && data.isBuiltin() && "call".equals(name);
final boolean isApply = !scopeCall && data.isBuiltin() && "apply".equals(name);
@@ -519,7 +513,7 @@
//megamorphic - replace call with apply
final MethodHandle handle;
//ensure that the callsite is vararg so apply can consume it
- if(type.parameterCount() == 3 && type.parameterType(2) == Object[].class) {
+ if (type.parameterCount() == 3 && type.parameterType(2) == Object[].class) {
// Vararg call site
handle = ScriptRuntime.APPLY.methodHandle();
} else {
@@ -532,7 +526,7 @@
return new GuardedInvocation(
handle,
null,
- null,
+ (SwitchPoint)null,
ClassCastException.class);
}
@@ -548,7 +542,12 @@
} //else just fall through and link as ordinary function or unstable apply
final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT;
- final GuardedInvocation bestInvoker = getBestInvoker(type, programPoint);
+ final CompiledFunction cf = data.getBestInvoker(type, programPoint, scope);
+ final GuardedInvocation bestInvoker =
+ new GuardedInvocation(
+ cf.createInvoker(type.returnType(), programPoint),
+ cf.getOptimisticAssumptionsSwitchPoint());
+
final MethodHandle callHandle = bestInvoker.getInvocation();
if (data.needsCallee()) {
@@ -587,65 +586,90 @@
boundHandle = pairArguments(boundHandle, type);
- return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this) : guard, bestInvoker.getSwitchPoint());
+ return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this, cf.getFlags()) : guard, bestInvoker.getSwitchPoints(), null);
}
private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) {
final MethodType descType = desc.getMethodType();
final int paramCount = descType.parameterCount();
+
final boolean passesThis = paramCount > 2;
final boolean passesArgs = paramCount > 3;
+ final int realArgCount = passesArgs ? paramCount - 3 : 0;
final Object appliedFn = args[1];
final boolean appliedFnNeedsWrappedThis = needsWrappedThis(appliedFn);
+ //box call back to apply
+ CallSiteDescriptor appliedDesc = desc;
+ final SwitchPoint applyToCallSwitchPoint = Global.instance().getChangeCallback("apply");
+ //enough to change the proto switchPoint here
+
+ final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc);
+ final boolean isFailedApplyToCall = isApplyToCall && applyToCallSwitchPoint.hasBeenInvalidated();
+
// R(apply|call, ...) => R(...)
MethodType appliedType = descType.dropParameterTypes(0, 1);
- if(!passesThis) {
+ if (!passesThis) {
// R() => R(this)
appliedType = appliedType.insertParameterTypes(1, Object.class);
- } else if(appliedFnNeedsWrappedThis) {
+ } else if (appliedFnNeedsWrappedThis) {
appliedType = appliedType.changeParameterType(1, Object.class);
}
- if(isApply) {
- if(passesArgs) {
+
+ if (isApply || isFailedApplyToCall) {
+ if (passesArgs) {
// R(this, args) => R(this, Object[])
appliedType = appliedType.changeParameterType(2, Object[].class);
+ // drop any extraneous arguments for the apply fail case
+ if (isFailedApplyToCall) {
+ appliedType = appliedType.dropParameterTypes(3, paramCount - 1);
+ }
} else {
// R(this) => R(this, Object[])
appliedType = appliedType.insertParameterTypes(2, Object[].class);
}
}
- final CallSiteDescriptor appliedDesc = desc.changeMethodType(appliedType);
+
+ appliedDesc = appliedDesc.changeMethodType(appliedType);
// Create the same arguments for the delegate linking request that would be passed in an actual apply'd invocation
final Object[] appliedArgs = new Object[isApply ? 3 : appliedType.parameterCount()];
appliedArgs[0] = appliedFn;
appliedArgs[1] = passesThis ? appliedFnNeedsWrappedThis ? ScriptFunctionData.wrapThis(args[2]) : args[2] : ScriptRuntime.UNDEFINED;
- if(isApply) {
+ if (isApply && !isFailedApplyToCall) {
appliedArgs[2] = passesArgs ? NativeFunction.toApplyArgs(args[3]) : ScriptRuntime.EMPTY_ARRAY;
} else {
- if(passesArgs) {
- System.arraycopy(args, 3, appliedArgs, 2, args.length - 3);
+ if (passesArgs) {
+ if (isFailedApplyToCall) {
+ final Object[] tmp = new Object[args.length - 3];
+ System.arraycopy(args, 3, tmp, 0, tmp.length);
+ appliedArgs[2] = NativeFunction.toApplyArgs(tmp);
+ } else {
+ System.arraycopy(args, 3, appliedArgs, 2, args.length - 3);
+ }
+ } else if (isFailedApplyToCall) {
+ appliedArgs[2] = ScriptRuntime.EMPTY_ARRAY;
}
}
// Ask the linker machinery for an invocation of the target function
final LinkRequest appliedRequest = request.replaceArguments(appliedDesc, appliedArgs);
- final GuardedInvocation appliedInvocation;
+ GuardedInvocation appliedInvocation;
try {
appliedInvocation = Bootstrap.getLinkerServices().getGuardedInvocation(appliedRequest);
- } catch(final RuntimeException | Error e) {
+ } catch (final RuntimeException | Error e) {
throw e;
- } catch(final Exception e) {
+ } catch (final Exception e) {
throw new RuntimeException(e);
}
assert appliedRequest != null; // Bootstrap.isCallable() returned true for args[1], so it must produce a linkage.
final Class<?> applyFnType = descType.parameterType(0);
MethodHandle inv = appliedInvocation.getInvocation(); //method handle from apply invocation. the applied function invocation
- if(isApply) {
- if(passesArgs) {
+
+ if (isApply && !isFailedApplyToCall) {
+ if (passesArgs) {
// Make sure that the passed argArray is converted to Object[] the same way NativeFunction.apply() would do it.
inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS);
} else {
@@ -653,17 +677,29 @@
inv = MH.insertArguments(inv, 2, (Object)ScriptRuntime.EMPTY_ARRAY);
}
}
- if(!passesThis) {
+
+ if (isApplyToCall) {
+ if (isFailedApplyToCall) {
+ //take the real arguments that were passed to a call and force them into the apply instead
+ ApplySpecialization.getLogger().info("Collection arguments to revert call to apply in " + appliedFn);
+ inv = MH.asCollector(inv, Object[].class, realArgCount);
+ } else {
+ appliedInvocation = appliedInvocation.addSwitchPoint(applyToCallSwitchPoint);
+ }
+ }
+
+ if (!passesThis) {
// If the original call site doesn't pass in a thisArg, pass in Global/undefined as needed
inv = bindImplicitThis(appliedFn, inv);
- } else if(appliedFnNeedsWrappedThis) {
+ } else if (appliedFnNeedsWrappedThis) {
// target function needs a wrapped this, so make sure we filter for that
inv = MH.filterArguments(inv, 1, WRAP_THIS);
}
inv = MH.dropArguments(inv, 0, applyFnType);
+
MethodHandle guard = appliedInvocation.getGuard();
// If the guard checks the value of "this" but we aren't passing thisArg, insert the default one
- if(!passesThis && guard.type().parameterCount() > 1) {
+ if (!passesThis && guard.type().parameterCount() > 1) {
guard = bindImplicitThis(appliedFn, guard);
}
final MethodType guardType = guard.type();
@@ -725,11 +761,14 @@
*
* @return method handle for guard
*/
- private static MethodHandle getFunctionGuard(final ScriptFunction function) {
+ private static MethodHandle getFunctionGuard(final ScriptFunction function, final int flags) {
assert function.data != null;
// Built-in functions have a 1-1 correspondence to their ScriptFunctionData, so we can use a cheaper identity
// comparison for them.
- return function.data.isBuiltin() ? Guards.getIdentityGuard(function) : MH.insertArguments(IS_FUNCTION_MH, 1, function.data);
+ if (function.data.isBuiltin()) {
+ return Guards.getIdentityGuard(function);
+ }
+ return MH.insertArguments(IS_FUNCTION_MH, 1, function.data);
}
/**
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Fri Apr 11 16:52:14 2014 +0200
@@ -37,7 +37,7 @@
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
-import jdk.internal.dynalink.linker.GuardedInvocation;
+
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
/**
@@ -144,6 +144,15 @@
return (flags & IS_STRICT) != 0;
}
+ /**
+ * Return the complete internal function name for this
+ * data, not anonymous or similar. May be identical
+ * @return internal function name
+ */
+ protected String getFunctionName() {
+ return getName();
+ }
+
boolean isBuiltin() {
return (flags & IS_BUILTIN) != 0;
}
@@ -210,19 +219,19 @@
* @return guarded invocation with method handle to best invoker and potentially a switch point guarding optimistic
* assumptions.
*/
- final GuardedInvocation getBestInvoker(final MethodType callSiteType, final int callerProgramPoint, final ScriptObject runtimeScope) {
+ final CompiledFunction getBestInvoker(final MethodType callSiteType, final int callerProgramPoint, final ScriptObject runtimeScope) {
final CompiledFunction cf = getBest(callSiteType, runtimeScope);
assert cf != null;
- return new GuardedInvocation(cf.createInvoker(callSiteType.returnType(), callerProgramPoint), cf.getOptimisticAssumptionsSwitchPoint());
+ return cf;
}
- final GuardedInvocation getBestConstructor(final MethodType callSiteType, final ScriptObject runtimeScope) {
+ final CompiledFunction getBestConstructor(final MethodType callSiteType, final ScriptObject runtimeScope) {
if (!isConstructor()) {
throw typeError("not.a.constructor", toSource());
}
// Constructor call sites don't have a "this", but getBest is meant to operate on "callee, this, ..." style
final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class), runtimeScope);
- return new GuardedInvocation(cf.getConstructor(), cf.getOptimisticAssumptionsSwitchPoint());
+ return cf;
}
/**
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Fri Apr 11 16:52:14 2014 +0200
@@ -1766,7 +1766,7 @@
case "call":
return findCallMethod(desc, request);
case "new":
- return findNewMethod(desc);
+ return findNewMethod(desc, request);
case "callMethod":
return findCallMethodMethod(desc, request);
default:
@@ -1778,10 +1778,11 @@
* Find the appropriate New method for an invoke dynamic call.
*
* @param desc The invoke dynamic call site descriptor.
+ * @param request The link request
*
* @return GuardedInvocation to be invoked at call site.
*/
- protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
+ protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
return notAFunction();
}
@@ -1866,6 +1867,17 @@
return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0))));
}
+ //this will only return true if apply is still builtin
+ private static SwitchPoint checkReservedName(final CallSiteDescriptor desc, final LinkRequest request) {
+ final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc);
+ final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ if ("apply".equals(name) && isApplyToCall && Global.instance().isSpecialNameValid(name)) {
+ assert Global.instance().getChangeCallback("apply") == Global.instance().getChangeCallback("call");
+ return Global.instance().getChangeCallback("apply");
+ }
+ return null;
+ }
+
/**
* Find the appropriate GET method for an invoke dynamic call.
*
@@ -1877,7 +1889,15 @@
*/
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
- final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ final String name;
+ final SwitchPoint reservedNameSwitchPoint;
+
+ reservedNameSwitchPoint = checkReservedName(desc, request);
+ if (reservedNameSwitchPoint != null) {
+ name = "call"; //turn apply into call, it is the builtin apply and has been modified to explode args
+ } else {
+ name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ }
if (request.isCallSiteUnstable() || hasWithScope()) {
return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator), isScope() && NashornCallSiteDescriptor.isScope(desc));
@@ -1893,7 +1913,7 @@
explicitInstanceOfCheck ?
getScriptObjectGuard(desc.getMethodType(), explicitInstanceOfCheck) :
null,
- null,
+ (SwitchPoint)null,
explicitInstanceOfCheck ?
null : ClassCastException.class);
}
@@ -1910,9 +1930,9 @@
}
}
- final GuardedInvocation inv = GlobalConstants.instance().findGetMethod(find, this, desc, request, operator);
- if (inv != null) {
- return inv;
+ final GuardedInvocation cinv = GlobalConstants.instance().findGetMethod(find, this, desc, request, operator);
+ if (cinv != null) {
+ return cinv;
}
final Class<?> returnType = desc.getMethodType().returnType();
@@ -1928,25 +1948,26 @@
final ScriptObject owner = find.getOwner();
final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class;
+ final SwitchPoint protoSwitchPoint;
+
if (mh == null) {
mh = Lookup.emptyGetter(returnType);
- } else if (find.isSelf()) {
- return new GuardedInvocation(mh, guard, null, exception);
- } else {
+ protoSwitchPoint = getProtoSwitchPoint(name, owner);
+ } else if (!find.isSelf()) {
assert mh.type().returnType().equals(returnType) : "returntype mismatch for getter " + mh.type().returnType() + " != " + returnType;
if (!property.hasGetterFunction(owner)) {
// Add a filter that replaces the self object with the prototype owning the property.
mh = addProtoFilter(mh, find.getProtoChainLength());
}
+ protoSwitchPoint = getProtoSwitchPoint(name, owner);
+ } else {
+ protoSwitchPoint = null;
}
assert OBJECT_FIELDS_ONLY || guard != null : "we always need a map guard here";
- return new GuardedInvocation(
- mh,
- guard,
- getProtoSwitchPoint(name, owner),
- exception);
+ final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception);
+ return inv.addSwitchPoint(reservedNameSwitchPoint);
}
private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod, final boolean isScope) {
@@ -1994,7 +2015,7 @@
}
final MethodHandle mh = findGetIndexMethodHandle(returnClass, name, keyClass, desc);
- return new GuardedInvocation(mh, getScriptObjectGuard(callType, explicitInstanceOfCheck), null, explicitInstanceOfCheck ? null : ClassCastException.class);
+ return new GuardedInvocation(mh, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
}
private static MethodHandle getScriptObjectGuard(final MethodType type, final boolean explicitInstanceOfCheck) {
@@ -2091,7 +2112,7 @@
return new GuardedInvocation(
SETPROTOCHECK,
getScriptObjectGuard(desc.getMethodType(), explicitInstanceOfCheck),
- null,
+ (SwitchPoint)null,
explicitInstanceOfCheck ? null : ClassCastException.class);
} else if (!isExtensible()) {
return createEmptySetMethod(desc, explicitInstanceOfCheck, "object.non.extensible", false);
@@ -2179,7 +2200,7 @@
MethodHandle methodHandle = findOwnMH_V(clazz, "set", void.class, keyClass, valueClass, boolean.class);
methodHandle = MH.insertArguments(methodHandle, 3, isStrict);
- return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType, explicitInstanceOfCheck), null, explicitInstanceOfCheck ? null : ClassCastException.class);
+ return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
}
/**
@@ -2215,7 +2236,7 @@
0,
Object.class),
NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck),
- null,
+ (SwitchPoint)null,
explicitInstanceOfCheck ? null : ClassCastException.class);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Fri Apr 11 16:52:14 2014 +0200
@@ -30,6 +30,8 @@
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
import java.lang.invoke.MethodHandle;
+import java.lang.invoke.SwitchPoint;
+
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
@@ -111,7 +113,7 @@
// getGuard returns a map guard that casts its argument to ScriptObject, and if that fails, we need to
// relink on ClassCastException.
return new GuardedInvocation(methodHandle, NashornGuards.getGuard(sobj, property, desc, explicitInstanceOfCheck),
- null, explicitInstanceOfCheck ? null : ClassCastException.class);
+ (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/SpillProperty.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/SpillProperty.java Fri Apr 11 16:52:14 2014 +0200
@@ -64,13 +64,13 @@
this.ensureSpillSize = MH.asType(MH.insertArguments(ScriptObject.ENSURE_SPILL_SIZE, 1, slot), MH.type(Object.class, Object.class));
}
- private static void ensure(int slot) {
+ private static void ensure(final int slot) {
int len = ACCESSOR_CACHE.length;
if (slot >= len) {
do {
len *= 2;
} while (slot >= len);
- Accessors newCache[] = new Accessors[len];
+ final Accessors newCache[] = new Accessors[len];
System.arraycopy(ACCESSOR_CACHE, 0, newCache, 0, ACCESSOR_CACHE.length);
ACCESSOR_CACHE = newCache;
}
@@ -157,7 +157,6 @@
* @param flags the property flags
* @param slot spill slot
*/
- @SuppressWarnings("unused")
public SpillProperty(final String key, final int flags, final int slot) {
super(key, flags, slot, primitiveGetter(slot), primitiveSetter(slot), objectGetter(slot), objectSetter(slot));
assert !OBJECT_FIELDS_ONLY || getCurrentType() == Object.class;
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java Fri Apr 11 16:52:14 2014 +0200
@@ -229,7 +229,7 @@
if (getElement != null) {
getElement = MH.filterArguments(getElement, 0, MH.asType(getArray, getArray.type().changeReturnType(clazz)));
final MethodHandle guard = MH.insertArguments(FAST_ACCESS_GUARD, 0, clazz);
- return new GuardedInvocation(getElement, guard, null, ClassCastException.class);
+ return new GuardedInvocation(getElement, guard, (SwitchPoint)null, ClassCastException.class);
}
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Fri Apr 11 16:52:14 2014 +0200
@@ -94,7 +94,7 @@
factory.setSyncOnRelink(true);
factory.setPrelinkFilter(new GuardedInvocationFilter() {
@Override
- public GuardedInvocation filter(GuardedInvocation inv, LinkRequest request, LinkerServices linkerServices) {
+ public GuardedInvocation filter(final GuardedInvocation inv, final LinkRequest request, final LinkerServices linkerServices) {
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
return OptimisticReturnFilters.filterOptimisticReturnValue(inv, desc).asType(linkerServices, desc.getMethodType());
}
@@ -341,7 +341,7 @@
* @param boundThis the bound "this" value.
* @return a bound dynamic method.
*/
- public static Object bindDynamicMethod(Object dynamicMethod, Object boundThis) {
+ public static Object bindDynamicMethod(final Object dynamicMethod, final Object boundThis) {
return new BoundDynamicMethod(dynamicMethod, boundThis);
}
@@ -362,7 +362,7 @@
* @param clazz the class being tested
* @param isStatic is access checked for static members (or instance members)
*/
- public static void checkReflectionAccess(Class<?> clazz, boolean isStatic) {
+ public static void checkReflectionAccess(final Class<?> clazz, final boolean isStatic) {
ReflectionCheckLinker.checkReflectionAccess(clazz, isStatic);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Fri Apr 11 16:52:14 2014 +0200
@@ -30,6 +30,7 @@
import java.lang.invoke.MethodType;
import java.util.HashMap;
import java.util.Map;
+
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.GuardedTypeConversion;
@@ -71,7 +72,7 @@
final GuardedInvocation inv;
if (self instanceof JSObject) {
- inv = lookup(desc);
+ inv = lookup(desc, request);
} else {
throw new AssertionError(); // Should never reach here.
}
@@ -95,9 +96,10 @@
}
- private static GuardedInvocation lookup(final CallSiteDescriptor desc) {
+ private static GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
final int c = desc.getNameTokenCount();
+
switch (operator) {
case "getProp":
case "getElem":
@@ -116,7 +118,8 @@
}
private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc) {
- final MethodHandle getter = MH.insertArguments(JSOBJECT_GETMEMBER, 1, desc.getNameToken(2));
+ final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ final MethodHandle getter = MH.insertArguments(JSOBJECT_GETMEMBER, 1, name);
return new GuardedInvocation(getter, IS_JSOBJECT_GUARD);
}
@@ -135,8 +138,11 @@
private static GuardedInvocation findCallMethod(final CallSiteDescriptor desc) {
// TODO: if call site is already a vararg, don't do asCollector
- final MethodHandle func = MH.asCollector(JSOBJECT_CALL, Object[].class, desc.getMethodType().parameterCount() - 2);
- return new GuardedInvocation(func, IS_JSOBJECT_GUARD);
+ MethodHandle mh = JSOBJECT_CALL;
+ if (NashornCallSiteDescriptor.isApplyToCall(desc)) {
+ mh = MH.insertArguments(JSOBJECT_CALL_TO_APPLY, 0, JSOBJECT_CALL);
+ }
+ return new GuardedInvocation(MH.asCollector(mh, Object[].class, desc.getMethodType().parameterCount() - 2), IS_JSOBJECT_GUARD);
}
private static GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
@@ -199,6 +205,21 @@
return JSType.isRepresentableAsInt(value) ? (int)value : -1;
}
+ @SuppressWarnings("unused")
+ private static Object callToApply(final MethodHandle mh, final JSObject obj, final Object thiz, final Object... args) {
+ assert args.length >= 2;
+ final Object receiver = args[0];
+ final Object[] arguments = new Object[args.length - 1];
+ System.arraycopy(args, 1, arguments, 0, arguments.length);
+ try {
+ return mh.invokeExact(obj, thiz, new Object[] { receiver, arguments });
+ } catch (final RuntimeException | Error e) {
+ throw e;
+ } catch (final Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
// method handles of the current class
@@ -207,10 +228,11 @@
private static final MethodHandle JSOBJECTLINKER_PUT = findOwnMH_S("put", Void.TYPE, Object.class, Object.class, Object.class);
// method handles of JSObject class
- private static final MethodHandle JSOBJECT_GETMEMBER = findJSObjectMH_V("getMember", Object.class, String.class);
- private static final MethodHandle JSOBJECT_SETMEMBER = findJSObjectMH_V("setMember", Void.TYPE, String.class, Object.class);
- private static final MethodHandle JSOBJECT_CALL = findJSObjectMH_V("call", Object.class, Object.class, Object[].class);
- private static final MethodHandle JSOBJECT_NEW = findJSObjectMH_V("newObject", Object.class, Object[].class);
+ private static final MethodHandle JSOBJECT_GETMEMBER = findJSObjectMH_V("getMember", Object.class, String.class);
+ private static final MethodHandle JSOBJECT_SETMEMBER = findJSObjectMH_V("setMember", Void.TYPE, String.class, Object.class);
+ private static final MethodHandle JSOBJECT_CALL = findJSObjectMH_V("call", Object.class, Object.class, Object[].class);
+ private static final MethodHandle JSOBJECT_CALL_TO_APPLY = findOwnMH_S("callToApply", Object.class, MethodHandle.class, JSObject.class, Object.class, Object[].class);
+ private static final MethodHandle JSOBJECT_NEW = findJSObjectMH_V("newObject", Object.class, Object[].class);
private static final Map<Class<?>, MethodHandle> CONVERTERS = new HashMap<>();
static {
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java Fri Apr 11 16:52:14 2014 +0200
@@ -297,7 +297,6 @@
}
static class ProfileDumper implements Runnable {
- @SuppressWarnings("resource")
@Override
public void run() {
PrintWriter out = null;
@@ -442,7 +441,7 @@
final Object arg = args[i];
out.print(", ");
- if (getNashornDescriptor().isTraceScope() || !(arg instanceof ScriptObject && ((ScriptObject)arg).isScope())) {
+ if (!(arg instanceof ScriptObject && ((ScriptObject)arg).isScope())) {
printObject(out, arg);
} else {
out.print("SCOPE");
@@ -470,7 +469,7 @@
*
* @throws Throwable if invocation fails or throws exception/error
*/
- @SuppressWarnings({"unused", "resource"})
+ @SuppressWarnings("unused")
public Object traceObject(final MethodHandle mh, final Object... args) throws Throwable {
final PrintWriter out = Context.getCurrentErr();
tracePrint(out, "ENTER ", args, null);
@@ -488,7 +487,7 @@
*
* @throws Throwable if invocation fails or throws exception/error
*/
- @SuppressWarnings({"unused", "resource"})
+ @SuppressWarnings("unused")
public void traceVoid(final MethodHandle mh, final Object... args) throws Throwable {
final PrintWriter out = Context.getCurrentErr();
tracePrint(out, "ENTER ", args, null);
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Fri Apr 11 16:52:14 2014 +0200
@@ -44,37 +44,37 @@
public final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor {
/** Flags that the call site references a scope variable (it's an identifier reference or a var declaration, not a
* property access expression. */
- public static final int CALLSITE_SCOPE = 1 << 0;
+ public static final int CALLSITE_SCOPE = 1 << 0;
/** Flags that the call site is in code that uses ECMAScript strict mode. */
- public static final int CALLSITE_STRICT = 1 << 1;
+ public static final int CALLSITE_STRICT = 1 << 1;
/** Flags that a property getter or setter call site references a scope variable that is located at a known distance
* in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */
- public static final int CALLSITE_FAST_SCOPE = 1 << 2;
+ public static final int CALLSITE_FAST_SCOPE = 1 << 2;
/** Flags that a callsite type is optimistic, i.e. we might get back a wider return value than encoded in the
* descriptor, and in that case we have to throw an UnwarrantedOptimismException */
- public static final int CALLSITE_OPTIMISTIC = 1 << 3;
+ public static final int CALLSITE_OPTIMISTIC = 1 << 3;
+ /** Is this really an apply that we try to call as a call? */
+ public static final int CALLSITE_APPLY_TO_CALL = 1 << 4;
/** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit
* code where call sites have this flag set. */
- public static final int CALLSITE_PROFILE = 1 << 4;
+ public static final int CALLSITE_PROFILE = 1 << 5;
/** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where
* call sites have this flag set. */
- public static final int CALLSITE_TRACE = 1 << 5;
+ public static final int CALLSITE_TRACE = 1 << 6;
/** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword
* {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
- public static final int CALLSITE_TRACE_MISSES = 1 << 6;
+ public static final int CALLSITE_TRACE_MISSES = 1 << 7;
/** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword
* {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
- public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 7;
+ public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 8;
/** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts
* that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites
* have this flag set. */
- public static final int CALLSITE_TRACE_VALUES = 1 << 8;
- /** Ordinarily, when {@link #CALLSITE_TRACE_VALUES} is set, scope objects are not printed in the trace but instead
- * the word {@code "SCOPE"} is printed instead With this flag, scope objects are also printed. Contexts that have
- * the keyword {@code "scope"} in their {@code "trace.callsites"} property emit code where call sites have this flag
- * set. */
- public static final int CALLSITE_TRACE_SCOPE = 1 << 9;
+ public static final int CALLSITE_TRACE_VALUES = 1 << 9;
+
+ //we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious
+ //right now given the program points
/**
* Number of bits the program point is shifted to the left in the flags (lowest bit containing a program point).
@@ -125,6 +125,9 @@
sb.append("scope ");
}
}
+ if ((flags & CALLSITE_APPLY_TO_CALL) != 0) {
+ sb.append("apply2call ");
+ }
if ((flags & CALLSITE_STRICT) != 0) {
sb.append("strict ");
}
@@ -308,6 +311,16 @@
}
/**
+ * Returns true if this is an apply call that we try to call as
+ * a "call"
+ * @param desc descriptor
+ * @return true if apply to call
+ */
+ public static boolean isApplyToCall(final CallSiteDescriptor desc) {
+ return isFlag(desc, CALLSITE_APPLY_TO_CALL);
+ }
+
+ /**
* Is this an optimistic call site
* @param desc descriptor
* @return true if optimistic
@@ -346,10 +359,6 @@
return isFlag(CALLSITE_TRACE_VALUES);
}
- boolean isTraceScope() {
- return isFlag(CALLSITE_TRACE_SCOPE);
- }
-
boolean isOptimistic() {
return isFlag(CALLSITE_OPTIMISTIC);
}
@@ -358,4 +367,5 @@
public CallSiteDescriptor changeMethodType(final MethodType newMethodType) {
return get(getLookup(), operator, operand, newMethodType, flags);
}
+
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java Fri Apr 11 16:52:14 2014 +0200
@@ -63,7 +63,7 @@
final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory, ConversionComparator {
private static final ClassValue<MethodHandle> ARRAY_CONVERTERS = new ClassValue<MethodHandle>() {
@Override
- protected MethodHandle computeValue(Class<?> type) {
+ protected MethodHandle computeValue(final Class<?> type) {
return createArrayConverter(type);
}
};
@@ -212,7 +212,7 @@
return MH.asType(converter, converter.type().changeReturnType(type));
}
- private static GuardedInvocation getMirrorConverter(Class<?> sourceType, Class<?> targetType) {
+ private static GuardedInvocation getMirrorConverter(final Class<?> sourceType, final Class<?> targetType) {
// Could've also used (targetType.isAssignableFrom(ScriptObjectMirror.class) && targetType != Object.class) but
// it's probably better to explicitly spell out the supported target types
if (targetType == Map.class || targetType == Bindings.class || targetType == JSObject.class || targetType == ScriptObjectMirror.class) {
@@ -274,7 +274,7 @@
return Comparison.INDETERMINATE;
}
- private static boolean isList(Class<?> clazz) {
+ private static boolean isList(final Class<?> clazz) {
return clazz == List.class || clazz == Deque.class;
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Fri Apr 11 16:52:14 2014 +0200
@@ -126,7 +126,7 @@
assert receiverType.isAssignableFrom(wrapType.returnType());
method = MH.filterArguments(method, 0, MH.asType(wrapFilter, wrapType.changeReturnType(receiverType)));
}
- return new GuardedInvocation(method, guard, link.getSwitchPoint());
+ return new GuardedInvocation(method, guard, link.getSwitchPoints(), null);
}
return null;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/examples/apply_to_call_benchmark.js Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,37 @@
+var Class = {
+ create: function() {
+ return function() { //vararg
+ this.initialize.apply(this, arguments);
+ }
+ }
+};
+
+Color = Class.create();
+Color.prototype = {
+ red: 0, green: 0, blue: 0,
+ initialize: function(r,g,b) {
+ this.red = r;
+ this.green = g;
+ this.blue = b;
+ }
+}
+
+function bench(x) {
+ var d = new Date;
+ var colors = new Array(16);
+ for (var i=0;i<1e8;i++) {
+ colors[i&0xf] = (new Color(1,2,3));
+ }
+ print(new Date - d);
+ return colors;
+}
+bench(17);
+
+print("Swapping out call");
+Function.prototype.call = function() {
+ throw "This should not happen, apply should be called instead";
+};
+
+bench(17);
+
+print("All done!");
--- a/nashorn/test/script/basic/JDK-8016618.js Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/test/script/basic/JDK-8016618.js Fri Apr 11 16:52:14 2014 +0200
@@ -61,11 +61,23 @@
function f() {
// mirror function called with local arguments
+ // global.func.apply(obj, arguments);
global.func.apply(obj, arguments);
}
f(23, "hello");
+f(24, "hello2");
+
+var oldCall = Function.prototype.call;
+Function.prototype.call = function() {
+ throw "this should never happen! go back to apply!";
+};
+
+f(25, "hello3");
+
+Function.prototype.call = oldCall;
+
var fObject = global.eval("Object");
// instanceof on mirrors
--- a/nashorn/test/script/basic/JDK-8016618.js.EXPECTED Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/test/script/basic/JDK-8016618.js.EXPECTED Fri Apr 11 16:52:14 2014 +0200
@@ -1,6 +1,10 @@
x = 33
func.x = 23
func.x = hello
+func.x = 24
+func.x = hello2
+func.x = 25
+func.x = hello3
global instanceof fObject? true
x is wriable ? true
x value = 33
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call1.js Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+/**
+ * apply_to_call1.js - do one apply to call specialization, then override, apply and make sure it reverts (i.e. stops
+ * calling call)
+ *
+ * @test
+ * @run
+ */
+
+print("start");
+
+var x = {
+ a : 0,
+ b : 0,
+ c : 0,
+ initialize : function(x,y,z) {
+ this.a = x;
+ this.b = y;
+ this.c = z;
+ }
+};
+
+function test() {
+ x.initialize.apply(x, arguments);
+}
+
+test(4711,23,17);
+print(x.a);
+print(x.b);
+print(x.c);
+
+print("Overwriting apply now");
+
+x.initialize = {
+ apply : function() {
+ for (var i=0;i<arguments.length;i++) {
+ print("I am not who you think " + arguments[i]);
+ }
+ x.a = arguments[1][0];
+ }
+};
+
+test(4712);
+print(x.a);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call1.js.EXPECTED Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,8 @@
+start
+4711
+23
+17
+Overwriting apply now
+I am not who you think [object Object]
+I am not who you think [object Arguments]
+4712
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call2.js Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+/**
+ * apply_to_call2.js - do one apply to call specialization, then override call and make sure it reverts (i.e. stops
+ * calling call)
+ *
+ * @test
+ * @run
+ */
+
+print("start");
+
+var x = {
+ a : 0,
+ b : 0,
+ c : 0,
+ initialize : function(x,y,z) {
+ this.a = x;
+ this.b = y;
+ this.c = z;
+ }
+};
+
+function test() {
+ x.initialize.apply(x, arguments);
+}
+
+test(4711,23,17);
+print(x.a);
+print(x.b);
+print(x.c);
+
+print("Overwriting call now");
+
+Function.prototype.call = function() {
+ throw "this should never be thrown - test should revert to builtin apply";
+};
+
+test(1,2,3);
+print(x.a);
+print(x.b);
+print(x.c);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call2.js.EXPECTED Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,8 @@
+start
+4711
+23
+17
+Overwriting call now
+1
+2
+3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call3.js Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+/**
+ * apply_to_call3.js - do one apply to call specialization, then override, apply and make sure it reverts (i.e. stops
+ * calling call). Then apply should break and throw exception
+ *
+ * @test
+ * @run
+ */
+
+print("start");
+
+var x = {
+ a : 0,
+ b : 0,
+ c : 0,
+ initialize : function(x,y,z) {
+ this.a = x;
+ this.b = y;
+ this.c = z;
+ }
+};
+
+function test() {
+ x.initialize.apply(x, arguments);
+}
+
+test(4711,23,17);
+print(x.a);
+print(x.b);
+print(x.c);
+
+print("Overwriting apply now");
+
+Function.prototype.apply = function() {
+ throw "This should be thrown, as the test call transform is no longer valid";
+};
+
+try {
+ test(1,2,3);
+} catch (e) {
+ print(e);
+}
+print(x.a);
+print(x.b);
+print(x.c);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call3.js.EXPECTED Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,9 @@
+start
+4711
+23
+17
+Overwriting apply now
+This should be thrown, as the test call transform is no longer valid
+4711
+23
+17
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call4.js Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+/**
+ * apply_to_call4.js - various escaping args patterns that prevent the optimization from being applied
+ * calling call)
+ *
+ * @test
+ * @run
+ */
+
+print("start");
+
+var x = {
+ a : 0,
+ b : 0,
+ c : 0,
+ initialize : function(x,y,z) {
+ this.a = x;
+ this.b = y;
+ this.c = z;
+ }
+};
+
+function f(x) {
+ print("this is a black hole - arguments escape");
+}
+
+function test() {
+ f(arguments);
+ x.initialize.apply(x, arguments);
+}
+
+test(4711,23,17);
+print(x.a);
+print(x.b);
+print(x.c);
+
+function test2() {
+ arguments[0] = 17;
+ x.initialize.apply(x, arguments);
+}
+
+test2(1,2,3);
+print(x.a);
+print(x.b);
+print(x.c);
+
+function test3() {
+ var escape = arguments[0];
+ f(escape);
+ x.initialize.apply(x, arguments);
+}
+
+test3("alpha", "beta", "gamma");
+print(x.a);
+print(x.b);
+print(x.c);
+
+function test4() {
+ var escape = arguments.length;
+ f(escape);
+ x.initialize.apply(x, arguments);
+}
+
+test4(1.2, 2.3, 3.4);
+print(x.a);
+print(x.b);
+print(x.c);
+
+function test5() {
+ x.initialize.apply(x, arguments, 17);
+}
+
+print("test 5 done");
+test5(11, 22);
+print("a="+typeof(x.a));
+print(x.b);
+print(x.c);
+
+print("Now it's time for transforms");
+
+function test6() {
+ x.initialize.apply(x, arguments);
+}
+
+test6(19, 20, 21);
+print(x.a);
+print(x.b);
+print(x.c);
+
+function test7() {
+ x.initialize.apply(x, arguments);
+}
+
+test7(1, 2.2, 17, 18);
+print(x.a);
+print(x.b);
+print(x.c);
+
+print("Should have transformed");
+
+function test8() {
+ var apply = f;
+ x.initialize.apply(x, arguments);
+}
+
+test8(7,8,9);
+print(x.a);
+print(x.b);
+print(x.c);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call4.js.EXPECTED Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,31 @@
+start
+this is a black hole - arguments escape
+4711
+23
+17
+17
+2
+3
+this is a black hole - arguments escape
+alpha
+beta
+gamma
+this is a black hole - arguments escape
+1.2
+2.3
+3.4
+test 5 done
+a=object
+17
+undefined
+Now it's time for transforms
+19
+20
+21
+1
+2.2
+17
+Should have transformed
+7
+8
+9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call_bench.js Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+/**
+ * sanity check that apply to call specialization is faster than apply.
+ *
+ * @test
+ * @run
+ */
+
+var Class = {
+ create: function() {
+ return function() { //vararg
+ this.initialize.apply(this, arguments);
+ }
+ }
+};
+
+Color = Class.create();
+Color.prototype = {
+ red: 0, green: 0, blue: 0,
+ initialize: function(r,g,b) {
+ this.red = r;
+ this.green = g;
+ this.blue = b;
+ }
+};
+
+var time1 = 0;
+var time2 = 0;
+
+function set_time1(t) {
+ time1 = t;
+}
+
+function set_time2(t) {
+ time2 = t;
+}
+
+function bench(x, set_time) {
+ var d = new Date;
+ var colors = new Array(16);
+ for (var i=0;i<1e8;i++) {
+ colors[i & 0xf] = new Color(1,2,3);
+ }
+ var t = new Date - d;
+ set_time(t);
+ return colors;
+}
+
+//warmup
+print("Running warmup");
+bench(17, set_time1);
+
+print("Running sharp run");
+bench(17, set_time1);
+
+print("Swapping out call");
+Function.prototype.call = function() {
+ throw "This should not happen, apply should be called instead";
+};
+
+print("Rerunning invalidated");
+bench(17, set_time2);
+
+print("All done!");
+
+if (time1 > time2) {
+ print("ERROR: time1 > time2 (" + time1 + " > " + time2 + ")");
+} else {
+ print("Times OK");
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call_bench.js.EXPECTED Fri Apr 11 16:52:14 2014 +0200
@@ -0,0 +1,6 @@
+Running warmup
+Running sharp run
+Swapping out call
+Rerunning invalidated
+All done!
+Times OK
--- a/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java Fri Apr 11 16:52:14 2014 +0200
@@ -42,7 +42,7 @@
*/
public class ScriptEngineSecurityTest {
- private void log(String msg) {
+ private void log(final String msg) {
org.testng.Reporter.log(msg, true);
}
@@ -185,8 +185,8 @@
// put an empty script object into array
e.eval("holder[0] = {}");
// holder[0] is an object of some subclass of ScriptObject
- Class ScriptObjectClass = holder[0].getClass().getSuperclass();
- Class PropertyAccessClass = ScriptObjectClass.getInterfaces()[0];
+ final Class<?> ScriptObjectClass = holder[0].getClass().getSuperclass();
+ final Class<?> PropertyAccessClass = ScriptObjectClass.getInterfaces()[0];
// implementation methods for PropertyAccess class
e.eval("function set() {}; function get() {}; function getInt(){} " +
"function getDouble(){}; function getLong() {}; " +
@@ -206,11 +206,11 @@
// @bug 8032948: Nashorn linkages awry
public static class FakeProxy extends Proxy {
- public FakeProxy(InvocationHandler ih) {
+ public FakeProxy(final InvocationHandler ih) {
super(ih);
}
- public static Class<?> makeProxyClass(ClassLoader cl, Class<?>... ifaces) {
+ public static Class<?> makeProxyClass(final ClassLoader cl, final Class<?>... ifaces) {
return Proxy.getProxyClass(cl, ifaces);
}
}
@@ -229,11 +229,11 @@
e.put("cl", ScriptEngineSecurityTest.class.getClassLoader());
e.put("intfs", new Class[] { Runnable.class });
- String getClass = "Java.type(name + '$FakeProxy').getProxyClass(cl, intfs);";
+ final String getClass = "Java.type(name + '$FakeProxy').getProxyClass(cl, intfs);";
// Should not be able to call static methods of Proxy via fake subclass
try {
- Class c = (Class)e.eval(getClass);
+ final Class<?> c = (Class<?>)e.eval(getClass);
fail("should have thrown SecurityException");
} catch (final Exception exp) {
if (! (exp instanceof SecurityException)) {
@@ -256,11 +256,11 @@
e.put("cl", ScriptEngineSecurityTest.class.getClassLoader());
e.put("intfs", new Class[] { Runnable.class });
- String getClass = "Java.type(name + '$FakeProxy').makeProxyClass(cl, intfs);";
+ final String getClass = "Java.type(name + '$FakeProxy').makeProxyClass(cl, intfs);";
// Should not be able to call static methods of Proxy via fake subclass
try {
- Class c = (Class)e.eval(getClass);
+ final Class<?> c = (Class<?>)e.eval(getClass);
fail("should have thrown SecurityException");
} catch (final Exception exp) {
if (! (exp instanceof SecurityException)) {
@@ -278,7 +278,7 @@
new Class[] { Runnable.class },
new InvocationHandler() {
@Override
- public Object invoke(Object p, Method m, Object[] a) {
+ public Object invoke(final Object p, final Method m, final Object[] a) {
return null;
}
});
--- a/nashorn/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java Wed Apr 02 10:52:39 2014 +0200
+++ b/nashorn/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java Fri Apr 11 16:52:14 2014 +0200
@@ -34,7 +34,6 @@
import javax.script.ScriptEngineManager;
import javax.script.ScriptContext;
import javax.script.ScriptException;
-import javax.script.SimpleBindings;
import javax.script.SimpleScriptContext;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import org.testng.annotations.Test;
@@ -55,7 +54,7 @@
private final boolean[] reached = new boolean[1];
@Override
- protected Class findClass(final String name) throws ClassNotFoundException {
+ protected Class<?> findClass(final String name) throws ClassNotFoundException {
// flag that it reached here
reached[0] = true;
return super.findClass(name);
@@ -72,7 +71,7 @@
@Test
public void factoryClassLoaderTest() {
final ScriptEngineManager sm = new ScriptEngineManager();
- for (ScriptEngineFactory fac : sm.getEngineFactories()) {
+ for (final ScriptEngineFactory fac : sm.getEngineFactories()) {
if (fac instanceof NashornScriptEngineFactory) {
final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac;
final MyClassLoader loader = new MyClassLoader();
@@ -96,7 +95,7 @@
@Test
public void factoryClassLoaderAndOptionsTest() {
final ScriptEngineManager sm = new ScriptEngineManager();
- for (ScriptEngineFactory fac : sm.getEngineFactories()) {
+ for (final ScriptEngineFactory fac : sm.getEngineFactories()) {
if (fac instanceof NashornScriptEngineFactory) {
final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac;
final String[] options = new String[] { "-strict" };
@@ -130,7 +129,7 @@
@Test
public void factoryOptionsTest() {
final ScriptEngineManager sm = new ScriptEngineManager();
- for (ScriptEngineFactory fac : sm.getEngineFactories()) {
+ for (final ScriptEngineFactory fac : sm.getEngineFactories()) {
if (fac instanceof NashornScriptEngineFactory) {
final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac;
// specify --no-syntax-extensions flag
@@ -156,7 +155,7 @@
*/
public void noLoaderPerCompilerTest() {
final ScriptEngineManager sm = new ScriptEngineManager();
- for (ScriptEngineFactory fac : sm.getEngineFactories()) {
+ for (final ScriptEngineFactory fac : sm.getEngineFactories()) {
if (fac instanceof NashornScriptEngineFactory) {
final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac;
final String[] options = new String[] { "--loader-per-compile=false" };
@@ -181,7 +180,7 @@
*/
public void noLoaderPerCompilerWithSameNameTest() {
final ScriptEngineManager sm = new ScriptEngineManager();
- for (ScriptEngineFactory fac : sm.getEngineFactories()) {
+ for (final ScriptEngineFactory fac : sm.getEngineFactories()) {
if (fac instanceof NashornScriptEngineFactory) {
final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac;
final String[] options = new String[] { "--loader-per-compile=false" };