/** * ECMA 15.4.4.2 Array.prototype.toString ( ) * * @param self self reference * @return string representation of array */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object toString(final Object self) { final Object obj = Global.toObject(self); if (obj instanceof ScriptObject) { final InvokeByName joinInvoker = getJOIN(); final ScriptObject sobj = (ScriptObject)obj; try { final Object join = joinInvoker.getGetter().invokeExact(sobj); if (Bootstrap.isCallable(join)) { return joinInvoker.getInvoker().invokeExact(join, sobj); } } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } } // FIXME: should lookup Object.prototype.toString and call that? return ScriptRuntime.builtinObjectToString(self); }
/** * ECMA 15.2.4.3 Object.prototype.toLocaleString ( ) * * @param self self reference * @return localized ToString */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object toLocaleString(final Object self) { final Object obj = JSType.toScriptObject(self); if (obj instanceof ScriptObject) { final InvokeByName toStringInvoker = getTO_STRING(); final ScriptObject sobj = (ScriptObject)self; try { final Object toString = toStringInvoker.getGetter().invokeExact(sobj); if (Bootstrap.isCallable(toString)) { return toStringInvoker.getInvoker().invokeExact(toString, sobj); } } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } throw typeError("not.a.function", "toString"); } return ScriptRuntime.builtinObjectToString(self); }
/** * Binds the source mirror object's properties to the target object. Binding * properties allows two-way read/write for the properties of the source object. * All inherited, enumerable properties are also bound. This method is used to * to make 'with' statement work with ScriptObjectMirror as scope object. * * @param target the target object to which the source object's properties are bound * @param source the source object whose properties are bound to the target * @return the target object after property binding */ public static Object bindAllProperties(final ScriptObject target, final ScriptObjectMirror source) { final Set<String> keys = source.keySet(); // make accessor properties using dynamic invoker getters and setters final AccessorProperty[] props = new AccessorProperty[keys.size()]; int idx = 0; for (final String name : keys) { final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, MIRROR_GETTER_TYPE); final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, MIRROR_SETTER_TYPE); props[idx] = AccessorProperty.create(name, 0, getter, setter); idx++; } target.addBoundProperties(source, props); return target; }
/** * ECMA 15.5.4.11 String.prototype.replace (searchValue, replaceValue) * @param self self reference * @param string item to replace * @param replacement item to replace it with * @return string after replacement * @throws Throwable if replacement fails */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static String replace(final Object self, final Object string, final Object replacement) throws Throwable { final String str = checkObjectToString(self); final NativeRegExp nativeRegExp; if (string instanceof NativeRegExp) { nativeRegExp = (NativeRegExp) string; } else { nativeRegExp = NativeRegExp.flatRegExp(JSType.toString(string)); } if (Bootstrap.isCallable(replacement)) { return nativeRegExp.replace(str, "", replacement); } return nativeRegExp.replace(str, JSType.toString(replacement), null); }
/** * * @param self the self reference * @param callbackFn the callback function * @param thisArg optional this-object */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static void forEach(final Object self, final Object callbackFn, final Object thisArg) { final NativeMap map = getNativeMap(self); if (!Bootstrap.isCallable(callbackFn)) { throw typeError("not.a.function", ScriptRuntime.safeToString(callbackFn)); } final MethodHandle invoker = Global.instance().getDynamicInvoker(FOREACH_INVOKER_KEY, () -> Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Object.class, Object.class, Object.class, Object.class)); final LinkedMap.LinkedMapIterator iterator = map.getJavaMap().getIterator(); for (;;) { final LinkedMap.Node node = iterator.next(); if (node == null) { break; } try { final Object result = invoker.invokeExact(callbackFn, thisArg, node.getValue(), node.getKey(), self); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } } }
/** * ECMA6 23.2.3.6 Set.prototype.forEach ( callbackfn [ , thisArg ] ) * * @param self the self reference * @param callbackFn the callback function * @param thisArg optional this object */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static void forEach(final Object self, final Object callbackFn, final Object thisArg) { final NativeSet set = getNativeSet(self); if (!Bootstrap.isCallable(callbackFn)) { throw typeError("not.a.function", ScriptRuntime.safeToString(callbackFn)); } final MethodHandle invoker = Global.instance().getDynamicInvoker(FOREACH_INVOKER_KEY, () -> Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Object.class, Object.class, Object.class, Object.class)); final LinkedMap.LinkedMapIterator iterator = set.getJavaMap().getIterator(); for (;;) { final LinkedMap.Node node = iterator.next(); if (node == null) { break; } try { final Object result = invoker.invokeExact(callbackFn, thisArg, node.getKey(), node.getKey(), self); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } } }
/** * ECMA 15.2.4.3 Object.prototype.toLocaleString ( ) * * @param self self reference * @return localized ToString */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object toLocaleString(final Object self) { final Object obj = JSType.toScriptObject(self); if (obj instanceof ScriptObject) { final InvokeByName toStringInvoker = getTO_STRING(); final ScriptObject sobj = (ScriptObject)obj; try { final Object toString = toStringInvoker.getGetter().invokeExact(sobj); if (Bootstrap.isCallable(toString)) { return toStringInvoker.getInvoker().invokeExact(toString, sobj); } } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } throw typeError("not.a.function", "toString"); } return ScriptRuntime.builtinObjectToString(self); }
/** * Convert the given object to the given type. * * @param obj object to be converted * @param type destination type to convert to * @return converted object */ public static Object convert(final Object obj, final Object type) { if (obj == null) { return null; } final Class<?> clazz; if (type instanceof Class) { clazz = (Class<?>)type; } else if (type instanceof StaticClass) { clazz = ((StaticClass)type).getRepresentedClass(); } else { throw new IllegalArgumentException("type expected"); } final LinkerServices linker = Bootstrap.getLinkerServices(); final Object objToConvert = unwrap(obj); final MethodHandle converter = linker.getTypeConverter(objToConvert.getClass(), clazz); if (converter == null) { // no supported conversion! throw new UnsupportedOperationException("conversion not supported"); } try { return converter.invoke(objToConvert); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } }
/** * ECMA 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]]) * * @param self self reference * @param args arguments for bind * @return function with bound arguments */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static Object bind(final Object self, final Object... args) { final Object thiz = (args.length == 0) ? UNDEFINED : args[0]; Object[] arguments; if (args.length > 1) { arguments = new Object[args.length - 1]; System.arraycopy(args, 1, arguments, 0, arguments.length); } else { arguments = ScriptRuntime.EMPTY_ARRAY; } return Bootstrap.bindCallable(self, thiz, arguments); }
private static MethodHandle getREPLACER_INVOKER() { return Global.instance().getDynamicInvoker(REPLACER_INVOKER, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicInvoker("dyn:call", Object.class, ScriptFunction.class, ScriptObject.class, Object.class, Object.class); } }); }
private static MethodHandle createIteratorCallbackInvoker(final Object key, final Class<?> rtype) { return Global.instance().getDynamicInvoker(key, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicInvoker("dyn:call", rtype, Object.class, Object.class, Object.class, long.class, Object.class); } }); }
private static MethodHandle getREDUCE_CALLBACK_INVOKER() { return Global.instance().getDynamicInvoker(REDUCE_CALLBACK_INVOKER, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicInvoker("dyn:call", Object.class, Object.class, Undefined.class, Object.class, Object.class, long.class, Object.class); } }); }
private static MethodHandle getCALL_CMP() { return Global.instance().getDynamicInvoker(CALL_CMP, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicInvoker("dyn:call", double.class, ScriptFunction.class, Object.class, Object.class, Object.class); } }); }
/** * ECMA 15.9.5.44 Date.prototype.toJSON ( key ) * * Provides a string representation of this Date for use by {@link NativeJSON#stringify(Object, Object, Object, Object)} * * @param self self reference * @param key ignored * @return JSON representation of this date */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object toJSON(final Object self, final Object key) { // NOTE: Date.prototype.toJSON is generic. Accepts other objects as well. final Object selfObj = Global.toObject(self); if (!(selfObj instanceof ScriptObject)) { return null; } final ScriptObject sobj = (ScriptObject)selfObj; final Object value = sobj.getDefaultValue(Number.class); if (value instanceof Number) { final double num = ((Number)value).doubleValue(); if (isInfinite(num) || isNaN(num)) { return null; } } try { final InvokeByName toIsoString = getTO_ISO_STRING(); final Object func = toIsoString.getGetter().invokeExact(sobj); if (Bootstrap.isCallable(func)) { return toIsoString.getInvoker().invokeExact(func, sobj, key); } throw typeError("not.a.function", ScriptRuntime.safeToString(func)); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } }
private static MethodHandle getBoundBeanMethodGetter(final Object source, final MethodHandle methodGetter) { try { // NOTE: we're relying on the fact that "dyn:getMethod:..." return value is constant for any given method // name and object linked with BeansLinker. (Actually, an even stronger assumption is true: return value is // constant for any given method name and object's class.) return MethodHandles.dropArguments(MethodHandles.constant(Object.class, Bootstrap.bindCallable(methodGetter.invoke(source), source, null)), 0, Object.class); } catch(RuntimeException|Error e) { throw e; } catch(final Throwable t) { throw new RuntimeException(t); } }
private static final MethodHandle getReplaceValueInvoker() { return Global.instance().getDynamicInvoker(REPLACE_VALUE, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicInvoker("dyn:call", String.class, ScriptFunction.class, Object.class, Object[].class); } }); }
/** * Apply action main loop. * @return result of apply */ public final T apply() { final boolean strict = Bootstrap.isStrictCallable(callbackfn); // for non-strict callback, need to translate undefined thisArg to be global object thisArg = (thisArg == ScriptRuntime.UNDEFINED && !strict)? Context.getGlobal() : thisArg; applyLoopBegin(iter); final boolean reverse = iter.isReverse(); while (iter.hasNext()) { final Object val = iter.next(); index = iter.nextIndex() + (reverse ? 1 : -1); try { if (!forEach(val, index)) { return result; } } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } } return result; }
private static MethodHandle getREVIVER_INVOKER() { return Context.getGlobal().getDynamicInvoker(REVIVER_INVOKER, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicInvoker("dyn:call", Object.class, ScriptFunction.class, ScriptObject.class, String.class, Object.class); } }); }
/** * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle. * * @param desc the call site descriptor. * @param request the link request * * @return GuardedInvocation to be invoked at call site. */ protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) { // R(P0, P1, ...) final MethodType callType = desc.getMethodType(); // use type Object(P0) for the getter final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0))); final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod"); // Object(P0) => Object(P0, P1, ...) final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount())); // R(Object, P0, P1, ...) final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType())); // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...) return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard()); }
static MethodHandle getINVOKE_UA_GETTER(final Class<?> returnType, final int programPoint) { if (UnwarrantedOptimismException.isValid(programPoint)) { final int flags = NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC | programPoint << CALLSITE_PROGRAM_POINT_SHIFT; return Bootstrap.createDynamicInvoker("dyn:call", flags, returnType, Object.class, Object.class); } else { return Bootstrap.createDynamicInvoker("dyn:call", Object.class, Object.class, Object.class); } }
/** * Convert the given object to the given type. * * @param obj object to be converted * @param type destination type to convert to. type is either a Class * or nashorn representation of a Java type returned by Java.type() call in script. * @return converted object */ public static Object convert(final Object obj, final Object type) { if (obj == null) { return null; } final Class<?> clazz; if (type instanceof Class) { clazz = (Class<?>)type; } else if (type instanceof StaticClass) { clazz = ((StaticClass)type).getRepresentedClass(); } else { throw new IllegalArgumentException("type expected"); } final LinkerServices linker = Bootstrap.getLinkerServices(); final Object objToConvert = unwrap(obj); final MethodHandle converter = linker.getTypeConverter(objToConvert.getClass(), clazz); if (converter == null) { // no supported conversion! throw new UnsupportedOperationException("conversion not supported"); } try { return converter.invoke(objToConvert); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } }
private static MethodHandle getJSOBJECT_INVOKER() { return Global.instance().getDynamicInvoker(JSOBJECT_INVOKER, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Object.class); } }); }
private static MethodHandle getREPLACER_INVOKER() { return Global.instance().getDynamicInvoker(REPLACER_INVOKER, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Object.class, Object.class, Object.class); } }); }
private static MethodHandle createIteratorCallbackInvoker(final Object key, final Class<?> rtype) { return Global.instance().getDynamicInvoker(key, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicCallInvoker(rtype, Object.class, Object.class, Object.class, double.class, Object.class); } }); }
private static MethodHandle getREDUCE_CALLBACK_INVOKER() { return Global.instance().getDynamicInvoker(REDUCE_CALLBACK_INVOKER, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Undefined.class, Object.class, Object.class, double.class, Object.class); } }); }
private static MethodHandle getCALL_CMP() { return Global.instance().getDynamicInvoker(CALL_CMP, new Callable<MethodHandle>() { @Override public MethodHandle call() { return Bootstrap.createDynamicCallInvoker(double.class, Object.class, Object.class, Object.class, Object.class); } }); }