package com.oracle.truffle.js.builtins;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.NoSuchFileException;
import com.oracle.truffle.api.CallTarget;
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.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Cached.Shared;
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.CachedLibrary;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotConstructNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotEvalFileNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotEvalNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotExecuteNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotExportNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotGetSizeNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotHasKeysNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotHasSizeNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotImportNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotIsBoxedPrimitiveNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotIsExecutableNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotIsInstantiableNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotIsNullNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotKeysNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotReadNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotRemoveNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotToJSValueNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotToPolyglotValueNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotUnboxValueNodeGen;
import com.oracle.truffle.js.builtins.PolyglotBuiltinsFactory.PolyglotWriteNodeGen;
import com.oracle.truffle.js.nodes.function.JSBuiltin;
import com.oracle.truffle.js.nodes.function.JSBuiltinNode;
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.Evaluator;
import com.oracle.truffle.js.runtime.JSConfig;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRealm;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.builtins.BuiltinEnum;
import com.oracle.truffle.js.runtime.builtins.JSArray;
import com.oracle.truffle.js.runtime.interop.JSInteropUtil;
import com.oracle.truffle.js.runtime.objects.Null;
import com.oracle.truffle.js.runtime.objects.Undefined;
import com.oracle.truffle.js.runtime.util.Pair;
public final class PolyglotBuiltins extends JSBuiltinsContainer.SwitchEnum<PolyglotBuiltins.Polyglot> {
public static final JSBuiltinsContainer BUILTINS = new PolyglotBuiltins();
public static final JSBuiltinsContainer INTERNAL_BUILTINS = new PolyglotInternalBuiltins();
protected PolyglotBuiltins() {
super(JSRealm.POLYGLOT_CLASS_NAME, Polyglot.class);
}
public enum Polyglot implements BuiltinEnum<Polyglot> {
export(2),
import_(1),
eval(2);
private final int length;
Polyglot(int length) {
this.length = length;
}
@Override
public int getLength() {
return length;
}
}
@Override
protected Object createNode(JSContext context, JSBuiltin builtin, boolean construct, boolean newTarget, Polyglot builtinEnum) {
switch (builtinEnum) {
case export:
return PolyglotExportNodeGen.create(context, builtin, args().fixedArgs(2).createArgumentNodes(context));
case import_:
return PolyglotImportNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case eval:
return PolyglotEvalNodeGen.create(context, builtin, args().fixedArgs(2).createArgumentNodes(context));
}
return null;
}
public static final class PolyglotInternalBuiltins extends JSBuiltinsContainer.SwitchEnum<PolyglotInternalBuiltins.PolyglotInternal> {
protected PolyglotInternalBuiltins() {
super(JSRealm.POLYGLOT_CLASS_NAME, PolyglotInternal.class);
}
public enum PolyglotInternal implements BuiltinEnum<PolyglotInternal> {
isExecutable(1),
isBoxed(1),
isNull(1),
hasSize(1),
read(2),
write(3),
unbox(1),
construct(1),
execute(1),
getSize(1),
remove(2),
toJSValue(1),
toPolyglotValue(1),
keys(1),
hasKeys(1),
isInstantiable(1),
evalFile(2);
private final int length;
PolyglotInternal(int length) {
this.length = length;
}
@Override
public int getLength() {
return length;
}
}
@Override
protected Object createNode(JSContext context, JSBuiltin builtin, boolean construct, boolean newTarget, PolyglotInternal builtinEnum) {
switch (builtinEnum) {
case isExecutable:
return PolyglotIsExecutableNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case isBoxed:
return PolyglotIsBoxedPrimitiveNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case isNull:
return PolyglotIsNullNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case isInstantiable:
return PolyglotIsInstantiableNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case hasSize:
return PolyglotHasSizeNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case read:
return PolyglotReadNodeGen.create(context, builtin, args().fixedArgs(2).createArgumentNodes(context));
case write:
return PolyglotWriteNodeGen.create(context, builtin, args().fixedArgs(3).createArgumentNodes(context));
case remove:
return PolyglotRemoveNodeGen.create(context, builtin, args().fixedArgs(2).createArgumentNodes(context));
case unbox:
return PolyglotUnboxValueNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case construct:
return PolyglotConstructNodeGen.create(context, builtin, args().fixedArgs(1).varArgs().createArgumentNodes(context));
case execute:
return PolyglotExecuteNodeGen.create(context, builtin, args().fixedArgs(1).varArgs().createArgumentNodes(context));
case getSize:
return PolyglotGetSizeNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case keys:
return PolyglotKeysNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case toJSValue:
return PolyglotToJSValueNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case toPolyglotValue:
return PolyglotToPolyglotValueNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case hasKeys:
return PolyglotHasKeysNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case evalFile:
return PolyglotEvalFileNodeGen.create(context, builtin, args().fixedArgs(2).createArgumentNodes(context));
}
return null;
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotExportNode extends JSBuiltinNode {
@Child private ExportValueNode exportValue;
PolyglotExportNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
exportValue = ExportValueNode.create();
}
@Specialization
protected Object doString(String identifier, Object value,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
Object polyglotBindings;
try {
polyglotBindings = getContext().getRealm().getEnv().getPolyglotBindings();
} catch (SecurityException e) {
throw Errors.createErrorFromException(e);
}
JSInteropUtil.writeMember(polyglotBindings, identifier, value, interop, exportValue, this);
return value;
}
@Specialization(guards = {"!isString(identifier)"})
protected Object doMaybeUnbox(TruffleObject identifier, Object value,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
if (interop.isString(identifier)) {
String unboxed;
try {
unboxed = interop.asString(identifier);
} catch (UnsupportedMessageException e) {
throw Errors.createTypeErrorUnboxException(identifier, e, this);
}
return doString(unboxed, value, interop);
}
return doInvalid(identifier, value);
}
@Specialization(guards = "!isString(identifier)")
@TruffleBoundary
protected Object doInvalid(Object identifier, @SuppressWarnings("unused") Object value) {
throw Errors.createTypeErrorInvalidIdentifier(identifier);
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotImportNode extends JSBuiltinNode {
PolyglotImportNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected Object doString(String identifier,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop,
@Shared("importValue") @Cached ImportValueNode importValueNode) {
Object polyglotBindings;
try {
polyglotBindings = getContext().getRealm().getEnv().getPolyglotBindings();
} catch (SecurityException e) {
throw Errors.createErrorFromException(e);
}
try {
return importValueNode.executeWithTarget(interop.readMember(polyglotBindings, identifier));
} catch (UnknownIdentifierException e) {
return Undefined.instance;
} catch (UnsupportedMessageException e) {
throw Errors.createTypeErrorInteropException(polyglotBindings, e, "readMember", identifier, this);
}
}
@Specialization(guards = {"!isString(identifier)"})
protected Object doMaybeUnbox(TruffleObject identifier,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop,
@Shared("importValue") @Cached ImportValueNode importValueNode) {
if (interop.isString(identifier)) {
String unboxed;
try {
unboxed = interop.asString(identifier);
} catch (UnsupportedMessageException e) {
throw Errors.createTypeErrorUnboxException(identifier, e, this);
}
return doString(unboxed, interop, importValueNode);
}
return doInvalid(identifier);
}
@Specialization(guards = {"!isString(identifier)", "!isTruffleObject(identifier)"})
@TruffleBoundary
protected Object doInvalid(Object identifier) {
throw Errors.createTypeErrorInvalidIdentifier(identifier);
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotIsExecutableNode extends JSBuiltinNode {
PolyglotIsExecutableNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected static boolean truffleObject(TruffleObject obj,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
return interop.isExecutable(obj);
}
@Specialization(guards = "isJavaPrimitive(obj)")
protected static boolean primitive(@SuppressWarnings("unused") Object obj) {
return false;
}
@SuppressWarnings("unused")
@Specialization(guards = {"!isTruffleObject(obj)", "!isJavaPrimitive(obj)"})
protected static boolean unsupported(Object obj) {
return false;
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotIsBoxedPrimitiveNode extends JSBuiltinNode {
PolyglotIsBoxedPrimitiveNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization(limit = "InteropLibraryLimit")
protected static boolean truffleObject(TruffleObject obj,
@CachedLibrary("obj") InteropLibrary interop) {
return interop.isBoolean(obj) || interop.isString(obj) || interop.isNumber(obj);
}
@Specialization(guards = "isJavaPrimitive(obj)")
protected static boolean primitive(@SuppressWarnings("unused") Object obj) {
return false;
}
@SuppressWarnings("unused")
@Specialization(guards = {"!isTruffleObject(obj)", "!isJavaPrimitive(obj)"})
protected static boolean unsupported(Object obj) {
return false;
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotIsNullNode extends JSBuiltinNode {
PolyglotIsNullNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected static boolean truffleObject(TruffleObject obj,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
return interop.isNull(obj);
}
@Specialization(guards = "isJavaPrimitive(obj)")
protected static boolean primitive(@SuppressWarnings("unused") Object obj) {
return false;
}
@SuppressWarnings("unused")
@Specialization(guards = {"!isTruffleObject(obj)", "!isJavaPrimitive(obj)"})
protected static boolean unsupported(Object obj) {
return false;
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotHasSizeNode extends JSBuiltinNode {
PolyglotHasSizeNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected boolean truffleObject(TruffleObject obj,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
return interop.hasArrayElements(obj);
}
@Specialization(guards = "isJavaPrimitive(obj)")
protected boolean primitive(@SuppressWarnings("unused") Object obj) {
return false;
}
@SuppressWarnings("unused")
@Specialization(guards = {"!isTruffleObject(obj)", "!isJavaPrimitive(obj)"})
protected boolean unsupported(Object obj) {
return false;
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotReadNode extends JSBuiltinNode {
PolyglotReadNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected Object member(TruffleObject obj, String name,
@Shared("importValue") @Cached("create()") ImportValueNode foreignConvert,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
return JSInteropUtil.readMemberOrDefault(obj, name, Null.instance, interop, foreignConvert, this);
}
@Specialization
protected Object arrayElementInt(TruffleObject obj, int index,
@Shared("importValue") @Cached("create()") ImportValueNode foreignConvert,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
return JSInteropUtil.readArrayElementOrDefault(obj, index, Null.instance, interop, foreignConvert, this);
}
@Specialization(guards = "isNumber(index)", replaces = "arrayElementInt")
protected Object arrayElement(TruffleObject obj, Number index,
@Shared("importValue") @Cached("create()") ImportValueNode foreignConvert,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
return JSInteropUtil.readArrayElementOrDefault(obj, JSRuntime.longValue(index), Null.instance, interop, foreignConvert, this);
}
@SuppressWarnings("unused")
@Specialization(guards = {"!isString(key)", "!isNumber(key)"})
protected Object unsupportedKey(TruffleObject obj, Object key,
@Shared("importValue") @Cached("create()") ImportValueNode foreignConvert,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary keyInterop) {
try {
if (keyInterop.isString(key)) {
return member(obj, keyInterop.asString(key), foreignConvert, interop);
} else if (keyInterop.fitsInInt(key)) {
return arrayElement(obj, keyInterop.asInt(key), foreignConvert, interop);
}
} catch (UnsupportedMessageException e) {
throw Errors.createTypeErrorUnboxException(obj, e, this);
}
return Null.instance;
}
@SuppressWarnings("unused")
@Specialization(guards = "!isTruffleObject(obj)")
protected boolean unsupported(Object obj, Object name) {
throw Errors.createTypeErrorNotATruffleObject("read");
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotWriteNode extends JSBuiltinNode {
PolyglotWriteNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected Object member(TruffleObject obj, String name, Object value,
@Shared("exportValue") @Cached ExportValueNode exportValue,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
Object convertedValue = exportValue.execute(value);
try {
interop.writeMember(obj, name, convertedValue);
return convertedValue;
} catch (UnknownIdentifierException e) {
return Null.instance;
} catch (UnsupportedMessageException | UnsupportedTypeException e) {
throw Errors.createTypeErrorInteropException(obj, e, "writeMember", name, this);
}
}
@Specialization
protected Object arrayElementInt(TruffleObject obj, int index, Object value,
@Shared("exportValue") @Cached ExportValueNode exportValue,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
Object convertedValue = exportValue.execute(value);
try {
interop.writeArrayElement(obj, index, convertedValue);
return convertedValue;
} catch (InvalidArrayIndexException e) {
return Null.instance;
} catch (UnsupportedMessageException | UnsupportedTypeException e) {
throw Errors.createTypeErrorInteropException(obj, e, "writeArrayElement", index, this);
}
}
@Specialization(guards = "isNumber(index)", replaces = "arrayElementInt")
protected Object arrayElement(TruffleObject obj, Number index, Object value,
@Shared("exportValue") @Cached ExportValueNode exportValue,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
Object convertedValue = exportValue.execute(value);
try {
interop.writeArrayElement(obj, JSRuntime.longValue(index), convertedValue);
return convertedValue;
} catch (InvalidArrayIndexException e) {
return Null.instance;
} catch (UnsupportedMessageException | UnsupportedTypeException e) {
throw Errors.createTypeErrorInteropException(obj, e, "writeArrayElement", index, this);
}
}
@SuppressWarnings("unused")
@Specialization(guards = {"!isString(key)", "!isNumber(key)"})
protected Object unsupportedKey(TruffleObject obj, Object key, Object value,
@Shared("exportValue") @Cached ExportValueNode exportValue,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary keyInterop) {
try {
if (keyInterop.isString(key)) {
return member(obj, keyInterop.asString(key), value, exportValue, interop);
} else if (keyInterop.fitsInInt(key)) {
return arrayElement(obj, keyInterop.asInt(key), value, exportValue, interop);
}
} catch (UnsupportedMessageException e) {
throw Errors.createTypeErrorUnboxException(obj, e, this);
}
return Null.instance;
}
@SuppressWarnings("unused")
@Specialization(guards = "!isTruffleObject(obj)")
protected boolean unsupported(Object obj, Object name, Object value) {
throw Errors.createTypeErrorNotATruffleObject("write");
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotRemoveNode extends JSBuiltinNode {
PolyglotRemoveNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected boolean member(TruffleObject obj, String name,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
try {
interop.removeMember(obj, name);
return true;
} catch (UnknownIdentifierException e) {
return false;
} catch (UnsupportedMessageException e) {
throw Errors.createTypeErrorInteropException(obj, e, "removeMember", name, this);
}
}
@Specialization
protected boolean arrayElementInt(TruffleObject obj, int index,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
try {
interop.removeArrayElement(obj, index);
return true;
} catch (InvalidArrayIndexException e) {
return false;
} catch (UnsupportedMessageException e) {
throw Errors.createTypeErrorInteropException(obj, e, "removeArrayElement", index, this);
}
}
@Specialization(guards = "isNumber(index)", replaces = "arrayElementInt")
protected boolean arrayElement(TruffleObject obj, Number index,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
try {
interop.removeArrayElement(obj, JSRuntime.longValue(index));
return true;
} catch (InvalidArrayIndexException e) {
return false;
} catch (UnsupportedMessageException e) {
throw Errors.createTypeErrorInteropException(obj, e, "removeArrayElement", index, this);
}
}
@SuppressWarnings("unused")
@Specialization(guards = {"!isString(key)", "!isNumber(key)"})
protected Object unsupportedKey(TruffleObject obj, Object key,
@Shared("interop") @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary keyInterop) {
try {
if (keyInterop.isString(key)) {
return member(obj, keyInterop.asString(key), interop);
} else if (keyInterop.fitsInInt(key)) {
return arrayElementInt(obj, keyInterop.asInt(key), interop);
}
} catch (UnsupportedMessageException e) {
throw Errors.createTypeErrorUnboxException(obj, e, this);
}
return Null.instance;
}
@SuppressWarnings("unused")
@Specialization(guards = "!isTruffleObject(obj)")
protected boolean unsupported(Object obj, Object key) {
throw Errors.createTypeErrorNotATruffleObject("remove");
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotUnboxValueNode extends JSBuiltinNode {
PolyglotUnboxValueNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected Object truffleObject(TruffleObject obj,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
Object unboxed = JSInteropUtil.toPrimitiveOrDefault(obj, obj, interop, this);
if (unboxed == obj) {
throw Errors.createTypeErrorNotATruffleObject("unbox");
}
return unboxed;
}
@Specialization(guards = "isJavaPrimitive(obj)")
protected Object primitive(Object obj) {
return obj;
}
@SuppressWarnings("unused")
@Specialization(guards = {"!isTruffleObject(obj)", "!isJavaPrimitive(obj)"})
protected boolean unsupported(Object obj) {
throw Errors.createTypeErrorNotATruffleObject("unbox");
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotExecuteNode extends JSBuiltinNode {
PolyglotExecuteNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected Object execute(TruffleObject obj, Object[] arguments,
@Cached ExportValueNode exportValue,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
Object target = exportValue.execute(obj);
Object[] convertedArgs = new Object[arguments.length];
for (int i = 0; i < arguments.length; i++) {
convertedArgs[i] = exportValue.execute(arguments[i]);
}
try {
return interop.execute(target, convertedArgs);
} catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
throw Errors.createTypeErrorInteropException(obj, e, "execute", this);
}
}
@SuppressWarnings("unused")
@Specialization(guards = "!isTruffleObject(obj)")
protected boolean unsupported(Object obj, Object[] arguments) {
throw Errors.createTypeErrorNotATruffleObject("execute");
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotConstructNode extends JSBuiltinNode {
PolyglotConstructNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected Object doNew(TruffleObject obj, Object[] arguments,
@Cached ExportValueNode exportValue,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
Object target = exportValue.execute(obj);
Object[] convertedArgs = new Object[arguments.length];
for (int i = 0; i < arguments.length; i++) {
convertedArgs[i] = exportValue.execute(arguments[i]);
}
try {
return interop.instantiate(target, convertedArgs);
} catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
throw Errors.createTypeErrorInteropException(obj, e, "instantiate", this);
}
}
@SuppressWarnings("unused")
@Specialization(guards = "!isTruffleObject(obj)")
protected boolean unsupported(Object obj, Object[] arguments) {
throw Errors.createTypeErrorNotATruffleObject("construct");
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotGetSizeNode extends JSBuiltinNode {
PolyglotGetSizeNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected Object getSize(TruffleObject obj,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
try {
return interop.getArraySize(obj);
} catch (UnsupportedMessageException e) {
return Null.instance;
}
}
@SuppressWarnings("unused")
@Specialization(guards = "!isTruffleObject(obj)")
protected boolean unsupported(Object obj) {
throw Errors.createTypeErrorNotATruffleObject("getSize");
}
}
abstract static class PolyglotEvalBaseNode extends JSBuiltinNode {
protected final ConditionProfile isValid = ConditionProfile.createBinaryProfile();
PolyglotEvalBaseNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
protected Pair<String, String> getLanguageIdAndMimeType(String languageIdOrMimeType) {
String languageId = languageIdOrMimeType;
String mimeType = null;
if (languageIdOrMimeType.indexOf('/') >= 0) {
String language = Source.findLanguage(languageIdOrMimeType);
if (language != null) {
languageId = language;
mimeType = languageIdOrMimeType;
}
}
return new Pair<>(languageId, mimeType);
}
}
abstract static class PolyglotEvalNode extends PolyglotEvalBaseNode {
PolyglotEvalNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@SuppressWarnings("unused")
@Specialization(guards = {"language.equals(cachedLanguage)"}, limit = "1")
@TruffleBoundary
protected Object evalCachedLanguage(String language, String source,
@Cached("language") String cachedLanguage,
@Cached("getLanguageIdAndMimeType(language)") Pair<String, String> languagePair,
@Cached @Shared("callNode") IndirectCallNode callNode) {
return callNode.call(evalStringIntl(source, languagePair.getFirst(), languagePair.getSecond()));
}
@Specialization(replaces = "evalCachedLanguage")
@TruffleBoundary
protected Object evalString(String language, String source,
@Cached @Shared("callNode") IndirectCallNode callNode) {
Pair<String, String> pair = getLanguageIdAndMimeType(language);
return callNode.call(evalStringIntl(source, pair.getFirst(), pair.getSecond()));
}
private CallTarget evalStringIntl(String sourceText, String languageId, String mimeType) {
CompilerAsserts.neverPartOfCompilation();
getContext().checkEvalAllowed();
Source source = Source.newBuilder(languageId, sourceText, Evaluator.EVAL_SOURCE_NAME).mimeType(mimeType).build();
TruffleLanguage.Env env = getContext().getRealm().getEnv();
try {
return env.parsePublic(source);
} catch (IllegalStateException ex) {
throw Errors.createErrorFromException(ex);
}
}
@SuppressWarnings("unused")
@Specialization(guards = "!isString(languageId) || !isString(source)")
protected Object eval(Object languageId, Object source) {
throw Errors.createTypeError("Expected arguments: (String languageId, String sourceCode)");
}
}
abstract static class PolyglotEvalFileNode extends PolyglotEvalBaseNode {
PolyglotEvalFileNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@SuppressWarnings("unused")
@Specialization(guards = {"language.equals(cachedLanguage)"}, limit = "1")
@TruffleBoundary
protected Object evalFileCachedLanguage(String language, String file,
@Cached("language") String cachedLanguage,
@Cached("getLanguageIdAndMimeType(language)") Pair<String, String> languagePair,
@Cached @Shared("callNode") IndirectCallNode callNode) {
return callNode.call(evalFileIntl(file, languagePair.getFirst(), languagePair.getSecond()));
}
@Specialization(replaces = "evalFileCachedLanguage")
@TruffleBoundary
protected Object evalFileString(String language, String file,
@Cached @Shared("callNode") IndirectCallNode callNode) {
Pair<String, String> pair = getLanguageIdAndMimeType(language);
return callNode.call(evalFileIntl(file, pair.getFirst(), pair.getSecond()));
}
private CallTarget evalFileIntl(String fileName, String languageId, String mimeType) {
CompilerAsserts.neverPartOfCompilation();
TruffleLanguage.Env env = getContext().getRealm().getEnv();
Source source;
try {
source = Source.newBuilder(languageId, env.getPublicTruffleFile(fileName)).mimeType(mimeType).build();
} catch (AccessDeniedException e) {
throw Errors.createError("Cannot evaluate file " + fileName + ": permission denied");
} catch (NoSuchFileException e) {
throw Errors.createError("Cannot evaluate file " + fileName + ": no such file");
} catch (IOException | SecurityException e) {
throw Errors.createError("Cannot evaluate file: " + e.getMessage());
}
try {
return env.parsePublic(source);
} catch (IllegalStateException ex) {
throw Errors.createErrorFromException(ex);
}
}
@SuppressWarnings("unused")
@Specialization(guards = "!isString(languageId) || !isString(fileName)")
protected Object eval(Object languageId, Object fileName) {
throw Errors.createTypeError("Expected arguments: (String languageId, String fileName)");
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotHasKeysNode extends JSBuiltinNode {
PolyglotHasKeysNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected boolean hasKeys(TruffleObject obj,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
return interop.hasMembers(obj);
}
@Specialization(guards = "!isTruffleObject(obj)")
protected boolean unsupported(@SuppressWarnings("unused") Object obj) {
return false;
}
}
abstract static class PolyglotKeysNode extends JSBuiltinNode {
PolyglotKeysNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@TruffleBoundary
@Specialization
protected Object keys(TruffleObject obj) {
return JSArray.createConstantObjectArray(getContext(), JSInteropUtil.keys(obj).toArray());
}
@SuppressWarnings("unused")
@Specialization(guards = "!isTruffleObject(obj)")
protected boolean unsupported(Object obj) {
throw Errors.createTypeErrorNotATruffleObject("keys");
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotIsInstantiableNode extends JSBuiltinNode {
PolyglotIsInstantiableNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected static boolean isInstantiable(TruffleObject obj,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
return interop.isInstantiable(obj);
}
@Specialization(guards = "!isTruffleObject(obj)")
protected static boolean unsupported(@SuppressWarnings("unused") Object obj) {
return false;
}
}
@ImportStatic({JSConfig.class})
abstract static class PolyglotToJSValueNode extends JSBuiltinNode {
PolyglotToJSValueNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected final Object toJSValue(TruffleObject obj,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
return JSInteropUtil.toPrimitiveOrDefault(obj, obj, interop, this);
}
@Specialization(guards = "!isTruffleObject(obj)")
protected static Object toJSValue(Object obj) {
return obj;
}
}
abstract static class PolyglotToPolyglotValueNode extends JSBuiltinNode {
@Child private ExportValueNode exportValueNode;
PolyglotToPolyglotValueNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
this.exportValueNode = ExportValueNode.create();
}
@Specialization
protected Object execute(Object value) {
return exportValueNode.execute(value);
}
}
}