All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltins Maven / Gradle / Ivy

/*
 * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.oracle.truffle.js.builtins.testing;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Exclusive;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.ImportStatic;
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.strings.TruffleString;
import com.oracle.truffle.js.builtins.JSBuiltinsContainer;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotConstructNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotExecuteNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotGetSizeNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotHasKeysNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotHasSizeNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotIsBoxedPrimitiveNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotIsExecutableNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotIsInstantiableNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotIsNullNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotKeysNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotReadNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotRemoveNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotToJSValueNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotToPolyglotValueNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.PolyglotUnboxValueNodeGen;
import com.oracle.truffle.js.builtins.testing.PolyglotInternalBuiltinsFactory.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.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.Strings;
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;

/**
 * Extra builtins injected into {@code Polyglot} object for Interop testing purposes.
 */
public final class PolyglotInternalBuiltins extends JSBuiltinsContainer.SwitchEnum {

    public static final JSBuiltinsContainer BUILTINS = new PolyglotInternalBuiltins();

    protected PolyglotInternalBuiltins() {
        super(JSRealm.POLYGLOT_CLASS_NAME, PolyglotInternalBuiltins.PolyglotInternal.class);
    }

    public enum PolyglotInternal implements BuiltinEnum {
        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);

        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, PolyglotInternalBuiltins.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));
        }
        return null;
    }

    @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 JSInteropUtil.isBoxedPrimitive(obj, interop);
        }

        @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, TruffleString name,
                        @Shared @Cached ImportValueNode foreignConvert,
                        @Shared @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
            return JSInteropUtil.readMemberOrDefault(obj, name, Null.instance, interop, foreignConvert);
        }

        @Specialization
        protected Object arrayElementInt(TruffleObject obj, int index,
                        @Shared @Cached ImportValueNode foreignConvert,
                        @Shared @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
            return JSInteropUtil.readArrayElementOrDefault(obj, index, Null.instance, interop, foreignConvert);
        }

        @Specialization(guards = "isNumber(index)", replaces = "arrayElementInt")
        protected Object arrayElement(TruffleObject obj, Number index,
                        @Shared @Cached ImportValueNode foreignConvert,
                        @Shared @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop) {
            return JSInteropUtil.readArrayElementOrDefault(obj, JSRuntime.longValue(index), Null.instance, interop, foreignConvert);
        }

        @SuppressWarnings("unused")
        @InliningCutoff
        @Specialization(guards = {"!isString(key)", "!isNumber(key)"})
        protected Object unsupportedKey(TruffleObject obj, Object key,
                        @Shared @Cached ImportValueNode foreignConvert,
                        @Shared @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop,
                        @Exclusive @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary keyInterop,
                        @Cached TruffleString.SwitchEncodingNode switchEncoding) {
            try {
                if (keyInterop.isString(key)) {
                    return member(obj, Strings.interopAsTruffleString(key, keyInterop, switchEncoding), 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, TruffleString name, Object value,
                        @Shared @Cached ExportValueNode exportValue,
                        @Shared @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop,
                        @Shared @Cached TruffleString.ToJavaStringNode toJavaStringNode) {
            Object convertedValue = exportValue.execute(value);
            try {
                interop.writeMember(obj, Strings.toJavaString(toJavaStringNode, 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 @Cached ExportValueNode exportValue,
                        @Shared @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 @Cached ExportValueNode exportValue,
                        @Shared @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")
        @InliningCutoff
        @Specialization(guards = {"!isString(key)", "!isNumber(key)"})
        protected Object unsupportedKey(TruffleObject obj, Object key, Object value,
                        @Shared @Cached ExportValueNode exportValue,
                        @Shared @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop,
                        @Exclusive @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary keyInterop,
                        @Shared @Cached TruffleString.ToJavaStringNode toJavaStringNode,
                        @Cached TruffleString.SwitchEncodingNode switchEncoding) {
            try {
                if (keyInterop.isString(key)) {
                    return member(obj, Strings.interopAsTruffleString(key, keyInterop, switchEncoding), value, exportValue, interop, toJavaStringNode);
                } 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, TruffleString name,
                        @Shared @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop,
                        @Shared @Cached TruffleString.ToJavaStringNode toJavaStringNode) {
            try {
                interop.removeMember(obj, Strings.toJavaString(toJavaStringNode, 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 @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 @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")
        @InliningCutoff
        @Specialization(guards = {"!isString(key)", "!isNumber(key)"})
        protected Object unsupportedKey(TruffleObject obj, Object key,
                        @Shared @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary interop,
                        @Exclusive @CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary keyInterop,
                        @Shared @Cached TruffleString.ToJavaStringNode toJavaStringNode,
                        @Cached TruffleString.SwitchEncodingNode switchEncoding) {
            try {
                if (keyInterop.isString(key)) {
                    return member(obj, Strings.interopAsTruffleString(key, keyInterop, switchEncoding), interop, toJavaStringNode);
                } 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.toPrimitiveOrDefaultLossy(obj, obj, interop, this);
            if (unboxed == obj) {
                throw Errors.createTypeErrorNotATruffleObject("unbox");
            }
            return unboxed;
        }

        @Specialization(guards = "isJavaPrimitive(obj)")
        protected Object primitive(Object obj) {
            // identity function
            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 doExecute(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");
        }
    }

    @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(), getRealm(), 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;
        }
    }

    /**
     * Forces the conversion of an (potential) interop value to a JavaScript compliant value. In
     * addition to the conversions forced at the language boundary anyway (e.g., Java primitive
     * types like short or float that are not supported by JavaScript), this operation also converts
     * Nullish interop values to the JavaScript null value, and unboxes boxed TruffleObjects.
     *
     */
    @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.toPrimitiveOrDefaultLossy(obj, obj, interop, this);
        }

        @Specialization(guards = "!isTruffleObject(obj)")
        protected static Object toJSValue(Object obj) {
            return obj;
        }
    }

    /**
     * Forces the conversion of a JavaScript value to a value compliant with Interop semantics. This
     * is done automatically at the language boundary and should rarely be necessary to be triggered
     * by user code.
     */
    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 toPolyglotValue(Object value) {
            return exportValueNode.execute(value);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy