
org.mozilla.javascript.ArrayLikeAbstractOperations Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhino Show documentation
Show all versions of rhino Show documentation
Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically
embedded into Java applications to provide scripting to end users.
The newest version!
package org.mozilla.javascript;
import static org.mozilla.javascript.NativeArray.getLengthProperty;
import static org.mozilla.javascript.ScriptRuntimeES6.requireObjectCoercible;
import static org.mozilla.javascript.Scriptable.NOT_FOUND;
import java.io.Serializable;
import java.util.Comparator;
/** Contains implementation of shared methods useful for arrays and typed arrays. */
public class ArrayLikeAbstractOperations {
public enum IterativeOperation {
EVERY,
FILTER,
FOR_EACH,
MAP,
SOME,
FIND,
FIND_INDEX,
FIND_LAST,
FIND_LAST_INDEX,
}
public enum ReduceOperation {
REDUCE,
REDUCE_RIGHT,
}
/**
* Implements the methods "every", "filter", "forEach", "map", and "some" without using an
* IdFunctionObject.
*/
public static Object iterativeMethod(
Context cx,
IterativeOperation operation,
Scriptable scope,
Scriptable thisObj,
Object[] args) {
return iterativeMethod(cx, null, operation, scope, thisObj, args, true);
}
/**
* Implements the methods "every", "filter", "forEach", "map", and "some" using an
* IdFunctionObject.
*/
public static Object iterativeMethod(
Context cx,
IdFunctionObject fun,
IterativeOperation operation,
Scriptable scope,
Scriptable thisObj,
Object[] args) {
return iterativeMethod(cx, fun, operation, scope, thisObj, args, false);
}
private static Object iterativeMethod(
Context cx,
IdFunctionObject fun,
IterativeOperation operation,
Scriptable scope,
Scriptable thisObj,
Object[] args,
boolean skipCoercibleCheck) {
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
if (!skipCoercibleCheck) {
if (IterativeOperation.FIND == operation
|| IterativeOperation.FIND_INDEX == operation
|| IterativeOperation.FIND_LAST == operation
|| IterativeOperation.FIND_LAST_INDEX == operation) {
requireObjectCoercible(cx, o, fun);
}
}
long length = getLengthProperty(cx, o);
if (operation == IterativeOperation.MAP && length > Integer.MAX_VALUE) {
String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
throw ScriptRuntime.rangeError(msg);
}
Object callbackArg = args.length > 0 ? args[0] : Undefined.instance;
Function f = getCallbackArg(cx, callbackArg);
Scriptable parent = ScriptableObject.getTopLevelScope(f);
Scriptable thisArg;
if (args.length < 2 || args[1] == null || args[1] == Undefined.instance) {
thisArg = parent;
} else {
thisArg = ScriptRuntime.toObject(cx, scope, args[1]);
}
Scriptable array = null;
if (operation == IterativeOperation.FILTER || operation == IterativeOperation.MAP) {
int resultLength = operation == IterativeOperation.MAP ? (int) length : 0;
array = cx.newArray(scope, resultLength);
}
long j = 0;
long start =
(operation == IterativeOperation.FIND_LAST
|| operation == IterativeOperation.FIND_LAST_INDEX)
? length - 1
: 0;
long end =
(operation == IterativeOperation.FIND_LAST
|| operation == IterativeOperation.FIND_LAST_INDEX)
? -1
: length;
long increment =
(operation == IterativeOperation.FIND_LAST
|| operation == IterativeOperation.FIND_LAST_INDEX)
? -1
: +1;
for (long i = start; i != end; i += increment) {
Object[] innerArgs = new Object[3];
Object elem = getRawElem(o, i);
if (elem == NOT_FOUND) {
if (operation == IterativeOperation.FIND
|| operation == IterativeOperation.FIND_INDEX
|| operation == IterativeOperation.FIND_LAST
|| operation == IterativeOperation.FIND_LAST_INDEX) {
elem = Undefined.instance;
} else {
continue;
}
}
innerArgs[0] = elem;
innerArgs[1] = Long.valueOf(i);
innerArgs[2] = o;
Object result = f.call(cx, parent, thisArg, innerArgs);
switch (operation) {
case EVERY:
if (!ScriptRuntime.toBoolean(result)) return Boolean.FALSE;
break;
case FILTER:
if (ScriptRuntime.toBoolean(result)) defineElem(cx, array, j++, innerArgs[0]);
break;
case FOR_EACH:
break;
case MAP:
defineElem(cx, array, i, result);
break;
case SOME:
if (ScriptRuntime.toBoolean(result)) return Boolean.TRUE;
break;
case FIND:
case FIND_LAST:
if (ScriptRuntime.toBoolean(result)) return elem;
break;
case FIND_INDEX:
case FIND_LAST_INDEX:
if (ScriptRuntime.toBoolean(result))
return ScriptRuntime.wrapNumber((double) i);
break;
}
}
switch (operation) {
case EVERY:
return Boolean.TRUE;
case FILTER:
case MAP:
return array;
case SOME:
return Boolean.FALSE;
case FIND_INDEX:
case FIND_LAST_INDEX:
return ScriptRuntime.wrapNumber(-1);
case FOR_EACH:
default:
return Undefined.instance;
}
}
static Function getCallbackArg(Context cx, Object callbackArg) {
if (!(callbackArg instanceof Function)) {
throw ScriptRuntime.notFunctionError(callbackArg);
}
Function f = (Function) callbackArg;
if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
RegExpProxy reProxy = ScriptRuntime.getRegExpProxy(cx);
if (reProxy != null && reProxy.isRegExp(f))
// Previously, it was allowed to pass RegExp instance as a callback (it implements
// Function)
// But according to ES2015 21.2.6 Properties of RegExp Instances:
// > RegExp instances are ordinary objects that inherit properties from the RegExp
// prototype object.
// > RegExp instances have internal slots [[RegExpMatcher]], [[OriginalSource]], and
// [[OriginalFlags]].
// so, no [[Call]] for RegExp-s
throw ScriptRuntime.notFunctionError(callbackArg);
}
return f;
}
static void defineElem(Context cx, Scriptable target, long index, Object value) {
if (index > Integer.MAX_VALUE) {
String id = Long.toString(index);
target.put(id, target, value);
} else {
target.put((int) index, target, value);
}
}
// same as NativeArray::getElem, but without converting NOT_FOUND to undefined
static Object getRawElem(Scriptable target, long index) {
if (index > Integer.MAX_VALUE) {
return ScriptableObject.getProperty(target, Long.toString(index));
}
return ScriptableObject.getProperty(target, (int) index);
}
public static long toSliceIndex(double value, long length) {
long result;
if (value < 0.0) {
if (value + length < 0.0) {
result = 0;
} else {
result = (long) (value + length);
}
} else if (value > length) {
result = length;
} else {
result = (long) value;
}
return result;
}
/** Implements the methods "reduce" and "reduceRight". */
public static Object reduceMethod(
Context cx,
ReduceOperation operation,
Scriptable scope,
Scriptable thisObj,
Object[] args) {
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
long length = getLengthProperty(cx, o);
Object callbackArg = args.length > 0 ? args[0] : Undefined.instance;
if (callbackArg == null || !(callbackArg instanceof Function)) {
throw ScriptRuntime.notFunctionError(callbackArg);
}
Function f = (Function) callbackArg;
Scriptable parent = ScriptableObject.getTopLevelScope(f);
// hack to serve both reduce and reduceRight with the same loop
boolean movingLeft = operation == ReduceOperation.REDUCE;
Object value = args.length > 1 ? args[1] : NOT_FOUND;
for (long i = 0; i < length; i++) {
long index = movingLeft ? i : (length - 1 - i);
Object elem = getRawElem(o, index);
if (elem == NOT_FOUND) {
continue;
}
if (value == NOT_FOUND) {
// no initial value passed, use first element found as initial value
value = elem;
} else {
Object[] innerArgs = {value, elem, index, o};
value = f.call(cx, parent, parent, innerArgs);
}
}
if (value == NOT_FOUND) {
// reproduce spidermonkey error message
throw ScriptRuntime.typeErrorById("msg.empty.array.reduce");
}
return value;
}
public static Comparator
© 2015 - 2025 Weber Informatics LLC | Privacy Policy