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

com.oracle.truffle.js.nodes.access.CreateObjectNode 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.nodes.access;

import java.util.Set;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.Executed;
import com.oracle.truffle.api.dsl.Idempotent;
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.object.DynamicObjectLibrary;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.access.CreateObjectNodeFactory.CreateObjectWithCachedPrototypeNodeGen;
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.Properties;
import com.oracle.truffle.js.runtime.builtins.JSClass;
import com.oracle.truffle.js.runtime.builtins.JSDictionary;
import com.oracle.truffle.js.runtime.builtins.JSOrdinary;
import com.oracle.truffle.js.runtime.builtins.JSPromise;
import com.oracle.truffle.js.runtime.objects.JSDynamicObject;
import com.oracle.truffle.js.runtime.objects.JSObject;
import com.oracle.truffle.js.runtime.objects.JSObjectUtil;
import com.oracle.truffle.js.runtime.objects.JSShape;
import com.oracle.truffle.js.runtime.objects.Null;

public abstract class CreateObjectNode extends JavaScriptBaseNode {
    protected final JSContext context;

    protected CreateObjectNode(JSContext context) {
        this.context = context;
    }

    @NeverDefault
    public static CreateObjectNode create(JSContext context) {
        return new CreateOrdinaryObjectNode(context);
    }

    @NeverDefault
    public static CreateObjectWithPrototypeNode createOrdinaryWithPrototype(JSContext context) {
        return createWithPrototype(context, null, JSOrdinary.INSTANCE);
    }

    public static CreateObjectWithPrototypeNode createOrdinaryWithPrototype(JSContext context, JavaScriptNode prototypeExpression) {
        return createWithPrototype(context, prototypeExpression, JSOrdinary.INSTANCE);
    }

    public static CreateObjectWithPrototypeNode createWithPrototype(JSContext context, JavaScriptNode prototypeExpression, JSClass jsclass) {
        return CreateObjectWithCachedPrototypeNode.create(context, prototypeExpression, jsclass);
    }

    static CreateObjectNode createDictionary(JSContext context) {
        return new CreateDictionaryObjectNode(context);
    }

    public JSObject execute(VirtualFrame frame) {
        return executeWithRealm(frame, getRealm());
    }

    public abstract JSObject executeWithRealm(VirtualFrame frame, JSRealm realm);

    protected abstract CreateObjectNode copyUninitialized(Set> materializedTags);

    public boolean seenArrayPrototype() {
        return false;
    }

    private static class CreateOrdinaryObjectNode extends CreateObjectNode {
        protected CreateOrdinaryObjectNode(JSContext context) {
            super(context);
        }

        @Override
        public JSObject executeWithRealm(VirtualFrame frame, JSRealm realm) {
            return JSOrdinary.create(context, realm);
        }

        @Override
        protected CreateObjectNode copyUninitialized(Set> materializedTags) {
            return new CreateOrdinaryObjectNode(context);
        }
    }

    public abstract static class CreateObjectWithPrototypeNode extends CreateObjectNode {
        @Child @Executed protected JavaScriptNode prototypeExpression;

        protected CreateObjectWithPrototypeNode(JSContext context, JavaScriptNode prototypeExpression) {
            super(context);
            this.prototypeExpression = prototypeExpression;
        }

        public abstract JSObject execute(JSDynamicObject prototype);

        @Override
        public final JSObject executeWithRealm(VirtualFrame frame, JSRealm realm) {
            return execute(frame);
        }

        @Override
        protected abstract CreateObjectWithPrototypeNode copyUninitialized(Set> materializedTags);
    }

    protected abstract static class CreateObjectWithCachedPrototypeNode extends CreateObjectWithPrototypeNode {
        protected final JSClass jsclass;

        @Child private DynamicObjectLibrary protoFlagsNode;
        @CompilationFinal private boolean seenArrayPrototype;

        protected CreateObjectWithCachedPrototypeNode(JSContext context, JavaScriptNode prototypeExpression, JSClass jsclass) {
            super(context, prototypeExpression);
            this.jsclass = jsclass;
            assert isOrdinaryObject() || isPromiseObject();
        }

        protected static CreateObjectWithPrototypeNode create(JSContext context, JavaScriptNode prototypeExpression, JSClass jsclass) {
            return CreateObjectWithCachedPrototypeNodeGen.create(context, prototypeExpression, jsclass);
        }

        @Specialization(guards = {"!context.isMultiContext()", "isValidPrototype(cachedPrototype)", "prototype == cachedPrototype"}, limit = "1")
        final JSObject doCachedPrototype(@SuppressWarnings("unused") JSDynamicObject prototype,
                        @Cached("prototype") @SuppressWarnings("unused") JSDynamicObject cachedPrototype,
                        @Cached("getProtoChildShape(cachedPrototype)") Shape protoChildShape) {
            if (isPromiseObject()) {
                return JSPromise.create(context, protoChildShape, cachedPrototype);
            } else if (isOrdinaryObject()) {
                return JSOrdinary.create(context, protoChildShape, cachedPrototype);
            } else {
                throw Errors.unsupported("unsupported object type");
            }
        }

        @Specialization(guards = {"isOrdinaryObject()", "isValidPrototype(prototype)"}, replaces = "doCachedPrototype")
        final JSObject doOrdinaryInstancePrototype(JSDynamicObject prototype,
                        @CachedLibrary(limit = "3") @Shared DynamicObjectLibrary setProtoNode) {
            JSObject object = JSOrdinary.createWithoutPrototype(context, prototype);
            Properties.put(setProtoNode, object, JSObject.HIDDEN_PROTO, prototype);
            handleArrayPrototype(object, prototype);
            return object;
        }

        @Specialization(guards = {"isPromiseObject()", "isValidPrototype(prototype)"}, replaces = "doCachedPrototype")
        final JSObject doPromiseInstancePrototype(JSDynamicObject prototype,
                        @CachedLibrary(limit = "3") @Shared DynamicObjectLibrary setProtoNode) {
            JSObject object = JSPromise.createWithoutPrototype(context, prototype);
            Properties.put(setProtoNode, object, JSObject.HIDDEN_PROTO, prototype);
            return object;
        }

        @Specialization(guards = {"isOrdinaryObject() || isPromiseObject()", "!isValidPrototype(prototype)"})
        final JSObject doNotJSObjectOrNull(@SuppressWarnings("unused") Object prototype) {
            return JSOrdinary.create(context, getRealm());
        }

        @NeverDefault
        final Shape getProtoChildShape(JSDynamicObject prototype) {
            if (prototype == Null.instance) {
                return context.getEmptyShapeNullPrototype();
            } else {
                Shape derivedShape = JSObjectUtil.getProtoChildShape(prototype, jsclass, context);
                if (isOrdinaryObject() && JSShape.isArrayPrototypeOrDerivative(prototype)) {
                    seenArrayPrototype = true;
                    derivedShape = Shape.newBuilder(derivedShape).shapeFlags(derivedShape.getFlags() | JSShape.ARRAY_PROTOTYPE_FLAG).build();
                }
                return derivedShape;
            }
        }

        @Idempotent
        final boolean isOrdinaryObject() {
            return jsclass == JSOrdinary.INSTANCE;
        }

        @Idempotent
        final boolean isPromiseObject() {
            return jsclass == JSPromise.INSTANCE;
        }

        /**
         * Check if the prototype is (derived from) %Array.prototype%, and if so, mark the new
         * object as a potential array prototype tracked by the no-prototype-elements assumption.
         */
        private void handleArrayPrototype(JSObject object, JSDynamicObject proto) {
            if (JSShape.isArrayPrototypeOrDerivative(proto)) {
                markAsArrayPrototype(object);
            }
        }

        private void markAsArrayPrototype(JSObject object) {
            if (protoFlagsNode == null) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                // Only used by member nodes that are guaranteed to be in the same compilation unit,
                // so a transferToInterpreterAndInvalidate() should be sufficient for this flag.
                seenArrayPrototype = true;
                protoFlagsNode = insert(DynamicObjectLibrary.getFactory().createDispatched(JSConfig.PropertyCacheLimit));
            }
            assert JSOrdinary.isJSOrdinaryObject(object) : object;
            protoFlagsNode.setShapeFlags(object, protoFlagsNode.getShapeFlags(object) | JSShape.ARRAY_PROTOTYPE_FLAG);
        }

        @Override
        public boolean seenArrayPrototype() {
            return seenArrayPrototype;
        }

        @Override
        protected CreateObjectWithPrototypeNode copyUninitialized(Set> materializedTags) {
            return create(context, JavaScriptNode.cloneUninitialized(prototypeExpression, materializedTags), jsclass);
        }
    }

    private static class CreateDictionaryObjectNode extends CreateObjectNode {
        protected CreateDictionaryObjectNode(JSContext context) {
            super(context);
        }

        @Override
        public JSObject executeWithRealm(VirtualFrame frame, JSRealm realm) {
            return JSDictionary.create(context, realm);
        }

        @Override
        protected CreateObjectNode copyUninitialized(Set> materializedTags) {
            return new CreateDictionaryObjectNode(context);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy