package com.oracle.truffle.js.runtime.array.dyn;
import static com.oracle.truffle.js.runtime.builtins.JSAbstractArray.arrayGetArray;
import static com.oracle.truffle.js.runtime.builtins.JSAbstractArray.arraySetArray;
import java.util.Arrays;
import java.util.List;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.js.runtime.JSConfig;
import com.oracle.truffle.js.runtime.array.DynamicArray;
import com.oracle.truffle.js.runtime.array.ScriptArray;
import com.oracle.truffle.js.runtime.objects.JSDynamicObject;
import com.oracle.truffle.js.runtime.objects.Undefined;
public final class ConstantObjectArray extends AbstractConstantArray {
private static final ConstantObjectArray CONSTANT_OBJECT_ARRAY = new ConstantObjectArray(false, INTEGRITY_LEVEL_NONE, createCache());
private static final ConstantObjectArray CONSTANT_HOLES_OBJECT_ARRAY = new ConstantObjectArray(true, INTEGRITY_LEVEL_NONE, createCache());
public static ConstantObjectArray createConstantObjectArray() {
return CONSTANT_OBJECT_ARRAY;
}
public static AbstractConstantArray createConstantHolesObjectArray() {
return CONSTANT_HOLES_OBJECT_ARRAY;
}
private final boolean holes;
private ConstantObjectArray(boolean holes, int integrityLevel, DynamicArrayCache cache) {
super(integrityLevel, cache);
this.holes = holes;
}
private static Object[] getArray(DynamicObject object) {
return (Object[]) arrayGetArray(object);
}
@Override
public boolean hasElement(DynamicObject object, long index) {
if (index >= 0 && index < getArray(object).length) {
return !holes || getArray(object)[(int) index] != null;
}
return false;
}
@Override
public Object getElementInBounds(DynamicObject object, int index) {
Object value = getElementInBoundsDirect(object, index);
if (holes && value == null) {
return Undefined.instance;
}
return value;
}
private static boolean isEmpty(DynamicObject object, int index) {
return getArray(object)[index] == null;
}
public static Object getElementInBoundsDirect(DynamicObject object, int index) {
return getArray(object)[index];
}
@Override
public boolean hasHoles(DynamicObject object) {
return holes;
}
@Override
public int lengthInt(DynamicObject object) {
return getArray(object).length;
}
@Override
public Object[] toArray(DynamicObject object) {
if (hasHoles(object)) {
Object[] array = getArray(object);
Object[] newArray = new Object[array.length];
for (int i = 0; i < array.length; i++) {
newArray[i] = array[i] == null ? Undefined.instance : array[i];
}
return newArray;
} else {
Object[] array = getArray(object);
return Arrays.copyOf(array, array.length);
}
}
@Override
public Object cloneArray(DynamicObject object) {
return getArray(object);
}
@Override
public long nextElementIndex(DynamicObject object, long index0) {
if (!holes) {
return super.nextElementIndex(object, index0);
}
int index = (int) index0;
do {
index++;
} while (index < super.lastElementIndex(object) && isEmpty(object, index));
return index;
}
@Override
public long previousElementIndex(DynamicObject object, long index0) {
if (!holes) {
return super.previousElementIndex(object, index0);
}
int index = (int) index0;
do {
index--;
} while (index >= super.firstElementIndex(object) && isEmpty(object, index));
return index;
}
@Override
public long firstElementIndex(DynamicObject object) {
if (!holes) {
return super.firstElementIndex(object);
}
int index = 0;
int length = lengthInt(object);
while (index < length && isEmpty(object, index)) {
index++;
}
return index;
}
@Override
public long lastElementIndex(DynamicObject object) {
if (!holes) {
return super.lastElementIndex(object);
}
int index = lengthInt(object);
do {
index--;
} while (index >= 0 && isEmpty(object, index));
return index;
}
@Override
public ScriptArray deleteElementImpl(DynamicObject object, long index, boolean strict) {
return createWriteableObject(object, index, null, ProfileHolder.empty()).deleteElementImpl(object, index, strict);
}
@Override
public ScriptArray setLengthImpl(DynamicObject object, long length, ProfileHolder profile) {
return createWriteableObject(object, length - 1, null, ProfileHolder.empty()).setLengthImpl(object, length, profile);
}
@Override
public AbstractObjectArray createWriteableInt(DynamicObject object, long index, int value, ProfileHolder profile) {
return createWriteableObject(object, index, value, profile);
}
@Override
public AbstractObjectArray createWriteableDouble(DynamicObject object, long index, double value, ProfileHolder profile) {
return createWriteableObject(object, index, value, profile);
}
@Override
public AbstractObjectArray createWriteableJSObject(DynamicObject object, long index, JSDynamicObject value, ProfileHolder profile) {
return createWriteableObject(object, index, value, profile);
}
@Override
public AbstractObjectArray createWriteableObject(DynamicObject object, long index, Object value, ProfileHolder profile) {
Object[] array = getArray(object);
AbstractObjectArray newArray;
if (holes) {
int arrayOffset = (int) firstElementIndex(object);
int usedLength = (int) lastElementIndex(object) + 1 - arrayOffset;
int holeCount = countHoles(object);
newArray = HolesObjectArray.makeHolesObjectArray(object, array.length, ArrayCopy.objectToObject(array), 0, arrayOffset, usedLength, holeCount, integrityLevel);
} else {
newArray = ZeroBasedObjectArray.makeZeroBasedObjectArray(object, array.length, array.length, ArrayCopy.objectToObject(array), integrityLevel);
}
if (JSConfig.TraceArrayTransitions) {
traceArrayTransition(this, newArray, index, value);
}
return newArray;
}
private int countHoles(DynamicObject object) {
int index = (int) (firstElementIndex(object));
int lastIndex = (int) (lastElementIndex(object) + 1);
Object[] objArray = getArray(object);
int holeCount = 0;
while (index < lastIndex) {
if (HolesObjectArray.isHoleValue(objArray[index])) {
holeCount++;
}
index++;
}
return holeCount;
}
@Override
public boolean isHolesType() {
return true;
}
@Override
public ScriptArray removeRangeImpl(DynamicObject object, long start, long end) {
Object[] array = getArray(object);
if ((array.length - (end - start)) == 0) {
AbstractConstantEmptyArray.setCapacity(object, 0);
} else {
Object[] newArray = new Object[array.length - (int) (end - start)];
System.arraycopy(array, 0, newArray, 0, (int) start);
System.arraycopy(array, (int) end, newArray, (int) start, (int) (array.length - end));
arraySetArray(object, newArray);
}
return this;
}
@Override
public ScriptArray addRangeImpl(DynamicObject object, long offset, int size) {
Object[] array = getArray(object);
if (array.length == 0) {
AbstractConstantEmptyArray.setCapacity(object, size);
return this;
} else {
Object[] newArray = new Object[array.length + size];
System.arraycopy(array, 0, newArray, 0, (int) offset);
System.arraycopy(array, (int) offset, newArray, (int) offset + size, (int) (array.length - offset));
arraySetArray(object, newArray);
return this;
}
}
@Override
protected DynamicArray withIntegrityLevel(int newIntegrityLevel) {
return new ConstantObjectArray(holes, newIntegrityLevel, cache);
}
@Override
public List<Object> ownPropertyKeys(DynamicObject object) {
if (holes) {
return ownPropertyKeysHoles(object);
} else {
return ownPropertyKeysContiguous(object);
}
}
}