This commit is contained in:
Athijegannathan Sundararajan 2013-10-15 22:13:56 +05:30
commit 1f8726f7e6
19 changed files with 980 additions and 173 deletions

View File

@ -91,6 +91,7 @@ import java.util.StringTokenizer;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.support.Guards;
import jdk.internal.dynalink.support.Lookup;
/**
* Base class for dynamic methods that dispatch to a single target Java method or constructor. Handles adaptation of the
@ -100,6 +101,9 @@ import jdk.internal.dynalink.support.Guards;
* @version $Id: $
*/
abstract class SingleDynamicMethod extends DynamicMethod {
private static final MethodHandle CAN_CONVERT_TO = Lookup.findOwnStatic(MethodHandles.lookup(), "canConvertTo", boolean.class, LinkerServices.class, Class.class, Object.class);
SingleDynamicMethod(String name) {
super(name);
}
@ -201,23 +205,69 @@ abstract class SingleDynamicMethod extends DynamicMethod {
return createConvertingInvocation(target, linkerServices, callSiteType).asVarargsCollector(
callSiteLastArgType);
}
if(!linkerServices.canConvert(callSiteLastArgType, varArgType)) {
// Call site signature guarantees the argument can definitely not be an array (i.e. it is primitive);
// link immediately to a vararg-packing method handle.
return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType);
// This method handle takes the single argument and packs it into a newly allocated single-element array. It
// will be used when the incoming argument can't be converted to the vararg array type (the "vararg packer"
// method).
final MethodHandle varArgCollectingInvocation = createConvertingInvocation(collectArguments(fixTarget,
argsLen), linkerServices, callSiteType);
// Is call site type assignable from an array type (e.g. Object:int[], or Object[]:String[])
final boolean isAssignableFromArray = callSiteLastArgType.isAssignableFrom(varArgType);
// Do we have a custom conversion that can potentially convert the call site type to an array?
final boolean isCustomConvertible = linkerServices.canConvert(callSiteLastArgType, varArgType);
if(!isAssignableFromArray && !isCustomConvertible) {
// Call site signature guarantees the argument can definitely not be converted to an array (i.e. it is
// primitive), and no conversion can help with it either. Link immediately to a vararg-packing method
// handle.
return varArgCollectingInvocation;
}
// Call site signature makes no guarantees that the single argument in the vararg position will be
// compatible across all invocations. Need to insert an appropriate guard and fall back to generic vararg
// method when it is not.
return MethodHandles.guardWithTest(Guards.isInstance(varArgType, fixParamsLen, callSiteType),
createConvertingInvocation(fixTarget, linkerServices, callSiteType),
createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType));
// This method handle employs language-specific conversions to convert the last argument into an array of
// vararg type.
final MethodHandle arrayConvertingInvocation = createConvertingInvocation(MethodHandles.filterArguments(
fixTarget, fixParamsLen, linkerServices.getTypeConverter(callSiteLastArgType, varArgType)),
linkerServices, callSiteType);
// This method handle determines whether the value can be converted to the array of vararg type using a
// language-specific conversion.
final MethodHandle canConvertArgToArray = MethodHandles.insertArguments(CAN_CONVERT_TO, 0, linkerServices,
varArgType);
// This one adjusts the previous one for the location of the argument and the call site type.
final MethodHandle canConvertLastArgToArray = MethodHandles.dropArguments(canConvertArgToArray, 0,
MethodType.genericMethodType(fixParamsLen).parameterList()).asType(callSiteType.changeReturnType(boolean.class));
// This one takes the previous ones and combines them into a method handle that converts the argument into
// a vararg array when it can, otherwise falls back to the vararg packer.
final MethodHandle convertToArrayWhenPossible = MethodHandles.guardWithTest(canConvertLastArgToArray,
arrayConvertingInvocation, varArgCollectingInvocation);
if(isAssignableFromArray) {
return MethodHandles.guardWithTest(
// Is incoming parameter already a compatible array?
Guards.isInstance(varArgType, fixParamsLen, callSiteType),
// Yes: just pass it to the method
createConvertingInvocation(fixTarget, linkerServices, callSiteType),
// No: either go through a custom conversion, or if it is not possible, go directly to the
// vararg packer.
isCustomConvertible ? convertToArrayWhenPossible : varArgCollectingInvocation);
}
// Just do the custom conversion with fallback to the vararg packer logic.
assert isCustomConvertible;
return convertToArrayWhenPossible;
}
// Remaining case: more than one vararg.
return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType);
}
@SuppressWarnings("unused")
private static boolean canConvertTo(final LinkerServices linkerServices, Class<?> to, Object obj) {
return obj == null ? false : linkerServices.canConvert(obj.getClass(), to);
}
/**
* Creates a method handle out of the original target that will collect the varargs for the exact component type of
* the varArg array. Note that this will nicely trigger language-specific type converters for exactly those varargs

View File

@ -88,6 +88,7 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.logging.Level;
import java.util.logging.Logger;
import jdk.internal.dynalink.DynamicLinker;
import jdk.internal.dynalink.linker.LinkerServices;
/**
@ -115,11 +116,11 @@ public class Guards {
public static MethodHandle isOfClass(Class<?> clazz, MethodType type) {
final Class<?> declaredType = type.parameterType(0);
if(clazz == declaredType) {
LOG.log(Level.WARNING, "isOfClassGuardAlwaysTrue", new Object[] { clazz.getName(), 0, type });
LOG.log(Level.WARNING, "isOfClassGuardAlwaysTrue", new Object[] { clazz.getName(), 0, type, DynamicLinker.getLinkedCallSiteLocation() });
return constantTrue(type);
}
if(!declaredType.isAssignableFrom(clazz)) {
LOG.log(Level.WARNING, "isOfClassGuardAlwaysFalse", new Object[] { clazz.getName(), 0, type });
LOG.log(Level.WARNING, "isOfClassGuardAlwaysFalse", new Object[] { clazz.getName(), 0, type, DynamicLinker.getLinkedCallSiteLocation() });
return constantFalse(type);
}
return getClassBoundArgumentTest(IS_OF_CLASS, clazz, 0, type);
@ -152,11 +153,11 @@ public class Guards {
public static MethodHandle isInstance(Class<?> clazz, int pos, MethodType type) {
final Class<?> declaredType = type.parameterType(pos);
if(clazz.isAssignableFrom(declaredType)) {
LOG.log(Level.WARNING, "isInstanceGuardAlwaysTrue", new Object[] { clazz.getName(), pos, type });
LOG.log(Level.WARNING, "isInstanceGuardAlwaysTrue", new Object[] { clazz.getName(), pos, type, DynamicLinker.getLinkedCallSiteLocation() });
return constantTrue(type);
}
if(!declaredType.isAssignableFrom(clazz)) {
LOG.log(Level.WARNING, "isInstanceGuardAlwaysFalse", new Object[] { clazz.getName(), pos, type });
LOG.log(Level.WARNING, "isInstanceGuardAlwaysFalse", new Object[] { clazz.getName(), pos, type, DynamicLinker.getLinkedCallSiteLocation() });
return constantFalse(type);
}
return getClassBoundArgumentTest(IS_INSTANCE, clazz, pos, type);
@ -174,11 +175,11 @@ public class Guards {
public static MethodHandle isArray(int pos, MethodType type) {
final Class<?> declaredType = type.parameterType(pos);
if(declaredType.isArray()) {
LOG.log(Level.WARNING, "isArrayGuardAlwaysTrue", new Object[] { pos, type });
LOG.log(Level.WARNING, "isArrayGuardAlwaysTrue", new Object[] { pos, type, DynamicLinker.getLinkedCallSiteLocation() });
return constantTrue(type);
}
if(!declaredType.isAssignableFrom(Object[].class)) {
LOG.log(Level.WARNING, "isArrayGuardAlwaysFalse", new Object[] { pos, type });
LOG.log(Level.WARNING, "isArrayGuardAlwaysFalse", new Object[] { pos, type, DynamicLinker.getLinkedCallSiteLocation() });
return constantFalse(type);
}
return asType(IS_ARRAY, pos, type);

View File

@ -76,11 +76,11 @@
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
isInstanceGuardAlwaysTrue=isInstance guard for {0} in position {1} in method type {2} will always return true
isInstanceGuardAlwaysFalse=isInstance guard for {0} in position {1} in method type {2} will always return false
isInstanceGuardAlwaysTrue=isInstance guard for {0} in position {1} in method type {2} at {3} will always return true
isInstanceGuardAlwaysFalse=isInstance guard for {0} in position {1} in method type {2} at {3} will always return false
isOfClassGuardAlwaysTrue=isOfClass guard for {0} in position {1} in method type {2} will always return true
isOfClassGuardAlwaysFalse=isOfClass guard for {0} in position {1} in method type {2} will always return false
isOfClassGuardAlwaysTrue=isOfClass guard for {0} in position {1} in method type {2} at {3} will always return true
isOfClassGuardAlwaysFalse=isOfClass guard for {0} in position {1} in method type {2} at {3} will always return false
isArrayGuardAlwaysTrue=isArray guard in position {0} in method type {1} will always return true
isArrayGuardAlwaysFalse=isArray guard in position {0} in method type {1} will always return false
isArrayGuardAlwaysTrue=isArray guard in position {0} in method type {1} at {2} will always return true
isArrayGuardAlwaysFalse=isArray guard in position {0} in method type {1} at {2} will always return false

View File

@ -25,6 +25,7 @@
package jdk.nashorn.api.scripting;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptRuntime;
/**
@ -57,4 +58,17 @@ public final class ScriptUtils {
public static String format(final String format, final Object[] args) {
return Formatter.format(format, args);
}
/**
* Create a wrapper function that calls {@code func} synchronized on {@code sync} or, if that is undefined,
* {@code self}. Used to implement "sync" function in resources/mozilla_compat.js.
*
* @param func the function to invoke
* @param sync the object to synchronize on
* @return a synchronizing wrapper function
*/
public static Object makeSynchronizedFunction(final ScriptFunction func, final Object sync) {
return func.makeSynchronizedFunction(sync);
}
}

