com.oracle.truffle.js.nodes.access.WriteElementNode 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 static com.oracle.truffle.js.runtime.builtins.JSAbstractArray.arraySetArrayType;
import java.util.Set;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.ImportStatic;
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.InstrumentableNode;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.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.Node;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.object.DynamicObjectLibrary;
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
import com.oracle.truffle.api.profiles.InlinedExactClassProfile;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.js.lang.JavaScriptLanguage;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.ArrayWriteElementCacheDispatchNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.BigIntWriteElementTypeCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.BooleanWriteElementTypeCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.ConstantArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.DoubleArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.ForeignObjectWriteElementTypeCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.HolesDoubleArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.HolesIntArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.HolesJSObjectArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.HolesObjectArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.IntArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.JSObjectArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.LazyRegexResultArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.LazyRegexResultIndicesArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.NumberWriteElementTypeCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.ObjectArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.StringWriteElementTypeCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.SymbolWriteElementTypeCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.TypedBigIntArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.TypedFloatArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.TypedIntArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.WritableArrayWriteElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.WriteElementNodeFactory.WriteElementTypeCacheDispatchNodeGen;
import com.oracle.truffle.js.nodes.cast.JSToBigIntNode;
import com.oracle.truffle.js.nodes.cast.JSToDoubleNode;
import com.oracle.truffle.js.nodes.cast.JSToInt32Node;
import com.oracle.truffle.js.nodes.cast.JSToPropertyKeyNode;
import com.oracle.truffle.js.nodes.cast.JSToPropertyKeyNode.JSToPropertyKeyWrapperNode;
import com.oracle.truffle.js.nodes.cast.ToArrayIndexNoToPropertyKeyNode;
import com.oracle.truffle.js.nodes.cast.ToArrayIndexNode;
import com.oracle.truffle.js.nodes.instrumentation.JSTaggedExecutionNode;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.WriteElementTag;
import com.oracle.truffle.js.nodes.interop.ExportValueNode;
import com.oracle.truffle.js.runtime.BigInt;
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.JSRuntime;
import com.oracle.truffle.js.runtime.Strings;
import com.oracle.truffle.js.runtime.Symbol;
import com.oracle.truffle.js.runtime.array.ScriptArray;
import com.oracle.truffle.js.runtime.array.ScriptArray.CreateWritableProfileAccess;
import com.oracle.truffle.js.runtime.array.SparseArray;
import com.oracle.truffle.js.runtime.array.TypedArray;
import com.oracle.truffle.js.runtime.array.TypedArray.AbstractUint8ClampedArray;
import com.oracle.truffle.js.runtime.array.TypedArray.TypedBigIntArray;
import com.oracle.truffle.js.runtime.array.TypedArray.TypedFloatArray;
import com.oracle.truffle.js.runtime.array.TypedArray.TypedIntArray;
import com.oracle.truffle.js.runtime.array.TypedArray.Uint8ClampedArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractConstantArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractContiguousDoubleArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractContiguousIntArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractContiguousJSObjectArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractContiguousObjectArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractDoubleArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractIntArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractJSObjectArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractObjectArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractWritableArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractWritableArray.SetSupportedProfileAccess;
import com.oracle.truffle.js.runtime.array.dyn.ContiguousIntArray;
import com.oracle.truffle.js.runtime.array.dyn.HolesDoubleArray;
import com.oracle.truffle.js.runtime.array.dyn.HolesIntArray;
import com.oracle.truffle.js.runtime.array.dyn.HolesJSObjectArray;
import com.oracle.truffle.js.runtime.array.dyn.HolesObjectArray;
import com.oracle.truffle.js.runtime.array.dyn.LazyRegexResultArray;
import com.oracle.truffle.js.runtime.array.dyn.LazyRegexResultIndicesArray;
import com.oracle.truffle.js.runtime.builtins.JSAbstractArray;
import com.oracle.truffle.js.runtime.builtins.JSArray;
import com.oracle.truffle.js.runtime.builtins.JSArrayBufferView;
import com.oracle.truffle.js.runtime.builtins.JSBigInt;
import com.oracle.truffle.js.runtime.builtins.JSBoolean;
import com.oracle.truffle.js.runtime.builtins.JSNumber;
import com.oracle.truffle.js.runtime.builtins.JSSlowArgumentsArray;
import com.oracle.truffle.js.runtime.builtins.JSSlowArray;
import com.oracle.truffle.js.runtime.builtins.JSString;
import com.oracle.truffle.js.runtime.builtins.JSSymbol;
import com.oracle.truffle.js.runtime.builtins.JSTypedArrayObject;
import com.oracle.truffle.js.runtime.interop.JSInteropUtil;
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.util.JSClassProfile;
import com.oracle.truffle.js.runtime.util.TRegexUtil;
public class WriteElementNode extends JSTargetableNode {
@Child protected JavaScriptNode targetNode;
@Child protected JavaScriptNode indexNode;
@Child protected JavaScriptNode valueNode;
@Child private WriteElementTypeCacheNode typeCacheNode;
final JSContext context;
final boolean isStrict;
final boolean writeOwn;
@CompilationFinal private byte indexState;
private static final byte INDEX_INT = 1;
private static final byte INDEX_OBJECT = 2;
/** Exact cache limit unknown, but effectively bounded by the number of types. */
static final int BOUNDED_BY_TYPES = Integer.MAX_VALUE;
@NeverDefault
public static WriteElementNode create(JSContext context, boolean isStrict) {
return create(null, null, null, context, isStrict, false);
}
@NeverDefault
public static WriteElementNode create(JSContext context, boolean isStrict, boolean writeOwn) {
return create(null, null, null, context, isStrict, writeOwn);
}
public static WriteElementNode create(JavaScriptNode targetNode, JavaScriptNode indexNode, JavaScriptNode valueNode, JSContext context, boolean isStrict) {
return create(targetNode, indexNode, valueNode, context, isStrict, false);
}
private static WriteElementNode create(JavaScriptNode targetNode, JavaScriptNode indexNode, JavaScriptNode valueNode, JSContext context, boolean isStrict, boolean writeOwn) {
return new WriteElementNode(targetNode, indexNode, valueNode, context, isStrict, writeOwn);
}
protected WriteElementNode(JavaScriptNode targetNode, JavaScriptNode indexNode, JavaScriptNode valueNode, JSContext context, boolean isStrict, boolean writeOwn) {
// ToPropertyKey conversion should not be performed by indexNode
// (we need to RequireObjectCoercible(target) before this conversion)
assert !(indexNode instanceof JSToPropertyKeyWrapperNode);
this.targetNode = targetNode;
this.indexNode = indexNode;
this.valueNode = valueNode;
this.context = context;
this.isStrict = isStrict;
this.writeOwn = writeOwn;
}
@Override
public boolean hasTag(Class extends Tag> tag) {
if (tag == WriteElementTag.class) {
return true;
} else {
return super.hasTag(tag);
}
}
@Override
public InstrumentableNode materializeInstrumentableNodes(Set> materializedTags) {
if (materializationNeeded() && materializedTags.contains(WriteElementTag.class)) {
JavaScriptNode clonedTarget = targetNode == null || targetNode.hasSourceSection() ? targetNode : JSTaggedExecutionNode.createForInput(targetNode, this, materializedTags);
JavaScriptNode clonedIndex = indexNode == null || indexNode.hasSourceSection() ? indexNode : JSTaggedExecutionNode.createForInput(indexNode, this, materializedTags);
JavaScriptNode clonedValue = valueNode == null || valueNode.hasSourceSection() ? valueNode : JSTaggedExecutionNode.createForInput(valueNode, this, materializedTags);
if (clonedTarget == targetNode && clonedIndex == indexNode && clonedValue == valueNode) {
return this;
}
if (clonedTarget == targetNode) {
clonedTarget = cloneUninitialized(targetNode, materializedTags);
}
if (clonedIndex == indexNode) {
clonedIndex = cloneUninitialized(indexNode, materializedTags);
}
if (clonedValue == valueNode) {
clonedValue = cloneUninitialized(valueNode, materializedTags);
}
WriteElementNode cloned = createMaterialized(clonedTarget, clonedIndex, clonedValue);
transferSourceSectionAndTags(this, cloned);
return cloned;
}
return this;
}
private boolean materializationNeeded() {
// Materialization is needed when source sections are missing.
return (targetNode != null && !targetNode.hasSourceSection()) || (indexNode != null && !indexNode.hasSourceSection()) || (valueNode != null && !valueNode.hasSourceSection());
}
protected WriteElementNode createMaterialized(JavaScriptNode newTarget, JavaScriptNode newIndex, JavaScriptNode newValue) {
return WriteElementNode.create(newTarget, newIndex, newValue, getContext(), isStrict(), writeOwn());
}
@Override
public Object evaluateTarget(VirtualFrame frame) {
return targetNode.execute(frame);
}
@Override
public Object execute(VirtualFrame frame) {
Object target = evaluateTarget(frame);
return executeWithTarget(frame, target, evaluateReceiver(targetNode, frame, target));
}
@Override
public int executeInt(VirtualFrame frame) throws UnexpectedResultException {
Object target = evaluateTarget(frame);
return executeWithTargetInt(frame, target, evaluateReceiver(targetNode, frame, target));
}
@Override
public double executeDouble(VirtualFrame frame) throws UnexpectedResultException {
Object target = evaluateTarget(frame);
return executeWithTargetDouble(frame, target, evaluateReceiver(targetNode, frame, target));
}
@Override
public Object executeWithTarget(VirtualFrame frame, Object target) {
return executeWithTarget(frame, target, target);
}
public Object executeWithTarget(VirtualFrame frame, Object target, Object receiver) {
byte is = indexState;
if (is == 0) {
CompilerDirectives.transferToInterpreterAndInvalidate();
Object index = indexNode.execute(frame);
if (index instanceof Integer) {
indexState = INDEX_INT;
return executeWithTargetAndIndex(frame, target, (int) index, receiver);
} else {
indexState = INDEX_OBJECT;
return executeWithTargetAndIndex(frame, target, index, receiver);
}
} else if (is == INDEX_INT) {
int index;
try {
index = indexNode.executeInt(frame);
} catch (UnexpectedResultException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
indexState = INDEX_OBJECT;
return executeWithTargetAndIndex(frame, target, e.getResult(), receiver);
}
return executeWithTargetAndIndex(frame, target, index, receiver);
} else {
assert is == INDEX_OBJECT;
Object index = indexNode.execute(frame);
return executeWithTargetAndIndex(frame, target, index, receiver);
}
}
public int executeWithTargetInt(VirtualFrame frame, Object target, Object receiver) throws UnexpectedResultException {
byte is = indexState;
if (is == 0) {
CompilerDirectives.transferToInterpreterAndInvalidate();
Object index = indexNode.execute(frame);
if (index instanceof Integer) {
indexState = INDEX_INT;
return executeWithTargetAndIndexInt(frame, target, (int) index, receiver);
} else {
indexState = INDEX_OBJECT;
return executeWithTargetAndIndexInt(frame, target, index, receiver);
}
} else if (is == INDEX_INT) {
int index;
try {
index = indexNode.executeInt(frame);
} catch (UnexpectedResultException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
indexState = INDEX_OBJECT;
return executeWithTargetAndIndexInt(frame, target, e.getResult(), receiver);
}
return executeWithTargetAndIndexInt(frame, target, index, receiver);
} else {
assert is == INDEX_OBJECT;
Object index = indexNode.execute(frame);
return executeWithTargetAndIndexInt(frame, target, index, receiver);
}
}
public double executeWithTargetDouble(VirtualFrame frame, Object target, Object receiver) throws UnexpectedResultException {
byte is = indexState;
if (is == 0) {
CompilerDirectives.transferToInterpreterAndInvalidate();
Object index = indexNode.execute(frame);
if (index instanceof Integer) {
indexState = INDEX_INT;
return executeWithTargetAndIndexDouble(frame, target, (int) index, receiver);
} else {
indexState = INDEX_OBJECT;
return executeWithTargetAndIndexDouble(frame, target, index, receiver);
}
} else if (is == INDEX_INT) {
int index;
try {
index = indexNode.executeInt(frame);
} catch (UnexpectedResultException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
indexState = INDEX_OBJECT;
return executeWithTargetAndIndexDouble(frame, target, e.getResult(), receiver);
}
return executeWithTargetAndIndexDouble(frame, target, index, receiver);
} else {
assert is == INDEX_OBJECT;
Object index = indexNode.execute(frame);
return executeWithTargetAndIndexDouble(frame, target, index, receiver);
}
}
protected Object executeWithTargetAndIndex(VirtualFrame frame, Object target, Object index, Object receiver) {
Object value = valueNode.execute(frame);
executeWithTargetAndIndexAndValue(target, index, value, receiver);
return value;
}
protected Object executeWithTargetAndIndex(VirtualFrame frame, Object target, int index, Object receiver) {
Object value = valueNode.execute(frame);
executeWithTargetAndIndexAndValue(target, index, value, receiver);
return value;
}
protected int executeWithTargetAndIndexInt(VirtualFrame frame, Object target, Object index, Object receiver) throws UnexpectedResultException {
try {
int value = valueNode.executeInt(frame);
executeWithTargetAndIndexAndValue(target, index, value, receiver);
return value;
} catch (UnexpectedResultException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
executeWithTargetAndIndexAndValue(target, index, e.getResult(), receiver);
throw e;
}
}
protected int executeWithTargetAndIndexInt(VirtualFrame frame, Object target, int index, Object receiver) throws UnexpectedResultException {
try {
int value = valueNode.executeInt(frame);
executeWithTargetAndIndexAndValue(target, index, (Object) value, receiver);
return value;
} catch (UnexpectedResultException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
executeWithTargetAndIndexAndValue(target, index, e.getResult(), receiver);
throw e;
}
}
protected double executeWithTargetAndIndexDouble(VirtualFrame frame, Object target, Object index, Object receiver) throws UnexpectedResultException {
try {
double value = valueNode.executeDouble(frame);
executeWithTargetAndIndexAndValue(target, index, value, receiver);
return value;
} catch (UnexpectedResultException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
executeWithTargetAndIndexAndValue(target, index, e.getResult(), receiver);
throw e;
}
}
protected double executeWithTargetAndIndexDouble(VirtualFrame frame, Object target, int index, Object receiver) throws UnexpectedResultException {
try {
double value = valueNode.executeDouble(frame);
executeWithTargetAndIndexAndValue(target, index, (Object) value, receiver);
return value;
} catch (UnexpectedResultException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
executeWithTargetAndIndexAndValue(target, index, e.getResult(), receiver);
throw e;
}
}
public final void executeWithTargetAndIndexAndValue(Object target, Object index, Object value) {
executeWithTargetAndIndexAndValue(target, index, value, target);
}
public final void executeWithTargetAndIndexAndValue(Object target, int index, Object value) {
executeWithTargetAndIndexAndValue(target, index, value, target);
}
public final void executeWithTargetAndIndexAndValue(Object target, long index, Object value) {
executeWithTargetAndIndexAndValue(target, index, value, target);
}
public final void executeWithTargetAndIndexAndValue(Object target, Object index, Object value, Object receiver) {
if (typeCacheNode == null) {
initTypeCacheNode();
}
typeCacheNode.executeWithTargetAndIndexUnguarded(target, index, value, receiver, this);
}
public final void executeWithTargetAndIndexAndValue(Object target, int index, Object value, Object receiver) {
if (typeCacheNode == null) {
initTypeCacheNode();
}
typeCacheNode.executeWithTargetAndIndexUnguarded(target, index, value, receiver, this);
}
public final void executeWithTargetAndIndexAndValue(Object target, long index, Object value, Object receiver) {
if (typeCacheNode == null) {
initTypeCacheNode();
}
typeCacheNode.executeWithTargetAndIndexUnguarded(target, index, value, receiver, this);
}
private void initTypeCacheNode() {
CompilerDirectives.transferToInterpreterAndInvalidate();
typeCacheNode = insert(WriteElementTypeCacheDispatchNodeGen.create());
}
abstract static class WriteElementTypeCacheNode extends JavaScriptBaseNode {
protected WriteElementTypeCacheNode() {
}
protected abstract void executeWithTargetAndIndexUnguarded(Object target, Object index, Object value, Object receiver, WriteElementNode root);
protected abstract void executeWithTargetAndIndexUnguarded(Object target, long index, Object value, Object receiver, WriteElementNode root);
}
abstract static class GuardedWriteElementTypeCacheNode extends JavaScriptBaseNode {
protected GuardedWriteElementTypeCacheNode() {
}
public abstract boolean guard(Object target);
protected abstract void executeWithTargetAndIndexUnguarded(Object target, Object index, Object value, Object receiver, WriteElementNode root);
}
@ImportStatic(WriteElementNode.class)
abstract static class WriteElementTypeCacheDispatchNode extends WriteElementTypeCacheNode {
protected WriteElementTypeCacheDispatchNode() {
super();
}
@SuppressWarnings("unused")
@Specialization(guards = "isObjectNode.executeBoolean(target)")
protected static void doJSObjectLongIndex(Object target, long index, Object value, Object receiver, WriteElementNode root,
@Cached @Shared IsJSDynamicObjectNode isObjectNode,
@Cached @Shared JSObjectWriteElementTypeCacheNode objectHandler) {
objectHandler.executeWithTargetAndIndexUnguarded(target, index, value, receiver, root);
}
@SuppressWarnings("unused")
@Specialization(guards = "isObjectNode.executeBoolean(target)", replaces = {"doJSObjectLongIndex"})
protected static void doJSObject(Object target, Object index, Object value, Object receiver, WriteElementNode root,
@Cached @Shared IsJSDynamicObjectNode isObjectNode,
@Cached @Shared JSObjectWriteElementTypeCacheNode objectHandler) {
objectHandler.executeWithTargetAndIndexUnguarded(target, index, value, receiver, root);
}
@Specialization(guards = "otherHandler.guard(target)", limit = "BOUNDED_BY_TYPES")
protected static void doOther(Object target, Object index, Object value, Object receiver, WriteElementNode root,
@Cached("makeHandler(target)") GuardedWriteElementTypeCacheNode otherHandler) {
otherHandler.executeWithTargetAndIndexUnguarded(target, index, value, receiver, root);
}
protected static GuardedWriteElementTypeCacheNode makeHandler(Object target) {
if (JSDynamicObject.isJSDynamicObject(target)) {
// Handled by other specializations.
throw Errors.shouldNotReachHere("JSDynamicObject");
} else if (Strings.isTString(target)) {
return StringWriteElementTypeCacheNodeGen.create();
} else if (target instanceof Boolean) {
return BooleanWriteElementTypeCacheNodeGen.create();
} else if (target instanceof Number) {
return NumberWriteElementTypeCacheNodeGen.create(target.getClass());
} else if (target instanceof Symbol) {
return SymbolWriteElementTypeCacheNodeGen.create();
} else if (target instanceof BigInt) {
return BigIntWriteElementTypeCacheNodeGen.create();
} else {
assert JSRuntime.isForeignObject(target) : target.getClass();
return ForeignObjectWriteElementTypeCacheNodeGen.create();
}
}
}
abstract static class JSObjectWriteElementTypeCacheNode extends WriteElementTypeCacheNode {
@Child private IsArrayNode isArrayNode;
@Child private ToArrayIndexNode toArrayIndexNode;
private final JSClassProfile jsclassProfile = JSClassProfile.create();
@Child private CachedSetPropertyNode setPropertyCachedNode;
JSObjectWriteElementTypeCacheNode() {
super();
this.isArrayNode = IsArrayNode.createIsFastOrTypedArray();
}
@Specialization
protected void doJSObjectIntegerIndex(Object target, long index, Object value, Object receiver, WriteElementNode root,
@Cached @Shared InlinedConditionProfile arrayIf,
@Cached @Shared InlinedConditionProfile intOrStringIndexIf,
@Cached(inline = true) @Shared ArrayWriteElementCacheDispatchNode arrayDispatch) {
JSDynamicObject targetObject = ((JSDynamicObject) target);
if (arrayIf.profile(this, isArrayNode.execute(targetObject))) {
ScriptArray array = JSObject.getArray(targetObject);
if (intOrStringIndexIf.profile(this, JSRuntime.isArrayIndex(index))) {
if (!arrayDispatch.executeSetArray(this, targetObject, array, index, value, root)) {
setPropertyGenericEvaluatedIndex(targetObject, index, value, receiver, root);
}
} else {
setPropertyGenericEvaluatedStringOrSymbol(targetObject, Strings.fromLong(index), value, receiver, root);
}
} else {
setPropertyGeneric(targetObject, index, value, receiver, root);
}
}
@Specialization
protected void doJSObject(Object target, Object index, Object value, Object receiver, WriteElementNode root,
@Cached @Shared InlinedConditionProfile arrayIf,
@Cached @Shared InlinedConditionProfile intOrStringIndexIf,
@Cached(inline = true) @Shared ArrayWriteElementCacheDispatchNode arrayDispatch) {
JSDynamicObject targetObject = ((JSDynamicObject) target);
if (arrayIf.profile(this, isArrayNode.execute(targetObject))) {
Object objIndex = toArrayIndex(index);
ScriptArray array = JSObject.getArray(targetObject);
if (intOrStringIndexIf.profile(this, objIndex instanceof Long)) {
long longIndex = (Long) objIndex;
if (!arrayDispatch.executeSetArray(this, targetObject, array, longIndex, value, root)) {
setPropertyGenericEvaluatedIndex(targetObject, longIndex, value, receiver, root);
}
} else {
setPropertyGenericEvaluatedStringOrSymbol(targetObject, objIndex, value, receiver, root);
}
} else {
setPropertyGeneric(targetObject, index, value, receiver, root);
}
}
private Object toArrayIndex(Object index) {
ToArrayIndexNode toArrayIndex = toArrayIndexNode;
if (toArrayIndex == null) {
toArrayIndex = initToArrayIndexNode();
}
return toArrayIndex.execute(index);
}
private ToArrayIndexNode initToArrayIndexNode() {
CompilerDirectives.transferToInterpreterAndInvalidate();
return toArrayIndexNode = insert(ToArrayIndexNode.create());
}
private void setPropertyGenericEvaluatedIndex(JSDynamicObject targetObject, long index, Object value, Object receiver, WriteElementNode root) {
JSObject.setWithReceiver(targetObject, index, value, receiver, root.isStrict, jsclassProfile, root);
}
private void setPropertyGenericEvaluatedStringOrSymbol(JSDynamicObject targetObject, Object key, Object value, Object receiver, WriteElementNode root) {
JSObject.setWithReceiver(targetObject, key, value, receiver, root.isStrict, jsclassProfile, root);
}
private void setPropertyGeneric(JSDynamicObject targetObject, Object index, Object value, Object receiver, WriteElementNode root) {
setCachedProperty(targetObject, index, value, receiver, root);
}
@InliningCutoff
private void setCachedProperty(JSDynamicObject targetObject, Object index, Object value, Object receiver, WriteElementNode root) {
if (setPropertyCachedNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
setPropertyCachedNode = insert(CachedSetPropertyNode.create(root.context, root.isStrict, root.writeOwn, root.isSuperProperty()));
}
setPropertyCachedNode.execute(targetObject, index, value, receiver);
}
}
static ArrayWriteElementCacheNode makeArrayCacheNode(JSDynamicObject target, ScriptArray array) {
if (JSSlowArray.isJSSlowArray(target) || JSSlowArgumentsArray.isJSSlowArgumentsObject(target)) {
return new ExactArrayWriteElementCacheNode();
}
if (array.isLengthNotWritable() || !array.isExtensible()) {
// TODO handle this case in the specializations below
return new ExactArrayWriteElementCacheNode();
}
if (array instanceof LazyRegexResultArray) {
return LazyRegexResultArrayWriteElementCacheNodeGen.create();
} else if (array instanceof LazyRegexResultIndicesArray) {
return LazyRegexResultIndicesArrayWriteElementCacheNodeGen.create();
} else if (array instanceof AbstractConstantArray) {
return ConstantArrayWriteElementCacheNodeGen.create();
} else if (array instanceof HolesIntArray) {
return HolesIntArrayWriteElementCacheNodeGen.create();
} else if (array instanceof HolesDoubleArray) {
return HolesDoubleArrayWriteElementCacheNodeGen.create();
} else if (array instanceof HolesJSObjectArray) {
return HolesJSObjectArrayWriteElementCacheNodeGen.create();
} else if (array instanceof HolesObjectArray) {
return HolesObjectArrayWriteElementCacheNodeGen.create();
} else if (array instanceof AbstractIntArray) {
return IntArrayWriteElementCacheNodeGen.create();
} else if (array instanceof AbstractDoubleArray) {
return DoubleArrayWriteElementCacheNodeGen.create();
} else if (array instanceof AbstractObjectArray) {
return ObjectArrayWriteElementCacheNodeGen.create();
} else if (array instanceof AbstractJSObjectArray) {
return JSObjectArrayWriteElementCacheNodeGen.create();
} else if (array instanceof AbstractWritableArray) {
return WritableArrayWriteElementCacheNodeGen.create();
} else if (array instanceof TypedArray) {
if (array instanceof TypedIntArray) {
return TypedIntArrayWriteElementCacheNodeGen.create((TypedArray) array);
} else if (array instanceof TypedFloatArray) {
return TypedFloatArrayWriteElementCacheNodeGen.create((TypedArray) array);
} else if (array instanceof TypedBigIntArray) {
return TypedBigIntArrayWriteElementCacheNodeGen.create((TypedArray) array);
} else {
throw Errors.shouldNotReachHere();
}
} else {
return new ExactArrayWriteElementCacheNode();
}
}
abstract static class ArrayWriteElementCacheNode extends JavaScriptBaseNode {
ArrayWriteElementCacheNode() {
}
protected abstract boolean executeSetArray(JSDynamicObject target, ScriptArray array, long index, Object value, WriteElementNode root);
}
@SuppressWarnings("truffle-inlining")
@GenerateInline
@GenerateCached(true)
@ImportStatic(WriteElementNode.class)
abstract static class ArrayWriteElementCacheDispatchNode extends JavaScriptBaseNode {
ArrayWriteElementCacheDispatchNode() {
}
protected abstract boolean executeSetArray(Node node, JSDynamicObject target, ScriptArray array, long index, Object value, WriteElementNode root);
@Specialization(guards = "arrayType == cachedArrayType", limit = "BOUNDED_BY_TYPES")
protected static boolean doDispatch(JSDynamicObject target, @SuppressWarnings("unused") ScriptArray arrayType, long index, Object value, WriteElementNode root,
@Cached("arrayType") ScriptArray cachedArrayType,
@Cached("makeHandler(target, cachedArrayType)") ArrayWriteElementCacheNode handler) {
return handler.executeSetArray(target, cachedArrayType, index, value, root);
}
protected static ArrayWriteElementCacheNode makeHandler(JSDynamicObject target, ScriptArray arrayType) {
return makeArrayCacheNode(target, arrayType);
}
}
private abstract static class RecursiveCachedArrayWriteElementCacheNode extends ArrayWriteElementCacheNode {
@Child private ArrayWriteElementCacheDispatchNode recursiveWrite;
RecursiveCachedArrayWriteElementCacheNode() {
super();
}
protected final boolean setArrayAndWrite(ScriptArray newArray, JSDynamicObject target, long index, Object value, WriteElementNode root) {
arraySetArrayType(target, newArray);
return executeRecursive(target, newArray, index, value, root);
}
protected static boolean nonHolesArrayNeedsSlowSet(JSDynamicObject target, AbstractWritableArray arrayType, long index, WriteElementNode root) {
assert !arrayType.isHolesType();
if (!root.writeOwn && !root.context.getArrayPrototypeNoElementsAssumption().isValid()) {
if (!arrayType.hasElement(target, index)) {
return true;
}
}
return false;
}
protected static boolean holesArrayNeedsSlowSet(JSDynamicObject target, AbstractWritableArray arrayType, long index, WriteElementNode root) {
assert arrayType.isHolesType();
if ((!root.writeOwn && !root.context.getArrayPrototypeNoElementsAssumption().isValid()) ||
(!root.context.getFastArrayAssumption().isValid() && JSSlowArray.isJSSlowArray(target)) ||
(!root.context.getFastArgumentsObjectAssumption().isValid() && JSSlowArgumentsArray.isJSSlowArgumentsObject(target))) {
if (!arrayType.hasElement(target, index)) {
return true;
}
}
return false;
}
private boolean executeRecursive(JSDynamicObject targetObject, ScriptArray array, long index, Object value, WriteElementNode root) {
if (recursiveWrite == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
recursiveWrite = insert(ArrayWriteElementCacheDispatchNodeGen.create());
}
return recursiveWrite.executeSetArray(null, targetObject, array, index, value, root);
}
}
private static class ExactArrayWriteElementCacheNode extends ArrayWriteElementCacheNode {
ExactArrayWriteElementCacheNode() {
super();
}
@Override
protected boolean executeSetArray(JSDynamicObject target, ScriptArray array, long index, Object value, WriteElementNode root) {
return false;
}
}
abstract static class LazyRegexResultArrayWriteElementCacheNode extends RecursiveCachedArrayWriteElementCacheNode {
@Child private DynamicObjectLibrary lazyRegexResultNode = JSObjectUtil.createDispatched(JSAbstractArray.LAZY_REGEX_RESULT_ID);
@Child private DynamicObjectLibrary lazyRegexResultOriginalInputNode = JSObjectUtil.createDispatched(JSAbstractArray.LAZY_REGEX_ORIGINAL_INPUT_ID);
@Child private TruffleString.SubstringByteIndexNode substringNode = TruffleString.SubstringByteIndexNode.create();
@Child private TRegexUtil.InvokeGetGroupBoundariesMethodNode getStartNode = TRegexUtil.InvokeGetGroupBoundariesMethodNode.create();
@Child private TRegexUtil.InvokeGetGroupBoundariesMethodNode getEndNode = TRegexUtil.InvokeGetGroupBoundariesMethodNode.create();
LazyRegexResultArrayWriteElementCacheNode() {
super();
}
@Specialization
protected boolean doLazyRegexResultArray(JSDynamicObject target, LazyRegexResultArray lazyRegexResultArray, long index, Object value, WriteElementNode root,
@Cached InlinedConditionProfile inBoundsIf) {
ScriptArray newArray = lazyRegexResultArray.createWritable(root.context, target, index, value,
lazyRegexResultNode, lazyRegexResultOriginalInputNode, null, substringNode, getStartNode, getEndNode);
if (inBoundsIf.profile(this, index >= 0 && index < 0x7fff_ffff)) {
return setArrayAndWrite(newArray, target, index, value, root);
} else {
arraySetArrayType(target, SparseArray.makeSparseArray(target, newArray).setElement(target, index, value, root.isStrict));
return true;
}
}
}
abstract static class LazyRegexResultIndicesArrayWriteElementCacheNode extends RecursiveCachedArrayWriteElementCacheNode {
@Child private TRegexUtil.InvokeGetGroupBoundariesMethodNode getStartNode = TRegexUtil.InvokeGetGroupBoundariesMethodNode.create();
@Child private TRegexUtil.InvokeGetGroupBoundariesMethodNode getEndNode = TRegexUtil.InvokeGetGroupBoundariesMethodNode.create();
LazyRegexResultIndicesArrayWriteElementCacheNode() {
super();
}
@Specialization
protected boolean doLazyRegexREsultIndicesArray(JSDynamicObject target, LazyRegexResultIndicesArray lazyRegexResultIndicesArray, long index, Object value, WriteElementNode root,
@Cached InlinedConditionProfile inBoundsIf) {
ScriptArray newArray = lazyRegexResultIndicesArray.createWritable(root.context, target, index, value,
null, getStartNode, getEndNode);
if (inBoundsIf.profile(this, index >= 0 && index < 0x7fff_ffff)) {
return setArrayAndWrite(newArray, target, index, value, root);
} else {
arraySetArrayType(target, SparseArray.makeSparseArray(target, newArray).setElement(target, index, value, root.isStrict));
return true;
}
}
}
abstract static class ConstantArrayWriteElementCacheNode extends RecursiveCachedArrayWriteElementCacheNode {
ConstantArrayWriteElementCacheNode() {
super();
}
@Specialization
protected boolean doConstantArray(JSDynamicObject target, AbstractConstantArray constantArray, long index, Object value, WriteElementNode root,
@Cached InlinedBranchProfile intValueBranch,
@Cached InlinedBranchProfile doubleValueBranch,
@Cached InlinedBranchProfile jsObjectValueBranch,
@Cached InlinedBranchProfile objectValueBranch,
@Cached InlinedConditionProfile inBoundsIf,
@Cached CreateWritableProfileAccess createWritableProfile) {
if (inBoundsIf.profile(this, index >= 0 && index < 0x7fff_ffff)) {
ScriptArray newArray;
if (value instanceof Integer) {
intValueBranch.enter(this);
newArray = constantArray.createWriteableInt(target, index, (int) value, this, createWritableProfile);
} else if (value instanceof Double) {
doubleValueBranch.enter(this);
newArray = constantArray.createWriteableDouble(target, index, (double) value, this, createWritableProfile);
} else if (JSDynamicObject.isJSDynamicObject(value)) {
jsObjectValueBranch.enter(this);
newArray = constantArray.createWriteableJSObject(target, index, (JSDynamicObject) value, this, createWritableProfile);
} else {
objectValueBranch.enter(this);
newArray = constantArray.createWriteableObject(target, index, value, this, createWritableProfile);
}
return setArrayAndWrite(newArray, target, index, value, root);
} else {
arraySetArrayType(target, SparseArray.makeSparseArray(target, constantArray).setElement(target, index, value, root.isStrict));
return true;
}
}
}
abstract static class WritableArrayWriteElementCacheNode extends ArrayWriteElementCacheNode {
WritableArrayWriteElementCacheNode() {
super();
}
@Specialization
protected final boolean doWritableArray(JSDynamicObject target, AbstractWritableArray writableArray, long index, Object value, WriteElementNode root,
@Cached InlinedConditionProfile inBoundsIf) {
if (inBoundsIf.profile(this, writableArray.isInBoundsFast(target, index))) {
arraySetArrayType(target, writableArray.setElement(target, index, value, root.isStrict));
return true;
} else {
return false;
}
}
}
abstract static class IntArrayWriteElementCacheNode extends RecursiveCachedArrayWriteElementCacheNode {
IntArrayWriteElementCacheNode() {
super();
}
@Specialization
protected boolean doIntArray(JSDynamicObject target, AbstractIntArray intArray, long index, Object value, WriteElementNode root,
@Cached InlinedBranchProfile intValueBranch,
@Cached InlinedBranchProfile doubleValueBranch,
@Cached InlinedBranchProfile objectValueBranch,
@Cached InlinedConditionProfile inBoundsFastIf,
@Cached InlinedConditionProfile inBoundsIf,
@Cached InlinedConditionProfile supportedNonZeroIf,
@Cached InlinedConditionProfile supportedZeroIf,
@Cached InlinedConditionProfile supportedContiguousIf,
@Cached InlinedConditionProfile supportedHolesIf,
@Cached InlinedBranchProfile needPrototypeBranch,
@Cached SetSupportedProfileAccess setSupportedProfile) {
if (value instanceof Integer) {
intValueBranch.enter(this);
return doIntArrayWithIntValue(target, intArray, index, (int) value, root,
inBoundsFastIf,
inBoundsIf,
supportedNonZeroIf,
supportedZeroIf,
supportedContiguousIf,
supportedHolesIf,
needPrototypeBranch,
setSupportedProfile);
} else if (value instanceof Double) {
doubleValueBranch.enter(this);
double doubleValue = (double) value;
return setArrayAndWrite(intArray.toDouble(target, index, doubleValue), target, index, doubleValue, root);
} else {
objectValueBranch.enter(this);
return setArrayAndWrite(intArray.toObject(target, index, value), target, index, value, root);
}
}
private boolean doIntArrayWithIntValue(JSDynamicObject target, AbstractIntArray intArray, long index, int intValue, WriteElementNode root,
@Cached InlinedConditionProfile inBoundsFastIf,
@Cached InlinedConditionProfile inBoundsIf,
@Cached InlinedConditionProfile supportedNonZeroIf,
@Cached InlinedConditionProfile supportedZeroIf,
@Cached InlinedConditionProfile supportedContiguousIf,
@Cached InlinedConditionProfile supportedHolesIf,
@Cached InlinedBranchProfile needPrototypeBranch,
@Cached SetSupportedProfileAccess setSupportedProfile) {
assert !(intArray instanceof HolesIntArray);
if (nonHolesArrayNeedsSlowSet(target, intArray, index, root)) {
needPrototypeBranch.enter(this);
return false;
}
int iIndex = (int) index;
if (inBoundsFastIf.profile(this, intArray.isInBoundsFast(target, index) && !mightTransferToNonContiguous(intArray, target, index))) {
intArray.setInBoundsFast(target, iIndex, intValue);
return true;
} else if (inBoundsIf.profile(this, intArray.isInBounds(target, iIndex) && !mightTransferToNonContiguous(intArray, target, index))) {
intArray.setInBounds(target, iIndex, intValue, this, setSupportedProfile);
return true;
} else if (supportedNonZeroIf.profile(this, intArray.isSupported(target, index) && !mightTransferToNonContiguous(intArray, target, index))) {
intArray.setSupported(target, iIndex, intValue, this, setSupportedProfile);
return true;
} else {
ScriptArray toArrayType;
if (supportedZeroIf.profile(this, mightTransferToNonContiguous(intArray, target, index) && intArray.isSupported(target, index))) {
toArrayType = intArray.toNonContiguous(target, iIndex, intValue, this, setSupportedProfile);
} else if (supportedContiguousIf.profile(this, !(intArray instanceof AbstractContiguousIntArray) && intArray.isSupportedContiguous(target, index))) {
toArrayType = intArray.toContiguous(target, index, intValue);
} else if (supportedHolesIf.profile(this, intArray.isSupportedHoles(target, index))) {
toArrayType = intArray.toHoles(target, index, intValue);
} else {
assert intArray.isSparse(target, index);
toArrayType = intArray.toSparse(target, index, intValue);
}
return setArrayAndWrite(toArrayType, target, index, intValue, root);
}
}
private static boolean mightTransferToNonContiguous(AbstractIntArray intArray, JSDynamicObject target, long index) {
return intArray instanceof ContiguousIntArray && index == 0 && intArray.firstElementIndex(target) == 1;
}
}
abstract static class DoubleArrayWriteElementCacheNode extends RecursiveCachedArrayWriteElementCacheNode {
DoubleArrayWriteElementCacheNode() {
super();
}
@Specialization
protected boolean doDoubleArray(JSDynamicObject target, AbstractDoubleArray doubleArray, long index, Object value, WriteElementNode root,
@Cached InlinedBranchProfile intValueBranch,
@Cached InlinedBranchProfile doubleValueBranch,
@Cached InlinedBranchProfile objectValueBranch,
@Cached InlinedConditionProfile inBoundsFastIf,
@Cached InlinedConditionProfile inBoundsIf,
@Cached InlinedConditionProfile supportedIf,
@Cached InlinedConditionProfile supportedContiguousIf,
@Cached InlinedConditionProfile supportedHolesIf,
@Cached InlinedBranchProfile needPrototypeBranch,
@Cached SetSupportedProfileAccess setSupportedProfile) {
double doubleValue;
if (value instanceof Double) {
doubleValueBranch.enter(this);
doubleValue = (double) value;
} else if (value instanceof Integer) {
intValueBranch.enter(this);
doubleValue = (int) value;
} else {
objectValueBranch.enter(this);
return setArrayAndWrite(doubleArray.toObject(target, index, value), target, index, value, root);
}
return executeWithDoubleValueInner(target, doubleArray, index, doubleValue, root,
inBoundsFastIf,
inBoundsIf,
supportedIf,
supportedContiguousIf,
supportedHolesIf,
needPrototypeBranch,
setSupportedProfile);
}
private boolean executeWithDoubleValueInner(JSDynamicObject target, AbstractDoubleArray doubleArray, long index, double doubleValue, WriteElementNode root,
@Cached InlinedConditionProfile inBoundsFastIf,
@Cached InlinedConditionProfile inBoundsIf,
@Cached InlinedConditionProfile supportedIf,
@Cached InlinedConditionProfile supportedContiguousIf,
@Cached InlinedConditionProfile supportedHolesIf,
@Cached InlinedBranchProfile needPrototypeBranch,
@Cached SetSupportedProfileAccess setSupportedProfile) {
assert !(doubleArray instanceof HolesDoubleArray);
if (nonHolesArrayNeedsSlowSet(target, doubleArray, index, root)) {
needPrototypeBranch.enter(this);
return false;
}
int iIndex = (int) index;
if (inBoundsFastIf.profile(this, doubleArray.isInBoundsFast(target, index))) {
doubleArray.setInBoundsFast(target, iIndex, doubleValue);
return true;
} else if (inBoundsIf.profile(this, doubleArray.isInBounds(target, iIndex))) {
doubleArray.setInBounds(target, iIndex, doubleValue, this, setSupportedProfile);
return true;
} else if (supportedIf.profile(this, doubleArray.isSupported(target, index))) {
doubleArray.setSupported(target, iIndex, doubleValue, this, setSupportedProfile);
return true;
} else {
ScriptArray toArrayType;
if (supportedContiguousIf.profile(this, !(doubleArray instanceof AbstractContiguousDoubleArray) && doubleArray.isSupportedContiguous(target, index))) {
toArrayType = doubleArray.toContiguous(target, index, doubleValue);
} else if (supportedHolesIf.profile(this, doubleArray.isSupportedHoles(target, index))) {
toArrayType = doubleArray.toHoles(target, index, doubleValue);
} else {
assert doubleArray.isSparse(target, index);
toArrayType = doubleArray.toSparse(target, index, doubleValue);
}
return setArrayAndWrite(toArrayType, target, index, doubleValue, root);
}
}
}
abstract static class ObjectArrayWriteElementCacheNode extends RecursiveCachedArrayWriteElementCacheNode {
ObjectArrayWriteElementCacheNode() {
super();
}
@Specialization
protected boolean doObjectArray(JSDynamicObject target, AbstractObjectArray objectArray, long index, Object value, WriteElementNode root,
@Cached InlinedConditionProfile inBoundsFastIf,
@Cached InlinedConditionProfile inBoundsIf,
@Cached InlinedConditionProfile supportedIf,
@Cached InlinedConditionProfile supportedContiguousIf,
@Cached InlinedConditionProfile supportedHolesIf,
@Cached InlinedBranchProfile needPrototypeBranch,
@Cached SetSupportedProfileAccess setSupportedProfile) {
assert !(objectArray instanceof HolesObjectArray);
if (nonHolesArrayNeedsSlowSet(target, objectArray, index, root)) {
needPrototypeBranch.enter(this);
return false;
}
int iIndex = (int) index;
if (inBoundsFastIf.profile(this, objectArray.isInBoundsFast(target, index))) {
objectArray.setInBoundsFast(target, iIndex, value);
return true;
} else if (inBoundsIf.profile(this, objectArray.isInBounds(target, iIndex))) {
objectArray.setInBounds(target, iIndex, value, this, setSupportedProfile);
return true;
} else if (supportedIf.profile(this, objectArray.isSupported(target, index))) {
objectArray.setSupported(target, iIndex, value, this, setSupportedProfile);
return true;
} else {
ScriptArray toArrayType;
if (supportedContiguousIf.profile(this, !(objectArray instanceof AbstractContiguousObjectArray) && objectArray.isSupportedContiguous(target, index))) {
toArrayType = objectArray.toContiguous(target, index, value);
} else if (supportedHolesIf.profile(this, objectArray.isSupportedHoles(target, index))) {
toArrayType = objectArray.toHoles(target, index, value);
} else {
assert objectArray.isSparse(target, index);
toArrayType = objectArray.toSparse(target, index, value);
}
return setArrayAndWrite(toArrayType, target, index, value, root);
}
}
}
abstract static class JSObjectArrayWriteElementCacheNode extends RecursiveCachedArrayWriteElementCacheNode {
JSObjectArrayWriteElementCacheNode() {
super();
}
@Specialization
protected boolean doJSObjectArray(JSDynamicObject target, AbstractJSObjectArray jsobjectArray, long index, Object value, WriteElementNode root,
@Cached InlinedBranchProfile jsObjectValueBranch,
@Cached InlinedBranchProfile objectValueBranch,
@Cached InlinedConditionProfile inBoundsFastIf,
@Cached InlinedConditionProfile inBoundsIf,
@Cached InlinedConditionProfile supportedIf,
@Cached InlinedConditionProfile supportedContiguousIf,
@Cached InlinedConditionProfile supportedHolesIf,
@Cached InlinedBranchProfile needPrototypeBranch,
@Cached SetSupportedProfileAccess setSupportedProfile) {
if (JSDynamicObject.isJSDynamicObject(value)) {
jsObjectValueBranch.enter(this);
JSDynamicObject jsobjectValue = (JSDynamicObject) value;
return executeWithJSObjectValueInner(target, jsobjectArray, index, jsobjectValue, root,
inBoundsFastIf,
inBoundsIf,
supportedIf,
supportedContiguousIf,
supportedHolesIf,
needPrototypeBranch,
setSupportedProfile);
} else {
objectValueBranch.enter(this);
return setArrayAndWrite(jsobjectArray.toObject(target, index, value), target, index, value, root);
}
}
private boolean executeWithJSObjectValueInner(JSDynamicObject target, AbstractJSObjectArray jsobjectArray, long index, JSDynamicObject jsobjectValue, WriteElementNode root,
InlinedConditionProfile inBoundsFastIf,
InlinedConditionProfile inBoundsIf,
InlinedConditionProfile supportedIf,
InlinedConditionProfile supportedContiguousIf,
InlinedConditionProfile supportedHolesIf,
InlinedBranchProfile needPrototypeBranch,
SetSupportedProfileAccess setSupportedProfile) {
assert !(jsobjectArray instanceof HolesJSObjectArray);
int iIndex = (int) index;
if (nonHolesArrayNeedsSlowSet(target, jsobjectArray, index, root)) {
needPrototypeBranch.enter(this);
return false;
}
if (inBoundsFastIf.profile(this, jsobjectArray.isInBoundsFast(target, index))) {
jsobjectArray.setInBoundsFast(target, iIndex, jsobjectValue);
return true;
} else if (inBoundsIf.profile(this, jsobjectArray.isInBounds(target, iIndex))) {
jsobjectArray.setInBounds(target, iIndex, jsobjectValue, this, setSupportedProfile);
return true;
} else if (supportedIf.profile(this, jsobjectArray.isSupported(target, index))) {
jsobjectArray.setSupported(target, iIndex, jsobjectValue, this, setSupportedProfile);
return true;
} else {
ScriptArray toArrayType;
if (supportedContiguousIf.profile(this, !(jsobjectArray instanceof AbstractContiguousJSObjectArray) && jsobjectArray.isSupportedContiguous(target, index))) {
toArrayType = jsobjectArray.toContiguous(target, index, jsobjectValue);
} else if (supportedHolesIf.profile(this, jsobjectArray.isSupportedHoles(target, index))) {
toArrayType = jsobjectArray.toHoles(target, index, jsobjectValue);
} else {
assert jsobjectArray.isSparse(target, index);
toArrayType = jsobjectArray.toSparse(target, index, jsobjectValue);
}
return setArrayAndWrite(toArrayType, target, index, jsobjectValue, root);
}
}
}
abstract static class HolesIntArrayWriteElementCacheNode extends RecursiveCachedArrayWriteElementCacheNode {
HolesIntArrayWriteElementCacheNode() {
super();
}
@Specialization
protected boolean doHolesIntArray(JSDynamicObject target, HolesIntArray holesIntArray, long index, Object value, WriteElementNode root,
@Cached InlinedBranchProfile intValueBranch,
@Cached InlinedBranchProfile doubleValueBranch,
@Cached InlinedBranchProfile objectValueBranch,
@Cached InlinedConditionProfile inBoundsFastIf,
@Cached InlinedConditionProfile inBoundsIf,
@Cached InlinedConditionProfile containsHolesIf,
@Cached InlinedConditionProfile inBoundsFastHoleIf,
@Cached InlinedConditionProfile supportedContainsHolesIf,
@Cached InlinedConditionProfile supportedNotContainsHolesIf,
@Cached InlinedConditionProfile hasExplicitHolesIf,
@Cached InlinedBranchProfile needPrototypeBranch,
@Cached SetSupportedProfileAccess setSupportedProfile) {
if (value instanceof Integer) {
intValueBranch.enter(this);
int intValue = (int) value;
return executeWithIntValueInner(target, holesIntArray, index, intValue, root,
inBoundsFastIf,
inBoundsIf,
containsHolesIf,
inBoundsFastHoleIf,
supportedContainsHolesIf,
supportedNotContainsHolesIf,
hasExplicitHolesIf,
needPrototypeBranch,
setSupportedProfile);
} else if (value instanceof Double) {
doubleValueBranch.enter(this);
double doubleValue = (double) value;
return setArrayAndWrite(holesIntArray.toDouble(target, index, doubleValue), target, index, doubleValue, root);
} else {
objectValueBranch.enter(this);
return setArrayAndWrite(holesIntArray.toObject(target, index, value), target, index, value, root);
}
}
private boolean executeWithIntValueInner(JSDynamicObject target, HolesIntArray holesIntArray, long index, int intValue, WriteElementNode root,
InlinedConditionProfile inBoundsFastIf,
InlinedConditionProfile inBoundsIf,
InlinedConditionProfile containsHolesIf,
InlinedConditionProfile inBoundsFastHoleIf,
InlinedConditionProfile supportedContainsHolesIf,
InlinedConditionProfile supportedNotContainsHolesIf,
InlinedConditionProfile hasExplicitHolesIf,
InlinedBranchProfile needPrototypeBranch,
SetSupportedProfileAccess setSupportedProfile) {
if (holesArrayNeedsSlowSet(target, holesIntArray, index, root)) {
needPrototypeBranch.enter(this);
return false;
}
int iIndex = (int) index;
boolean containsHoles = containsHolesIf.profile(this, containsHoles(target, holesIntArray, index, hasExplicitHolesIf));
if (containsHoles && inBoundsFastIf.profile(this, holesIntArray.isInBoundsFast(target, index) && !HolesIntArray.isHoleValue(intValue))) {
if (inBoundsFastHoleIf.profile(this, holesIntArray.isHoleFast(target, iIndex))) {
holesIntArray.setInBoundsFastHole(target, iIndex, intValue);
} else {
holesIntArray.setInBoundsFastNonHole(target, iIndex, intValue);
}
return true;
} else if (containsHoles && inBoundsIf.profile(this, holesIntArray.isInBounds(target, iIndex) && !HolesIntArray.isHoleValue(intValue))) {
holesIntArray.setInBounds(target, iIndex, intValue, this, setSupportedProfile);
return true;
} else if (containsHoles && supportedContainsHolesIf.profile(this, holesIntArray.isSupported(target, index) && !HolesIntArray.isHoleValue(intValue))) {
holesIntArray.setSupported(target, iIndex, intValue, this, setSupportedProfile);
return true;
} else {
ScriptArray toArrayType;
if (!containsHoles && supportedNotContainsHolesIf.profile(this, holesIntArray.isSupported(target, index))) {
toArrayType = holesIntArray.toNonHoles(target, index, intValue);
} else {
assert holesIntArray.isSparse(target, index) || HolesIntArray.isHoleValue(intValue);
toArrayType = holesIntArray.toSparse(target, index, intValue);
}
return setArrayAndWrite(toArrayType, target, index, intValue, root);
}
}
private boolean containsHoles(JSDynamicObject target, HolesIntArray holesIntArray, long index, InlinedConditionProfile hasExplicitHolesIf) {
return hasExplicitHolesIf.profile(this, JSArray.arrayGetHoleCount(target) > 0) || !holesIntArray.isInBoundsFast(target, index);
}
}
abstract static class HolesDoubleArrayWriteElementCacheNode extends RecursiveCachedArrayWriteElementCacheNode {
HolesDoubleArrayWriteElementCacheNode() {
super();
}
@Specialization
protected boolean doHolesDoubleArray(JSDynamicObject target, HolesDoubleArray holesDoubleArray, long index, Object value, WriteElementNode root,
@Cached InlinedBranchProfile intValueBranch,
@Cached InlinedBranchProfile doubleValueBranch,
@Cached InlinedBranchProfile objectValueBranch,
@Cached InlinedConditionProfile inBoundsFastIf,
@Cached InlinedConditionProfile inBoundsIf,
@Cached InlinedConditionProfile containsHolesIf,
@Cached InlinedConditionProfile inBoundsFastHoleIf,
@Cached InlinedConditionProfile supportedContainsHolesIf,
@Cached InlinedConditionProfile supportedNotContainsHolesIf,
@Cached InlinedConditionProfile hasExplicitHolesIf,
@Cached InlinedBranchProfile needPrototypeBranch,
@Cached SetSupportedProfileAccess setSupportedProfile) {
double doubleValue;
if (value instanceof Double) {
doubleValueBranch.enter(this);
doubleValue = (double) value;
} else if (value instanceof Integer) {
intValueBranch.enter(this);
doubleValue = (int) value;
} else {
objectValueBranch.enter(this);
return setArrayAndWrite(holesDoubleArray.toObject(target, index, value), target, index, value, root);
}
return executeWithDoubleValueInner(target, holesDoubleArray, index, doubleValue, root,
inBoundsFastIf,
inBoundsIf,
containsHolesIf,
inBoundsFastHoleIf,
supportedContainsHolesIf,
supportedNotContainsHolesIf,
hasExplicitHolesIf,
needPrototypeBranch,
setSupportedProfile);
}
private boolean executeWithDoubleValueInner(JSDynamicObject target, HolesDoubleArray holesDoubleArray, long index, double doubleValue, WriteElementNode root,
InlinedConditionProfile inBoundsFastIf,
InlinedConditionProfile inBoundsIf,
InlinedConditionProfile containsHolesIf,
InlinedConditionProfile inBoundsFastHoleIf,
InlinedConditionProfile supportedContainsHolesIf,
InlinedConditionProfile supportedNotContainsHolesIf,
InlinedConditionProfile hasExplicitHolesIf,
InlinedBranchProfile needPrototypeBranch,
SetSupportedProfileAccess setSupportedProfile) {
if (holesArrayNeedsSlowSet(target, holesDoubleArray, index, root)) {
needPrototypeBranch.enter(this);
return false;
}
int iIndex = (int) index;
boolean containsHoles = containsHolesIf.profile(this, containsHoles(target, holesDoubleArray, index, hasExplicitHolesIf));
if (containsHoles && inBoundsFastIf.profile(this, holesDoubleArray.isInBoundsFast(target, index) && !HolesDoubleArray.isHoleValue(doubleValue))) {
if (inBoundsFastHoleIf.profile(this, holesDoubleArray.isHoleFast(target, iIndex))) {
holesDoubleArray.setInBoundsFastHole(target, iIndex, doubleValue);
} else {
holesDoubleArray.setInBoundsFastNonHole(target, iIndex, doubleValue);
}
return true;
} else if (containsHoles && inBoundsIf.profile(this, holesDoubleArray.isInBounds(target, iIndex) && !HolesDoubleArray.isHoleValue(doubleValue))) {
holesDoubleArray.setInBounds(target, iIndex, doubleValue, this, setSupportedProfile);
return true;
} else if (containsHoles && supportedContainsHolesIf.profile(this, holesDoubleArray.isSupported(target, index) && !HolesDoubleArray.isHoleValue(doubleValue))) {
holesDoubleArray.setSupported(target, iIndex, doubleValue, this, setSupportedProfile);
return true;
} else {
ScriptArray toArrayType;
if (!containsHoles && supportedNotContainsHolesIf.profile(this, holesDoubleArray.isSupported(target, index))) {
toArrayType = holesDoubleArray.toNonHoles(target, index, doubleValue);
} else {
assert holesDoubleArray.isSparse(target, index) || HolesDoubleArray.isHoleValue(doubleValue);
toArrayType = holesDoubleArray.toSparse(target, index, doubleValue);
}
return setArrayAndWrite(toArrayType, target, index, doubleValue, root);
}
}
private boolean containsHoles(JSDynamicObject target, HolesDoubleArray holesDoubleArray, long index, InlinedConditionProfile hasExplicitHolesIf) {
return hasExplicitHolesIf.profile(this, JSArray.arrayGetHoleCount(target) > 0) || !holesDoubleArray.isInBoundsFast(target, index);
}
}
abstract static class HolesJSObjectArrayWriteElementCacheNode extends RecursiveCachedArrayWriteElementCacheNode {
HolesJSObjectArrayWriteElementCacheNode() {
super();
}
@Specialization
protected boolean doJSObjectArray(JSDynamicObject target, HolesJSObjectArray holesArray, long index, Object value, WriteElementNode root,
@Cached InlinedBranchProfile jsObjectValueBranch,
@Cached InlinedBranchProfile objectValueBranch,
@Cached InlinedConditionProfile inBoundsFastIf,
@Cached InlinedConditionProfile inBoundsIf,
@Cached InlinedConditionProfile containsHolesIf,
@Cached InlinedConditionProfile inBoundsFastHoleIf,
@Cached InlinedConditionProfile supportedContainsHolesIf,
@Cached InlinedConditionProfile supportedNotContainsHolesIf,
@Cached InlinedConditionProfile hasExplicitHolesIf,
@Cached InlinedBranchProfile needPrototypeBranch,
@Cached SetSupportedProfileAccess setSupportedProfile) {
if (JSDynamicObject.isJSDynamicObject(value)) {
jsObjectValueBranch.enter(this);
return executeWithJSObjectValueInner(target, holesArray, index, (JSDynamicObject) value, root,
inBoundsFastIf,
inBoundsIf,
containsHolesIf,
inBoundsFastHoleIf,
supportedContainsHolesIf,
supportedNotContainsHolesIf,
hasExplicitHolesIf,
needPrototypeBranch,
setSupportedProfile);
} else {
objectValueBranch.enter(this);
return setArrayAndWrite(holesArray.toObject(target, index, value), target, index, value, root);
}
}
private boolean executeWithJSObjectValueInner(JSDynamicObject target, HolesJSObjectArray jsobjectArray, long index, JSDynamicObject value, WriteElementNode root,
InlinedConditionProfile inBoundsFastIf,
InlinedConditionProfile inBoundsIf,
InlinedConditionProfile containsHolesIf,
InlinedConditionProfile inBoundsFastHoleIf,
InlinedConditionProfile supportedContainsHolesIf,
InlinedConditionProfile supportedNotContainsHolesIf,
InlinedConditionProfile hasExplicitHolesIf,
InlinedBranchProfile needPrototypeBranch,
SetSupportedProfileAccess setSupportedProfile) {
if (holesArrayNeedsSlowSet(target, jsobjectArray, index, root)) {
needPrototypeBranch.enter(this);
return false;
}
boolean containsHoles = containsHolesIf.profile(this, containsHoles(target, jsobjectArray, index, hasExplicitHolesIf));
if (containsHoles && inBoundsFastIf.profile(this, jsobjectArray.isInBoundsFast(target, index))) {
assert !HolesJSObjectArray.isHoleValue(value);
if (inBoundsFastHoleIf.profile(this, jsobjectArray.isHoleFast(target, (int) index))) {
jsobjectArray.setInBoundsFastHole(target, (int) index, value);
} else {
jsobjectArray.setInBoundsFastNonHole(target, (int) index, value);
}
return true;
} else if (containsHoles && inBoundsIf.profile(this, jsobjectArray.isInBounds(target, (int) index))) {
assert !HolesJSObjectArray.isHoleValue(value);
jsobjectArray.setInBounds(target, (int) index, value, this, setSupportedProfile);
return true;
} else if (containsHoles && supportedContainsHolesIf.profile(this, jsobjectArray.isSupported(target, index))) {
assert !HolesJSObjectArray.isHoleValue(value);
jsobjectArray.setSupported(target, (int) index, value, this, setSupportedProfile);
return true;
} else {
ScriptArray toArrayType;
if (!containsHoles && supportedNotContainsHolesIf.profile(this, jsobjectArray.isSupported(target, index))) {
toArrayType = jsobjectArray.toNonHoles(target, index, value);
} else {
assert jsobjectArray.isSparse(target, index);
toArrayType = jsobjectArray.toSparse(target, index, value);
}
return setArrayAndWrite(toArrayType, target, index, value, root);
}
}
private boolean containsHoles(JSDynamicObject target, HolesJSObjectArray holesJSObjectArray, long index, InlinedConditionProfile hasExplicitHolesIf) {
return hasExplicitHolesIf.profile(this, JSArray.arrayGetHoleCount(target) > 0) || !holesJSObjectArray.isInBoundsFast(target, index);
}
}
abstract static class HolesObjectArrayWriteElementCacheNode extends RecursiveCachedArrayWriteElementCacheNode {
HolesObjectArrayWriteElementCacheNode() {
super();
}
@Specialization
protected boolean doHolesObjectArray(JSDynamicObject target, HolesObjectArray objectArray, long index, Object value, WriteElementNode root,
@Cached InlinedConditionProfile inBoundsFastIf,
@Cached InlinedConditionProfile inBoundsIf,
@Cached InlinedConditionProfile inBoundsFastHoleIf,
@Cached InlinedConditionProfile supportedIf,
@Cached InlinedBranchProfile needPrototypeBranch,
@Cached SetSupportedProfileAccess setSupportedProfile) {
if (holesArrayNeedsSlowSet(target, objectArray, index, root)) {
needPrototypeBranch.enter(this);
return false;
}
if (inBoundsFastIf.profile(this, objectArray.isInBoundsFast(target, index))) {
assert !HolesObjectArray.isHoleValue(value);
if (inBoundsFastHoleIf.profile(this, objectArray.isHoleFast(target, (int) index))) {
objectArray.setInBoundsFastHole(target, (int) index, value);
} else {
objectArray.setInBoundsFastNonHole(target, (int) index, value);
}
return true;
} else if (inBoundsIf.profile(this, objectArray.isInBounds(target, (int) index))) {
assert !HolesObjectArray.isHoleValue(value);
objectArray.setInBounds(target, (int) index, value, this, setSupportedProfile);
return true;
} else if (supportedIf.profile(this, objectArray.isSupported(target, index))) {
assert !HolesObjectArray.isHoleValue(value);
objectArray.setSupported(target, (int) index, value, this, setSupportedProfile);
return true;
} else {
assert objectArray.isSparse(target, index);
return setArrayAndWrite(objectArray.toSparse(target, index, value), target, index, value, root);
}
}
}
private abstract static class AbstractTypedArrayWriteElementCacheNode extends ArrayWriteElementCacheNode {
@Child protected InteropLibrary interop;
AbstractTypedArrayWriteElementCacheNode(TypedArray arrayType) {
super();
this.interop = arrayType.isInterop() ? InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit) : InteropLibrary.getUncached();
}
}
abstract static class TypedIntArrayWriteElementCacheNode extends AbstractTypedArrayWriteElementCacheNode {
TypedIntArrayWriteElementCacheNode(TypedArray arrayType) {
super(arrayType);
}
@Specialization
protected final boolean doTypedIntArrayIntValue(JSDynamicObject target, TypedIntArray typedArray, long index, int iValue, WriteElementNode root,
@Cached @Shared InlinedConditionProfile inBoundsIf) {
if (inBoundsIf.profile(this, !JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, root.context) && typedArray.hasElement(target, index))) {
typedArray.setInt(target, (int) index, iValue, interop);
} else if (root.writeOwn && root.isStrict) {
/*
* CreateDataPropertyOrThrow semantics; throw if [[DefineOwnProperty]] returns
* false, i.e. if IsValidIntegerIndex(O, index) is false.
*/
throw Errors.createTypeErrorCannotRedefineProperty(index);
}
/* Else: do nothing, see TypedArraySetElement(O, index, value). */
return true;
}
protected static boolean isSpecial(TypedIntArray typedArray) {
return typedArray instanceof AbstractUint8ClampedArray;
}
@Specialization(guards = "!isSpecial(typedArray)", replaces = "doTypedIntArrayIntValue")
protected final boolean doTypedIntArray(JSDynamicObject target, TypedIntArray typedArray, long index, Object value, WriteElementNode root,
@Cached JSToInt32Node toIntNode,
@Cached @Shared InlinedConditionProfile inBoundsIf) {
int iValue = toIntNode.executeInt(value); // could throw
return doTypedIntArrayIntValue(target, typedArray, index, iValue, root, inBoundsIf);
}
@Specialization(replaces = "doTypedIntArrayIntValue")
protected final boolean doTypedIntArray(JSDynamicObject target, AbstractUint8ClampedArray typedArray, long index, Object value, WriteElementNode root,
@Cached JSToDoubleNode toDoubleNode,
@Cached @Shared InlinedConditionProfile inBoundsIf) {
double doubleValue = toDoubleNode.executeDouble(value);
int iValue = Uint8ClampedArray.toInt(doubleValue);
return doTypedIntArrayIntValue(target, typedArray, index, iValue, root, inBoundsIf);
}
}
abstract static class TypedBigIntArrayWriteElementCacheNode extends AbstractTypedArrayWriteElementCacheNode {
@Child private JSToBigIntNode toBigIntNode;
TypedBigIntArrayWriteElementCacheNode(TypedArray arrayType) {
super(arrayType);
this.toBigIntNode = JSToBigIntNode.create();
}
@Specialization
protected final boolean doBigIntArray(JSDynamicObject target, TypedBigIntArray typedArray, long index, Object value, WriteElementNode root,
@Cached InlinedConditionProfile inBoundsIf) {
BigInt biValue = toBigIntNode.executeBigInteger(value); // could throw
if (inBoundsIf.profile(this, !JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, root.context) && typedArray.hasElement(target, index))) {
typedArray.setBigInt(target, (int) index, biValue, interop);
} else if (root.writeOwn && root.isStrict) {
/*
* CreateDataPropertyOrThrow semantics; throw if [[DefineOwnProperty]] returns
* false, i.e. if IsValidIntegerIndex(O, index) is false.
*/
throw Errors.createTypeErrorCannotRedefineProperty(index);
}
/* Else: do nothing, see TypedArraySetElement(O, index, value). */
return true;
}
}
abstract static class TypedFloatArrayWriteElementCacheNode extends AbstractTypedArrayWriteElementCacheNode {
TypedFloatArrayWriteElementCacheNode(TypedArray arrayType) {
super(arrayType);
}
@Specialization
protected boolean doTypedFloatArray(JSDynamicObject target, TypedFloatArray typedArray, long index, Object value, WriteElementNode root,
@Cached InlinedConditionProfile inBoundsIf,
@Cached JSToDoubleNode toDouble) {
double dValue = toDouble.executeDouble(value); // could throw
if (inBoundsIf.profile(this, !JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, root.context) && typedArray.hasElement(target, index))) {
typedArray.setDouble(target, (int) index, dValue, interop);
} else if (root.writeOwn && root.isStrict) {
/*
* CreateDataPropertyOrThrow semantics; throw if [[DefineOwnProperty]] returns
* false, i.e. if IsValidIntegerIndex(O, index) is false.
*/
throw Errors.createTypeErrorCannotRedefineProperty(index);
}
/* Else: do nothing, see TypedArraySetElement(O, index, value). */
return true;
}
}
private abstract static class ToPropertyKeyCachedWriteElementTypeCacheNode extends GuardedWriteElementTypeCacheNode {
protected final JSClassProfile classProfile = JSClassProfile.create();
ToPropertyKeyCachedWriteElementTypeCacheNode() {
super();
}
}
abstract static class StringWriteElementTypeCacheNode extends ToPropertyKeyCachedWriteElementTypeCacheNode {
StringWriteElementTypeCacheNode() {
super();
}
@Specialization
protected void doStringIntegerIndex(Object target, long index, Object value, Object receiver, WriteElementNode root,
@Cached @Shared InlinedConditionProfile isImmutable) {
TruffleString string = (TruffleString) target;
if (isImmutable.profile(this, index >= 0 && index < Strings.length(string))) {
// cannot set characters of immutable strings
if (root.isStrict) {
throw Errors.createTypeErrorNotWritableProperty(index, string, this);
}
return;
} else {
JSObject.setWithReceiver(JSString.create(root.context, getRealm(), string), index, value, receiver, root.isStrict, classProfile, root);
}
}
@Specialization(replaces = {"doStringIntegerIndex"})
protected void doString(Object target, Object index, Object value, Object receiver, WriteElementNode root,
@Cached @Shared InlinedConditionProfile isImmutable,
@Cached ToArrayIndexNoToPropertyKeyNode toArrayIndexNode,
@Cached JSToPropertyKeyNode indexToPropertyKeyNode) {
TruffleString string = (TruffleString) target;
long longIndex = toArrayIndexNode.executeLong(this, index);
if (isImmutable.profile(this, longIndex >= 0 && longIndex < Strings.length(string))) {
// cannot set characters of immutable strings
if (root.isStrict) {
throw Errors.createTypeErrorNotWritableProperty(longIndex, string, this);
}
return;
}
Object propertyKey = indexToPropertyKeyNode.execute(index);
JSObject.setWithReceiver(JSString.create(root.context, getRealm(), string), propertyKey, value, receiver, root.isStrict, classProfile, root);
}
@Override
public boolean guard(Object target) {
return target instanceof TruffleString;
}
}
abstract static class NumberWriteElementTypeCacheNode extends ToPropertyKeyCachedWriteElementTypeCacheNode {
private final Class> numberClass;
NumberWriteElementTypeCacheNode(Class> numberClass) {
super();
this.numberClass = numberClass;
}
@Specialization
protected void doNumber(Object target, long index, Object value, Object receiver, WriteElementNode root) {
Number number = (Number) CompilerDirectives.castExact(target, numberClass);
JSObject.setWithReceiver(JSNumber.create(root.context, getRealm(), number), index, value, receiver, root.isStrict, classProfile, root);
}
@Specialization
protected void doNumber(Object target, Object index, Object value, Object receiver, WriteElementNode root,
@Cached JSToPropertyKeyNode indexToPropertyKeyNode) {
Number number = (Number) CompilerDirectives.castExact(target, numberClass);
JSObject.setWithReceiver(JSNumber.create(root.context, getRealm(), number), indexToPropertyKeyNode.execute(index), value, receiver, root.isStrict, classProfile, root);
}
@Override
public boolean guard(Object target) {
return CompilerDirectives.isExact(target, numberClass);
}
}
abstract static class BooleanWriteElementTypeCacheNode extends ToPropertyKeyCachedWriteElementTypeCacheNode {
BooleanWriteElementTypeCacheNode() {
super();
}
@Specialization
protected void doBoolean(Object target, long index, Object value, Object receiver, WriteElementNode root) {
Boolean bool = (Boolean) target;
JSObject.setWithReceiver(JSBoolean.create(root.context, getRealm(), bool), index, value, receiver, root.isStrict, classProfile, root);
}
@Specialization
protected void doBoolean(Object target, Object index, Object value, Object receiver, WriteElementNode root,
@Cached JSToPropertyKeyNode indexToPropertyKeyNode) {
Boolean bool = (Boolean) target;
JSObject.setWithReceiver(JSBoolean.create(root.context, getRealm(), bool), indexToPropertyKeyNode.execute(index), value, receiver, root.isStrict, classProfile, root);
}
@Override
public boolean guard(Object target) {
return target instanceof Boolean;
}
}
abstract static class SymbolWriteElementTypeCacheNode extends ToPropertyKeyCachedWriteElementTypeCacheNode {
SymbolWriteElementTypeCacheNode() {
super();
}
@Specialization
protected void doSymbol(Object target, long index, Object value, Object receiver, WriteElementNode root) {
if (root.isStrict) {
throw Errors.createTypeError("cannot set element on Symbol in strict mode", this);
}
Symbol symbol = (Symbol) target;
JSObject.setWithReceiver(JSSymbol.create(root.context, getRealm(), symbol), index, value, receiver, root.isStrict, classProfile, root);
}
@Specialization
protected void doSymbol(Object target, Object index, Object value, Object receiver, WriteElementNode root,
@Cached JSToPropertyKeyNode indexToPropertyKeyNode) {
if (root.isStrict) {
throw Errors.createTypeError("cannot set element on Symbol in strict mode", this);
}
Symbol symbol = (Symbol) target;
JSObject.setWithReceiver(JSSymbol.create(root.context, getRealm(), symbol), indexToPropertyKeyNode.execute(index), value, receiver, root.isStrict, classProfile, root);
}
@Override
public boolean guard(Object target) {
return target instanceof Symbol;
}
}
abstract static class BigIntWriteElementTypeCacheNode extends ToPropertyKeyCachedWriteElementTypeCacheNode {
BigIntWriteElementTypeCacheNode() {
super();
}
@Specialization
protected void doBigIntIntegerIndex(Object target, long index, Object value, Object receiver, WriteElementNode root) {
BigInt bigInt = (BigInt) target;
JSContext context = root.context;
JSObject.setWithReceiver(JSBigInt.create(context, getRealm(), bigInt), index, value, receiver, root.isStrict, classProfile, root);
}
@Specialization
protected void doBigInt(Object target, Object index, Object value, Object receiver, WriteElementNode root,
@Cached JSToPropertyKeyNode indexToPropertyKeyNode) {
BigInt bigInt = (BigInt) target;
JSContext context = root.context;
JSObject.setWithReceiver(JSBigInt.create(context, getRealm(), bigInt), indexToPropertyKeyNode.execute(index), value, receiver, root.isStrict, classProfile, root);
}
@Override
public boolean guard(Object target) {
return target instanceof BigInt;
}
}
abstract static class ForeignObjectWriteElementTypeCacheNode extends GuardedWriteElementTypeCacheNode {
@Child private InteropLibrary interop;
@Child private InteropLibrary setterInterop;
@Child private JSToPropertyKeyNode toPropertyKeyNode;
@Child private ExportValueNode exportValue;
@Child private ToArrayIndexNode toArrayIndexNode;
ForeignObjectWriteElementTypeCacheNode() {
super();
this.exportValue = ExportValueNode.create();
this.interop = InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit);
}
@Specialization
protected void doForeignObject(Object target, Object index, Object value, @SuppressWarnings("unused") Object receiver, WriteElementNode root,
@Cached InlinedExactClassProfile classProfile,
@Cached InlinedBranchProfile errorBranch) {
Object truffleObject = classProfile.profile(this, target);
if (interop.isNull(truffleObject)) {
throw Errors.createTypeErrorCannotSetProperty(index, truffleObject, this);
}
Object propertyKey;
Object exportedValue = exportValue.execute(value);
boolean hasArrayElements = interop.hasArrayElements(truffleObject);
if (hasArrayElements) {
Object indexOrPropertyKey = toArrayIndex(index);
if (indexOrPropertyKey instanceof Long) {
try {
interop.writeArrayElement(truffleObject, (long) indexOrPropertyKey, exportedValue);
return;
} catch (InvalidArrayIndexException | UnsupportedTypeException | UnsupportedMessageException e) {
if (root.isStrict) {
errorBranch.enter(this);
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeArrayElement", this);
} else {
return;
}
}
} else {
propertyKey = indexOrPropertyKey;
assert JSRuntime.isPropertyKey(propertyKey);
}
} else {
propertyKey = toPropertyKey(index);
}
if (root.context.getLanguageOptions().hasForeignHashProperties() && interop.hasHashEntries(truffleObject)) {
try {
interop.writeHashEntry(truffleObject, propertyKey, exportedValue);
return;
} catch (UnknownKeyException | UnsupportedMessageException | UnsupportedTypeException e) {
if (root.isStrict) {
errorBranch.enter(this);
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeHashEntry", this);
} else {
return;
}
}
}
if (propertyKey instanceof Symbol) {
return;
}
TruffleString stringKey = (TruffleString) propertyKey;
if (hasArrayElements && Strings.equals(JSAbstractArray.LENGTH, stringKey)) {
JSInteropUtil.setArraySize(truffleObject, value, root.isStrict, interop, this, null);
}
if (root.context.isOptionNashornCompatibilityMode()) {
if (tryInvokeSetter(truffleObject, stringKey, exportedValue, root.context)) {
return;
}
}
try {
String javaPropertyKey = Strings.toJavaString(stringKey);
interop.writeMember(truffleObject, javaPropertyKey, exportedValue);
} catch (UnknownIdentifierException | UnsupportedTypeException | UnsupportedMessageException e) {
if (root.isStrict) {
errorBranch.enter(this);
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeMember", this);
} else {
return;
}
}
}
@Override
public boolean guard(Object target) {
return JSRuntime.isForeignObject(target);
}
private boolean tryInvokeSetter(Object thisObj, TruffleString key, Object value, JSContext context) {
assert context.isOptionNashornCompatibilityMode();
TruffleLanguage.Env env = getRealm().getEnv();
if (env.isHostObject(thisObj)) {
TruffleString setterKey = PropertyCacheNode.getAccessorKey(Strings.SET, key);
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;
}
private Object toArrayIndex(Object index) {
if (toArrayIndexNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toArrayIndexNode = insert(ToArrayIndexNode.create());
}
return toArrayIndexNode.execute(index);
}
private Object toPropertyKey(Object index) {
if (toPropertyKeyNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toPropertyKeyNode = insert(JSToPropertyKeyNode.create());
}
return toPropertyKeyNode.execute(index);
}
}
@Override
public JavaScriptNode getTarget() {
return targetNode;
}
public JavaScriptNode getElement() {
return indexNode;
}
public JavaScriptNode getValue() {
return valueNode;
}
public JSContext getContext() {
return context;
}
public boolean isStrict() {
return isStrict;
}
public boolean writeOwn() {
return writeOwn;
}
boolean isSuperProperty() {
return targetNode instanceof SuperPropertyReferenceNode;
}
@Override
protected JavaScriptNode copyUninitialized(Set> materializedTags) {
return create(cloneUninitialized(targetNode, materializedTags), cloneUninitialized(indexNode, materializedTags), cloneUninitialized(valueNode, materializedTags), getContext(), isStrict(),
writeOwn());
}
@Override
public boolean isResultAlwaysOfType(Class> clazz) {
return valueNode.isResultAlwaysOfType(clazz);
}
@NeverDefault
public static WriteElementNode createCachedInterop() {
return create(JavaScriptLanguage.get(null).getJSContext(), true);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy