/** * ECMA 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] ) * * @param self self reference * @param callbackfn callback function per element * @param thisArg this argument * @return array with elements transformed by map function */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static NativeArray map(final Object self, final Object callbackfn, final Object thisArg) { return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, null) { private final MethodHandle mapInvoker = getMAP_CALLBACK_INVOKER(); @Override protected boolean forEach(final Object val, final long i) throws Throwable { final Object r = mapInvoker.invokeExact(callbackfn, thisArg, val, i, self); result.defineOwnProperty(ArrayIndex.getArrayIndex(index), r); return true; } @Override public void applyLoopBegin(final ArrayLikeIterator<Object> iter0) { // map return array should be of same length as source array // even if callback reduces source array length result = new NativeArray(iter0.getLength()); } }.apply(); }
/** * ECMA 15.4.4.20 Array.prototype.filter ( callbackfn [ , thisArg ] ) * * @param self self reference * @param callbackfn callback function per element * @param thisArg this argument * @return filtered array */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static NativeArray filter(final Object self, final Object callbackfn, final Object thisArg) { return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, new NativeArray()) { private long to = 0; private final MethodHandle filterInvoker = getFILTER_CALLBACK_INVOKER(); @Override protected boolean forEach(final Object val, final long i) throws Throwable { if ((boolean)filterInvoker.invokeExact(callbackfn, thisArg, val, i, self)) { result.defineOwnProperty(ArrayIndex.getArrayIndex(to++), val); } return true; } }.apply(); }
/** * ECMA 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] ) * * @param self self reference * @param callbackfn callback function per element * @param thisArg this argument * @return array with elements transformed by map function */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static NativeArray map(final Object self, final Object callbackfn, final Object thisArg) { return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, null) { private final MethodHandle mapInvoker = getMAP_CALLBACK_INVOKER(); @Override protected boolean forEach(final Object val, final double i) throws Throwable { final Object r = mapInvoker.invokeExact(callbackfn, thisArg, val, i, self); result.defineOwnProperty(ArrayIndex.getArrayIndex(index), r); return true; } @Override public void applyLoopBegin(final ArrayLikeIterator<Object> iter0) { // map return array should be of same length as source array // even if callback reduces source array length result = new NativeArray(iter0.getLength()); } }.apply(); }
/** * ECMA 15.4.4.20 Array.prototype.filter ( callbackfn [ , thisArg ] ) * * @param self self reference * @param callbackfn callback function per element * @param thisArg this argument * @return filtered array */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static NativeArray filter(final Object self, final Object callbackfn, final Object thisArg) { return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, new NativeArray()) { private long to = 0; private final MethodHandle filterInvoker = getFILTER_CALLBACK_INVOKER(); @Override protected boolean forEach(final Object val, final double i) throws Throwable { if ((boolean)filterInvoker.invokeExact(callbackfn, thisArg, val, i, self)) { result.defineOwnProperty(ArrayIndex.getArrayIndex(to++), val); } return true; } }.apply(); }
@Override public void populateRange(final MethodEmitter method, final Type objectType, final int objectSlot, final int start, final int end) { method.load(objectType, objectSlot); // Set values. for (int i = start; i < end; i++) { final MapTuple<T> tuple = tuples.get(i); //we only load when we have both symbols and values (which can be == the symbol) //if we didn't load, we need an array property if (tuple.symbol != null && tuple.value != null) { final int index = getArrayIndex(tuple.key); method.dup(); if (!isValidArrayIndex(index)) { putField(method, tuple.key, tuple.symbol.getFieldIndex(), tuple); } else { putSlot(method, ArrayIndex.toLongIndex(index), tuple); } //this is a nop of tuple.key isn't e.g. "apply" or another special name method.invalidateSpecialName(tuple.key); } } }
@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); }
@Override public Object get(final Object key) { final Object primitiveKey = JSType.toPrimitive(key, String.class); final int index = ArrayIndex.getArrayIndex(primitiveKey); if (index >= 0 && index < value.length()) { return String.valueOf(value.charAt(index)); } return super.get(primitiveKey); }
@Override public Object getOwnPropertyDescriptor(final String key) { final int index = ArrayIndex.getArrayIndex(key); if (index >= 0 && index < value.length()) { final Global global = Global.instance(); return global.newDataDescriptor(String.valueOf(value.charAt(index)), false, true, false); } return super.getOwnPropertyDescriptor(key); }
/** * ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw ) as specialized in * ECMA 10.6 for Arguments object. */ @Override public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) { final int index = ArrayIndex.getArrayIndex(key); if (index >= 0) { final boolean isMapped = isMapped(index); final Object oldValue = isMapped ? getArray().getObject(index) : null; if (!super.defineOwnProperty(key, propertyDesc, false)) { if (reject) { throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this)); } return false; } if (isMapped) { // When mapped argument is redefined, if new descriptor is accessor property // or data-non-writable property, we have to "unmap" (unlink). final PropertyDescriptor desc = toPropertyDescriptor(Global.instance(), propertyDesc); if (desc.type() == PropertyDescriptor.ACCESSOR) { setDeleted(index, oldValue); } else if (desc.has(PropertyDescriptor.WRITABLE) && !desc.isWritable()) { // delete and set value from new descriptor if it has one, otherwise use old value setDeleted(index, desc.has(PropertyDescriptor.VALUE) ? desc.getValue() : oldValue); } else if (desc.has(PropertyDescriptor.VALUE)) { setArray(getArray().set(index, desc.getValue(), false)); } } return true; } return super.defineOwnProperty(key, propertyDesc, reject); }
/** * ECMA 15.4.4.10 Array.prototype.slice ( start [ , end ] ) * * @param self self reference * @param start start of slice (inclusive) * @param end end of slice (optional, exclusive) * @return sliced array */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object slice(final Object self, final Object start, final Object end) { final Object obj = Global.toObject(self); if (!(obj instanceof ScriptObject)) { return ScriptRuntime.UNDEFINED; } final ScriptObject sobj = (ScriptObject)obj; final long len = JSType.toUint32(sobj.getLength()); final long relativeStart = JSType.toLong(start); final long relativeEnd = end == ScriptRuntime.UNDEFINED ? len : JSType.toLong(end); long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); if (k >= finale) { return new NativeArray(0); } if (bulkable(sobj)) { return new NativeArray(sobj.getArray().slice(k, finale)); } // Construct array with proper length to have a deleted filter on undefined elements final NativeArray copy = new NativeArray(finale - k); for (long n = 0; k < finale; n++, k++) { if (sobj.has(k)) { copy.defineOwnProperty(ArrayIndex.getArrayIndex(n), sobj.get(k)); } } return copy; }
private static void setPropertyValue(final ScriptObject sobj, final String name, final Object value) { final int index = ArrayIndex.getArrayIndex(name); if (ArrayIndex.isValidArrayIndex(index)) { // array index key sobj.defineOwnProperty(index, value); } else if (sobj.getMap().findProperty(name) != null) { // pre-existing non-inherited property, call set sobj.set(name, value, 0); } else { // add new property sobj.addOwnProperty(name, Property.WRITABLE_ENUMERABLE_CONFIGURABLE, value); } }
private void doesNotHave(final int index, final int value, final int callSiteFlags) { final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); setArray(getArray().set(index, value, strict)); doesNotHaveEnsureDelete(longIndex, oldLength, strict); } }
private void doesNotHave(final int index, final long value, final int callSiteFlags) { final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); setArray(getArray().set(index, value, strict)); doesNotHaveEnsureDelete(longIndex, oldLength, strict); } }
private void doesNotHave(final int index, final double value, final int callSiteFlags) { final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); setArray(getArray().set(index, value, strict)); doesNotHaveEnsureDelete(longIndex, oldLength, strict); } }
private void doesNotHave(final int index, final Object value, final int callSiteFlags) { final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); setArray(getArray().set(index, value, strict)); doesNotHaveEnsureDelete(longIndex, oldLength, strict); } }
private boolean hasArrayProperty(final int index) { boolean hasArrayKeys = false; for (ScriptObject self = this; self != null; self = self.getProto()) { if (self.getArray().has(index)) { return true; } hasArrayKeys = hasArrayKeys || self.getMap().containsArrayKeys(); } return hasArrayKeys && hasProperty(ArrayIndex.toKey(index), true); }
private static ArrayData addArrayElement(final ArrayData arrayData, final int index, final Object value) { final long oldLength = arrayData.length(); final long longIndex = ArrayIndex.toLongIndex(index); ArrayData newArrayData = arrayData; if (longIndex >= oldLength) { newArrayData = newArrayData.ensure(longIndex); if (longIndex > oldLength) { newArrayData = newArrayData.delete(oldLength, longIndex - 1); } } return newArrayData.set(index, value, false); }
@Override public Object getOwnPropertyDescriptor(final Object key) { final int index = ArrayIndex.getArrayIndex(key); if (index >= 0 && index < value.length()) { final Global global = Global.instance(); return global.newDataDescriptor(String.valueOf(value.charAt(index)), false, true, false); } return super.getOwnPropertyDescriptor(key); }
/** * ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw ) as specialized in * ECMA 10.6 for Arguments object. */ @Override public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) { final int index = ArrayIndex.getArrayIndex(key); if (index >= 0) { final boolean isMapped = isMapped(index); final Object oldValue = isMapped ? getArray().getObject(index) : null; if (!super.defineOwnProperty(key, propertyDesc, false)) { if (reject) { throw typeError("cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); } return false; } if (isMapped) { // When mapped argument is redefined, if new descriptor is accessor property // or data-non-writable property, we have to "unmap" (unlink). final PropertyDescriptor desc = toPropertyDescriptor(Global.instance(), propertyDesc); if (desc.type() == PropertyDescriptor.ACCESSOR) { setDeleted(index, oldValue); } else if (desc.has(PropertyDescriptor.WRITABLE) && !desc.isWritable()) { // delete and set value from new descriptor if it has one, otherwise use old value setDeleted(index, desc.has(PropertyDescriptor.VALUE) ? desc.getValue() : oldValue); } else if (desc.has(PropertyDescriptor.VALUE)) { setArray(getArray().set(index, desc.getValue(), false)); } } return true; } return super.defineOwnProperty(key, propertyDesc, reject); }
private void doesNotHave(final int index, final int value, final int callSiteFlags) { final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); } }
private void doesNotHave(final int index, final double value, final int callSiteFlags) { final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); } }
private void doesNotHave(final int index, final Object value, final int callSiteFlags) { final long oldLength = getArray().length(); final long longIndex = ArrayIndex.toLongIndex(index); if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { final boolean strict = isStrictFlag(callSiteFlags); setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); } }