package com.oracle.truffle.js.test.polyglot;
import java.util.HashMap;
import java.util.Set;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.utilities.TriState;
import com.oracle.truffle.js.lang.JavaScriptLanguage;
import com.oracle.truffle.js.runtime.JSRealm;
import com.oracle.truffle.js.runtime.JSRuntime;
@ExportLibrary(InteropLibrary.class)
public class ForeignTestMap implements TruffleObject {
private final HashMap<Object, Object> container = new HashMap<>();
public HashMap<Object, Object> getContainer() {
CompilerAsserts.neverPartOfCompilation();
return container;
}
public static TruffleObject newNull() {
ForeignTestMap obj = new ForeignTestMap();
obj.getContainer().put("IS_NULL", true);
return obj;
}
@ExportMessage
@TruffleBoundary
public boolean isNull() {
return getContainer().containsKey("IS_NULL");
}
@ExportMessage
public boolean hasMembers() {
return true;
}
@ExportMessage
@TruffleBoundary
final Object readMember(String key) throws UnknownIdentifierException {
if (getContainer().containsKey(key)) {
return JSRuntime.nullToUndefined(getContainer().get(key));
} else {
throw UnknownIdentifierException.create(key);
}
}
@ExportMessage
@TruffleBoundary
final void writeMember(String key, Object value) {
getContainer().put(key, value);
}
@ExportMessage
@TruffleBoundary
final void removeMember(String key) throws UnknownIdentifierException {
if (getContainer().containsKey(key)) {
getContainer().remove(key);
} else {
throw UnknownIdentifierException.create(key);
}
}
@ExportMessage
@TruffleBoundary
final Object invokeMember(String key, Object[] args) throws UnknownIdentifierException, UnsupportedTypeException, ArityException, UnsupportedMessageException {
if (getContainer().containsKey(key)) {
Object member = getContainer().get(key);
InteropLibrary lib = InteropLibrary.getFactory().getUncached();
return lib.execute(member, args);
} else {
throw UnknownIdentifierException.create(key);
}
}
@ExportMessage
@TruffleBoundary
final boolean isMemberInvocable(String key) {
if (getContainer().containsKey(key)) {
Object member = getContainer().get(key);
InteropLibrary lib = InteropLibrary.getFactory().getUncached();
return lib.isExecutable(member);
} else {
return false;
}
}
@ExportMessage(name = "isMemberReadable")
@ExportMessage(name = "isMemberModifiable")
@ExportMessage(name = "isMemberRemovable")
@TruffleBoundary
final boolean isMemberExisting(String key) {
return getContainer().containsKey(key);
}
@ExportMessage
@TruffleBoundary
final boolean isMemberInsertable(String key) {
return !isMemberExisting(key);
}
@ExportMessage
@TruffleBoundary
final Object getMembers(@SuppressWarnings("unused") boolean includeInternal,
@CachedContext(JavaScriptLanguage.class) JSRealm realm) {
Set<Object> keys = getContainer().keySet();
TruffleLanguage.Env env = realm.getEnv();
return env.asGuestValue(keys.toArray(new Object[keys.size()]));
}
@ExportMessage
@TruffleBoundary
final boolean hasArrayElements() {
return getContainer().containsKey("length");
}
@ExportMessage
@TruffleBoundary
final long getArraySize() throws UnsupportedMessageException {
if (hasArrayElements()) {
InteropLibrary lib = InteropLibrary.getFactory().getUncached();
return lib.asLong(getContainer().get("length"));
} else {
throw UnsupportedMessageException.create();
}
}
@ExportMessage
@TruffleBoundary
final Object readArrayElement(long index) throws UnsupportedMessageException, InvalidArrayIndexException {
if (hasArrayElements()) {
if (getContainer().containsKey(index)) {
return getContainer().get(index);
} else {
throw InvalidArrayIndexException.create(index);
}
} else {
throw UnsupportedMessageException.create();
}
}
@ExportMessage
@TruffleBoundary
final void writeArrayElement(long index, Object value) throws UnsupportedMessageException, InvalidArrayIndexException {
if (hasArrayElements()) {
if (index >= 0L && index < getArraySize()) {
getContainer().put(index, value);
} else {
throw InvalidArrayIndexException.create(index);
}
} else {
throw UnsupportedMessageException.create();
}
}
@ExportMessage(name = "isArrayElementModifiable")
@ExportMessage
@TruffleBoundary
final boolean isArrayElementReadable(long index) {
return hasArrayElements() && getContainer().containsKey(index);
}
@ExportMessage
@TruffleBoundary
final boolean isArrayElementInsertable(long index) {
return hasArrayElements() && !getContainer().containsKey(index);
}
@ExportMessage
@TruffleBoundary
final boolean isString() {
return getContainer().get("BOX") instanceof String;
}
@ExportMessage
@TruffleBoundary
final String asString() throws UnsupportedMessageException {
if (isString()) {
return (String) getContainer().get("BOX");
} else {
throw UnsupportedMessageException.create();
}
}
@ExportMessage
static final class IsIdenticalOrUndefined {
@Specialization
static TriState doSLObject(ForeignTestMap receiver, ForeignTestMap other) {
return TriState.valueOf(receiver == other);
}
@SuppressWarnings("unused")
@Fallback
static TriState doOther(ForeignTestMap receiver, Object other) {
return TriState.UNDEFINED;
}
}
@TruffleBoundary
@ExportMessage
final int identityHashCode() {
return System.identityHashCode(this);
}
}