package com.oracle.truffle.js.runtime.builtins;
import java.util.HashMap;
import java.util.Map;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage.LanguageReference;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.CachedLanguage;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.js.lang.JavaScriptLanguage;
import com.oracle.truffle.js.nodes.access.ReadElementNode;
import com.oracle.truffle.js.nodes.access.WriteElementNode;
import com.oracle.truffle.js.nodes.interop.ArrayElementInfoNode;
import com.oracle.truffle.js.nodes.interop.ExportValueNode;
import com.oracle.truffle.js.nodes.interop.ImportValueNode;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.array.ScriptArray;
import com.oracle.truffle.js.runtime.interop.InteropArray;
import com.oracle.truffle.js.runtime.objects.JSObject;
import com.oracle.truffle.js.runtime.objects.Undefined;
@ExportLibrary(InteropLibrary.class)
public class JSArgumentsObject extends JSArrayBase {
protected JSArgumentsObject(Shape shape, ScriptArray arrayType, Object array, int length) {
super(shape, arrayType, array, null, length, 0, 0, 0, 0);
}
public static final class Unmapped extends JSArgumentsObject {
protected Unmapped(Shape shape, ScriptArray arrayType, Object array, int length) {
super(shape, arrayType, array, length);
}
}
public static final class Mapped extends JSArgumentsObject {
protected Mapped(Shape shape, ScriptArray arrayType, Object array, int length) {
super(shape, arrayType, array, length);
this.connectedArgumentCount = length;
}
protected int connectedArgumentCount;
protected Map<Long, Object> disconnectedIndices;
public int getConnectedArgumentCount() {
return connectedArgumentCount;
}
public Map<Long, Object> getDisconnectedIndices() {
assert JSAbstractArgumentsArray.hasDisconnectedIndices(this);
return disconnectedIndices;
}
@TruffleBoundary
public void initDisconnectedIndices() {
assert JSAbstractArgumentsArray.hasDisconnectedIndices(this);
this.disconnectedIndices = new HashMap<>();
}
}
@Override
public final String getClassName() {
return JSArgumentsArray.CLASS_NAME;
}
@ExportMessage
public final Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
assert JSObject.getJSClass(this) == JSArgumentsArray.INSTANCE;
return InteropArray.create(filterEnumerableNames(this, JSObject.ownPropertyKeys(this), JSArgumentsArray.INSTANCE));
}
@SuppressWarnings("static-method")
@ExportMessage
public final boolean hasArrayElements() {
return true;
}
@ExportMessage
public final long getArraySize() {
return JSRuntime.toInteger(JSObject.get(this, JSAbstractArray.LENGTH));
}
@ExportMessage
public final Object readArrayElement(long index,
@CachedLanguage @SuppressWarnings("unused") LanguageReference<JavaScriptLanguage> languageRef,
@Cached(value = "create(languageRef.get().getJSContext())", uncached = "getUncachedRead()") ReadElementNode readNode,
@Cached ExportValueNode exportNode,
@CachedLibrary("this") InteropLibrary thisLibrary) throws InvalidArrayIndexException, UnsupportedMessageException {
if (index < 0 || index >= thisLibrary.getArraySize(this)) {
throw InvalidArrayIndexException.create(index);
}
Object result;
if (readNode == null) {
result = JSObject.getOrDefault(this, index, this, Undefined.instance);
} else {
result = readNode.executeWithTargetAndIndexOrDefault(this, index, Undefined.instance);
}
return exportNode.execute(result);
}
@ExportMessage
public final boolean isArrayElementReadable(long index,
@CachedLibrary("this") InteropLibrary thisLibrary) {
try {
return index >= 0 && index < thisLibrary.getArraySize(this);
} catch (UnsupportedMessageException e) {
throw Errors.shouldNotReachHere(e);
}
}
@ExportMessage
public final void writeArrayElement(long index, Object value,
@Shared("elementInfo") @Cached ArrayElementInfoNode elements,
@Cached ImportValueNode castValueNode,
@CachedLanguage @SuppressWarnings("unused") LanguageReference<JavaScriptLanguage> languageRef,
@Cached(value = "createCachedInterop(languageRef)", uncached = "getUncachedWrite()") WriteElementNode writeNode) throws InvalidArrayIndexException, UnsupportedMessageException {
elements.executeCheck(this, index, ArrayElementInfoNode.WRITABLE);
Object importedValue = castValueNode.executeWithTarget(value);
if (writeNode == null) {
JSObject.set(this, index, importedValue, true, null);
} else {
writeNode.executeWithTargetAndIndexAndValue(this, index, importedValue);
}
}
@ExportMessage
public final boolean isArrayElementModifiable(long index,
@Shared("elementInfo") @Cached ArrayElementInfoNode elements) {
return elements.executeBoolean(this, index, ArrayElementInfoNode.MODIFIABLE);
}
@ExportMessage
public final boolean isArrayElementInsertable(long index,
@Shared("elementInfo") @Cached ArrayElementInfoNode elements) {
return elements.executeBoolean(this, index, ArrayElementInfoNode.INSERTABLE);
}
}