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

com.oracle.truffle.js.runtime.objects.JSObject Maven / Gradle / Ivy

There is a newer version: 4.15.102
Show newest version
/*
 * Copyright (c) 2018, 2020, 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.runtime.objects;

import java.util.ArrayList;
import java.util.List;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleLanguage.LanguageReference;
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.CachedContext;
import com.oracle.truffle.api.dsl.CachedLanguage;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
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.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.HiddenKey;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.js.lang.JavaScriptLanguage;
import com.oracle.truffle.js.nodes.JSGuards;
import com.oracle.truffle.js.nodes.access.ReadElementNode;
import com.oracle.truffle.js.nodes.access.WriteElementNode;
import com.oracle.truffle.js.nodes.interop.ExportValueNode;
import com.oracle.truffle.js.nodes.interop.ImportValueNode;
import com.oracle.truffle.js.nodes.interop.JSInteropInvokeNode;
import com.oracle.truffle.js.nodes.interop.KeyInfoNode;
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.Symbol;
import com.oracle.truffle.js.runtime.array.ScriptArray;
import com.oracle.truffle.js.runtime.builtins.JSArgumentsArray;
import com.oracle.truffle.js.runtime.builtins.JSArray;
import com.oracle.truffle.js.runtime.builtins.JSArrayBase;
import com.oracle.truffle.js.runtime.builtins.JSArrayBufferView;
import com.oracle.truffle.js.runtime.builtins.JSClass;
import com.oracle.truffle.js.runtime.builtins.JSObjectPrototype;
import com.oracle.truffle.js.runtime.builtins.JSTypedArrayObject;
import com.oracle.truffle.js.runtime.interop.InteropArray;
import com.oracle.truffle.js.runtime.interop.JSInteropUtil;
import com.oracle.truffle.js.runtime.util.JSClassProfile;

/**
 * The common base class for all JavaScript objects (values of type Object according to the spec).
 *
 * Includes static methods for dealing with JS objects (internal methods).
 */
@ExportLibrary(InteropLibrary.class)
public abstract class JSObject extends JSDynamicObject {

    public static final String CONSTRUCTOR = "constructor";
    public static final String PROTOTYPE = "prototype";
    public static final String PROTO = "__proto__";
    public static final HiddenKey HIDDEN_PROTO = new HiddenKey("[[Prototype]]");

    public static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
    public static final String NO_SUCH_METHOD_NAME = "__noSuchMethod__";
    protected static final String[] EMPTY_STRING_ARRAY = new String[0];

    protected JSObject(Shape shape) {
        super(shape);
    }

    protected JSObject copyWithoutProperties(@SuppressWarnings("unused") Shape shape) {
        throw Errors.notImplemented("copy");
    }

    /**
     * Returns whether object is a proper JavaScript Object.
     */
    public static boolean isJSObject(Object object) {
        return JSRuntime.isObject(object);
    }

    @SuppressWarnings("static-method")
    @ExportMessage
    public final boolean hasMembers() {
        return true;
    }

    @ImportStatic({JSGuards.class, JSObject.class})
    @ExportMessage
    public abstract static class GetMembers {
        @Specialization(guards = {"cachedJSClass != null", "getJSClass(target) == cachedJSClass"})
        public static Object nonArrayCached(JSObject target, @SuppressWarnings("unused") boolean internal,
                        @Cached("getJSClass(target)") @SuppressWarnings("unused") JSClass cachedJSClass) {
            return InteropArray.create(JSObject.enumerableOwnNames(target));
        }

        @Specialization(replaces = "nonArrayCached")
        public static Object nonArrayUncached(JSObject target, @SuppressWarnings("unused") boolean internal) {
            return InteropArray.create(JSObject.enumerableOwnNames(target));
        }
    }

    @TruffleBoundary
    protected static String[] filterEnumerableNames(DynamicObject target, Iterable ownKeys, JSClass jsclass) {
        List names = new ArrayList<>();
        for (Object obj : ownKeys) {
            if (obj instanceof String && !JSRuntime.isArrayIndex((String) obj)) {
                PropertyDescriptor desc = jsclass.getOwnProperty(target, obj);
                if (desc != null && desc.getEnumerable()) {
                    names.add((String) obj);
                }
            }
        }
        return names.toArray(EMPTY_STRING_ARRAY);
    }

    @ExportMessage
    public final Object readMember(String key,
                    @CachedLanguage @SuppressWarnings("unused") LanguageReference languageRef,
                    @Cached(value = "create(languageRef.get().getJSContext())", uncached = "getUncachedRead()") ReadElementNode readNode,
                    @Cached(value = "languageRef.get().bindMemberFunctions()", allowUncached = true) boolean bindMemberFunctions,
                    @Cached @Exclusive ExportValueNode exportNode) throws UnknownIdentifierException {
        DynamicObject target = this;
        Object result;
        if (readNode == null) {
            result = JSObject.getOrDefault(target, key, target, null, JSClassProfile.getUncached());
        } else {
            result = readNode.executeWithTargetAndIndexOrDefault(target, key, null);
        }
        if (result == null) {
            throw UnknownIdentifierException.create(key);
        }
        return exportNode.execute(result, target, bindMemberFunctions);
    }

    @ExportMessage
    public final boolean isMemberReadable(String key,
                    @Shared("keyInfo") @Cached KeyInfoNode keyInfo) {
        return keyInfo.execute(this, key, KeyInfoNode.READABLE);
    }

    @ExportMessage
    public final void writeMember(String key, Object value,
                    @Shared("keyInfo") @Cached KeyInfoNode keyInfo,
                    @Cached ImportValueNode castValueNode,
                    @CachedLanguage @SuppressWarnings("unused") LanguageReference languageRef,
                    @Cached(value = "createCachedInterop(languageRef)", uncached = "getUncachedWrite()") WriteElementNode writeNode)
                    throws UnknownIdentifierException, UnsupportedMessageException {
        DynamicObject target = this;
        if (testIntegrityLevel(true)) {
            throw UnsupportedMessageException.create();
        }
        if (!keyInfo.execute(target, key, KeyInfoNode.WRITABLE)) {
            throw UnknownIdentifierException.create(key);
        }
        Object importedValue = castValueNode.executeWithTarget(value);
        if (writeNode == null) {
            JSObject.set(target, key, importedValue, true);
        } else {
            writeNode.executeWithTargetAndIndexAndValue(target, key, importedValue);
        }
    }

    @ExportMessage
    public final boolean isMemberModifiable(String key,
                    @Shared("keyInfo") @Cached KeyInfoNode keyInfo) {
        return keyInfo.execute(this, key, KeyInfoNode.MODIFIABLE);
    }

    @ExportMessage
    public final boolean isMemberInsertable(String key,
                    @Shared("keyInfo") @Cached KeyInfoNode keyInfo) {
        return keyInfo.execute(this, key, KeyInfoNode.INSERTABLE);
    }

    @ExportMessage
    public final void removeMember(String key) throws UnsupportedMessageException {
        if (testIntegrityLevel(false)) {
            throw UnsupportedMessageException.create();
        }
        JSObject.delete(this, key, true);
    }

    @ExportMessage
    public final boolean isMemberRemovable(String key,
                    @Shared("keyInfo") @Cached KeyInfoNode keyInfo) {
        return keyInfo.execute(this, key, KeyInfoNode.REMOVABLE);
    }

    @ExportMessage
    public final Object invokeMember(String id, Object[] args,
                    @CachedLanguage JavaScriptLanguage language,
                    @CachedContext(JavaScriptLanguage.class) JSRealm realm,
                    @Cached JSInteropInvokeNode callNode,
                    @Cached @Exclusive ExportValueNode exportNode) throws UnsupportedMessageException, UnknownIdentifierException {
        language.interopBoundaryEnter(realm);
        try {
            Object result = callNode.execute(this, id, args);
            return exportNode.execute(result);
        } finally {
            language.interopBoundaryExit(realm);
        }
    }

    @ExportMessage
    public final boolean isMemberInvocable(String key,
                    @Shared("keyInfo") @Cached KeyInfoNode keyInfo) {
        return keyInfo.execute(this, key, KeyInfoNode.INVOCABLE);
    }

    @ExportMessage
    public final boolean hasMemberReadSideEffects(String key,
                    @Shared("keyInfo") @Cached KeyInfoNode keyInfo) {
        return keyInfo.execute(this, key, KeyInfoNode.READ_SIDE_EFFECTS);
    }

    @ExportMessage
    public final boolean hasMemberWriteSideEffects(String key,
                    @Shared("keyInfo") @Cached KeyInfoNode keyInfo) {
        return keyInfo.execute(this, key, KeyInfoNode.WRITE_SIDE_EFFECTS);
    }

    @SuppressWarnings("static-method")
    @ExportMessage
    public final boolean hasLanguage() {
        return true;
    }

    @SuppressWarnings("static-method")
    @ExportMessage
    public final Class> getLanguage() {
        return JavaScriptLanguage.class;
    }

    @ExportMessage
    public final Object toDisplayString(boolean allowSideEffects) {
        return JSRuntime.toDisplayString(this, allowSideEffects);
    }

    public static ReadElementNode getUncachedRead() {
        return null;
    }

    public static WriteElementNode getUncachedWrite() {
        return null;
    }

    public static JSClass getJSClass(DynamicObject obj) {
        return JSShape.getJSClass(obj.getShape());
    }

    @TruffleBoundary
    public static DynamicObject getPrototype(DynamicObject obj) {
        return JSObject.getJSClass(obj).getPrototypeOf(obj);
    }

    public static DynamicObject getPrototype(DynamicObject obj, JSClassProfile jsclassProfile) {
        return jsclassProfile.getJSClass(obj).getPrototypeOf(obj);
    }

    @TruffleBoundary
    public static boolean setPrototype(DynamicObject obj, DynamicObject newPrototype) {
        assert newPrototype != null;
        return JSObject.getJSClass(obj).setPrototypeOf(obj, newPrototype);
    }

    public static boolean setPrototype(DynamicObject obj, DynamicObject newPrototype, JSClassProfile jsclassProfile) {
        assert newPrototype != null;
        return jsclassProfile.getJSClass(obj).setPrototypeOf(obj, newPrototype);
    }

    @TruffleBoundary
    public static Object get(DynamicObject obj, long index) {
        return JSObject.getJSClass(obj).get(obj, index);
    }

    @TruffleBoundary
    public static Object get(DynamicObject obj, Object key) {
        assert JSRuntime.isPropertyKey(key);
        return JSObject.getJSClass(obj).get(obj, key);
    }

    @TruffleBoundary
    public static Object get(TruffleObject obj, Object key) {
        assert JSRuntime.isPropertyKey(key);
        if (JSDynamicObject.isJSDynamicObject(obj)) {
            return get((DynamicObject) obj, key);
        } else {
            return JSInteropUtil.readMemberOrDefault(obj, key, Undefined.instance);
        }
    }

    @TruffleBoundary
    public static Object get(TruffleObject obj, long index) {
        if (JSDynamicObject.isJSDynamicObject(obj)) {
            return get((DynamicObject) obj, index);
        } else {
            return JSInteropUtil.readArrayElementOrDefault(obj, index, Undefined.instance);
        }
    }

    @TruffleBoundary
    public static Object getMethod(DynamicObject obj, Object name) {
        assert JSRuntime.isPropertyKey(name);
        Object result = JSRuntime.nullToUndefined(JSObject.getJSClass(obj).getMethodHelper(obj, obj, name));
        return (result == Null.instance) ? Undefined.instance : result;
    }

    @TruffleBoundary
    public static boolean set(DynamicObject obj, long index, Object value) {
        return set(obj, index, value, false);
    }

    @TruffleBoundary
    public static boolean set(DynamicObject obj, Object key, Object value) {
        return set(obj, key, value, false);
    }

    @TruffleBoundary
    public static boolean set(DynamicObject obj, long index, Object value, boolean isStrict) {
        return JSObject.getJSClass(obj).set(obj, index, value, obj, isStrict);
    }

    @TruffleBoundary
    public static boolean set(DynamicObject obj, Object key, Object value, boolean isStrict) {
        assert JSRuntime.isPropertyKey(key);
        return JSObject.getJSClass(obj).set(obj, key, value, obj, isStrict);
    }

    /**
     * [[Set]] with a receiver different than the default.
     */
    @TruffleBoundary
    public static boolean setWithReceiver(DynamicObject obj, Object key, Object value, Object receiver, boolean isStrict) {
        assert JSRuntime.isPropertyKey(key);
        return JSObject.getJSClass(obj).set(obj, key, value, receiver, isStrict);
    }

    @TruffleBoundary
    public static boolean setWithReceiver(DynamicObject obj, long index, Object value, Object receiver, boolean isStrict) {
        return JSObject.getJSClass(obj).set(obj, index, value, receiver, isStrict);
    }

    public static boolean setWithReceiver(DynamicObject obj, Object key, Object value, Object receiver, boolean isStrict, JSClassProfile classProfile) {
        assert JSRuntime.isPropertyKey(key);
        return classProfile.getJSClass(obj).set(obj, key, value, receiver, isStrict);
    }

    public static boolean setWithReceiver(DynamicObject obj, long index, Object value, Object receiver, boolean isStrict, JSClassProfile classProfile) {
        return classProfile.getJSClass(obj).set(obj, index, value, receiver, isStrict);
    }

    @TruffleBoundary
    public static boolean delete(DynamicObject obj, long index) {
        return JSObject.getJSClass(obj).delete(obj, index, false);
    }

    @TruffleBoundary
    public static boolean delete(DynamicObject obj, long index, boolean isStrict) {
        return JSObject.getJSClass(obj).delete(obj, index, isStrict);
    }

    public static boolean delete(DynamicObject obj, long index, boolean isStrict, JSClassProfile classProfile) {
        return classProfile.getJSClass(obj).delete(obj, index, isStrict);
    }

    @TruffleBoundary
    public static boolean delete(DynamicObject obj, Object key) {
        return delete(obj, key, false);
    }

    @TruffleBoundary
    public static boolean delete(DynamicObject obj, Object key, boolean isStrict) {
        assert JSRuntime.isPropertyKey(key);
        return JSObject.getJSClass(obj).delete(obj, key, isStrict);
    }

    public static boolean delete(DynamicObject obj, Object key, boolean isStrict, JSClassProfile classProfile) {
        assert JSRuntime.isPropertyKey(key);
        return classProfile.getJSClass(obj).delete(obj, key, isStrict);
    }

    @TruffleBoundary
    public static boolean hasOwnProperty(DynamicObject obj, long index) {
        return JSObject.getJSClass(obj).hasOwnProperty(obj, index);
    }

    public static boolean hasOwnProperty(DynamicObject obj, long index, JSClassProfile classProfile) {
        return classProfile.getJSClass(obj).hasOwnProperty(obj, index);
    }

    @TruffleBoundary
    public static boolean hasOwnProperty(DynamicObject obj, Object key) {
        assert JSRuntime.isPropertyKey(key);
        return JSObject.getJSClass(obj).hasOwnProperty(obj, key);
    }

    public static boolean hasOwnProperty(DynamicObject obj, Object key, JSClassProfile classProfile) {
        assert JSRuntime.isPropertyKey(key);
        return classProfile.getJSClass(obj).hasOwnProperty(obj, key);
    }

    @TruffleBoundary
    public static boolean hasProperty(DynamicObject obj, long index) {
        return JSObject.getJSClass(obj).hasProperty(obj, index);
    }

    public static boolean hasProperty(DynamicObject obj, long index, JSClassProfile classProfile) {
        return classProfile.getJSClass(obj).hasProperty(obj, index);
    }

    @TruffleBoundary
    public static boolean hasProperty(DynamicObject obj, Object key) {
        assert JSRuntime.isPropertyKey(key);
        return JSObject.getJSClass(obj).hasProperty(obj, key);
    }

    public static boolean hasProperty(DynamicObject obj, Object key, JSClassProfile classProfile) {
        assert JSRuntime.isPropertyKey(key);
        return classProfile.getJSClass(obj).hasProperty(obj, key);
    }

    public static PropertyDescriptor getOwnProperty(DynamicObject obj, Object key) {
        assert JSRuntime.isPropertyKey(key);
        return JSObject.getJSClass(obj).getOwnProperty(obj, key);
    }

    public static PropertyDescriptor getOwnProperty(DynamicObject obj, Object key, JSClassProfile classProfile) {
        assert JSRuntime.isPropertyKey(key);
        return classProfile.getJSClass(obj).getOwnProperty(obj, key);
    }

    /**
     * [[OwnPropertyKeys]]. The returned keys are instanceof (String, Symbol).
     */
    public static List ownPropertyKeys(DynamicObject obj) {
        return JSObject.getJSClass(obj).ownPropertyKeys(obj);
    }

    public static List ownPropertyKeys(DynamicObject obj, JSClassProfile classProfile) {
        return classProfile.getJSClass(obj).ownPropertyKeys(obj);
    }

    /**
     * 7.3.21 EnumerableOwnNames (O).
     */
    @TruffleBoundary
    public static List enumerableOwnNames(DynamicObject thisObj) {
        JSClass jsclass = JSObject.getJSClass(thisObj);
        if (JSConfig.FastOwnKeys && jsclass.hasOnlyShapeProperties(thisObj)) {
            return JSShape.getEnumerablePropertyNames(thisObj.getShape());
        }
        Iterable ownKeys = jsclass.ownPropertyKeys(thisObj);
        List names = new ArrayList<>();
        for (Object obj : ownKeys) {
            if (obj instanceof String) {
                PropertyDescriptor desc = jsclass.getOwnProperty(thisObj, obj);
                if (desc != null && desc.getEnumerable()) {
                    names.add((String) obj);
                }
            }
        }
        return names;
    }

    @TruffleBoundary
    public static boolean defineOwnProperty(DynamicObject obj, Object key, PropertyDescriptor desc) {
        assert JSRuntime.isPropertyKey(key);
        return JSObject.getJSClass(obj).defineOwnProperty(obj, key, desc, false);
    }

    @TruffleBoundary
    public static boolean defineOwnProperty(DynamicObject obj, Object key, PropertyDescriptor desc, boolean doThrow) {
        assert JSRuntime.isPropertyKey(key);
        return JSObject.getJSClass(obj).defineOwnProperty(obj, key, desc, doThrow);
    }

    public static Object get(DynamicObject obj, Object key, JSClassProfile jsclassProfile) {
        assert JSRuntime.isPropertyKey(key);
        return jsclassProfile.getJSClass(obj).get(obj, key);
    }

    public static Object get(DynamicObject obj, long index, JSClassProfile jsclassProfile) {
        return jsclassProfile.getJSClass(obj).get(obj, index);
    }

    public static Object getOrDefault(DynamicObject obj, Object key, Object receiver, Object defaultValue, JSClassProfile jsclassProfile) {
        assert JSRuntime.isPropertyKey(key);
        Object result = jsclassProfile.getJSClass(obj).getHelper(obj, receiver, key);
        return result == null ? defaultValue : result;
    }

    public static Object getOrDefault(DynamicObject obj, long index, Object receiver, Object defaultValue, JSClassProfile jsclassProfile) {
        Object result = jsclassProfile.getJSClass(obj).getHelper(obj, receiver, index);
        return result == null ? defaultValue : result;
    }

    @TruffleBoundary
    public static Object getWithReceiver(DynamicObject obj, Object key, Object receiver) {
        assert JSRuntime.isPropertyKey(key);
        Object result = getJSClass(obj).getHelper(obj, receiver, key);
        return result == null ? Undefined.instance : result;
    }

    @TruffleBoundary
    public static String defaultToString(DynamicObject obj) {
        return JSObject.getJSClass(obj).defaultToString(obj);
    }

    @ExportMessage.Ignore
    @TruffleBoundary
    public static String toDisplayString(DynamicObject obj, int depth, boolean allowSideEffects) {
        return JSObject.getJSClass(obj).toDisplayStringImpl(obj, depth, allowSideEffects, JavaScriptLanguage.getCurrentLanguage().getJSContext());
    }

    /**
     * ES2015 7.1.1 ToPrimitive in case an Object is passed.
     */
    @TruffleBoundary
    public static Object toPrimitive(DynamicObject obj, String hint) {
        assert obj != Null.instance && obj != Undefined.instance;
        Object exoticToPrim = JSObject.getMethod(obj, Symbol.SYMBOL_TO_PRIMITIVE);
        if (exoticToPrim != Undefined.instance) {
            Object result = JSRuntime.call(exoticToPrim, obj, new Object[]{hint});
            if (JSRuntime.isObject(result)) {
                throw Errors.createTypeError("[Symbol.toPrimitive] method returned a non-primitive object");
            }
            return result;
        }
        if (hint.equals(JSRuntime.HINT_DEFAULT)) {
            return ordinaryToPrimitive(obj, JSRuntime.HINT_NUMBER);
        } else {
            return ordinaryToPrimitive(obj, hint);
        }
    }

    @TruffleBoundary
    public static Object toPrimitive(DynamicObject obj) {
        return toPrimitive(obj, JSRuntime.HINT_DEFAULT);
    }

    /**
     * ES2018 7.1.1.1 OrdinaryToPrimitive.
     */
    @TruffleBoundary
    public static Object ordinaryToPrimitive(DynamicObject obj, String hint) {
        assert JSRuntime.isObject(obj);
        assert JSRuntime.HINT_STRING.equals(hint) || JSRuntime.HINT_NUMBER.equals(hint);
        String[] methodNames;
        if (JSRuntime.HINT_STRING.equals(hint)) {
            methodNames = new String[]{JSRuntime.TO_STRING, JSRuntime.VALUE_OF};
        } else {
            methodNames = new String[]{JSRuntime.VALUE_OF, JSRuntime.TO_STRING};
        }
        for (String name : methodNames) {
            Object method = JSObject.getMethod(obj, name);
            if (JSRuntime.isCallable(method)) {
                Object result = JSRuntime.call(method, obj, new Object[]{});
                if (!JSRuntime.isObject(result)) {
                    return result;
                }
            }
        }
        throw Errors.createTypeErrorCannotConvertToPrimitiveValue();
    }

    @TruffleBoundary
    public static boolean preventExtensions(DynamicObject obj) {
        return preventExtensions(obj, false);
    }

    @TruffleBoundary
    public static boolean preventExtensions(DynamicObject obj, boolean doThrow) {
        return JSObject.getJSClass(obj).preventExtensions(obj, doThrow);
    }

    @TruffleBoundary
    public static boolean isExtensible(DynamicObject obj) {
        return JSObject.getJSClass(obj).isExtensible(obj);
    }

    public static boolean isExtensible(DynamicObject obj, JSClassProfile classProfile) {
        return classProfile.getJSClass(obj).isExtensible(obj);
    }

    /**
     * The property [[Class]] of the object. This is the second part of what
     * Object.prototype.toString.call(myObj) returns, e.g. "[object Array]".
     *
     * @return the internal property [[Class]] of the object.
     */
    @TruffleBoundary
    public static String getClassName(DynamicObject obj) {
        return JSObject.getJSClass(obj).getClassName(obj);
    }

    @TruffleBoundary
    public static boolean isFrozen(DynamicObject obj) {
        return testIntegrityLevel(obj, true);
    }

    @TruffleBoundary
    public static boolean isSealed(DynamicObject obj) {
        return testIntegrityLevel(obj, false);
    }

    public static ScriptArray getArray(DynamicObject obj) {
        assert hasArray(obj);
        if (obj instanceof JSArrayBase) {
            return ((JSArrayBase) obj).getArrayType();
        } else {
            return ((JSTypedArrayObject) obj).getArrayType();
        }
    }

    public static void setArray(DynamicObject obj, ScriptArray array) {
        assert hasArray(obj);
        ((JSArrayBase) obj).setArrayType(array);
    }

    public static boolean hasArray(DynamicObject obj) {
        return JSArray.isJSArray(obj) || JSArgumentsArray.isJSArgumentsObject(obj) || JSArrayBufferView.isJSArrayBufferView(obj) || JSObjectPrototype.isJSObjectPrototype(obj);
    }

    public static JSContext getJSContext(DynamicObject obj) {
        return JSShape.getJSContext(obj.getShape());
    }

    @TruffleBoundary(transferToInterpreterOnException = false)
    public static boolean testIntegrityLevel(DynamicObject obj, boolean frozen) {
        return JSObject.getJSClass(obj).testIntegrityLevel(obj, frozen);
    }

    @TruffleBoundary
    public static boolean setIntegrityLevel(DynamicObject obj, boolean freeze) {
        return setIntegrityLevel(obj, freeze, false);
    }

    @TruffleBoundary
    public static boolean setIntegrityLevel(DynamicObject obj, boolean freeze, boolean doThrow) {
        return JSObject.getJSClass(obj).setIntegrityLevel(obj, freeze, doThrow);
    }

}