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

com.oracle.truffle.js.nodes.access.PropertySetNode 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 com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnknownKeyException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.object.DynamicObjectLibrary;
import com.oracle.truffle.api.object.HiddenKey;
import com.oracle.truffle.api.object.Location;
import com.oracle.truffle.api.object.Property;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.js.nodes.JSGuards;
import com.oracle.truffle.js.nodes.array.ArrayLengthNode.ArrayLengthWriteNode;
import com.oracle.truffle.js.nodes.cast.JSToObjectNode;
import com.oracle.truffle.js.nodes.function.JSFunctionCallNode;
import com.oracle.truffle.js.nodes.interop.ExportValueNode;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSArguments;
import com.oracle.truffle.js.runtime.JSConfig;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.Strings;
import com.oracle.truffle.js.runtime.Symbol;
import com.oracle.truffle.js.runtime.builtins.JSAbstractArray;
import com.oracle.truffle.js.runtime.builtins.JSAdapter;
import com.oracle.truffle.js.runtime.builtins.JSArray;
import com.oracle.truffle.js.runtime.builtins.JSArrayBufferView;
import com.oracle.truffle.js.runtime.builtins.JSGlobal;
import com.oracle.truffle.js.runtime.builtins.JSProxy;
import com.oracle.truffle.js.runtime.interop.JSInteropUtil;
import com.oracle.truffle.js.runtime.objects.Accessor;
import com.oracle.truffle.js.runtime.objects.Dead;
import com.oracle.truffle.js.runtime.objects.JSAttributes;
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.JSProperty;
import com.oracle.truffle.js.runtime.objects.JSShape;
import com.oracle.truffle.js.runtime.objects.Null;
import com.oracle.truffle.js.runtime.objects.PropertyDescriptor;
import com.oracle.truffle.js.runtime.objects.PropertyProxy;
import com.oracle.truffle.js.runtime.objects.Undefined;
import com.oracle.truffle.js.runtime.util.JSClassProfile;

/**
 * @see WritePropertyNode
 */
@SuppressWarnings("deprecation")
public class PropertySetNode extends PropertyCacheNode {
    private final boolean isGlobal;
    private final boolean isStrict;
    private final boolean setOwnProperty;
    private final boolean declaration;
    private final boolean superProperty;
    private final byte attributeFlags;
    private boolean propertyAssumptionCheckEnabled;
    @Child protected SetCacheNode cacheNode;

    @NeverDefault
    public static PropertySetNode create(Object key, boolean isGlobal, JSContext context, boolean isStrict) {
        final boolean setOwnProperty = false;
        return createImpl(key, isGlobal, context, isStrict, setOwnProperty, JSAttributes.getDefault());
    }

    @NeverDefault
    public static PropertySetNode createImpl(Object key, boolean isGlobal, JSContext context, boolean isStrict, boolean setOwnProperty, int attributeFlags) {
        return createImpl(key, isGlobal, context, isStrict, setOwnProperty, attributeFlags, false, false);
    }

    @NeverDefault
    public static PropertySetNode createImpl(Object key, boolean isGlobal, JSContext context, boolean isStrict, boolean setOwnProperty, int attributeFlags, boolean declaration) {
        return createImpl(key, isGlobal, context, isStrict, setOwnProperty, attributeFlags, declaration, false);
    }

    @NeverDefault
    public static PropertySetNode createImpl(Object key, boolean isGlobal, JSContext context, boolean isStrict, boolean setOwnProperty, int attributeFlags, boolean declaration,
                    boolean superProperty) {
        boolean privateSymbol = JSRuntime.isPrivateSymbol(key);
        int flags = attributeFlags | (privateSymbol ? JSAttributes.NOT_ENUMERABLE : 0);
        return new PropertySetNode(key, context, isGlobal, isStrict, setOwnProperty || privateSymbol, flags, declaration, superProperty);
    }

    @NeverDefault
    public static PropertySetNode createSetHidden(HiddenKey key, JSContext context) {
        return createImpl(key, false, context, false, true, 0);
    }

    protected PropertySetNode(Object key, JSContext context, boolean isGlobal, boolean isStrict, boolean setOwnProperty, int attributeFlags, boolean declaration, boolean superProperty) {
        super(key, context);
        assert setOwnProperty ? attributeFlags == (attributeFlags & (JSAttributes.ATTRIBUTES_MASK | JSProperty.CONST)) : attributeFlags == JSAttributes.getDefault();
        this.isGlobal = isGlobal;
        this.isStrict = isStrict;
        this.setOwnProperty = setOwnProperty;
        this.attributeFlags = (byte) attributeFlags;
        this.declaration = declaration;
        this.superProperty = superProperty;
    }

    public final void setValue(Object obj, Object value) {
        setValue(obj, value, obj);
    }

    public final void setValueInt(Object obj, int value) {
        setValueInt(obj, value, obj);
    }

    public final void setValueBoolean(Object obj, boolean value) {
        setValueBoolean(obj, value, obj);
    }

    @ExplodeLoop
    protected void setValue(Object thisObj, Object value, Object receiver) {
        SetCacheNode c = cacheNode;
        for (; c != null; c = c.next) {
            if (c instanceof GenericPropertySetNode) {
                ((GenericPropertySetNode) c).setValue(thisObj, value, receiver, this, false);
                return;
            }
            boolean isSimpleShapeCheck = c.isSimpleShapeCheck();
            ReceiverCheckNode receiverCheck = c.receiverCheck;
            boolean guard;
            Object castObj;
            if (isSimpleShapeCheck) {
                Shape shape = receiverCheck.getShape();
                if (isDynamicObject(thisObj, shape)) {
                    JSDynamicObject jsobj = castDynamicObject(thisObj, shape);
                    guard = shape.check(jsobj);
                    castObj = jsobj;
                    if (!shape.getValidAssumption().isValid()) {
                        break;
                    }
                } else {
                    continue;
                }
            } else {
                guard = receiverCheck.accept(thisObj);
                castObj = thisObj;
            }
            if (guard) {
                if (!isSimpleShapeCheck && !receiverCheck.isValid()) {
                    break;
                }
                if (c.setValue(castObj, value, receiver, this, guard)) {
                    return;
                }
            }
        }
        deoptimize(c);
        setValueAndSpecialize(thisObj, value, receiver);
    }

    @TruffleBoundary
    private boolean setValueAndSpecialize(Object thisObj, Object value, Object receiver) {
        SetCacheNode c = specialize(thisObj, value);
        return c.setValue(thisObj, value, receiver, this, false);
    }

    @ExplodeLoop
    protected void setValueInt(Object thisObj, int value, Object receiver) {
        SetCacheNode c = cacheNode;
        for (; c != null; c = c.next) {
            if (c instanceof GenericPropertySetNode) {
                ((GenericPropertySetNode) c).setValueInt(thisObj, value, receiver, this, false);
                return;
            }
            boolean isSimpleShapeCheck = c.isSimpleShapeCheck();
            ReceiverCheckNode receiverCheck = c.receiverCheck;
            boolean guard;
            Object castObj;
            if (isSimpleShapeCheck) {
                Shape shape = receiverCheck.getShape();
                if (isDynamicObject(thisObj, shape)) {
                    JSDynamicObject jsobj = castDynamicObject(thisObj, shape);
                    guard = shape.check(jsobj);
                    castObj = jsobj;
                    if (!shape.getValidAssumption().isValid()) {
                        break;
                    }
                } else {
                    continue;
                }
            } else {
                guard = receiverCheck.accept(thisObj);
                castObj = thisObj;
            }
            if (guard) {
                if (!isSimpleShapeCheck && !receiverCheck.isValid()) {
                    break;
                }
                if (c.setValueInt(castObj, value, receiver, this, guard)) {
                    return;
                }
            }
        }
        deoptimize(c);
        setValueIntAndSpecialize(thisObj, value, receiver);
    }

    @TruffleBoundary
    private void setValueIntAndSpecialize(Object thisObj, int value, Object receiver) {
        SetCacheNode c = specialize(thisObj, value);
        c.setValueInt(thisObj, value, receiver, this, false);
    }

    @ExplodeLoop
    protected void setValueDouble(Object thisObj, double value, Object receiver) {
        SetCacheNode c = cacheNode;
        for (; c != null; c = c.next) {
            if (c instanceof GenericPropertySetNode) {
                ((GenericPropertySetNode) c).setValueDouble(thisObj, value, receiver, this, false);
                return;
            }
            boolean isSimpleShapeCheck = c.isSimpleShapeCheck();
            ReceiverCheckNode receiverCheck = c.receiverCheck;
            boolean guard;
            Object castObj;
            if (isSimpleShapeCheck) {
                Shape shape = receiverCheck.getShape();
                if (isDynamicObject(thisObj, shape)) {
                    JSDynamicObject jsobj = castDynamicObject(thisObj, shape);
                    guard = shape.check(jsobj);
                    castObj = jsobj;
                    if (!shape.getValidAssumption().isValid()) {
                        break;
                    }
                } else {
                    continue;
                }
            } else {
                guard = receiverCheck.accept(thisObj);
                castObj = thisObj;
            }
            if (guard) {
                if (!isSimpleShapeCheck && !receiverCheck.isValid()) {
                    break;
                }
                if (c.setValueDouble(castObj, value, receiver, this, guard)) {
                    return;
                }
            }
        }
        deoptimize(c);
        setValueDoubleAndSpecialize(thisObj, value, receiver);
    }

    @TruffleBoundary
    private void setValueDoubleAndSpecialize(Object thisObj, double value, Object receiver) {
        SetCacheNode c = specialize(thisObj, value);
        c.setValueDouble(thisObj, value, receiver, this, false);
    }

    @ExplodeLoop
    protected void setValueBoolean(Object thisObj, boolean value, Object receiver) {
        SetCacheNode c = cacheNode;
        for (; c != null; c = c.next) {
            if (c instanceof GenericPropertySetNode) {
                ((GenericPropertySetNode) c).setValueBoolean(thisObj, value, receiver, this, false);
                return;
            }
            boolean isSimpleShapeCheck = c.isSimpleShapeCheck();
            ReceiverCheckNode receiverCheck = c.receiverCheck;
            boolean guard;
            Object castObj;
            if (isSimpleShapeCheck) {
                Shape shape = receiverCheck.getShape();
                if (isDynamicObject(thisObj, shape)) {
                    JSDynamicObject jsobj = castDynamicObject(thisObj, shape);
                    guard = shape.check(jsobj);
                    castObj = jsobj;
                    if (!shape.getValidAssumption().isValid()) {
                        break;
                    }
                } else {
                    continue;
                }
            } else {
                guard = receiverCheck.accept(thisObj);
                castObj = thisObj;
            }
            if (guard) {
                if (!isSimpleShapeCheck && !receiverCheck.isValid()) {
                    break;
                }
                if (c.setValueBoolean(castObj, value, receiver, this, guard)) {
                    return;
                }
            }
        }
        deoptimize(c);
        setValueBooleanAndSpecialize(thisObj, value, receiver);
    }

    @TruffleBoundary
    private void setValueBooleanAndSpecialize(Object thisObj, boolean value, Object receiver) {
        SetCacheNode c = specialize(thisObj, value);
        c.setValueBoolean(thisObj, value, receiver, this, false);
    }

    @Override
    protected SetCacheNode getCacheNode() {
        return this.cacheNode;
    }

    @Override
    protected void setCacheNode(SetCacheNode cache) {
        this.cacheNode = cache;
    }

    public abstract static class SetCacheNode extends PropertyCacheNode.CacheNode {
        @Child protected SetCacheNode next;

        protected SetCacheNode(ReceiverCheckNode receiverCheck) {
            super(receiverCheck);
        }

        @Override
        protected final SetCacheNode getNext() {
            return next;
        }

        @Override
        protected final void setNext(SetCacheNode next) {
            this.next = next;
        }

        protected abstract boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard);

        protected boolean setValueInt(Object thisObj, int value, Object receiver, PropertySetNode root, boolean guard) {
            return setValue(thisObj, value, receiver, root, guard);
        }

        protected boolean setValueDouble(Object thisObj, double value, Object receiver, PropertySetNode root, boolean guard) {
            return setValue(thisObj, value, receiver, root, guard);
        }

        protected boolean setValueBoolean(Object thisObj, boolean value, Object receiver, PropertySetNode root, boolean guard) {
            return setValue(thisObj, value, receiver, root, guard);
        }

        @Override
        protected boolean acceptsValue(Object value) {
            return true;
        }
    }

    public abstract static class LinkedPropertySetNode extends SetCacheNode {
        protected LinkedPropertySetNode(ReceiverCheckNode receiverCheck) {
            super(receiverCheck);
        }
    }

    public static final class ObjectPropertySetNode extends LinkedPropertySetNode {
        private final Location location;

        public ObjectPropertySetNode(Property property, ReceiverCheckNode shapeCheck) {
            super(shapeCheck);
            this.location = property.getLocation();
            assert JSProperty.isData(property) && JSProperty.isWritable(property) && !JSProperty.isDataSpecial(property) : property;
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            if (location.canStore(value)) {
                JSDynamicObject store = receiverCheck.getStore(thisObj);
                try {
                    location.set(store, value, receiverCheck.getShape());
                } catch (com.oracle.truffle.api.object.IncompatibleLocationException | com.oracle.truffle.api.object.FinalLocationException e) {
                    throw Errors.shouldNotReachHere(e);
                }
                return true;
            } else {
                return false;
            }
        }

        @Override
        protected boolean acceptsValue(Object value) {
            return location.canStore(value);
        }
    }

    public static final class PropertyProxySetNode extends LinkedPropertySetNode {
        private final boolean isStrict;
        private final Location location;
        private final BranchProfile errorBranch = BranchProfile.create();

        public PropertyProxySetNode(Property property, AbstractShapeCheckNode shapeCheck, boolean isStrict) {
            super(shapeCheck);
            this.isStrict = isStrict;
            this.location = property.getLocation();
            assert JSProperty.isData(property) && JSProperty.isWritable(property) && JSProperty.isProxy(property) : property;
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = receiverCheck.getStore(thisObj);
            boolean ret = ((PropertyProxy) location.get(store, guard)).set(store, value);
            if (!ret && isStrict) {
                errorBranch.enter();
                throw Errors.createTypeErrorNotWritableProperty(root.getKey(), thisObj, this);
            }
            return true;
        }
    }

    public static final class IntPropertySetNode extends LinkedPropertySetNode {

        private final com.oracle.truffle.api.object.IntLocation location;

        public IntPropertySetNode(Property property, ReceiverCheckNode shapeCheck) {
            super(shapeCheck);
            this.location = (com.oracle.truffle.api.object.IntLocation) property.getLocation();
            assert JSProperty.isData(property) && JSProperty.isWritable(property) : property;
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            if (value instanceof Integer) {
                int intValue = (int) value;
                JSDynamicObject store = receiverCheck.getStore(thisObj);
                try {
                    location.setInt(store, intValue, receiverCheck.getShape());
                } catch (com.oracle.truffle.api.object.FinalLocationException e) {
                    throw Errors.shouldNotReachHere(e);
                }
                return true;
            } else {
                return false;
            }
        }

        @Override
        protected boolean setValueInt(Object thisObj, int value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = receiverCheck.getStore(thisObj);
            try {
                location.setInt(store, value, receiverCheck.getShape());
                return true;
            } catch (com.oracle.truffle.api.object.FinalLocationException e) {
                throw Errors.shouldNotReachHere(e);
            }
        }

        @Override
        protected boolean acceptsValue(Object value) {
            return value instanceof Integer;
        }
    }

    public static final class DoublePropertySetNode extends LinkedPropertySetNode {
        private final com.oracle.truffle.api.object.DoubleLocation location;

        @CompilationFinal int valueProfile;
        private static final int INT = 1 << 0;
        private static final int DOUBLE = 1 << 1;
        private static final int OTHER = 1 << 2;

        public DoublePropertySetNode(Property property, ReceiverCheckNode shapeCheck) {
            super(shapeCheck);
            this.location = (com.oracle.truffle.api.object.DoubleLocation) property.getLocation();
            assert JSProperty.isData(property) && JSProperty.isWritable(property) : property;
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            int p = valueProfile;
            double doubleValue;
            if ((p & DOUBLE) != 0 && value instanceof Double) {
                doubleValue = (double) value;
            } else if ((p & INT) != 0 && value instanceof Integer) {
                doubleValue = (int) value;
            } else if ((p & OTHER) != 0 && !(value instanceof Double || value instanceof Integer)) {
                return false;
            } else {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                if (value instanceof Double) {
                    p |= DOUBLE;
                } else if (value instanceof Integer) {
                    p |= INT;
                } else {
                    p |= OTHER;
                }
                valueProfile = p;
                return setValue(thisObj, value, receiver, root, guard);
            }
            JSDynamicObject store = receiverCheck.getStore(thisObj);
            try {
                location.setDouble(store, doubleValue, receiverCheck.getShape());
                return true;
            } catch (com.oracle.truffle.api.object.FinalLocationException e) {
                throw Errors.shouldNotReachHere(e);
            }
        }

        @Override
        protected boolean setValueInt(Object thisObj, int value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = receiverCheck.getStore(thisObj);
            try {
                location.setDouble(store, value, receiverCheck.getShape());
                return true;
            } catch (com.oracle.truffle.api.object.FinalLocationException e) {
                throw Errors.shouldNotReachHere(e);
            }
        }

        @Override
        protected boolean setValueDouble(Object thisObj, double value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = receiverCheck.getStore(thisObj);
            try {
                location.setDouble(store, value, receiverCheck.getShape());
                return true;
            } catch (com.oracle.truffle.api.object.FinalLocationException e) {
                throw Errors.shouldNotReachHere(e);
            }
        }

        @Override
        protected boolean acceptsValue(Object value) {
            return value instanceof Double || value instanceof Integer;
        }
    }

    public static final class BooleanPropertySetNode extends LinkedPropertySetNode {

        private final com.oracle.truffle.api.object.BooleanLocation location;

        public BooleanPropertySetNode(Property property, ReceiverCheckNode shapeCheck) {
            super(shapeCheck);
            this.location = (com.oracle.truffle.api.object.BooleanLocation) property.getLocation();
            assert JSProperty.isData(property) && JSProperty.isWritable(property) : property;
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            if (value instanceof Boolean) {
                JSDynamicObject store = receiverCheck.getStore(thisObj);
                try {
                    location.setBoolean(store, (boolean) value, receiverCheck.getShape());
                } catch (com.oracle.truffle.api.object.FinalLocationException e) {
                    throw Errors.shouldNotReachHere(e);
                }
                return true;
            } else {
                return false;
            }
        }

        @Override
        protected boolean setValueBoolean(Object thisObj, boolean value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = receiverCheck.getStore(thisObj);
            try {
                location.setBoolean(store, value, receiverCheck.getShape());
                return true;
            } catch (com.oracle.truffle.api.object.FinalLocationException e) {
                throw Errors.shouldNotReachHere(e);
            }
        }

        @Override
        protected boolean acceptsValue(Object value) {
            return value instanceof Boolean;
        }
    }

    public static final class AccessorPropertySetNode extends LinkedPropertySetNode {
        private final boolean isStrict;
        private final Location location;
        @Child private JSFunctionCallNode callNode;
        private final BranchProfile undefinedSetterBranch = BranchProfile.create();

        public AccessorPropertySetNode(Property property, ReceiverCheckNode receiverCheck, boolean isStrict) {
            super(receiverCheck);
            assert JSProperty.isAccessor(property);
            this.isStrict = isStrict;
            this.location = property.getLocation();
            this.callNode = JSFunctionCallNode.createCall();
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = receiverCheck.getStore(thisObj);
            Accessor accessor = (Accessor) location.get(store, guard);

            Object setter = accessor.getSetter();
            if (setter != Undefined.instance) {
                callNode.executeCall(JSArguments.createOneArg(receiver, setter, value));
            } else {
                undefinedSetterBranch.enter();
                if (isStrict) {
                    throw Errors.createTypeErrorCannotSetAccessorProperty(root.getKey(), store, this);
                }
            }
            return true;
        }
    }

    public static class DataPropertyPutWithoutFlagsNode extends LinkedPropertySetNode {
        @Child protected DynamicObjectLibrary objectLib;

        public DataPropertyPutWithoutFlagsNode(Object key, ReceiverCheckNode receiverCheck) {
            super(receiverCheck);
            this.objectLib = JSObjectUtil.createDispatched(key);
        }

        protected static JSDynamicObject getStore(Object thisObj) {
            return ((JSDynamicObject) thisObj);
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = getStore(thisObj);
            objectLib.put(store, root.key, value);
            return true;
        }

        @Override
        protected boolean setValueInt(Object thisObj, int value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = getStore(thisObj);
            objectLib.putInt(store, root.key, value);
            return true;
        }

        @Override
        protected boolean setValueDouble(Object thisObj, double value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = getStore(thisObj);
            objectLib.putDouble(store, root.key, value);
            return true;
        }
    }

    public static class DataPropertyPutWithFlagsNode extends LinkedPropertySetNode {
        @Child protected DynamicObjectLibrary objectLib;

        protected DataPropertyPutWithFlagsNode(Object key, ReceiverCheckNode receiverCheck) {
            super(receiverCheck);
            this.objectLib = JSObjectUtil.createDispatched(key);
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = (JSDynamicObject) thisObj;
            this.objectLib.putWithFlags(store, root.key, value, root.getAttributeFlags());
            return true;
        }
    }

    public static class DataPropertyPutConstantNode extends LinkedPropertySetNode {
        @Child protected DynamicObjectLibrary objectLib;

        protected DataPropertyPutConstantNode(Object key, ReceiverCheckNode receiverCheck) {
            super(receiverCheck);
            this.objectLib = JSObjectUtil.createDispatched(key);
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = (JSDynamicObject) thisObj;
            this.objectLib.putConstant(store, root.key, value, root.getAttributeFlags());
            return true;
        }
    }

    public static final class ReadOnlyPropertySetNode extends LinkedPropertySetNode {
        private final boolean isStrict;
        private final Property property;

        public ReadOnlyPropertySetNode(ReceiverCheckNode receiverCheck, boolean isStrict, Property property) {
            super(receiverCheck);
            this.isStrict = isStrict;
            this.property = property;
        }

        public ReadOnlyPropertySetNode(ReceiverCheckNode receiverCheck, boolean isStrict) {
            this(receiverCheck, isStrict, null);
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            if (isStrict) {
                Object key = root.getKey();
                if (property == null) {
                    if (receiver instanceof JSObject receiverObj) {
                        // Cannot create property because receiver is not extensible.
                        throw Errors.createTypeErrorNotExtensible(receiverObj, key);
                    } else {
                        // Cannot create property because receiver is not an object.
                        throw Errors.createTypeErrorSetNonObjectReceiver(receiver, key);
                    }
                } else if (root.setOwnProperty) {
                    // Property is not configurable.
                    throw Errors.createTypeErrorCannotRedefineProperty(key);
                } else {
                    // Property is not writable or a module namespace export.
                    throw Errors.createTypeErrorNotWritableProperty(key, thisObj, this);
                }
            }
            return true;
        }
    }

    /**
     * If object is undefined or null, throw TypeError.
     */
    public static final class TypeErrorPropertySetNode extends LinkedPropertySetNode {

        public TypeErrorPropertySetNode(AbstractShapeCheckNode shapeCheckNode) {
            super(shapeCheckNode);
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            assert thisObj == Undefined.instance || thisObj == Null.instance;
            throw Errors.createTypeErrorCannotSetProperty(root.getKey(), thisObj, this);
        }
    }

    public static final class JSAdapterPropertySetNode extends LinkedPropertySetNode {
        public JSAdapterPropertySetNode(ReceiverCheckNode receiverCheckNode) {
            super(receiverCheckNode);
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            JSObject.set((JSDynamicObject) thisObj, root.getKey(), value, root.isStrict(), root);
            return true;
        }
    }

    public static final class JSProxyDispatcherPropertySetNode extends LinkedPropertySetNode {
        @Child private JSProxyPropertySetNode proxySet;

        public JSProxyDispatcherPropertySetNode(JSContext context, ReceiverCheckNode receiverCheckNode, boolean isStrict, boolean defineProperty, int attributes) {
            super(receiverCheckNode);
            this.proxySet = JSProxyPropertySetNode.create(context, isStrict, defineProperty, attributes);
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            proxySet.executeWithReceiverAndValue(receiverCheck.getStore(thisObj), receiver, value, root.getKey());
            return true;
        }

        @Override
        protected boolean setValueInt(Object thisObj, int value, Object receiver, PropertySetNode root, boolean guard) {
            proxySet.executeWithReceiverAndValueInt(receiverCheck.getStore(thisObj), receiver, value, root.getKey());
            return true;
        }
    }

    public static final class GenericPropertySetNode extends SetCacheNode {
        @Child private JSToObjectNode toObjectNode;
        @Child private ForeignPropertySetNode foreignSetNode;
        private final JSClassProfile jsclassProfile = JSClassProfile.create();
        private final ConditionProfile isObject = ConditionProfile.create();
        private final ConditionProfile isStrictSymbol = ConditionProfile.create();
        private final ConditionProfile isForeignObject = ConditionProfile.create();

        public GenericPropertySetNode() {
            super(null);
            this.toObjectNode = JSToObjectNode.create();
        }

        @InliningCutoff
        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            if (isObject.profile(JSDynamicObject.isJSDynamicObject(thisObj))) {
                setValueInDynamicObject(thisObj, value, receiver, root);
            } else if (isStrictSymbol.profile(root.isStrict() && thisObj instanceof Symbol)) {
                throw Errors.createTypeError("Cannot create property on symbol", this);
            } else if (isForeignObject.profile(JSGuards.isForeignObject(thisObj))) {
                if (foreignSetNode == null) {
                    CompilerDirectives.transferToInterpreterAndInvalidate();
                    foreignSetNode = insert(new ForeignPropertySetNode(root.getContext(), root.getKey()));
                }
                foreignSetNode.setValue(thisObj, value, receiver, root, guard);
            } else {
                setValueInDynamicObject(toObjectNode.execute(thisObj), value, receiver, root);
            }
            return true;
        }

        private void setValueInDynamicObject(Object thisObj, Object value, Object receiver, PropertySetNode root) {
            JSDynamicObject thisJSObj = ((JSDynamicObject) thisObj);
            Object key = root.getKey();
            if (key instanceof HiddenKey) {
                JSObjectUtil.putHiddenProperty(thisJSObj, key, value);
            } else if (root.isOwnProperty()) {
                if (root.isDeclaration()) {
                    assert JSGlobal.isJSGlobalObject(thisJSObj) && !JSObject.hasProperty(thisJSObj, key);
                    JSObjectUtil.defineConstantDataProperty(root.getContext(), thisJSObj, key, value, root.getAttributeFlags());
                } else {
                    JSObject.defineOwnProperty(thisJSObj, key, PropertyDescriptor.createData(value, root.getAttributeFlags()), root.isStrict());
                }
            } else {
                JSObject.setWithReceiver(thisJSObj, key, value, receiver, root.isStrict(), jsclassProfile, root);
            }
        }

        @InliningCutoff
        @Override
        protected boolean setValueInt(Object thisObj, int value, Object receiver, PropertySetNode root, boolean guard) {
            return setValue(thisObj, value, receiver, root, guard);
        }

        @InliningCutoff
        @Override
        protected boolean setValueDouble(Object thisObj, double value, Object receiver, PropertySetNode root, boolean guard) {
            return setValue(thisObj, value, receiver, root, guard);
        }

        @InliningCutoff
        @Override
        protected boolean setValueBoolean(Object thisObj, boolean value, Object receiver, PropertySetNode root, boolean guard) {
            return setValue(thisObj, value, receiver, root, guard);
        }
    }

    public static final class ForeignPropertySetNode extends LinkedPropertySetNode {

        @Child private ExportValueNode export;
        @CompilationFinal private boolean optimistic = true;
        private final JSContext context;
        @Child private InteropLibrary interop;
        @Child private InteropLibrary setterInterop;
        private final BranchProfile errorBranch = BranchProfile.create();
        private final boolean isLength;

        public ForeignPropertySetNode(JSContext context, Object key) {
            super(new ForeignLanguageCheckNode());
            this.context = context;
            this.export = ExportValueNode.create();
            this.interop = InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit);
            this.isLength = key.equals(JSAbstractArray.LENGTH);
        }

        private Object nullCheck(Object truffleObject, Object key) {
            if (interop.isNull(truffleObject)) {
                throw Errors.createTypeErrorCannotSetProperty(key, truffleObject, this);
            }
            return truffleObject;
        }

        private boolean setValueImpl(Object thisObj, Object value, PropertySetNode root) {
            Object key = root.getKey();
            Object truffleObject = nullCheck(thisObj, key);
            if (!(key instanceof TruffleString)) {
                return false;
            }
            if (isLength && interop.hasArrayElements(thisObj)) {
                return JSInteropUtil.setArraySize(thisObj, value, root.isStrict, interop, this, errorBranch);
            }
            return performWriteMember(truffleObject, value, root);
        }

        @Override
        protected boolean setValueInt(Object thisObj, int value, Object receiver, PropertySetNode root, boolean guard) {
            return setValueImpl(thisObj, value, root);
        }

        @Override
        protected boolean setValueDouble(Object thisObj, double value, Object receiver, PropertySetNode root, boolean guard) {
            return setValueImpl(thisObj, value, root);
        }

        @InliningCutoff
        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            return setValueImpl(thisObj, export.execute(value), root);
        }

        private boolean performWriteMember(Object truffleObject, Object value, PropertySetNode root) {
            if (context.getLanguageOptions().hasForeignHashProperties() && interop.hasHashEntries(truffleObject)) {
                try {
                    interop.writeHashEntry(truffleObject, root.getKey(), value);
                    return true;
                } catch (UnknownKeyException | UnsupportedMessageException | UnsupportedTypeException e) {
                    if (root.isStrict) {
                        errorBranch.enter();
                        throw Errors.createTypeErrorInteropException(truffleObject, e, "writeHashEntry", this);
                    } else {
                        return false;
                    }
                }
            }

            if (context.isOptionNashornCompatibilityMode()) {
                if (tryInvokeSetter(truffleObject, value, root)) {
                    return true;
                }
            }
            String stringKey = Strings.toJavaString((TruffleString) root.getKey());
            // strict mode always throws if the member is not writable
            if (root.isStrict || optimistic) {
                try {
                    interop.writeMember(truffleObject, stringKey, value);
                    return true;
                } catch (UnknownIdentifierException | UnsupportedTypeException | UnsupportedMessageException e) {
                    if (root.isStrict) {
                        errorBranch.enter();
                        throw Errors.createTypeErrorInteropException(truffleObject, e, "writeMember", stringKey, this);
                    } else if (e instanceof UnknownIdentifierException) {
                        CompilerDirectives.transferToInterpreterAndInvalidate();
                        optimistic = false;
                    }
                    return false;
                }
            } else {
                assert !root.isStrict;
                if (interop.isMemberWritable(truffleObject, stringKey)) {
                    try {
                        interop.writeMember(truffleObject, stringKey, value);
                        return true;
                    } catch (UnknownIdentifierException | UnsupportedTypeException | UnsupportedMessageException e) {
                        return false;
                    }
                } else {
                    return false;
                }
            }
        }

        // in nashorn-compat mode, `javaObj.xyz = a` can mean `javaObj.setXyz(a)`.
        private boolean tryInvokeSetter(Object thisObj, Object value, PropertySetNode root) {
            assert context.isOptionNashornCompatibilityMode();
            TruffleLanguage.Env env = getRealm().getEnv();
            if (env.isHostObject(thisObj)) {
                TruffleString setterKey = root.getAccessorKey(Strings.SET);
                if (setterKey == null) {
                    return false;
                }
                if (setterInterop == null) {
                    CompilerDirectives.transferToInterpreterAndInvalidate();
                    setterInterop = insert(InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit));
                }
                if (!setterInterop.isMemberInvocable(thisObj, Strings.toJavaString(setterKey))) {
                    return false;
                }
                try {
                    setterInterop.invokeMember(thisObj, Strings.toJavaString(setterKey), value);
                    return true;
                } catch (UnknownIdentifierException | UnsupportedMessageException | UnsupportedTypeException | ArityException e) {
                    // silently ignore
                }
            }
            return false;
        }
    }

    public static final class ArrayLengthPropertySetNode extends LinkedPropertySetNode {

        @Child private ArrayLengthWriteNode arrayLengthWrite;
        private final boolean isStrict;
        private final BranchProfile errorBranch = BranchProfile.create();

        public ArrayLengthPropertySetNode(Property property, AbstractShapeCheckNode shapeCheck, boolean isStrict) {
            super(shapeCheck);
            assert JSProperty.isData(property) && JSProperty.isWritable(property) && isArrayLengthProperty(property);
            this.isStrict = isStrict;
            this.arrayLengthWrite = ArrayLengthWriteNode.create(isStrict);
        }

        @Override
        protected boolean setValue(Object thisObj, Object value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = receiverCheck.getStore(thisObj);
            boolean ret = JSArray.setLength(store, value, this);
            if (!ret && isStrict) {
                errorBranch.enter();
                throw Errors.createTypeErrorNotWritableProperty(JSArray.LENGTH, thisObj, this);
            }
            return true;
        }

        @Override
        protected boolean setValueInt(Object thisObj, int value, Object receiver, PropertySetNode root, boolean guard) {
            JSDynamicObject store = receiverCheck.getStore(thisObj);
            // shape check should be sufficient to guarantee this
            assert JSArray.isJSFastArray(store);
            if (value < 0) {
                errorBranch.enter();
                throw Errors.createRangeErrorInvalidArrayLength(this);
            }
            arrayLengthWrite.executeVoid(store, value);
            return true;
        }
    }

    /**
     * Make a cache for a JSObject with this property map and requested property.
     *
     * @param property The particular entry of the property being accessed.
     */
    @Override
    protected SetCacheNode createCachedPropertyNode(Property property, Object thisObj, JSDynamicObject proto, int depth, Object value, SetCacheNode currentHead) {
        if (JSDynamicObject.isJSDynamicObject(thisObj)) {
            return createCachedPropertyNodeJSObject(property, (JSDynamicObject) thisObj, proto, depth, value);
        } else {
            return createCachedPropertyNodeNotJSObject(property, thisObj, proto, depth);
        }
    }

    private SetCacheNode createCachedPropertyNodeJSObject(Property property, JSDynamicObject thisObj, JSDynamicObject proto, int depth, Object value) {
        Shape cacheShape = thisObj.getShape();
        AbstractShapeCheckNode shapeCheck = createShapeCheckNode(cacheShape, thisObj, proto, depth, false, false);

        // isOwnProperty() means CreateDataProperty, i.e., we must redefine
        // the property when the current flags do not match data property and
        // when it is possible to do so (i.e. when it is configurable)
        if (isOwnProperty()) {
            if (JSAttributes.isConfigurable(property.getFlags()) && property.getFlags() != JSAttributes.configurableEnumerableWritable()) {
                return new DataPropertyPutWithFlagsNode(key, shapeCheck);
            } else if (!JSAttributes.isConfigurable(property.getFlags()) && JSAttributes.isConfigurable(getAttributeFlags())) {
                return new ReadOnlyPropertySetNode(shapeCheck, isStrict(), property);
            }
        }

        if (JSProperty.isData(property)) {
            return createCachedDataPropertyNodeJSObject(thisObj, proto, depth, value, shapeCheck, property);
        } else {
            assert JSProperty.isAccessor(property);
            return new AccessorPropertySetNode(property, shapeCheck, isStrict());
        }
    }

    private SetCacheNode createCachedDataPropertyNodeJSObject(JSDynamicObject thisObj, JSDynamicObject proto, int depth, Object value, AbstractShapeCheckNode shapeCheck, Property property) {
        assert !JSProperty.isConst(property) || (depth == 0 && isGlobal() && property.getLocation().isConstant() && property.getLocation().getConstantValue() == Dead.instance()) : "const assignment";
        if (!JSProperty.isWritable(property) || JSProperty.isModuleNamespaceExport(property)) {
            return new ReadOnlyPropertySetNode(shapeCheck, isStrict(), property);
        } else if (superProperty) {
            // define the property on the receiver; currently not handled, rewrite to generic
            return createGenericPropertyNode();
        } else if (depth > 0) {
            // define a new own property, shadowing an existing prototype property
            // NB: must have a guarding test that the inherited property is writable
            assert JSProperty.isWritable(property);
            return createUndefinedPropertyNode(thisObj, thisObj, proto, depth, value);
        } else if (JSProperty.isProxy(property)) {
            if (isArrayLengthProperty(property) && JSArray.isJSFastArray(thisObj)) {
                return new ArrayLengthPropertySetNode(property, shapeCheck, isStrict());
            }
            return new PropertyProxySetNode(property, shapeCheck, isStrict());
        } else {
            assert JSProperty.isWritable(property) && !JSProperty.isDataSpecial(property) && depth == 0;
            if (property.getLocation().isConstant() || !property.getLocation().canStore(value)) {
                return createRedefinePropertyNode(key, shapeCheck, shapeCheck.getShape(), property);
            }

            if (property.getLocation() instanceof com.oracle.truffle.api.object.IntLocation) {
                return new IntPropertySetNode(property, shapeCheck);
            } else if (property.getLocation() instanceof com.oracle.truffle.api.object.DoubleLocation) {
                return new DoublePropertySetNode(property, shapeCheck);
            } else if (property.getLocation() instanceof com.oracle.truffle.api.object.BooleanLocation) {
                return new BooleanPropertySetNode(property, shapeCheck);
            } else {
                return new ObjectPropertySetNode(property, shapeCheck);
            }
        }
    }

    private SetCacheNode createDefineNewPropertyNode(ReceiverCheckNode shapeCheck) {
        JSObjectUtil.checkForNoSuchPropertyOrMethod(context, key);
        if (JSShape.hasNoElementsAssumption(shapeCheck.getShape())) {
            if (context.getArrayPrototypeNoElementsAssumption().isValid() && JSRuntime.isArrayIndex(key)) {
                context.getArrayPrototypeNoElementsAssumption().invalidate("Set element on an Array prototype");
            }
        }
        if (isDeclaration()) {
            return new DataPropertyPutConstantNode(key, shapeCheck);
        } else if (getAttributeFlags() == 0) {
            // new property and flags=0 means we can use put without flags
            // must not use this node if the property already exists and we want to change the flags
            return new DataPropertyPutWithoutFlagsNode(key, shapeCheck);
        } else {
            return new DataPropertyPutWithFlagsNode(key, shapeCheck);
        }
    }

    private static SetCacheNode createRedefinePropertyNode(Object key, ReceiverCheckNode shapeCheck, Shape oldShape, Property property) {
        assert JSProperty.isData(property) && JSProperty.isWritable(property);
        assert property == oldShape.getProperty(key);

        return new DataPropertyPutWithoutFlagsNode(key, shapeCheck);
    }

    private SetCacheNode createCachedPropertyNodeNotJSObject(Property property, Object thisObj, JSDynamicObject proto, int depth) {
        ReceiverCheckNode receiverCheck = createPrimitiveReceiverCheck(thisObj, proto, depth);

        if (JSProperty.isData(property)) {
            return new ReadOnlyPropertySetNode(receiverCheck, isStrict(), property);
        } else {
            assert JSProperty.isAccessor(property);
            return new AccessorPropertySetNode(property, receiverCheck, isStrict());
        }
    }

    @Override
    protected SetCacheNode createUndefinedPropertyNode(Object thisObj, Object store, JSDynamicObject proto, int depth, Object value) {
        SetCacheNode specialized = createJavaPropertyNodeMaybe(thisObj, proto, depth);
        if (specialized != null) {
            return specialized;
        }
        if (JSDynamicObject.isJSDynamicObject(thisObj)) {
            JSDynamicObject thisJSObj = (JSDynamicObject) thisObj;
            Shape cacheShape = thisJSObj.getShape();
            if (JSAdapter.isJSAdapter(store)) {
                return new JSAdapterPropertySetNode(createJSClassCheck(thisObj, proto, depth));
            } else if (JSProxy.isJSProxy(store) && JSRuntime.isPropertyKey(key)) {
                return new JSProxyDispatcherPropertySetNode(context, createJSClassCheck(thisObj, proto, depth), isStrict(), isOwnProperty(), getAttributeFlags());
            } else if (JSArrayBufferView.isJSArrayBufferView(store) && (key instanceof TruffleString indexStr) && JSRuntime.canonicalNumericIndexString(indexStr) != Undefined.instance) {
                assert !JSArrayBufferView.isValidIntegerIndex((JSDynamicObject) store, (Number) JSRuntime.canonicalNumericIndexString(indexStr));
                return new ReadOnlyPropertySetNode(createShapeCheckNode(cacheShape, thisJSObj, proto, depth, false, false), false);
            } else if (!JSRuntime.isObject(thisJSObj)) {
                return new TypeErrorPropertySetNode(createShapeCheckNode(cacheShape, thisJSObj, proto, depth, false, true));
            } else if (superProperty) {
                // define the property on the receiver; currently not handled, rewrite to generic
                return createGenericPropertyNode();
            } else if (JSShape.isExtensible(cacheShape) || key instanceof HiddenKey) {
                return createDefineNewPropertyNode(createShapeCheckNode(cacheShape, thisJSObj, proto, depth, false, true));
            } else {
                return new ReadOnlyPropertySetNode(createShapeCheckNode(cacheShape, thisJSObj, proto, depth, false, false), isStrict());
            }
        } else if (JSProxy.isJSProxy(store)) {
            ReceiverCheckNode receiverCheck = createPrimitiveReceiverCheck(thisObj, proto, depth);
            return new JSProxyDispatcherPropertySetNode(context, receiverCheck, isStrict(), isOwnProperty(), getAttributeFlags());
        } else {
            boolean doThrow = isStrict();
            if (!JSRuntime.isJSNative(thisObj)) {
                // Nashorn never throws when setting inexistent properties on Java objects
                doThrow = false;
            }
            return new ReadOnlyPropertySetNode(new InstanceofCheckNode(thisObj.getClass()), doThrow);
        }
    }

    @Override
    protected SetCacheNode createJavaPropertyNodeMaybe(Object thisObj, JSDynamicObject proto, int depth) {
        return null;
    }

    @Override
    protected SetCacheNode createGenericPropertyNode() {
        return new GenericPropertySetNode();
    }

    @Override
    protected boolean isGlobal() {
        return isGlobal;
    }

    @Override
    protected boolean isOwnProperty() {
        return setOwnProperty;
    }

    protected final boolean isStrict() {
        return this.isStrict;
    }

    protected final int getAttributeFlags() {
        return attributeFlags;
    }

    protected final boolean isDeclaration() {
        return declaration;
    }

    @Override
    protected SetCacheNode createTruffleObjectPropertyNode() {
        return new ForeignPropertySetNode(context, getKey());
    }

    @Override
    protected boolean isPropertyAssumptionCheckEnabled() {
        return propertyAssumptionCheckEnabled && context.isSingleRealm();
    }

    @Override
    protected void setPropertyAssumptionCheckEnabled(boolean value) {
        this.propertyAssumptionCheckEnabled = value;
    }

    @Override
    protected boolean canCombineShapeCheck(Shape parentShape, Shape cacheShape, Object thisObj, int depth, Object value, Property property) {
        assert shapesHaveCommonLayoutForKey(parentShape, cacheShape);
        if (JSObject.isJSObject(thisObj) && JSProperty.isData(property) && !JSProperty.isDataSpecial(property) && JSProperty.isWritable(property) &&
                        depth == 0 && !superProperty) {
            return !property.getLocation().isConstant() && property.getLocation().canStore(value);
        }
        return false;
    }

    @Override
    protected SetCacheNode createCombinedIcPropertyNode(Shape parentShape, Shape cacheShape, Object thisObj, int depth, Object value, Property property) {
        assert JSProperty.isData(property) && !JSProperty.isDataSpecial(property) : property;
        PropertyGetNode.CombinedShapeCheckNode shapeCheck = new PropertyGetNode.CombinedShapeCheckNode(parentShape, cacheShape);

        if (property.getLocation() instanceof com.oracle.truffle.api.object.IntLocation) {
            return new IntPropertySetNode(property, shapeCheck);
        } else if (property.getLocation() instanceof com.oracle.truffle.api.object.DoubleLocation) {
            return new DoublePropertySetNode(property, shapeCheck);
        } else if (property.getLocation() instanceof com.oracle.truffle.api.object.BooleanLocation) {
            return new BooleanPropertySetNode(property, shapeCheck);
        } else {
            return new ObjectPropertySetNode(property, shapeCheck);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy