package com.oracle.truffle.js.nodes.array;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.profiles.ValueProfile;
import com.oracle.truffle.js.nodes.access.JSHasPropertyNode;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.array.ScriptArray;
import com.oracle.truffle.js.runtime.objects.JSObject;
import com.oracle.truffle.js.runtime.objects.Null;
public abstract class JSArrayPreviousElementIndexNode extends JSArrayElementIndexNode {
protected JSArrayPreviousElementIndexNode(JSContext context) {
super(context);
}
public static JSArrayPreviousElementIndexNode create(JSContext context) {
return JSArrayPreviousElementIndexNodeGen.create(context);
}
public final long executeLong(Object object, long currentIndex) {
return executeLong(object, currentIndex, isArray(object));
}
public abstract long executeLong(Object object, long currentIndex, boolean isArray);
@Specialization(guards = {"isArray", "!hasPrototypeElements(object)", "getArrayType(object) == cachedArrayType",
"!cachedArrayType.hasHoles(object)"}, limit = "MAX_CACHED_ARRAY_TYPES")
public long doWithoutHolesCached(DynamicObject object, long currentIndex, @SuppressWarnings("unused") boolean isArray,
@Cached("getArrayTypeIfArray(object, isArray)") ScriptArray cachedArrayType) {
assert isSupportedArray(object) && cachedArrayType == getArrayType(object);
return cachedArrayType.previousElementIndex(object, currentIndex);
}
@Specialization(guards = {"isArray", "!hasPrototypeElements(object)", "!hasHoles(object)"}, replaces = "doWithoutHolesCached")
public long doWithoutHolesUncached(DynamicObject object, long currentIndex, @SuppressWarnings("unused") boolean isArray) {
assert isSupportedArray(object);
return getArrayType(object).previousElementIndex(object, currentIndex);
}
@Specialization(guards = {"isArray", "!hasPrototypeElements(object)", "getArrayType(object) == cachedArrayType",
"cachedArrayType.hasHoles(object)"}, limit = "MAX_CACHED_ARRAY_TYPES")
public long previousWithHolesCached(DynamicObject object, long currentIndex, boolean isArray,
@Cached("getArrayTypeIfArray(object, isArray)") ScriptArray cachedArrayType,
@Cached("create(context)") JSArrayPreviousElementIndexNode previousElementIndexNode,
@Cached("createBinaryProfile()") ConditionProfile isMinusOne) {
assert isSupportedArray(object) && cachedArrayType == getArrayType(object);
return holesArrayImpl(object, currentIndex, isArray, cachedArrayType, previousElementIndexNode, isMinusOne);
}
@Specialization(guards = {"isArray", "hasPrototypeElements(object) || hasHoles(object)"}, replaces = "previousWithHolesCached")
public long previousWithHolesUncached(DynamicObject object, long currentIndex, boolean isArray,
@Cached("create(context)") JSArrayPreviousElementIndexNode previousElementIndexNode,
@Cached("createBinaryProfile()") ConditionProfile isMinusOne,
@Cached("createClassProfile()") ValueProfile arrayTypeProfile) {
assert isSupportedArray(object);
ScriptArray arrayType = arrayTypeProfile.profile(getArrayType(object));
return holesArrayImpl(object, currentIndex, isArray, arrayType, previousElementIndexNode, isMinusOne);
}
private long holesArrayImpl(DynamicObject object, long currentIndex, @SuppressWarnings("unused") boolean isArray, ScriptArray array,
JSArrayPreviousElementIndexNode previousElementIndexNode, ConditionProfile isMinusOne) {
long previousIndex = array.previousElementIndex(object, currentIndex);
long minusOne = (currentIndex - 1);
if (isMinusOne.profile(previousIndex == minusOne)) {
return previousIndex;
}
if (!context.getArrayPrototypeNoElementsAssumption().isValid()) {
DynamicObject prototype = JSObject.getPrototype(object);
while (prototype != Null.instance) {
long candidate = previousElementIndexNode.executeLong(prototype, currentIndex);
if (minusOne >= candidate && candidate >= -1) {
previousIndex = Math.max(previousIndex, candidate);
}
prototype = JSObject.getPrototype(prototype);
}
}
return previousIndex;
}
@Specialization(guards = {"!isArray", "isSuitableForEnumBasedProcessingUsingOwnKeys(object, currentIndex)"})
public long previousObjectViaEnumeration(DynamicObject object, long currentIndex, @SuppressWarnings("unused") boolean isArray,
@Cached("create()") JSHasPropertyNode hasPropertyNode) {
long currentIndexMinusOne = currentIndex - 1;
if (hasPropertyNode.executeBoolean(object, currentIndexMinusOne)) {
return currentIndexMinusOne;
}
return previousObjectViaEnumerationIntl(object, currentIndex);
}
@Specialization(guards = {"!isArray", "!isSuitableForEnumBasedProcessingUsingOwnKeys(object, currentIndex)", "isSuitableForEnumBasedProcessing(object, currentIndex)"})
public long previousObjectViaFullEnumeration(DynamicObject object, long currentIndex, @SuppressWarnings("unused") boolean isArray,
@Cached("create()") JSHasPropertyNode hasPropertyNode) {
long currentIndexMinusOne = currentIndex - 1;
if (hasPropertyNode.executeBoolean(object, currentIndexMinusOne)) {
return currentIndexMinusOne;
}
return previousObjectViaFullEnumerationIntl(object, currentIndex);
}
@Specialization(guards = {"!isArray", "!isSuitableForEnumBasedProcessing(object, currentIndex)"})
public long previousObjectViaIteration(Object object, long currentIndex, @SuppressWarnings("unused") boolean isArray,
@Cached("create()") JSHasPropertyNode hasPropertyNode) {
long index = currentIndex - 1;
while (index >= 0 && !hasPropertyNode.executeBoolean(object, index)) {
index--;
}
return index;
}
@TruffleBoundary
private static long previousObjectViaEnumerationIntl(DynamicObject object, long currentIndex) {
long result = -1;
for (Object key : JSObject.ownPropertyKeys(object)) {
if (key == null) {
continue;
}
if (key instanceof String) {
long candidate = JSRuntime.propertyNameToIntegerIndex((String) key);
if (candidate < currentIndex && candidate > result) {
result = candidate;
}
}
}
return result;
}
@TruffleBoundary
private static long previousObjectViaFullEnumerationIntl(DynamicObject object, long currentIndex) {
long result = -1;
DynamicObject chainObject = object;
do {
result = Math.max(result, previousObjectViaEnumerationIntl(chainObject, currentIndex));
chainObject = JSObject.getPrototype(chainObject);
} while (chainObject != Null.instance);
return result;
}
}