View File

@ -453,7 +453,13 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
public boolean enterFunctionNode(FunctionNode functionNode) {
// function nodes will always leave a constructed function object on stack, no need to load the symbol
// separately as in enterDefault()
lc.pop(functionNode);
functionNode.accept(codegen);
// NOTE: functionNode.accept() will produce a different FunctionNode that we discard. This incidentally
// doesn't cause problems as we're never touching FunctionNode again after it's visited here - codegen
// is the last element in the compilation pipeline, the AST it produces is not used externally. So, we
// re-push the original functionNode.
lc.push(functionNode);
method.convert(type);
return false;
}

View File

@ -25,6 +25,7 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
@ -255,6 +256,12 @@ public class ScriptFunctionImpl extends ScriptFunction {
return makeFunction(name, methodHandle, null);
}
@Override
public ScriptFunction makeSynchronizedFunction(final Object sync) {
final MethodHandle mh = MH.insertArguments(ScriptFunction.INVOKE_SYNC, 0, this, sync);
return makeFunction(getName(), mh);
}
/**
* Same as {@link ScriptFunction#makeBoundFunction(Object, Object[])}. The only reason we override it is so that we
* can expose it to methods in this package.

View File

@ -31,6 +31,8 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Array;
import java.util.Deque;
import java.util.List;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
@ -110,6 +112,15 @@ public enum JSType {
/** Combined call to toPrimitive followed by toString. */
public static final Call TO_PRIMITIVE_TO_STRING = staticCall(myLookup, JSType.class, "toPrimitiveToString", String.class, Object.class);
/** Method handle to convert a JS Object to a Java array. */
public static final Call TO_JAVA_ARRAY = staticCall(myLookup, JSType.class, "toJavaArray", Object.class, Object.class, Class.class);
/** Method handle to convert a JS Object to a Java List. */
public static final Call TO_JAVA_LIST = staticCall(myLookup, JSType.class, "toJavaList", List.class, Object.class);
/** Method handle to convert a JS Object to a Java deque. */
public static final Call TO_JAVA_DEQUE = staticCall(myLookup, JSType.class, "toJavaDeque", Deque.class, Object.class);
private static final double INT32_LIMIT = 4294967296.0;
/**
@ -890,6 +901,8 @@ public enum JSType {
res[idx++] = itr.next();
}
return convertArray(res, componentType);
} else if(obj == null) {
return null;
} else {
throw new IllegalArgumentException("not a script object");
}
@ -918,6 +931,24 @@ public enum JSType {
return dst;
}
/**
* Converts a JavaScript object to a Java List. See {@link ListAdapter} for details.
* @param obj the object to convert. Can be any array-like object.
* @return a List that is live-backed by the JavaScript object.
*/
public static List<?> toJavaList(final Object obj) {
return ListAdapter.create(obj);
}
/**
* Converts a JavaScript object to a Java Deque. See {@link ListAdapter} for details.
* @param obj the object to convert. Can be any array-like object.
* @return a Deque that is live-backed by the JavaScript object.
*/
public static Deque<?> toJavaDeque(final Object obj) {
return ListAdapter.create(obj);
}
/**
* Check if an object is null or undefined
*

View File

@ -53,7 +53,7 @@ import jdk.nashorn.internal.parser.TokenType;
public final class RecompilableScriptFunctionData extends ScriptFunctionData {
/** FunctionNode with the code for this ScriptFunction */
private volatile FunctionNode functionNode;
private FunctionNode functionNode;
/** Source from which FunctionNode was parsed. */
private final Source source;
@ -65,7 +65,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
private final PropertyMap allocatorMap;
/** Code installer used for all further recompilation/specialization of this ScriptFunction */
private volatile CodeInstaller<ScriptEnvironment> installer;
private CodeInstaller<ScriptEnvironment> installer;
/** Name of class where allocator function resides */
private final String allocatorClassName;
@ -178,7 +178,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
}
@Override
protected void ensureCodeGenerated() {
protected synchronized void ensureCodeGenerated() {
if (!code.isEmpty()) {
return; // nothing to do, we have code, at least some.
}
@ -336,7 +336,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
}
@Override
MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
synchronized MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
final MethodType runtimeType = runtimeType(callSiteType, args);
assert runtimeType.parameterCount() == callSiteType.parameterCount();

View File

@ -59,6 +59,9 @@ public abstract class ScriptFunction extends ScriptObject {
/** Method handle for name getter for this ScriptFunction */
public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
/** Method handle used for implementing sync() in mozilla_compat */
public static final MethodHandle INVOKE_SYNC = findOwnMH("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class);
/** Method handle for allocate function for this ScriptFunction */
static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
@ -300,6 +303,14 @@ public abstract class ScriptFunction extends ScriptObject {
*/
public abstract void setPrototype(Object prototype);
/**
* Create a function that invokes this function synchronized on {@code sync} or the self object
* of the invocation.
* @param sync the Object to synchronize on, or undefined
* @return synchronized function
*/
public abstract ScriptFunction makeSynchronizedFunction(Object sync);
/**
* Return the most appropriate invoke handle if there are specializations
* @param type most specific method type to look for invocation with
@ -614,6 +625,15 @@ public abstract class ScriptFunction extends ScriptObject {
return result;
}
@SuppressWarnings("unused")
private static Object invokeSync(final ScriptFunction func, final Object sync, final Object self, final Object... args)
throws Throwable {
final Object syncObj = sync == UNDEFINED ? self : sync;
synchronized (syncObj) {
return func.invoke(self, args);
}
}
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
final Class<?> own = ScriptFunction.class;
final MethodType mt = MH.type(rtype, types);

View File

@ -675,7 +675,7 @@ public abstract class ScriptFunctionData {
/**
* Heuristic to figure out if the method handle has a callee argument. If it's type is either
* {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
* {@code (boolean, ScriptFunction, ...)} or {@code (ScriptFunction, ...)}, then we'll assume it has
* a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
* assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
* they also always receive a callee).
@ -692,11 +692,11 @@ public abstract class ScriptFunctionData {
return false;
}
if (type.parameterType(0) == boolean.class) {
return length > 1 && type.parameterType(1) == ScriptFunction.class;
if (type.parameterType(0) == ScriptFunction.class) {
return true;
}
return type.parameterType(0) == ScriptFunction.class;
return length > 1 && type.parameterType(0) == boolean.class && type.parameterType(1) == ScriptFunction.class;
}
/**

View File

@ -37,6 +37,8 @@ import static jdk.nashorn.internal.runtime.PropertyDescriptor.SET;
import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@ -131,6 +133,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
static final MethodHandle GETPROTO = findOwnMH("getProto", ScriptObject.class);
static final MethodHandle SETPROTOCHECK = findOwnMH("setProtoCheck", void.class, Object.class);
static final MethodHandle MEGAMORPHIC_GET = findOwnMH("megamorphicGet", Object.class, String.class, boolean.class);
static final MethodHandle SETFIELD = findOwnMH("setField", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
static final MethodHandle SETSPILL = findOwnMH("setSpill", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
@ -388,7 +391,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable);
}
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -592,7 +595,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
* @param value value to define
*/
protected final void defineOwnProperty(final int index, final Object value) {
assert ArrayIndex.isValidArrayIndex(index) : "invalid array index";
assert isValidArrayIndex(index) : "invalid array index";
final long longIndex = ArrayIndex.toLongIndex(index);
if (longIndex >= getArray().length()) {
// make array big enough to hold..
@ -602,9 +605,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
private void checkIntegerKey(final String key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
final ArrayData data = getArray();
if (data.has(index)) {
@ -614,7 +617,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
private void removeArraySlot(final String key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -716,6 +719,28 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return null;
}
/**
* Low level property API. This is similar to {@link #findProperty(String, boolean)} but returns a
* {@code boolean} value instead of a {@link FindProperty} object.
* @param key Property key.
* @param deep Whether the search should look up proto chain.
* @return true if the property was found.
*/
boolean hasProperty(final String key, final boolean deep) {
if (getMap().findProperty(key) != null) {
return true;
}
if (deep) {
final ScriptObject myProto = getProto();
if (myProto != null) {
return myProto.hasProperty(key, deep);
}
}
return false;
}
/**
* Add a new property to the object.
* <p>
@ -1708,8 +1733,11 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
*/
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final FindProperty find = findProperty(name, true);
if (request.isCallSiteUnstable()) {
return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator));
}
final FindProperty find = findProperty(name, true);
MethodHandle methodHandle;
if (find == null) {
@ -1727,10 +1755,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
throw new AssertionError(); // never invoked with any other operation
}
if (request.isCallSiteUnstable()) {
return findMegaMorphicGetMethod(desc, name);
}
final Class<?> returnType = desc.getMethodType().returnType();
final Property property = find.getProperty();
methodHandle = find.getGetter(returnType);
@ -1757,11 +1781,21 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
}
private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
final GuardedInvocation inv = findGetIndexMethod(mhType);
private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod);
final MethodHandle guard = getScriptObjectGuard(desc.getMethodType());
return new GuardedInvocation(invoker, guard);
}
return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
@SuppressWarnings("unused")
private Object megamorphicGet(final String key, final boolean isMethod) {
final FindProperty find = findProperty(key, true);
if (find != null) {
return getObjectValue(find);
}
return isMethod ? getNoSuchMethod(key) : invokeNoSuchProperty(key);
}
/**
@ -1810,7 +1844,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
*/
protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
if(request.isCallSiteUnstable()) {
if (request.isCallSiteUnstable()) {
return findMegaMorphicSetMethod(desc, name);
}
@ -2045,6 +2079,26 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return UNDEFINED;
}
/**
* Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined.
* @param name the method name
* @return the bound function, or undefined
*/
private Object getNoSuchMethod(final String name) {
final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
if (find == null) {
return invokeNoSuchProperty(name);
}
final Object value = getObjectValue(find);
if (! (value instanceof ScriptFunction)) {
return UNDEFINED;
}
return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name});
}
private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
}
@ -2308,7 +2362,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
private int getInt(final int index, final String key) {
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
for (ScriptObject object = this; ; ) {
final FindProperty find = object.findProperty(key, false, false, this);
@ -2339,7 +2393,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public int getInt(final Object key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2351,7 +2405,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public int getInt(final double key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2363,7 +2417,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public int getInt(final long key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2385,7 +2439,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
private long getLong(final int index, final String key) {
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
for (ScriptObject object = this; ; ) {
final FindProperty find = object.findProperty(key, false, false, this);
@ -2416,7 +2470,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public long getLong(final Object key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2428,7 +2482,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public long getLong(final double key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2440,7 +2494,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public long getLong(final long key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2462,7 +2516,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
private double getDouble(final int index, final String key) {
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
for (ScriptObject object = this; ; ) {
final FindProperty find = object.findProperty(key, false, false, this);
@ -2493,7 +2547,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public double getDouble(final Object key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2505,7 +2559,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public double getDouble(final double key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2517,7 +2571,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public double getDouble(final long key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2539,7 +2593,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
private Object get(final int index, final String key) {
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
for (ScriptObject object = this; ; ) {
final FindProperty find = object.findProperty(key, false, false, this);
@ -2570,7 +2624,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public Object get(final Object key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2582,7 +2636,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public Object get(final double key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2594,7 +2648,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public Object get(final long key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -2710,9 +2764,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public void set(final Object key, final int value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2722,14 +2776,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(key, JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final Object key, final long value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2739,14 +2794,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(key, JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final Object key, final double value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2756,14 +2812,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(key, JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final Object key, final Object value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2773,17 +2830,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
final String propName = JSType.toString(key);
final FindProperty find = findProperty(propName, true);
setObject(find, strict, propName, value);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, value);
}
@Override
public void set(final double key, final int value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2793,14 +2848,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final double key, final long value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2810,14 +2866,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final double key, final double value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2827,14 +2884,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final double key, final Object value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2844,14 +2902,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), value, strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, value);
}
@Override
public void set(final long key, final int value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2861,14 +2920,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final long key, final long value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2878,14 +2938,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final long key, final double value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2895,14 +2956,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final long key, final Object value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2912,14 +2974,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), value, strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, value);
}
@Override
public void set(final int key, final int value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2929,14 +2992,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final int key, final long value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2946,14 +3010,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final int key, final double value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2963,14 +3028,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), JSType.toObject(value), strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
}
@Override
public void set(final int key, final Object value, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
setArray(getArray().set(index, value, strict));
} else {
@ -2980,14 +3046,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
set(JSType.toObject(key), value, strict);
final String propName = JSType.toString(key);
setObject(findProperty(propName, true), strict, propName, value);
}
@Override
public boolean has(final Object key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
for (ScriptObject self = this; self != null; self = self.getProto()) {
if (self.getArray().has(index)) {
return true;
@ -2995,16 +3062,14 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
}
final FindProperty find = findProperty(JSType.toString(key), true);
return find != null;
return hasProperty(JSType.toString(key), true);
}
@Override
public boolean has(final double key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
for (ScriptObject self = this; self != null; self = self.getProto()) {
if (self.getArray().has(index)) {
return true;
@ -3012,16 +3077,14 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
}
final FindProperty find = findProperty(JSType.toString(key), true);
return find != null;
return hasProperty(JSType.toString(key), true);
}
@Override
public boolean has(final long key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
for (ScriptObject self = this; self != null; self = self.getProto()) {
if (self.getArray().has(index)) {
return true;
@ -3029,16 +3092,14 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
}
final FindProperty find = findProperty(JSType.toString(key), true);
return find != null;
return hasProperty(JSType.toString(key), true);
}
@Override
public boolean has(final int key) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
if (ArrayIndex.isValidArrayIndex(index)) {
if (isValidArrayIndex(index)) {
for (ScriptObject self = this; self != null; self = self.getProto()) {
if (self.getArray().has(index)) {
return true;
@ -3046,66 +3107,32 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
}
final FindProperty find = findProperty(JSType.toString(key), true);
return find != null;
return hasProperty(JSType.toString(key), true);
}
@Override
public boolean hasOwnProperty(final Object key) {
final int index = ArrayIndex.getArrayIndex(key);
if (getArray().has(index)) {
return true;
}
final FindProperty find = findProperty(JSType.toString(key), false);
return find != null;
return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false);
}
@Override
public boolean hasOwnProperty(final int key) {
final int index = ArrayIndex.getArrayIndex(key);
if (getArray().has(index)) {
return true;
}
final FindProperty find = findProperty(JSType.toString(key), false);
return find != null;
return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false);
}
@Override
public boolean hasOwnProperty(final long key) {
final int index = ArrayIndex.getArrayIndex(key);
if (getArray().has(index)) {
return true;
}
final FindProperty find = findProperty(JSType.toString(key), false);
return find != null;
return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false);
}
@Override
public boolean hasOwnProperty(final double key) {
final int index = ArrayIndex.getArrayIndex(key);
if (getArray().has(index)) {
return true;
}
final FindProperty find = findProperty(JSType.toString(key), false);
return find != null;
return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false);
}
@Override
public boolean delete(final int key, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -3121,7 +3148,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public boolean delete(final long key, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -3137,7 +3164,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public boolean delete(final double key, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {
@ -3153,7 +3180,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
@Override
public boolean delete(final Object key, final boolean strict) {
final int index = ArrayIndex.getArrayIndex(key);
final int index = getArrayIndex(key);
final ArrayData array = getArray();
if (array.has(index)) {

View File

@ -25,9 +25,9 @@
package jdk.nashorn.internal.runtime.linker;
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 static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@ -211,6 +211,20 @@ final class JavaArgumentConverters {
return null;
} else if (obj instanceof Long) {
return (Long) obj;
} else if (obj instanceof Integer) {
return ((Integer)obj).longValue();
} else if (obj instanceof Double) {
final Double d = (Double)obj;
if(Double.isInfinite(d.doubleValue())) {
return 0L;
}
return d.longValue();
} else if (obj instanceof Float) {
final Float f = (Float)obj;
if(Float.isInfinite(f.floatValue())) {
return 0L;
}
return f.longValue();
} else if (obj instanceof Number) {
return ((Number)obj).longValue();
} else if (obj instanceof String || obj instanceof ConsString) {

View File

@ -30,6 +30,8 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Modifier;
import java.util.Deque;
import java.util.List;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.ConversionComparator;
import jdk.internal.dynalink.linker.GuardedInvocation;
@ -38,6 +40,8 @@ import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
import jdk.internal.dynalink.support.Guards;
import jdk.nashorn.internal.objects.NativeArray;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.Undefined;
@ -47,6 +51,13 @@ import jdk.nashorn.internal.runtime.Undefined;
* includes {@link ScriptFunction} and its subclasses) as well as {@link Undefined}.
*/
final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory, ConversionComparator {
private static final ClassValue<MethodHandle> ARRAY_CONVERTERS = new ClassValue<MethodHandle>() {
@Override
protected MethodHandle computeValue(Class<?> type) {
return createArrayConverter(type);
}
};
/**
* Returns true if {@code ScriptObject} is assignable from {@code type}, or it is {@code Undefined}.
*/
@ -103,6 +114,12 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
if (mh != null) {
return new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : IS_NASHORN_OR_UNDEFINED_TYPE);
}
GuardedInvocation inv = getArrayConverter(sourceType, targetType);
if(inv != null) {
return inv;
}
return getSamTypeConverter(sourceType, targetType);
}
@ -129,6 +146,41 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
return null;
}
/**
* Returns a guarded invocation that converts from a source type that is NativeArray to a Java array or List or
* Deque type.
* @param sourceType the source type (presumably NativeArray a superclass of it)
* @param targetType the target type (presumably an array type, or List or Deque)
* @return a guarded invocation that converts from the source type to the target type. null is returned if
* either the source type is neither NativeArray, nor a superclass of it, or if the target type is not an array
* type, List, or Deque.
*/
private static GuardedInvocation getArrayConverter(final Class<?> sourceType, final Class<?> targetType) {
final boolean isSourceTypeNativeArray = sourceType == NativeArray.class;
// If source type is more generic than ScriptFunction class, we'll need to use a guard
final boolean isSourceTypeGeneric = !isSourceTypeNativeArray && sourceType.isAssignableFrom(NativeArray.class);
if (isSourceTypeNativeArray || isSourceTypeGeneric) {
final MethodHandle guard = isSourceTypeGeneric ? IS_NATIVE_ARRAY : null;
if(targetType.isArray()) {
return new GuardedInvocation(ARRAY_CONVERTERS.get(targetType), guard);
}
if(targetType == List.class) {
return new GuardedInvocation(JSType.TO_JAVA_LIST.methodHandle(), guard);
}
if(targetType == Deque.class) {
return new GuardedInvocation(JSType.TO_JAVA_DEQUE.methodHandle(), guard);
}
}
return null;
}
private static MethodHandle createArrayConverter(final Class<?> type) {
assert type.isArray();
final MethodHandle converter = MH.insertArguments(JSType.TO_JAVA_ARRAY.methodHandle(), 1, type.getComponentType());
return MH.asType(converter, converter.type().changeReturnType(type));
}
private static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
return isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) &&
JavaAdapterFactory.isAutoConvertibleFromFunction(clazz);
@ -148,7 +200,26 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
@Override
public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
if(sourceType == NativeArray.class) {
// Prefer lists, as they're less costly to create than arrays.
if(isList(targetType1)) {
if(!isList(targetType2)) {
return Comparison.TYPE_1_BETTER;
}
} else if(isList(targetType2)) {
return Comparison.TYPE_2_BETTER;
}
// Then prefer arrays
if(targetType1.isArray()) {
if(!targetType2.isArray()) {
return Comparison.TYPE_1_BETTER;
}
} else if(targetType2.isArray()) {
return Comparison.TYPE_2_BETTER;
}
}
if(ScriptObject.class.isAssignableFrom(sourceType)) {
// Prefer interfaces
if(targetType1.isInterface()) {
if(!targetType2.isInterface()) {
return Comparison.TYPE_1_BETTER;
@ -160,7 +231,12 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
return Comparison.INDETERMINATE;
}
private static boolean isList(Class<?> clazz) {
return clazz == List.class || clazz == Deque.class;
}
private static final MethodHandle IS_SCRIPT_FUNCTION = Guards.isInstance(ScriptFunction.class, MH.type(Boolean.TYPE, Object.class));
private static final MethodHandle IS_NATIVE_ARRAY = Guards.isOfClass(NativeArray.class, MH.type(Boolean.TYPE, Object.class));
private static final MethodHandle IS_NASHORN_OR_UNDEFINED_TYPE = findOwnMH("isNashornTypeOrUndefined",
Boolean.TYPE, Object.class);

View File

@ -98,6 +98,17 @@ Object.defineProperty(this, "importPackage", {
}
// sync
Object.defineProperty(this, "sync", {
configurable: true, enumerable: false, writable: true,
value: function(func, syncobj) {
if (arguments.length < 1 || arguments.length > 2 ) {
throw "sync(function [,object]) parameter count mismatch";
}
return Packages.jdk.nashorn.api.scripting.ScriptUtils.makeSynchronizedFunction(func, syncobj);
}
});
// Object.prototype.__defineGetter__
Object.defineProperty(Object.prototype, "__defineGetter__", {
configurable: true, enumerable: false, writable: true,

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8026016: too many relinks dominate avatar.js http benchmark
*
* @test
* @run
*/
function accessMegamorphic() {
for (var i = 0; i < 26; i++) {
var o = {};
o[String.fromCharCode(i + 97)] = 1;
o._;
}
}
function invokeMegamorphic() {
for (var i = 0; i < 26; i++) {
var o = {};
o[String.fromCharCode(i + 97)] = 1;
try {
o._(i);
} catch (e) {
print(e);
}
}
}
Object.prototype.__noSuchProperty__ = function() {
print("no such property", Array.prototype.slice.call(arguments));
};
invokeMegamorphic();
accessMegamorphic();
Object.prototype.__noSuchMethod__ = function() {
print("no such method", Array.prototype.slice.call(arguments));
};
invokeMegamorphic();
accessMegamorphic();
Object.prototype.__noSuchMethod__ = "nofunction";
invokeMegamorphic();
accessMegamorphic();

View File

@ -0,0 +1,182 @@
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
TypeError: Cannot call undefined
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such method _,0
no such method _,1
no such method _,2
no such method _,3
no such method _,4
no such method _,5
no such method _,6
no such method _,7
no such method _,8
no such method _,9
no such method _,10
no such method _,11
no such method _,12
no such method _,13
no such method _,14
no such method _,15
no such method _,16
no such method _,17
no such method _,18
no such method _,19
no such method _,20
no such method _,21
no such method _,22
no such method _,23
no such method _,24
no such method _,25
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
TypeError: Cannot call undefined
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _
no such property _

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8026367: Add a sync keyword to mozilla_compat
*
* @test
* @run
*/
if (typeof sync === "undefined") {
load("nashorn:mozilla_compat.js");
}
var obj = {
count: 0,
// Sync called with one argument will synchronize on this-object of invocation
inc: sync(function(d) {
this.count += d;
}),
// Pass explicit object to synchronize on as second argument
dec: sync(function(d) {
this.count -= d;
}, obj)
};
var t1 = new java.lang.Thread(function() {
for (var i = 0; i < 100000; i++) obj.inc(1);
});
var t2 = new java.lang.Thread(function() {
for (var i = 0; i < 100000; i++) obj.dec(1);
});
t1.start();
t2.start();
t1.join();
t2.join();
if (obj.count !== 0) {
throw new Error("Expected count == 0, got " + obj.count);
}

View File

@ -48,3 +48,7 @@ if (typeof JavaAdapter != 'function') {
if (typeof importPackage != 'function') {
fail("importPackage function is missing in compatibility script");
}
if (typeof sync != 'function') {
fail("sync function is missing in compatibility script");
}

View File

@ -0,0 +1,235 @@
/*
* 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. 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.api.javaaccess;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;
import java.util.Arrays;
import java.util.List;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.testng.TestNG;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class ArrayConversionTest {
private static ScriptEngine e = null;
public static void main(final String[] args) {
TestNG.main(args);
}
@BeforeClass
public static void setUpClass() throws ScriptException {
e = new ScriptEngineManager().getEngineByName("nashorn");
}
@AfterClass
public static void tearDownClass() {
e = null;
}
@Test
public void testIntArrays() throws ScriptException {
runTest("assertNullIntArray", "null");
runTest("assertEmptyIntArray", "[]");
runTest("assertSingle42IntArray", "[42]");
runTest("assertSingle42IntArray", "['42']");
runTest("assertIntArrayConversions", "[false, true, NaN, Infinity, -Infinity, 0.4, 0.6, null, undefined, [], {}, [1], [1, 2]]");
}
@Test
public void testIntIntArrays() throws ScriptException {
runTest("assertNullIntIntArray", "null");
runTest("assertEmptyIntIntArray", "[]");
runTest("assertSingleEmptyIntIntArray", "[[]]");
runTest("assertSingleNullIntIntArray", "[null]");
runTest("assertLargeIntIntArray", "[[false], [1], [2, 3], [4, 5, 6], ['7', {valueOf: function() { return 8 }}]]");
}
@Test
public void testObjectObjectArrays() throws ScriptException {
runTest("assertLargeObjectObjectArray", "[[false], [1], ['foo', 42.3], [{x: 17}]]");
}
@Test
public void testBooleanArrays() throws ScriptException {
runTest("assertBooleanArrayConversions", "[false, true, '', 'false', 0, 1, 0.4, 0.6, {}, [], [false], [true], NaN, Infinity, null, undefined]");
}
@Test
public void testArrayAmbiguity() throws ScriptException {
runTest("x", "'abc'");
runTest("x", "['foo', 'bar']");
}
@Test
public void testListArrays() throws ScriptException {
runTest("assertListArray", "[['foo', 'bar'], ['apple', 'orange']]");
}
@Test
public void testVarArgs() throws ScriptException {
// Sole NativeArray in vararg position becomes vararg array itself
runTest("assertVarArg_42_17", "[42, 17]");
// NativeArray in vararg position becomes an argument if there are more arguments
runTest("assertVarArg_array_17", "[42], 18");
// Only NativeArray is converted to vararg array, other objects (e.g. a function) aren't
runTest("assertVarArg_function", "function() { return 'Hello' }");
}
private static void runTest(final String testMethodName, final String argument) throws ScriptException {
e.eval("Java.type('" + ArrayConversionTest.class.getName() + "')." + testMethodName + "(" + argument + ")");
}
public static void assertNullIntArray(int[] array) {
assertNull(array);
}
public static void assertNullIntIntArray(int[][] array) {
assertNull(array);
}
public static void assertEmptyIntArray(int[] array) {
assertEquals(0, array.length);
}
public static void assertSingle42IntArray(int[] array) {
assertEquals(1, array.length);
assertEquals(42, array[0]);
}
public static void assertIntArrayConversions(int[] array) {
assertEquals(13, array.length);
assertEquals(0, array[0]); // false
assertEquals(1, array[1]); // true
assertEquals(0, array[2]); // NaN
assertEquals(0, array[3]); // Infinity
assertEquals(0, array[4]); // -Infinity
assertEquals(0, array[5]); // 0.4
assertEquals(0, array[6]); // 0.6 - floor, not round
assertEquals(0, array[7]); // null
assertEquals(0, array[8]); // undefined
assertEquals(0, array[9]); // []
assertEquals(0, array[10]); // {}
assertEquals(1, array[11]); // [1]
assertEquals(0, array[12]); // [1, 2]
}
public static void assertEmptyIntIntArray(int[][] array) {
assertEquals(0, array.length);
}
public static void assertSingleEmptyIntIntArray(int[][] array) {
assertEquals(1, array.length);
assertTrue(Arrays.equals(new int[0], array[0]));
}
public static void assertSingleNullIntIntArray(int[][] array) {
assertEquals(1, array.length);
assertNull(null, array[0]);
}
public static void assertLargeIntIntArray(int[][] array) {
assertEquals(5, array.length);
assertTrue(Arrays.equals(new int[] { 0 }, array[0]));
assertTrue(Arrays.equals(new int[] { 1 }, array[1]));
assertTrue(Arrays.equals(new int[] { 2, 3 }, array[2]));
assertTrue(Arrays.equals(new int[] { 4, 5, 6 }, array[3]));
assertTrue(Arrays.equals(new int[] { 7, 8 }, array[4]));
}
public static void assertLargeObjectObjectArray(Object[][] array) throws ScriptException {
assertEquals(4, array.length);
assertTrue(Arrays.equals(new Object[] { Boolean.FALSE }, array[0]));
assertTrue(Arrays.equals(new Object[] { 1 }, array[1]));
assertTrue(Arrays.equals(new Object[] { "foo", 42.3d }, array[2]));
assertEquals(1, array[3].length);
e.getBindings(ScriptContext.ENGINE_SCOPE).put("obj", array[3][0]);
assertEquals(17, e.eval("obj.x"));
}
public static void assertBooleanArrayConversions(boolean[] array) {
assertEquals(16, array.length);
assertFalse(array[0]); // false
assertTrue(array[1]); // true
assertFalse(array[2]); // ''
assertTrue(array[3]); // 'false' (yep, every non-empty string converts to true)
assertFalse(array[4]); // 0
assertTrue(array[5]); // 1
assertTrue(array[6]); // 0.4
assertTrue(array[7]); // 0.6
assertTrue(array[8]); // {}
assertTrue(array[9]); // []
assertTrue(array[10]); // [false]
assertTrue(array[11]); // [true]
assertFalse(array[12]); // NaN
assertTrue(array[13]); // Infinity
assertFalse(array[14]); // null
assertFalse(array[15]); // undefined
}
public static void assertListArray(List<?>[] array) {
assertEquals(2, array.length);
assertEquals(Arrays.asList("foo", "bar"), array[0]);
assertEquals(Arrays.asList("apple", "orange"), array[1]);
}
public static void assertVarArg_42_17(Object... args) throws ScriptException {
assertEquals(2, args.length);
assertEquals(42, ((Number)args[0]).intValue());
assertEquals(17, ((Number)args[1]).intValue());
}
public static void assertVarArg_array_17(Object... args) throws ScriptException {
assertEquals(2, args.length);
e.getBindings(ScriptContext.ENGINE_SCOPE).put("arr", args[0]);
assertTrue((Boolean)e.eval("arr instanceof Array && arr.length == 1 && arr[0] == 42"));
assertEquals(18, ((Number)args[1]).intValue());
}
public static void assertVarArg_function(Object... args) throws ScriptException {
assertEquals(1, args.length);
e.getBindings(ScriptContext.ENGINE_SCOPE).put("fn", args[0]);
assertEquals("Hello", e.eval("fn()"));
}
public static void x(String y) {
assertEquals("abc", y);
}
public static void x(String[] y) {
assertTrue(Arrays.equals(new String[] { "foo", "bar"}, y));
}
}