package com.oracle.truffle.js.runtime.interop;
import java.util.ArrayList;
import java.util.List;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.interop.InteropLibrary;
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.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.DynamicObjectLibrary;
import com.oracle.truffle.js.nodes.interop.ExportValueNode;
import com.oracle.truffle.js.runtime.objects.Dead;
import com.oracle.truffle.js.runtime.objects.JSProperty;
@ExportLibrary(InteropLibrary.class)
public final class DynamicScopeWrapper implements TruffleObject {
final DynamicObject scope;
public DynamicScopeWrapper(DynamicObject scope) {
this.scope = scope;
}
boolean isConst(String name, DynamicObjectLibrary access) {
return JSProperty.isConst(access.getProperty(scope, name));
}
@SuppressWarnings("static-method")
@ExportMessage
boolean hasMembers() {
return true;
}
@ExportMessage
@TruffleBoundary
Object getMembers(@SuppressWarnings("unused") boolean includeInternal,
@CachedLibrary("this.scope") DynamicObjectLibrary access) {
List<String> keys = new ArrayList<>();
for (Object key : access.getKeyArray(scope)) {
if (key instanceof String) {
Object value = access.getOrDefault(scope, key, null);
if (value != null && value != Dead.instance()) {
keys.add((String) key);
}
}
}
return InteropList.create(keys);
}
@ExportMessage
boolean isMemberReadable(String name,
@CachedLibrary("this.scope") DynamicObjectLibrary access) {
Object value = access.getOrDefault(scope, name, null);
if (value == null || value == Dead.instance()) {
return false;
}
return true;
}
@ExportMessage
boolean isMemberModifiable(String name,
@CachedLibrary("this.scope") DynamicObjectLibrary access) {
return isMemberReadable(name, access) && !isConst(name, access);
}
@SuppressWarnings("static-method")
@ExportMessage
boolean isMemberInsertable(@SuppressWarnings("unused") String name) {
return false;
}
@ExportMessage
Object readMember(String name,
@CachedLibrary("this.scope") DynamicObjectLibrary access,
@Cached ExportValueNode exportValueNode) throws UnknownIdentifierException {
Object value = access.getOrDefault(scope, name, null);
if (value == null || value == Dead.instance()) {
throw UnknownIdentifierException.create(name);
} else {
return exportValueNode.execute(value);
}
}
@ExportMessage
void writeMember(String name, Object value,
@CachedLibrary("this.scope") DynamicObjectLibrary access) throws UnsupportedMessageException, UnknownIdentifierException {
Object curValue = access.getOrDefault(scope, name, null);
if (curValue == null || curValue == Dead.instance()) {
throw UnknownIdentifierException.create(name);
} else if (!isConst(name, access)) {
access.putIfPresent(scope, name, value);
} else {
throw UnsupportedMessageException.create();
}
}
}