/** * ECMA 15.8.2.11 max(x) * * @param self self reference * @param args arguments * * @return the largest of the arguments, {@link Double#NEGATIVE_INFINITY} if no args given, or identity if one arg is given */ @Function(arity = 2, attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static double max(final Object self, final Object... args) { switch (args.length) { case 0: return Double.NEGATIVE_INFINITY; case 1: return JSType.toNumber(args[0]); default: double res = JSType.toNumber(args[0]); for (int i = 1; i < args.length; i++) { res = Math.max(res, JSType.toNumber(args[i])); } return res; } }
/** * Test of toString method, of class Runtime. */ @Test public void testToString_Object() { assertEquals(JSType.toString(ScriptRuntime.UNDEFINED), "undefined"); assertEquals(JSType.toString(null), "null"); assertEquals(JSType.toString(Boolean.TRUE), "true"); assertEquals(JSType.toString(Boolean.FALSE), "false"); assertEquals(JSType.toString(""), ""); assertEquals(JSType.toString("nashorn"), "nashorn"); assertEquals(JSType.toString(Double.NaN), "NaN"); assertEquals(JSType.toString(Double.POSITIVE_INFINITY), "Infinity"); assertEquals(JSType.toString(Double.NEGATIVE_INFINITY), "-Infinity"); assertEquals(JSType.toString(0.0), "0"); // FIXME: add more number-to-string test cases // FIXME: add case for Object type (JSObject with getDefaultValue) }
/** * ECMA 15.4.4.5 Array.prototype.join (separator) * * @param self self reference * @param separator element separator * @return string representation after join */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static String join(final Object self, final Object separator) { final StringBuilder sb = new StringBuilder(); final Iterator<Object> iter = arrayLikeIterator(self, true); final String sep = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator); while (iter.hasNext()) { final Object obj = iter.next(); if (obj != null && obj != ScriptRuntime.UNDEFINED) { sb.append(JSType.toString(obj)); } if (iter.hasNext()) { sb.append(sep); } } return sb.toString(); }
/** * 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); }
/** * ECMA 15.5.4.14 String.prototype.split (separator, limit) * * @param self self reference * @param separator separator for split * @param limit limit for splits * @return array object in which splits have been placed */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static ScriptObject split(final Object self, final Object separator, final Object limit) { final String str = checkObjectToString(self); final long lim = limit == UNDEFINED ? JSType.MAX_UINT : JSType.toUint32(limit); if (separator == UNDEFINED) { return lim == 0 ? new NativeArray() : new NativeArray(new Object[]{str}); } if (separator instanceof NativeRegExp) { return ((NativeRegExp) separator).split(str, lim); } // when separator is a string, it is treated as a literal search string to be used for splitting. return splitString(str, JSType.toString(separator), lim); }
/** * ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single object argument * * @param self self reference * @param arg argument to push * @return array after pushes */ @SpecializedFunction public static double push(final Object self, final Object arg) { try { final ScriptObject sobj = (ScriptObject)self; final ArrayData arrayData = sobj.getArray(); final long length = arrayData.length(); if (bulkable(sobj) && length < JSType.MAX_UINT) { sobj.setArray(arrayData.push(true, arg)); return length + 1; } long len = JSType.toUint32(sobj.getLength()); sobj.set(len++, arg, CALLSITE_STRICT); sobj.set("length", len, CALLSITE_STRICT); return len; } catch (final ClassCastException | NullPointerException e) { throw typeError("not.an.object", ScriptRuntime.safeToString(self)); } }
@Override public PropertyDescriptor fillFrom(final ScriptObject sobj) { if (sobj.has(CONFIGURABLE)) { this.configurable = JSType.toBoolean(sobj.get(CONFIGURABLE)); } else { delete(CONFIGURABLE, false); } if (sobj.has(ENUMERABLE)) { this.enumerable = JSType.toBoolean(sobj.get(ENUMERABLE)); } else { delete(ENUMERABLE, false); } return this; }
/** * ECMA 15.8.2.12 min(x) * * @param self self reference * @param args arguments * * @return the smallest of the arguments, {@link Double#NEGATIVE_INFINITY} if no args given, or identity if one arg is given */ @Function(arity = 2, attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static double min(final Object self, final Object... args) { switch (args.length) { case 0: return Double.POSITIVE_INFINITY; case 1: return JSType.toNumber(args[0]); default: double res = JSType.toNumber(args[0]); for (int i = 1; i < args.length; i++) { res = Math.min(res, JSType.toNumber(args[i])); } return res; } }
private static double[] convertCtorArgs(final Object[] args) { final double[] d = new double[7]; boolean nullReturn = false; // should not bailout on first NaN or infinite. Need to convert all // subsequent args for possible side-effects via valueOf/toString overrides // on argument objects. for (int i = 0; i < d.length; i++) { if (i < args.length) { final double darg = JSType.toNumber(args[i]); if (isNaN(darg) || isInfinite(darg)) { nullReturn = true; } d[i] = (long)darg; } else { d[i] = i == 2 ? 1 : 0; // day in month defaults to 1 } } if (0 <= d[0] && d[0] <= 99) { d[0] += 1900; } return nullReturn? null : d; }
@Override public double toNumber() { return inGlobal(new Callable<Double>() { @Override public Double call() { return JSType.toNumber(sobj); } }); }
@SuppressWarnings("unused") private static Object get(final Object self, final Object key) { final CharSequence cs = JSType.toCharSequence(self); final Object primitiveKey = JSType.toPrimitive(key, String.class); final int index = ArrayIndex.getArrayIndex(primitiveKey); if (index >= 0 && index < cs.length()) { return String.valueOf(cs.charAt(index)); } return ((ScriptObject) Global.toObject(self)).get(primitiveKey); }
/** * Converts {@code result} to a printable string. The reason we don't use {@link JSType#toString(Object)} * or {@link ScriptRuntime#safeToString(Object)} is that we want to be able to render Symbol values * even if they occur within an Array, and therefore have to implement our own Array to String * conversion. * * @param result the result * @param global the global object * @return the string representation */ protected static String toString(final Object result, final Global global) { if (result instanceof Symbol) { // Normal implicit conversion of symbol to string would throw TypeError return result.toString(); } if (result instanceof NativeSymbol) { return JSType.toPrimitive(result).toString(); } if (isArrayWithDefaultToString(result, global)) { // This should yield the same string as Array.prototype.toString but // will not throw if the array contents include symbols. final StringBuilder sb = new StringBuilder(); final Iterator<Object> iter = ArrayLikeIterator.arrayLikeIterator(result, true); while (iter.hasNext()) { final Object obj = iter.next(); if (obj != null && obj != ScriptRuntime.UNDEFINED) { sb.append(toString(obj, global)); } if (iter.hasNext()) { sb.append(','); } } return sb.toString(); } return JSType.toString(result); }
@Override public int getIntOptimistic(final int index, final int programPoint) { if (index >= length()) { return JSType.toInt32Optimistic(get(index), programPoint); } return underlying.getIntOptimistic(index, programPoint); }
@Override public PropertyDescriptor fillFrom(final ScriptObject sobj) { if (sobj.has(CONFIGURABLE)) { this.configurable = JSType.toBoolean(sobj.get(CONFIGURABLE)); } else { delete(CONFIGURABLE, false); } if (sobj.has(ENUMERABLE)) { this.enumerable = JSType.toBoolean(sobj.get(ENUMERABLE)); } else { delete(ENUMERABLE, false); } if (sobj.has(WRITABLE)) { this.writable = JSType.toBoolean(sobj.get(WRITABLE)); } else { delete(WRITABLE, false); } if (sobj.has(VALUE)) { this.value = sobj.get(VALUE); } else { delete(VALUE, false); } return this; }
/** * ECMA 15.2.4.5 Object.prototype.hasOwnProperty (V) * * @param self self reference * @param v property to check for * @return true if property exists in object */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static boolean hasOwnProperty(final Object self, final Object v) { // Convert ScriptObjects to primitive with String.class hint // but no need to convert other primitives to string. final Object key = JSType.toPrimitive(v, String.class); final Object obj = Global.toObject(self); return obj instanceof ScriptObject && ((ScriptObject)obj).hasOwnProperty(key); }
@SuppressWarnings("LeakingThisInConstructor") NativeSyntaxError(final Object msg, final Global global) { super(global.getSyntaxErrorPrototype(), $nasgenmap$); if (msg != UNDEFINED) { this.instMessage = JSType.toString(msg); } else { this.delete(NativeError.MESSAGE, false); } NativeError.initException(this); }
/** * Convert given object to NativeRegExp type. * * @param obj object to be converted * @return NativeRegExp instance */ public static NativeRegExp toRegExp(final Object obj) { if (obj instanceof NativeRegExp) { return (NativeRegExp)obj; } return new NativeRegExp(JSType.toString(obj)); }
/** * return a List of own keys associated with the object. * @param all True if to include non-enumerable keys. * @param nonEnumerable set of non-enumerable properties seen already.Used * to filter out shadowed, but enumerable properties from proto children. * @return Array of keys. */ @Override protected String[] getOwnKeys(final boolean all, final Set<String> nonEnumerable) { final List<Object> keys = new ArrayList<>(); // add string index keys for (int i = 0; i < value.length(); i++) { keys.add(JSType.toString(i)); } // add super class properties keys.addAll(Arrays.asList(super.getOwnKeys(all, nonEnumerable))); return keys.toArray(new String[keys.size()]); }
/** * ECMA 5.2.3.7 Object.defineProperties ( O, Properties ) * * @param self self reference * @param obj object in which to define properties * @param props properties * @return object */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static ScriptObject defineProperties(final Object self, final Object obj, final Object props) { final ScriptObject sobj = Global.checkObject(obj); final Object propsObj = Global.toObject(props); if (propsObj instanceof ScriptObject) { final Object[] keys = ((ScriptObject)propsObj).getOwnKeys(false); for (final Object key : keys) { final String prop = JSType.toString(key); sobj.defineOwnProperty(prop, ((ScriptObject)propsObj).get(prop), true); } } return sobj; }
private static Long toLong(final Object obj0) { // TODO - Order tests for performance. for (Object obj = obj0; ;) { if (obj == null) { 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) { return JSType.toLong(obj); } else if (obj instanceof Boolean) { return (Boolean)obj ? 1L : 0L; } else if (obj instanceof ScriptObject) { obj = JSType.toPrimitive(obj, Number.class); continue; } else if (obj == UNDEFINED) { return null; // null or 0L? } throw assertUnexpectedType(obj); } }
@SuppressWarnings("LeakingThisInConstructor") private NativeReferenceError(final Object msg, final ScriptObject proto, final PropertyMap map) { super(proto, map); if (msg != UNDEFINED) { this.instMessage = JSType.toString(msg); } else { this.delete(NativeError.MESSAGE, false); } NativeError.initException(this); }
@Override public Node leaveCaseNode(final CaseNode caseNode) { // Try to represent the case test as an integer final Node test = caseNode.getTest(); if (test instanceof LiteralNode) { final LiteralNode<?> lit = (LiteralNode<?>)test; if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) { if (JSType.isRepresentableAsInt(lit.getNumber())) { return caseNode.setTest((Expression)LiteralNode.newInstance(lit, lit.getInt32()).accept(this)); } } } return caseNode; }
/** * Load a key value in the proper form. * * @param key */ //TODO move this and break it apart MethodEmitter loadKey(final Object key) { if (key instanceof IdentNode) { method.visitLdcInsn(((IdentNode) key).getName()); } else if (key instanceof LiteralNode) { method.visitLdcInsn(((LiteralNode<?>)key).getString()); } else { method.visitLdcInsn(JSType.toString(key)); } pushType(Type.OBJECT); //STRING return this; }
/** * ECMA 15.5.4.13 String.prototype.slice (start, end) * * @param self self reference * @param start start position for slice * @param end end position for slice * @return sliced out substring */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static String slice(final Object self, final Object start, final Object end) { final String str = checkObjectToString(self); if (end == UNDEFINED) { return slice(str, JSType.toInteger(start)); } return slice(str, JSType.toInteger(start), JSType.toInteger(end)); }
@Override public double getDouble(final int index) { if (index >= length()) { return JSType.toNumber(get(index)); } return underlying.getDouble(index); }
/** * ECMA 15.5.4.15 String.prototype.substring (start, end) * * @param self self reference * @param start start position of substring * @param end end position of substring * @return substring given start and end indexes */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static String substring(final Object self, final Object start, final Object end) { final String str = checkObjectToString(self); if (end == UNDEFINED) { return substring(str, JSType.toInteger(start)); } return substring(str, JSType.toInteger(start), JSType.toInteger(end)); }
/** * Get 16-bit signed int from given byteOffset * * @param self DataView object * @param byteOffset byte offset to read from * @param littleEndian (optional) flag indicating whether to read in little endian order * @return 16-bit signed int value at the byteOffset */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static int getInt16(final Object self, final Object byteOffset, final Object littleEndian) { try { return getBuffer(self, littleEndian).getShort(JSType.toInt32(byteOffset)); } catch (final IllegalArgumentException iae) { throw rangeError(iae, "dataview.offset"); } }
/** * ECMA 15.7.4.2 Number.prototype.toString ( [ radix ] ) * * @param self self reference * @param radix radix to use for string conversion * @return string representation of this Number in the given radix */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static String toString(final Object self, final Object radix) { if (radix != UNDEFINED) { final int intRadix = JSType.toInteger(radix); if (intRadix != 10) { if (intRadix < 2 || intRadix > 36) { throw rangeError("invalid.radix"); } return JSType.toString(getNumberValue(self), intRadix); } } return JSType.toString(getNumberValue(self)); }
/** * Set 32-bit float at the given byteOffset * * @param self DataView object * @param byteOffset byte offset to write at * @param value float value to set * @param littleEndian (optional) flag indicating whether to write in little endian order * @return undefined */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) public static Object setFloat32(final Object self, final Object byteOffset, final Object value, final Object littleEndian) { try { getBuffer(self, littleEndian).putFloat((int)JSType.toUint32(byteOffset), (float)JSType.toNumber(value)); return UNDEFINED; } catch (final IllegalArgumentException iae) { throw rangeError(iae, "dataview.offset"); } }
/** * Set 8-bit signed int at the given byteOffset * * @param self DataView object * @param byteOffset byte offset to read from * @param value byte value to set * @return undefined */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) public static Object setInt8(final Object self, final Object byteOffset, final Object value) { try { getBuffer(self).put(JSType.toInt32(byteOffset), (byte)JSType.toInt32(value)); return UNDEFINED; } catch (final IllegalArgumentException iae) { throw rangeError(iae, "dataview.offset"); } }
/** * Get 32-bit unsigned int from given byteOffset * * @param self DataView object * @param byteOffset byte offset to read from * @return 32-bit unsigned int value at the byteOffset */ @SpecializedFunction public static double getUint32(final Object self, final int byteOffset) { try { return JSType.toUint32(getBuffer(self, false).getInt(JSType.toInt32(byteOffset))); } catch (final IllegalArgumentException iae) { throw rangeError(iae, "dataview.offset"); } }
/** * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) * @param self self reference * @param pos position in string * @return number representing charcode at position */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static double charCodeAt(final Object self, final Object pos) { final String str = checkObjectToString(self); final int idx = JSType.toInteger(pos); return idx < 0 || idx >= str.length() ? Double.NaN : str.charAt(idx); }
/** * Get 64-bit float value from given byteOffset * * @param self DataView object * @param byteOffset byte offset to read from * @param littleEndian (optional) flag indicating whether to read in little endian order * @return 64-bit float value at the byteOffset */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static double getFloat64(final Object self, final Object byteOffset, final Object littleEndian) { try { return getBuffer(self, littleEndian).getDouble(JSType.toInt32(byteOffset)); } catch (final IllegalArgumentException iae) { throw rangeError(iae, "dataview.offset"); } }
@Override public Type rem(final MethodVisitor method, final int programPoint) { if (programPoint == INVALID_PROGRAM_POINT) { JSType.REM_ZERO_LONG.invoke(method); } else { method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint); } return LONG; }
@Override public boolean delete(final long key, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { return super.delete(key, strict); } return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict)); }
@Override public boolean delete(final double key, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { return super.delete(key, strict); } return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict)); }
@Override public Type rem(final MethodVisitor method, final int programPoint) { if (programPoint == INVALID_PROGRAM_POINT) { JSType.REM_ZERO.invoke(method); } else { ldc(method, programPoint); JSType.REM_EXACT.invoke(method); } return INT; }
private static Object get(final Object self, final int key) { final CharSequence cs = JSType.toCharSequence(self); if (key >= 0 && key < cs.length()) { return String.valueOf(cs.charAt(key)); } return ((ScriptObject) Global.toObject(self)).get(key); }
/** * Returns a canonicalized key object by converting numbers to their narrowest representation and * ConsStrings to strings. Conversion of Double to Integer also takes care of converting -0 to 0 * as required by step 6 of ECMA6 23.1.3.9. * * @param key a key * @return the canonical key */ static Object convertKey(final Object key) { if (key instanceof ConsString) { return key.toString(); } if (key instanceof Double) { final Double d = (Double) key; if (JSType.isRepresentableAsInt(d.doubleValue())) { return d.intValue(); } } return key; }
@SuppressWarnings("LeakingThisInConstructor") private NativeEvalError(final Object msg, final ScriptObject proto, final PropertyMap map) { super(proto, map); if (msg != UNDEFINED) { this.instMessage = JSType.toString(msg); } else { this.delete(NativeError.MESSAGE, false); } NativeError.initException(this); }