com.oracle.truffle.js.nodes.access.ReadElementNode Maven / Gradle / Ivy
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.truffle.js.nodes.access;
import java.util.Objects;
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.builtins.helper.ListGetNode;
import com.oracle.truffle.js.builtins.helper.ListGetNodeGen;
import com.oracle.truffle.js.nodes.JSTypesGen;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.ReadNode;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.BigIntReadElementTypeCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.BooleanReadElementTypeCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.ConstantArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.ConstantObjectArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.EmptyArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.ForeignObjectReadElementTypeCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.HolesDoubleArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.HolesIntArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.HolesJSObjectArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.HolesObjectArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.LazyArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.LazyRegexResultArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.LazyRegexResultIndicesArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.NumberReadElementTypeCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.ReadElementArrayDispatchNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.ReadElementTypeCacheDispatchNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.SymbolReadElementTypeCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.TypedBigIntArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.TypedFloatArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.TypedIntArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.Uint32ArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.access.ReadElementNodeFactory.WritableArrayReadElementCacheNodeGen;
import com.oracle.truffle.js.nodes.cast.JSToPropertyKeyNode;
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.ReadElementTag;
import com.oracle.truffle.js.nodes.interop.ForeignObjectPrototypeNode;
import com.oracle.truffle.js.nodes.interop.ImportValueNode;
import com.oracle.truffle.js.runtime.BigInt;
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.array.ScriptArray;
import com.oracle.truffle.js.runtime.array.TypedArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractConstantArray;
import com.oracle.truffle.js.runtime.array.dyn.AbstractWritableArray;
import com.oracle.truffle.js.runtime.array.dyn.ConstantEmptyArray;
import com.oracle.truffle.js.runtime.array.dyn.ConstantObjectArray;
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.LazyArray;
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.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.objects.Undefined;
import com.oracle.truffle.js.runtime.util.JSClassProfile;
import com.oracle.truffle.js.runtime.util.TRegexUtil;
public class ReadElementNode extends JSTargetableNode implements ReadNode {
@Child private JavaScriptNode targetNode;
@Child private JavaScriptNode indexNode;
@Child private ReadElementTypeCacheDispatchNode typeCacheNode;
protected final JSContext context;
@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;
static final int EXPECT_RETURN_OBJECT = 0;
static final int EXPECT_RETURN_INT = 1;
static final int EXPECT_RETURN_DOUBLE = 2;
static final boolean ALLOW_RETURN_TYPE_SPECULATION = true;
static boolean isExpectedReturnInt(int expectedReturn) {
return expectedReturn == EXPECT_RETURN_INT && ALLOW_RETURN_TYPE_SPECULATION;
}
static boolean isExpectedReturnDouble(int expectedReturn) {
return expectedReturn == EXPECT_RETURN_DOUBLE && ALLOW_RETURN_TYPE_SPECULATION;
}
@NeverDefault
public static ReadElementNode create(JSContext context) {
return new ReadElementNode(null, null, context);
}
public static ReadElementNode create(JavaScriptNode targetNode, JavaScriptNode indexNode, JSContext context) {
return new ReadElementNode(targetNode, indexNode, context);
}
protected ReadElementNode(JavaScriptNode targetNode, JavaScriptNode indexNode, JSContext context) {
this.targetNode = targetNode;
this.indexNode = indexNode;
this.context = context;
}
@Override
public InstrumentableNode materializeInstrumentableNodes(Set> materializedTags) {
if (materializedTags.contains(ReadElementTag.class) && materializationNeeded()) {
JavaScriptNode clonedTarget = targetNode == null || targetNode.hasSourceSection() ? targetNode : JSTaggedExecutionNode.createForInput(targetNode, this, materializedTags);
JavaScriptNode clonedIndex = indexNode == null || indexNode.hasSourceSection() ? indexNode : JSTaggedExecutionNode.createForInput(indexNode, this, materializedTags);
if (clonedTarget == targetNode && clonedIndex == indexNode) {
return this;
}
if (clonedTarget == targetNode) {
clonedTarget = cloneUninitialized(targetNode, materializedTags);
}
if (clonedIndex == indexNode) {
clonedIndex = cloneUninitialized(indexNode, materializedTags);
}
JavaScriptNode cloned = ReadElementNode.create(clonedTarget, clonedIndex, getContext());
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());
}
@Override
public boolean hasTag(Class extends Tag> tag) {
if (tag == ReadElementTag.class) {
return true;
} else {
return super.hasTag(tag);
}
}
@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 = getIndexNode().execute(frame);
if (index instanceof Integer) {
indexState = INDEX_INT;
return executeWithTargetAndIndex(target, (int) index, receiver);
} else {
indexState = INDEX_OBJECT;
return executeWithTargetAndIndex(target, index, receiver);
}
} else if (is == INDEX_INT) {
int index;
try {
index = getIndexNode().executeInt(frame);
} catch (UnexpectedResultException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
indexState = INDEX_OBJECT;
return executeWithTargetAndIndex(target, e.getResult(), receiver);
}
return executeWithTargetAndIndex(target, index);
} else {
assert is == INDEX_OBJECT;
Object index = getIndexNode().execute(frame);
return executeWithTargetAndIndex(target, index, receiver);
}
}
public int executeWithTargetInt(VirtualFrame frame, Object target, Object receiver) throws UnexpectedResultException {
byte is = indexState;
if (is == 0) {
CompilerDirectives.transferToInterpreterAndInvalidate();
Object index = getIndexNode().execute(frame);
if (index instanceof Integer) {
indexState = INDEX_INT;
return executeWithTargetAndIndexInt(target, (int) index, receiver);
} else {
indexState = INDEX_OBJECT;
return executeWithTargetAndIndexInt(target, index, receiver);
}
} else if (is == INDEX_INT) {
int index;
try {
index = getIndexNode().executeInt(frame);
} catch (UnexpectedResultException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
indexState = INDEX_OBJECT;
return executeWithTargetAndIndexInt(target, e.getResult(), receiver);
}
return executeWithTargetAndIndexInt(target, index, receiver);
} else {
assert is == INDEX_OBJECT;
Object index = getIndexNode().execute(frame);
return executeWithTargetAndIndexInt(target, index, receiver);
}
}
public double executeWithTargetDouble(VirtualFrame frame, Object target, Object receiver) throws UnexpectedResultException {
byte is = indexState;
if (is == 0) {
CompilerDirectives.transferToInterpreterAndInvalidate();
Object index = getIndexNode().execute(frame);
if (index instanceof Integer) {
indexState = INDEX_INT;
return executeWithTargetAndIndexDouble(target, (int) index, receiver);
} else {
indexState = INDEX_OBJECT;
return executeWithTargetAndIndexDouble(target, index, receiver);
}
} else if (is == INDEX_INT) {
int index;
try {
index = getIndexNode().executeInt(frame);
} catch (UnexpectedResultException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
indexState = INDEX_OBJECT;
return executeWithTargetAndIndexDouble(target, e.getResult(), receiver);
}
return executeWithTargetAndIndexDouble(target, index, receiver);
} else {
assert is == INDEX_OBJECT;
Object index = getIndexNode().execute(frame);
return executeWithTargetAndIndexDouble(target, index, receiver);
}
}
public final Object executeWithTargetAndIndex(Object target, Object index) {
return executeTypeDispatch(target, index, target, Undefined.instance);
}
public final Object executeWithTargetAndIndex(Object target, int index) {
return executeTypeDispatch(target, index, target, Undefined.instance);
}
public final Object executeWithTargetAndIndex(Object target, long index) {
return executeTypeDispatch(target, index, target, Undefined.instance);
}
public final Object executeWithTargetAndIndex(Object target, Object index, Object receiver) {
return executeTypeDispatch(target, index, receiver, Undefined.instance);
}
public final Object executeWithTargetAndIndex(Object target, int index, Object receiver) {
return executeTypeDispatch(target, index, receiver, Undefined.instance);
}
public final int executeWithTargetAndIndexInt(Object target, Object index, Object receiver) throws UnexpectedResultException {
return executeTypeDispatchInt(target, index, receiver, Undefined.instance);
}
public final int executeWithTargetAndIndexInt(Object target, int index, Object receiver) throws UnexpectedResultException {
return executeTypeDispatchInt(target, index, receiver, Undefined.instance);
}
public final double executeWithTargetAndIndexDouble(Object target, Object index, Object receiver) throws UnexpectedResultException {
return executeTypeDispatchDouble(target, index, receiver, Undefined.instance);
}
public final double executeWithTargetAndIndexDouble(Object target, int index, Object receiver) throws UnexpectedResultException {
return executeTypeDispatchDouble(target, index, receiver, Undefined.instance);
}
public final Object executeWithTargetAndIndexOrDefault(Object target, Object index, Object defaultValue) {
return executeTypeDispatch(target, index, target, defaultValue);
}
private ReadElementTypeCacheDispatchNode initTypeCacheDispatchNode() {
CompilerDirectives.transferToInterpreterAndInvalidate();
return typeCacheNode = insert(ReadElementTypeCacheDispatchNodeGen.create());
}
protected final Object executeTypeDispatch(Object target, Object index, Object receiver, Object defaultValue) {
ReadElementTypeCacheDispatchNode dispatch = typeCacheNode;
if (dispatch == null) {
dispatch = initTypeCacheDispatchNode();
}
return dispatch.executeWithTargetAndIndexUnchecked(target, index, receiver, defaultValue, this);
}
protected final Object executeTypeDispatch(Object target, int index, Object receiver, Object defaultValue) {
ReadElementTypeCacheDispatchNode dispatch = typeCacheNode;
if (dispatch == null) {
dispatch = initTypeCacheDispatchNode();
}
return dispatch.executeWithTargetAndIndexUnchecked(target, index, receiver, defaultValue, this);
}
protected final Object executeTypeDispatch(Object target, long index, Object receiver, Object defaultValue) {
ReadElementTypeCacheDispatchNode dispatch = typeCacheNode;
if (dispatch == null) {
dispatch = initTypeCacheDispatchNode();
}
return dispatch.executeWithTargetAndIndexUnchecked(target, index, receiver, defaultValue, this);
}
protected final int executeTypeDispatchInt(Object target, Object index, Object receiver, Object defaultValue) throws UnexpectedResultException {
ReadElementTypeCacheDispatchNode dispatch = typeCacheNode;
if (dispatch == null) {
dispatch = initTypeCacheDispatchNode();
}
return dispatch.executeWithTargetAndIndexUncheckedInt(target, index, receiver, defaultValue, this);
}
protected final int executeTypeDispatchInt(Object target, int index, Object receiver, Object defaultValue) throws UnexpectedResultException {
ReadElementTypeCacheDispatchNode dispatch = typeCacheNode;
if (dispatch == null) {
dispatch = initTypeCacheDispatchNode();
}
return dispatch.executeWithTargetAndIndexUncheckedInt(target, index, receiver, defaultValue, this);
}
protected final double executeTypeDispatchDouble(Object target, Object index, Object receiver, Object defaultValue) throws UnexpectedResultException {
ReadElementTypeCacheDispatchNode dispatch = typeCacheNode;
if (dispatch == null) {
dispatch = initTypeCacheDispatchNode();
}
return dispatch.executeWithTargetAndIndexUncheckedDouble(target, index, receiver, defaultValue, this);
}
protected final double executeTypeDispatchDouble(Object target, int index, Object receiver, Object defaultValue) throws UnexpectedResultException {
ReadElementTypeCacheDispatchNode dispatch = typeCacheNode;
if (dispatch == null) {
dispatch = initTypeCacheDispatchNode();
}
return dispatch.executeWithTargetAndIndexUncheckedDouble(target, index, receiver, defaultValue, this);
}
abstract static class GuardedReadElementTypeCacheNode extends JavaScriptBaseNode {
public abstract boolean guard(Object target);
protected abstract Object executeWithTargetAndIndexUnchecked(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root);
protected abstract Object executeWithTargetAndIndexUnchecked(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root);
}
@ImportStatic(ReadElementNode.class)
abstract static class ReadElementTypeCacheDispatchNode extends JavaScriptBaseNode {
protected abstract Object executeExpectReturn(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root, int expectedReturn);
protected abstract Object executeExpectReturn(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root, int expectedReturn);
@InliningCutoff
protected final Object executeWithTargetAndIndexUnchecked(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root) {
return executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_OBJECT);
}
@InliningCutoff
protected final Object executeWithTargetAndIndexUnchecked(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root) {
return executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_OBJECT);
}
@InliningCutoff
protected final int executeWithTargetAndIndexUncheckedInt(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root) throws UnexpectedResultException {
return JSTypesGen.expectInteger(executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_INT));
}
@InliningCutoff
protected final int executeWithTargetAndIndexUncheckedInt(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root) throws UnexpectedResultException {
return JSTypesGen.expectInteger(executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_INT));
}
@InliningCutoff
protected final double executeWithTargetAndIndexUncheckedDouble(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root) throws UnexpectedResultException {
return JSTypesGen.expectDouble(executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_DOUBLE));
}
@InliningCutoff
protected final double executeWithTargetAndIndexUncheckedDouble(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root) throws UnexpectedResultException {
return JSTypesGen.expectDouble(executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_DOUBLE));
}
@SuppressWarnings("unused")
@Specialization(guards = "isObjectNode.executeBoolean(target)")
protected static Object doJSObjectLongIndex(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root, int expectedReturn,
@Cached @Shared IsJSDynamicObjectNode isObjectNode,
@Cached @Shared JSObjectReadElementTypeCacheNode objectHandler) {
if (isExpectedReturnInt(expectedReturn)) {
try {
return objectHandler.executeWithTargetAndIndexUncheckedInt(target, index, receiver, defaultValue, root);
} catch (UnexpectedResultException e) {
// UnexpectedResultException is not declared here but declared in the caller.
throw JSRuntime.rethrow(e);
}
} else if (isExpectedReturnDouble(expectedReturn)) {
try {
return objectHandler.executeWithTargetAndIndexUncheckedDouble(target, index, receiver, defaultValue, root);
} catch (UnexpectedResultException e) {
// UnexpectedResultException is not declared here but declared in the caller.
throw JSRuntime.rethrow(e);
}
}
assert expectedReturn == EXPECT_RETURN_OBJECT;
return objectHandler.executeWithTargetAndIndexUnchecked(target, index, receiver, defaultValue, root);
}
@SuppressWarnings("unused")
@Specialization(guards = "isObjectNode.executeBoolean(target)", replaces = {"doJSObjectLongIndex"})
protected static Object doJSObject(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root, int expectedReturn,
@Cached @Shared IsJSDynamicObjectNode isObjectNode,
@Cached @Shared JSObjectReadElementTypeCacheNode objectHandler) {
if (isExpectedReturnInt(expectedReturn)) {
try {
return objectHandler.executeWithTargetAndIndexUncheckedInt(target, index, receiver, defaultValue, root);
} catch (Throwable e) {
// UnexpectedResultException is not declared here but declared in the caller.
throw JSRuntime.rethrow(e);
}
} else if (isExpectedReturnDouble(expectedReturn)) {
try {
return objectHandler.executeWithTargetAndIndexUncheckedDouble(target, index, receiver, defaultValue, root);
} catch (Throwable e) {
// UnexpectedResultException is not declared here but declared in the caller.
throw JSRuntime.rethrow(e);
}
}
assert expectedReturn == EXPECT_RETURN_OBJECT;
return objectHandler.executeWithTargetAndIndexUnchecked(target, index, receiver, defaultValue, root);
}
@InliningCutoff
@Specialization
protected static Object doStringLongIndex(TruffleString target, long index, Object receiver, Object defaultValue, ReadElementNode root, @SuppressWarnings("unused") int expectedReturn,
@Cached @Shared StringReadElementTypeCacheNode stringHandler) {
return stringHandler.executeWithTargetAndIndexUnchecked(target, index, receiver, defaultValue, root);
}
@InliningCutoff
@Specialization(replaces = {"doStringLongIndex"})
protected static Object doString(TruffleString target, Object index, Object receiver, Object defaultValue, ReadElementNode root, @SuppressWarnings("unused") int expectedReturn,
@Cached @Shared StringReadElementTypeCacheNode stringHandler) {
return stringHandler.executeWithTargetAndIndexUnchecked(target, index, receiver, defaultValue, root);
}
@InliningCutoff
@Specialization(guards = "otherHandler.guard(target)", limit = "BOUNDED_BY_TYPES")
protected static Object doOther(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root, @SuppressWarnings("unused") int expectedReturn,
@Cached("makeTypeCacheNode(target)") GuardedReadElementTypeCacheNode otherHandler) {
return otherHandler.executeWithTargetAndIndexUnchecked(target, index, receiver, defaultValue, root);
}
protected static GuardedReadElementTypeCacheNode makeTypeCacheNode(Object target) {
if (JSDynamicObject.isJSDynamicObject(target)) {
// Handled by other specializations.
throw CompilerDirectives.shouldNotReachHere("JSDynamicObject");
} else if (Strings.isTString(target)) {
// Handled by other specializations.
throw CompilerDirectives.shouldNotReachHere("TruffleString");
} else if (target instanceof Boolean) {
return BooleanReadElementTypeCacheNodeGen.create();
} else if (target instanceof Number) {
return NumberReadElementTypeCacheNodeGen.create(target.getClass());
} else if (target instanceof Symbol) {
return SymbolReadElementTypeCacheNodeGen.create();
} else if (target instanceof BigInt) {
return BigIntReadElementTypeCacheNodeGen.create();
} else {
assert JSRuntime.isForeignObject(target) : target.getClass();
return ForeignObjectReadElementTypeCacheNodeGen.create();
}
}
}
abstract static class JSObjectReadElementTypeCacheNode extends JavaScriptBaseNode {
@Child private IsArrayNode isArrayNode;
@Child private ToArrayIndexNode toArrayIndexNode;
@Child private JSObjectReadElementNonArrayTypeCacheNode nonArrayCaseNode;
private final JSClassProfile jsclassProfile = JSClassProfile.create();
JSObjectReadElementTypeCacheNode() {
this.isArrayNode = IsArrayNode.createIsAnyArray();
}
protected abstract Object executeExpectReturn(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root, int expectedReturn);
protected abstract Object executeExpectReturn(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root, int expectedReturn);
@InliningCutoff
protected final Object executeWithTargetAndIndexUnchecked(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root) {
return executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_OBJECT);
}
@InliningCutoff
protected final Object executeWithTargetAndIndexUnchecked(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root) {
return executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_OBJECT);
}
@InliningCutoff
protected final int executeWithTargetAndIndexUncheckedInt(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root) throws UnexpectedResultException {
return JSTypesGen.expectInteger(executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_INT));
}
@InliningCutoff
protected final int executeWithTargetAndIndexUncheckedInt(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root) throws UnexpectedResultException {
return JSTypesGen.expectInteger(executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_INT));
}
@InliningCutoff
protected final double executeWithTargetAndIndexUncheckedDouble(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root) throws UnexpectedResultException {
return JSTypesGen.expectDouble(executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_DOUBLE));
}
@InliningCutoff
protected final double executeWithTargetAndIndexUncheckedDouble(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root) throws UnexpectedResultException {
return JSTypesGen.expectDouble(executeExpectReturn(target, index, receiver, defaultValue, root, EXPECT_RETURN_DOUBLE));
}
@InliningCutoff
private boolean isArray(Object target) {
return isArrayNode.execute(target);
}
@InliningCutoff
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());
}
@InliningCutoff
private Object readNonArrayObjectIndex(JSDynamicObject targetObject, Object index, Object receiver, Object defaultValue, ReadElementNode root) {
return getNonArrayNode().execute(targetObject, index, receiver, defaultValue, root);
}
/**
* NB: Need to be careful not to blow inlining budget to ensure boxing elimination works.
*/
@Specialization
protected Object doLongIndex(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root, int expectedReturn,
@Cached @Shared ArrayReadElementCacheDispatchNode arrayDispatch,
@Cached @Shared InlinedConditionProfile arrayIf,
@Cached @Shared InlinedConditionProfile arrayIndexIf,
@Cached TruffleString.FromLongNode fromLong) {
JSDynamicObject targetObject = (JSDynamicObject) target;
if (arrayIf.profile(this, isArray(targetObject))) {
ScriptArray array = JSObject.getArray(targetObject);
if (arrayIndexIf.profile(this, JSRuntime.isArrayIndex(index))) {
return arrayDispatch.executeDelegateReturn(this, targetObject, array, index, receiver, defaultValue, root.context, expectedReturn);
} else {
return getProperty(targetObject, Strings.fromLong(fromLong, index), receiver, defaultValue);
}
} else {
return readNonArrayObjectIndex(targetObject, index, receiver, defaultValue, root);
}
}
/**
* NB: Need to be careful not to blow inlining budget to ensure boxing elimination works.
*/
@Specialization(replaces = {"doLongIndex"})
protected Object doObjectIndex(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root, int expectedReturn,
@Cached @Shared ArrayReadElementCacheDispatchNode arrayDispatch,
@Cached @Shared InlinedConditionProfile arrayIf,
@Cached @Shared InlinedConditionProfile arrayIndexIf) {
JSDynamicObject targetObject = (JSDynamicObject) target;
if (arrayIf.profile(this, isArray(targetObject))) {
Object objIndex = toArrayIndex(index);
ScriptArray array = JSObject.getArray(targetObject);
if (arrayIndexIf.profile(this, objIndex instanceof Long)) {
long longIndex = (Long) objIndex;
return arrayDispatch.executeDelegateReturn(this, targetObject, array, longIndex, receiver, defaultValue, root.context, expectedReturn);
} else {
return getProperty(targetObject, objIndex, receiver, defaultValue);
}
} else {
return readNonArrayObjectIndex(targetObject, index, receiver, defaultValue, root);
}
}
private Object getProperty(JSDynamicObject targetObject, Object objIndex, Object receiver, Object defaultValue) {
return JSObject.getOrDefault(targetObject, objIndex, receiver, defaultValue, jsclassProfile, this);
}
private JSObjectReadElementNonArrayTypeCacheNode getNonArrayNode() {
if (nonArrayCaseNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
nonArrayCaseNode = insert(new JSObjectReadElementNonArrayTypeCacheNode());
}
return nonArrayCaseNode;
}
}
private static class JSObjectReadElementNonArrayTypeCacheNode extends JavaScriptBaseNode {
@Child private CachedGetPropertyNode getPropertyCachedNode;
JSObjectReadElementNonArrayTypeCacheNode() {
}
public Object execute(JSDynamicObject targetObject, Object index, Object receiver, Object defaultValue, ReadElementNode root) {
if (getPropertyCachedNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getPropertyCachedNode = insert(CachedGetPropertyNode.create(root.context));
}
return getPropertyCachedNode.execute(targetObject, index, receiver, defaultValue);
}
}
protected static ArrayReadElementCacheNode makeArrayCacheNode(@SuppressWarnings("unused") JSDynamicObject target, ScriptArray array) {
if (array instanceof ConstantEmptyArray) {
return EmptyArrayReadElementCacheNodeGen.create();
} else if (array instanceof ConstantObjectArray && array.isHolesType()) {
return ConstantObjectArrayReadElementCacheNodeGen.create();
} else if (array instanceof LazyRegexResultArray) {
return LazyRegexResultArrayReadElementCacheNodeGen.create();
} else if (array instanceof LazyRegexResultIndicesArray) {
return LazyRegexResultIndicesArrayReadElementCacheNodeGen.create();
} else if (array instanceof LazyArray) {
return LazyArrayReadElementCacheNodeGen.create();
} else if (array instanceof AbstractConstantArray) {
return ConstantArrayReadElementCacheNodeGen.create();
} else if (array instanceof HolesIntArray) {
return HolesIntArrayReadElementCacheNodeGen.create();
} else if (array instanceof HolesDoubleArray) {
return HolesDoubleArrayReadElementCacheNodeGen.create();
} else if (array instanceof HolesJSObjectArray) {
return HolesJSObjectArrayReadElementCacheNodeGen.create();
} else if (array instanceof HolesObjectArray) {
return HolesObjectArrayReadElementCacheNodeGen.create();
} else if (array instanceof AbstractWritableArray) {
return WritableArrayReadElementCacheNodeGen.create();
} else if (array instanceof TypedArray) {
if (array instanceof TypedArray.AbstractUint32Array) {
return Uint32ArrayReadElementCacheNodeGen.create((TypedArray) array);
} else if (array instanceof TypedArray.TypedIntArray) {
return TypedIntArrayReadElementCacheNodeGen.create((TypedArray) array);
} else if (array instanceof TypedArray.TypedFloatArray) {
return TypedFloatArrayReadElementCacheNodeGen.create((TypedArray) array);
} else if (array instanceof TypedArray.TypedBigIntArray) {
return TypedBigIntArrayReadElementCacheNodeGen.create((TypedArray) array);
} else {
throw Errors.shouldNotReachHere();
}
} else {
return new ExactArrayReadElementCacheNode();
}
}
abstract static class ArrayReadElementCacheNode extends JavaScriptBaseNode {
protected ArrayReadElementCacheNode() {
}
protected abstract Object executeArrayGet(JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context);
protected int executeArrayGetInt(JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context)
throws UnexpectedResultException {
return JSTypesGen.expectInteger(executeArrayGet(target, array, index, receiver, defaultValue, context));
}
protected double executeArrayGetDouble(JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context)
throws UnexpectedResultException {
return JSTypesGen.expectDouble(executeArrayGet(target, array, index, receiver, defaultValue, context));
}
}
public abstract static class ReadElementArrayDispatchNode extends JavaScriptBaseNode {
protected ReadElementArrayDispatchNode() {
}
public static ReadElementArrayDispatchNode create() {
return ReadElementArrayDispatchNodeGen.create();
}
protected abstract Object executeArrayGet(JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context);
@Specialization
protected final Object doDispatch(JSDynamicObject target, ScriptArray arrayType, long index, Object receiver, Object defaultValue, JSContext context,
@Cached ArrayReadElementCacheDispatchNode dispatcher) {
return dispatcher.executeArrayGet(this, target, arrayType, index, receiver, defaultValue, context);
}
}
@SuppressWarnings("truffle-inlining")
@GenerateInline
@GenerateCached(false)
@ImportStatic(ReadElementNode.class)
public abstract static class ArrayReadElementCacheDispatchNode extends JavaScriptBaseNode {
protected ArrayReadElementCacheDispatchNode() {
}
protected abstract Object executeExpectReturn(Node node, JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context, int expectedReturn);
/**
* This could simply be {@link #executeExpectReturn}, but delegating a method with
* {@link InliningCutoff} allows us to save inlining budget by not inlining the actual
* dispatch code, making it more likely that the caller will inline up to a method call of
* the expected unboxed return type.
*/
protected final Object executeDelegateReturn(Node node, JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context, int expectedReturn) {
if (isExpectedReturnInt(expectedReturn)) {
try {
return executeArrayGetInt(node, target, array, index, receiver, defaultValue, context);
} catch (Throwable e) {
// UnexpectedResultException is not declared here but declared in the caller.
throw JSRuntime.rethrow(e);
}
} else if (isExpectedReturnDouble(expectedReturn)) {
try {
return executeArrayGetDouble(node, target, array, index, receiver, defaultValue, context);
} catch (Throwable e) {
// UnexpectedResultException is not declared here but declared in the caller.
throw JSRuntime.rethrow(e);
}
}
assert expectedReturn == EXPECT_RETURN_OBJECT;
return executeArrayGet(node, target, array, index, receiver, defaultValue, context);
}
@InliningCutoff
protected final Object executeArrayGet(Node node, JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context) {
return executeExpectReturn(node, target, array, index, receiver, defaultValue, context, EXPECT_RETURN_OBJECT);
}
@InliningCutoff
protected final int executeArrayGetInt(Node node, JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context)
throws UnexpectedResultException {
return JSTypesGen.expectInteger(executeExpectReturn(node, target, array, index, receiver, defaultValue, context, EXPECT_RETURN_INT));
}
@InliningCutoff
protected final double executeArrayGetDouble(Node node, JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context)
throws UnexpectedResultException {
return JSTypesGen.expectDouble(executeExpectReturn(node, target, array, index, receiver, defaultValue, context, EXPECT_RETURN_DOUBLE));
}
@Specialization(guards = "arrayType == cachedArrayType", limit = "BOUNDED_BY_TYPES")
protected static Object doDispatch(JSDynamicObject target, @SuppressWarnings("unused") ScriptArray arrayType, long index, Object receiver, Object defaultValue, JSContext context,
int expectedReturn,
@Cached("arrayType") ScriptArray cachedArrayType,
@Cached("makeHandler(target, cachedArrayType)") ArrayReadElementCacheNode handler) {
if (isExpectedReturnInt(expectedReturn)) {
try {
return handler.executeArrayGetInt(target, cachedArrayType, index, receiver, defaultValue, context);
} catch (Throwable e) {
// UnexpectedResultException is not declared here but declared in the caller.
throw JSRuntime.rethrow(e);
}
} else if (isExpectedReturnDouble(expectedReturn)) {
try {
return handler.executeArrayGetDouble(target, cachedArrayType, index, receiver, defaultValue, context);
} catch (Throwable e) {
// UnexpectedResultException is not declared here but declared in the caller.
throw JSRuntime.rethrow(e);
}
}
assert expectedReturn == EXPECT_RETURN_OBJECT;
return handler.executeArrayGet(target, cachedArrayType, index, receiver, defaultValue, context);
}
protected static ArrayReadElementCacheNode makeHandler(JSDynamicObject target, ScriptArray arrayType) {
return makeArrayCacheNode(target, arrayType);
}
}
abstract static class ArrayClassGuardCachedArrayReadElementCacheNode extends ArrayReadElementCacheNode {
private final JSClassProfile outOfBoundsClassProfile = JSClassProfile.create();
protected Object readOutOfBounds(JSDynamicObject target, long index, Object receiver, Object defaultValue, JSContext context, InlinedConditionProfile needGetProperty) {
if (needGetProperty.profile(this, needsSlowGet(target, context))) {
return JSObject.getOrDefault(target, index, receiver, defaultValue, outOfBoundsClassProfile, this);
} else {
return defaultValue;
}
}
private static boolean needsSlowGet(JSDynamicObject target, JSContext context) {
return !context.getArrayPrototypeNoElementsAssumption().isValid() || (!context.getFastArrayAssumption().isValid() && JSSlowArray.isJSSlowArray(target)) ||
(!context.getFastArgumentsObjectAssumption().isValid() && JSSlowArgumentsArray.isJSSlowArgumentsObject(target));
}
}
private static class ExactArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
private final JSClassProfile classProfile = JSClassProfile.create();
@Override
protected Object executeArrayGet(JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context) {
return JSObject.getOrDefault(target, index, receiver, defaultValue, classProfile, this);
}
}
abstract static class ConstantArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@Specialization
protected Object doConstantArray(JSDynamicObject target, AbstractConstantArray constantArray, long index, Object receiver, Object defaultValue, JSContext context,
@Cached InlinedConditionProfile inBounds,
@Cached InlinedConditionProfile needGetProperty) {
if (inBounds.profile(this, constantArray.hasElement(target, index))) {
return constantArray.getElementInBounds(target, (int) index);
} else {
return readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty);
}
}
}
abstract static class EmptyArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@Specialization
protected Object doEmptyArray(JSDynamicObject target, @SuppressWarnings("unused") ConstantEmptyArray emptyArray, long index, Object receiver, Object defaultValue, JSContext context,
@Cached InlinedConditionProfile needGetProperty) {
return readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty);
}
}
abstract static class ConstantObjectArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@Specialization
protected Object doConstantObjectArray(JSDynamicObject target, ConstantObjectArray constantObjectArray, long index, Object receiver, Object defaultValue, JSContext context,
@Cached InlinedConditionProfile inBounds,
@Cached InlinedConditionProfile notHoleArray,
@Cached InlinedConditionProfile notHoleValue,
@Cached InlinedConditionProfile needGetProperty) {
if (inBounds.profile(this, constantObjectArray.isInBoundsFast(target, index))) {
Object value = ConstantObjectArray.getElementInBoundsDirect(target, (int) index);
if (notHoleArray.profile(this, !constantObjectArray.hasHoles(target))) {
return value;
} else {
if (notHoleValue.profile(this, !HolesObjectArray.isHoleValue(value))) {
return value;
}
}
}
return readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty);
}
}
abstract static class LazyRegexResultArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@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();
@Specialization
protected Object doLazyRegexResultArray(JSDynamicObject target, LazyRegexResultArray lazyRegexResultArray, long index, Object receiver, Object defaultValue, JSContext context,
@Cached InlinedConditionProfile inBounds,
@Cached InlinedConditionProfile needGetProperty) {
int intIndex = (int) index;
if (inBounds.profile(this, lazyRegexResultArray.hasElement(target, intIndex))) {
return LazyRegexResultArray.materializeGroup(context, target, intIndex,
lazyRegexResultNode, lazyRegexResultOriginalInputNode, null, substringNode, getStartNode, getEndNode);
} else {
return readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty);
}
}
}
abstract static class LazyRegexResultIndicesArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@Child private TRegexUtil.InvokeGetGroupBoundariesMethodNode getStartNode = TRegexUtil.InvokeGetGroupBoundariesMethodNode.create();
@Child private TRegexUtil.InvokeGetGroupBoundariesMethodNode getEndNode = TRegexUtil.InvokeGetGroupBoundariesMethodNode.create();
@Specialization
protected Object doLazyRegexResultIndicesArray(JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context,
@Cached InlinedConditionProfile inBounds,
@Cached InlinedConditionProfile needGetProperty) {
LazyRegexResultIndicesArray lazyRegexResultIndicesArray = (LazyRegexResultIndicesArray) array;
int intIndex = (int) index;
if (inBounds.profile(this, lazyRegexResultIndicesArray.hasElement(target, intIndex))) {
return LazyRegexResultIndicesArray.materializeGroup(context, target, intIndex,
null, getStartNode, getEndNode);
} else {
return readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty);
}
}
}
abstract static class LazyArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@Child private ListGetNode listGetNode = ListGetNodeGen.create();
@Specialization
protected Object doLazyArray(JSDynamicObject target, ScriptArray array, long index, Object receiver, Object defaultValue, JSContext context,
@Cached InlinedConditionProfile inBounds,
@Cached InlinedConditionProfile needGetProperty) {
LazyArray lazyRegexResultArray = (LazyArray) array;
int intIndex = (int) index;
if (inBounds.profile(this, lazyRegexResultArray.hasElement(target, intIndex))) {
return lazyRegexResultArray.getElementInBounds(target, intIndex, listGetNode);
} else {
return readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty);
}
}
}
abstract static class WritableArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@Specialization(rewriteOn = UnexpectedResultException.class)
protected int doWritableArrayInt(JSDynamicObject target, AbstractWritableArray writableArray, long index, Object receiver, Object defaultValue, JSContext context,
@Cached @Shared InlinedConditionProfile inBounds,
@Cached @Shared InlinedConditionProfile needGetProperty)
throws UnexpectedResultException {
if (inBounds.profile(this, writableArray.isInBoundsFast(target, index))) {
return writableArray.getInBoundsFastInt(target, (int) index);
} else {
return JSTypesGen.expectInteger(readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty));
}
}
@Specialization(rewriteOn = UnexpectedResultException.class)
protected double doWritableArrayDouble(JSDynamicObject target, AbstractWritableArray writableArray, long index, Object receiver, Object defaultValue, JSContext context,
@Cached @Shared InlinedConditionProfile inBounds,
@Cached @Shared InlinedConditionProfile needGetProperty)
throws UnexpectedResultException {
if (inBounds.profile(this, writableArray.isInBoundsFast(target, index))) {
return writableArray.getInBoundsFastDouble(target, (int) index);
} else {
return JSTypesGen.expectDouble(readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty));
}
}
@Specialization
protected Object doWritableArray(JSDynamicObject target, AbstractWritableArray writableArray, long index, Object receiver, Object defaultValue, JSContext context,
@Cached @Shared InlinedConditionProfile inBounds,
@Cached @Shared InlinedConditionProfile needGetProperty) {
if (inBounds.profile(this, writableArray.isInBoundsFast(target, index))) {
return writableArray.getInBoundsFast(target, (int) index);
} else {
return readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty);
}
}
}
abstract static class HolesIntArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@Specialization
protected Object doHolesIntArray(JSDynamicObject target, HolesIntArray holesIntArray, long index, Object receiver, Object defaultValue, JSContext context,
@Cached InlinedConditionProfile inBounds,
@Cached InlinedConditionProfile notHoleValue,
@Cached InlinedConditionProfile needGetProperty) {
if (inBounds.profile(this, holesIntArray.isInBoundsFast(target, index))) {
int value = holesIntArray.getInBoundsFastInt(target, (int) index);
if (notHoleValue.profile(this, !HolesIntArray.isHoleValue(value))) {
return value;
}
}
return readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty);
}
}
abstract static class HolesDoubleArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@Specialization
protected Object doHolesDoubleArray(JSDynamicObject target, HolesDoubleArray holesDoubleArray, long index, Object receiver, Object defaultValue, JSContext context,
@Cached InlinedConditionProfile inBounds,
@Cached InlinedConditionProfile notHoleValue,
@Cached InlinedConditionProfile needGetProperty) {
if (inBounds.profile(this, holesDoubleArray.isInBoundsFast(target, index))) {
double value = holesDoubleArray.getInBoundsFastDouble(target, (int) index);
if (notHoleValue.profile(this, !HolesDoubleArray.isHoleValue(value))) {
return value;
}
}
return readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty);
}
}
abstract static class HolesJSObjectArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@Specialization
protected Object doHolesJSObjectArray(JSDynamicObject target, HolesJSObjectArray holesArray, long index, Object receiver, Object defaultValue, JSContext context,
@Cached InlinedConditionProfile inBounds,
@Cached InlinedConditionProfile notHoleValue,
@Cached InlinedConditionProfile needGetProperty) {
if (inBounds.profile(this, holesArray.isInBoundsFast(target, index))) {
JSDynamicObject value = holesArray.getInBoundsFastJSObject(target, (int) index);
if (notHoleValue.profile(this, !HolesJSObjectArray.isHoleValue(value))) {
return value;
}
}
return readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty);
}
}
abstract static class HolesObjectArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@Specialization
protected Object doHolesObjectArray(JSDynamicObject target, HolesObjectArray holesArray, long index, Object receiver, Object defaultValue, JSContext context,
@Cached InlinedConditionProfile inBounds,
@Cached InlinedConditionProfile notHoleValue,
@Cached InlinedConditionProfile needGetProperty) {
if (inBounds.profile(this, holesArray.isInBoundsFast(target, index))) {
Object value = holesArray.getInBoundsFastObject(target, (int) index);
if (notHoleValue.profile(this, !HolesObjectArray.isHoleValue(value))) {
return value;
}
}
return readOutOfBounds(target, index, receiver, defaultValue, context, needGetProperty);
}
}
private abstract static class AbstractTypedArrayReadElementCacheNode extends ArrayClassGuardCachedArrayReadElementCacheNode {
@Child protected InteropLibrary interop;
AbstractTypedArrayReadElementCacheNode(TypedArray arrayType) {
this.interop = arrayType.isInterop() ? InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit) : InteropLibrary.getUncached();
}
}
abstract static class TypedIntArrayReadElementCacheNode extends AbstractTypedArrayReadElementCacheNode {
TypedIntArrayReadElementCacheNode(TypedArray arrayType) {
super(arrayType);
}
@Specialization(rewriteOn = UnexpectedResultException.class)
protected int doTypedIntArrayInt(JSDynamicObject target, TypedArray.TypedIntArray typedArray, long index, @SuppressWarnings("unused") Object receiver, Object defaultValue, JSContext context)
throws UnexpectedResultException {
if (!JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, context) && typedArray.hasElement(target, index)) {
return typedArray.getInt(target, (int) index, interop);
} else {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new UnexpectedResultException(defaultValue);
}
}
@Specialization(rewriteOn = UnexpectedResultException.class)
protected double doTypedIntArrayDouble(JSDynamicObject target, TypedArray.TypedIntArray typedArray, long index, @SuppressWarnings("unused") Object receiver, Object defaultValue,
JSContext context) throws UnexpectedResultException {
if (!JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, context) && typedArray.hasElement(target, index)) {
return typedArray.getInt(target, (int) index, interop);
} else {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new UnexpectedResultException(defaultValue);
}
}
@Specialization
protected Object doTypedIntArray(JSDynamicObject target, TypedArray.TypedIntArray typedArray, long index, @SuppressWarnings("unused") Object receiver, Object defaultValue, JSContext context,
@Cached InlinedConditionProfile inBounds) {
if (inBounds.profile(this, !JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, context) && typedArray.hasElement(target, index))) {
return typedArray.getInt(target, (int) index, interop);
} else {
return defaultValue;
}
}
}
abstract static class Uint32ArrayReadElementCacheNode extends AbstractTypedArrayReadElementCacheNode {
Uint32ArrayReadElementCacheNode(TypedArray arrayType) {
super(arrayType);
}
@Specialization(rewriteOn = UnexpectedResultException.class)
protected int doTypedUint32Array(JSDynamicObject target, TypedArray.AbstractUint32Array typedArray, long index, @SuppressWarnings("unused") Object receiver, Object defaultValue,
JSContext context) throws UnexpectedResultException {
if (!JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, context) && typedArray.hasElement(target, index)) {
int intValue = typedArray.getInt(target, (int) index, interop);
if (intValue >= 0) {
return intValue;
} else {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new UnexpectedResultException((double) (intValue & 0xffff_ffffL));
}
} else {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new UnexpectedResultException(defaultValue);
}
}
@Specialization(rewriteOn = UnexpectedResultException.class)
protected double doTypedUint32ArrayDouble(JSDynamicObject target, TypedArray.AbstractUint32Array typedArray, long index, @SuppressWarnings("unused") Object receiver, Object defaultValue,
JSContext context) throws UnexpectedResultException {
if (!JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, context) && typedArray.hasElement(target, index)) {
return typedArray.getInt(target, (int) index, interop) & 0xffff_ffffL;
} else {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new UnexpectedResultException(defaultValue);
}
}
@Specialization
protected Object doTypedUint32ArrayGet(JSDynamicObject target, TypedArray.AbstractUint32Array typedArray, long index, @SuppressWarnings("unused") Object receiver, Object defaultValue,
JSContext context,
@Cached InlinedConditionProfile inBounds,
@Cached InlinedConditionProfile notNegative) {
if (inBounds.profile(this, !JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, context) && typedArray.hasElement(target, index))) {
int intValue = typedArray.getInt(target, (int) index, interop);
if (notNegative.profile(this, intValue >= 0)) {
return intValue;
} else {
return (double) (intValue & 0xffff_ffffL);
}
} else {
return defaultValue;
}
}
}
abstract static class TypedFloatArrayReadElementCacheNode extends AbstractTypedArrayReadElementCacheNode {
TypedFloatArrayReadElementCacheNode(TypedArray arrayType) {
super(arrayType);
}
@Specialization(rewriteOn = UnexpectedResultException.class)
protected double doTypedFloatArrayDouble(JSDynamicObject target, TypedArray.TypedFloatArray typedArray, long index, @SuppressWarnings("unused") Object receiver, Object defaultValue,
JSContext context) throws UnexpectedResultException {
if (!JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, context) && typedArray.hasElement(target, index)) {
return typedArray.getDouble(target, (int) index, interop);
} else {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new UnexpectedResultException(defaultValue);
}
}
@Specialization
protected Object doTypedFloatArray(JSDynamicObject target, TypedArray.TypedFloatArray typedArray, long index, @SuppressWarnings("unused") Object receiver, Object defaultValue,
JSContext context,
@Cached InlinedConditionProfile inBounds) {
if (inBounds.profile(this, !JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, context) && typedArray.hasElement(target, index))) {
return typedArray.getDouble(target, (int) index, interop);
} else {
return defaultValue;
}
}
}
abstract static class TypedBigIntArrayReadElementCacheNode extends AbstractTypedArrayReadElementCacheNode {
TypedBigIntArrayReadElementCacheNode(TypedArray arrayType) {
super(arrayType);
}
@Specialization
protected Object doTypedBigIntArray(JSDynamicObject target, TypedArray.TypedBigIntArray typedArray, long index, @SuppressWarnings("unused") Object receiver, Object defaultValue,
JSContext context,
@Cached InlinedConditionProfile inBounds) {
if (inBounds.profile(this, !JSArrayBufferView.isOutOfBounds((JSTypedArrayObject) target, context) && typedArray.hasElement(target, index))) {
return typedArray.getBigInt(target, (int) index, interop);
} else {
return defaultValue;
}
}
}
private abstract static class ToPropertyKeyCachedReadElementTypeCacheNode extends GuardedReadElementTypeCacheNode {
@Child private CachedGetPropertyNode getPropertyCachedNode;
protected final JSClassProfile jsclassProfile = JSClassProfile.create();
ToPropertyKeyCachedReadElementTypeCacheNode() {
super();
}
@InliningCutoff
public Object readFromWrapper(JSDynamicObject wrapper, Object index, Object receiver, Object defaultValue, ReadElementNode root) {
if (getPropertyCachedNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getPropertyCachedNode = insert(CachedGetPropertyNode.create(root.context));
}
return getPropertyCachedNode.execute(wrapper, index, receiver, defaultValue);
}
}
abstract static class StringReadElementTypeCacheNode extends ToPropertyKeyCachedReadElementTypeCacheNode {
@Child private TruffleString.SubstringByteIndexNode substringByteIndexNode;
StringReadElementTypeCacheNode() {
this.substringByteIndexNode = TruffleString.SubstringByteIndexNode.create();
}
@Specialization
protected Object doStringLongIndex(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root,
@Cached @Shared InlinedConditionProfile stringIndexInBounds) {
TruffleString string = (TruffleString) target;
if (stringIndexInBounds.profile(this, index >= 0 && index < Strings.length(string))) {
return Strings.substring(root.context, substringByteIndexNode, string, (int) index, 1);
} else {
return doStringLongIndexOOB(string, index, receiver, defaultValue, root);
}
}
@InliningCutoff
private Object doStringLongIndexOOB(TruffleString string, long index, Object receiver, Object defaultValue, ReadElementNode root) {
return JSObject.getOrDefault(JSString.create(root.context, getRealm(), string), index, receiver, defaultValue, jsclassProfile, root);
}
@Specialization
protected Object doString(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root,
@Cached @Shared InlinedConditionProfile stringIndexInBounds,
@Cached ToArrayIndexNoToPropertyKeyNode toArrayIndexNode,
@Cached JSToPropertyKeyNode indexToPropertyKeyNode) {
TruffleString string = (TruffleString) target;
long longIndex = toArrayIndexNode.executeLong(this, index);
if (stringIndexInBounds.profile(this, longIndex >= 0 && longIndex < Strings.length(string))) {
return Strings.substring(root.context, substringByteIndexNode, string, (int) longIndex, 1);
}
return doStringOOB(string, index, receiver, defaultValue, root, indexToPropertyKeyNode);
}
@InliningCutoff
private Object doStringOOB(TruffleString string, Object index, Object receiver, Object defaultValue, ReadElementNode root,
JSToPropertyKeyNode indexToPropertyKeyNode) {
return readFromWrapper(JSString.create(root.context, getRealm(), string), indexToPropertyKeyNode.execute(index), receiver, defaultValue, root);
}
@Override
public boolean guard(Object target) {
return target instanceof TruffleString;
}
}
abstract static class NumberReadElementTypeCacheNode extends ToPropertyKeyCachedReadElementTypeCacheNode {
private final Class> numberClass;
NumberReadElementTypeCacheNode(Class> stringClass) {
this.numberClass = stringClass;
}
@Specialization
protected Object doNumber(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root) {
Number number = (Number) CompilerDirectives.castExact(target, numberClass);
return JSObject.getOrDefault(JSNumber.create(root.context, getRealm(), number), index, receiver, defaultValue, jsclassProfile, root);
}
@Specialization
protected Object doNumber(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root,
@Cached JSToPropertyKeyNode indexToPropertyKeyNode) {
Number number = (Number) CompilerDirectives.castExact(target, numberClass);
return readFromWrapper(JSNumber.create(root.context, getRealm(), number), indexToPropertyKeyNode.execute(index), receiver, defaultValue, root);
}
@Override
public boolean guard(Object target) {
return CompilerDirectives.isExact(target, numberClass);
}
}
abstract static class BooleanReadElementTypeCacheNode extends ToPropertyKeyCachedReadElementTypeCacheNode {
BooleanReadElementTypeCacheNode() {
}
@Specialization
protected Object doBoolean(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root) {
Boolean bool = (Boolean) target;
return JSObject.getOrDefault(JSBoolean.create(root.context, getRealm(), bool), index, receiver, defaultValue, jsclassProfile, root);
}
@Specialization
protected Object doBoolean(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root,
@Cached JSToPropertyKeyNode indexToPropertyKeyNode) {
Boolean bool = (Boolean) target;
return readFromWrapper(JSBoolean.create(root.context, getRealm(), bool), indexToPropertyKeyNode.execute(index), receiver, defaultValue, root);
}
@Override
public boolean guard(Object target) {
return target instanceof Boolean;
}
}
abstract static class SymbolReadElementTypeCacheNode extends ToPropertyKeyCachedReadElementTypeCacheNode {
SymbolReadElementTypeCacheNode() {
}
@Specialization
protected Object doSymbol(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root) {
Symbol symbol = (Symbol) target;
return JSObject.getOrDefault(JSSymbol.create(root.context, getRealm(), symbol), index, receiver, defaultValue, jsclassProfile, root);
}
@Specialization
protected Object doSymbol(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root,
@Cached JSToPropertyKeyNode indexToPropertyKeyNode) {
Symbol symbol = (Symbol) target;
return readFromWrapper(JSSymbol.create(root.context, getRealm(), symbol), indexToPropertyKeyNode.execute(index), receiver, defaultValue, root);
}
@Override
public boolean guard(Object target) {
return target instanceof Symbol;
}
}
abstract static class BigIntReadElementTypeCacheNode extends ToPropertyKeyCachedReadElementTypeCacheNode {
BigIntReadElementTypeCacheNode() {
super();
}
@Specialization
protected Object doBigInt(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root) {
BigInt bigInt = (BigInt) target;
return JSObject.getOrDefault(JSBigInt.create(root.context, getRealm(), bigInt), index, receiver, defaultValue, jsclassProfile, root);
}
@Specialization
protected Object doBigInt(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root,
@Cached JSToPropertyKeyNode indexToPropertyKeyNode) {
BigInt bigInt = (BigInt) target;
return readFromWrapper(JSBigInt.create(root.context, getRealm(), bigInt), indexToPropertyKeyNode.execute(index), receiver, defaultValue, root);
}
@Override
public boolean guard(Object target) {
return target instanceof BigInt;
}
}
abstract static class ForeignObjectReadElementTypeCacheNode extends GuardedReadElementTypeCacheNode {
@Child private InteropLibrary interop;
@Child private JSToPropertyKeyNode toPropertyKeyNode;
@Child private ImportValueNode importValueNode;
@Child private InteropLibrary getterInterop;
@Child private ForeignObjectPrototypeNode foreignObjectPrototypeNode;
@Child private CachedGetPropertyNode readFromPrototypeNode;
@Child private ToArrayIndexNode toArrayIndexNode;
@CompilationFinal private boolean optimistic = true;
ForeignObjectReadElementTypeCacheNode() {
super();
this.importValueNode = ImportValueNode.create();
this.interop = InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit);
}
@Specialization
protected Object doForeignObject(Object target, Object index, Object receiver, Object defaultValue, ReadElementNode root,
@Cached InlinedExactClassProfile classProfile,
@Cached InlinedBranchProfile errorBranch) {
Object truffleObject = classProfile.profile(this, target);
if (interop.isNull(truffleObject)) {
errorBranch.enter(this);
throw Errors.createTypeErrorCannotGetProperty(index, target, false, this);
}
Object foreignResult = getImpl(truffleObject, index, receiver, defaultValue, root, errorBranch);
if (foreignResult == defaultValue) {
return foreignResult;
}
return importValueNode.executeWithTarget(foreignResult);
}
private Object getImpl(Object truffleObject, Object key, Object receiver, Object defaultValue, ReadElementNode root,
@Cached InlinedBranchProfile errorBranch) {
Object propertyKey;
boolean hasArrayElements = interop.hasArrayElements(truffleObject);
if (hasArrayElements) {
try {
Object indexOrPropertyKey = toArrayIndex(key);
if (indexOrPropertyKey instanceof Long) {
return interop.readArrayElement(truffleObject, (long) indexOrPropertyKey);
} else {
propertyKey = indexOrPropertyKey;
assert JSRuntime.isPropertyKey(propertyKey);
}
} catch (InvalidArrayIndexException | UnsupportedMessageException e) {
return defaultValue;
}
} else {
propertyKey = toPropertyKey(key);
}
if (root.context.getLanguageOptions().hasForeignHashProperties() && interop.hasHashEntries(truffleObject)) {
try {
return interop.readHashValue(truffleObject, propertyKey);
} catch (UnknownKeyException e) {
// fall through: still need to try members
} catch (UnsupportedMessageException e) {
return defaultValue;
}
}
if (propertyKey instanceof Symbol) {
return maybeReadFromPrototype(truffleObject, propertyKey, receiver, root.context);
}
TruffleString exportedKeyStr = (TruffleString) propertyKey;
if (hasArrayElements && Strings.equals(JSAbstractArray.LENGTH, exportedKeyStr)) {
return getSize(truffleObject, errorBranch);
}
if (root.context.isOptionNashornCompatibilityMode()) {
Object result = tryGetters(truffleObject, exportedKeyStr, root.context);
if (result != null) {
return result;
}
}
String stringKey = Strings.toJavaString(exportedKeyStr);
if (optimistic) {
try {
return interop.readMember(truffleObject, stringKey);
} catch (UnknownIdentifierException | UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
optimistic = false;
return maybeReadFromPrototype(truffleObject, exportedKeyStr, receiver, root.context);
}
} else {
if (interop.isMemberReadable(truffleObject, stringKey)) {
try {
return interop.readMember(truffleObject, stringKey);
} catch (UnknownIdentifierException | UnsupportedMessageException e) {
return defaultValue;
}
} else {
return maybeReadFromPrototype(truffleObject, exportedKeyStr, receiver, root.context);
}
}
}
private Object tryGetters(Object thisObj, TruffleString key, JSContext context) {
assert context.isOptionNashornCompatibilityMode();
TruffleLanguage.Env env = getRealm().getEnv();
if (env.isHostObject(thisObj)) {
Object result = tryInvokeGetter(thisObj, Strings.GET, key);
if (result != null) {
return result;
}
result = tryInvokeGetter(thisObj, Strings.IS, key);
if (result != null) {
return result;
}
}
return null;
}
private Object tryInvokeGetter(Object thisObj, TruffleString prefix, TruffleString key) {
TruffleString getterKey = PropertyCacheNode.getAccessorKey(prefix, key);
if (getterKey == null) {
return null;
}
if (getterInterop == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getterInterop = insert(InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit));
}
if (!getterInterop.isMemberInvocable(thisObj, Strings.toJavaString(getterKey))) {
return null;
}
try {
return getterInterop.invokeMember(thisObj, Strings.toJavaString(getterKey), JSArguments.EMPTY_ARGUMENTS_ARRAY);
} catch (UnknownIdentifierException | UnsupportedMessageException | UnsupportedTypeException | ArityException e) {
return null; // try the next fallback
}
}
private Object getSize(Object truffleObject, InlinedBranchProfile errorBranch) {
try {
return JSRuntime.longToIntOrDouble(interop.getArraySize(truffleObject));
} catch (UnsupportedMessageException e) {
errorBranch.enter(this);
throw Errors.createTypeErrorInteropException(truffleObject, e, "getArraySize", this);
}
}
@InliningCutoff
private Object maybeReadFromPrototype(Object truffleObject, Object key, Object receiver, JSContext context) {
assert JSRuntime.isPropertyKey(key);
if (context.getLanguageOptions().hasForeignObjectPrototype() || key instanceof Symbol || JSInteropUtil.isBoxedPrimitive(truffleObject, interop)) {
if (readFromPrototypeNode == null || foreignObjectPrototypeNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.readFromPrototypeNode = insert(CachedGetPropertyNode.create(context));
this.foreignObjectPrototypeNode = insert(ForeignObjectPrototypeNode.create());
}
JSDynamicObject prototype = foreignObjectPrototypeNode.execute(truffleObject);
return readFromPrototypeNode.execute(prototype, key, receiver, Undefined.instance);
} else {
return Undefined.instance;
}
}
@Override
protected Object executeWithTargetAndIndexUnchecked(Object target, long index, Object receiver, Object defaultValue, ReadElementNode root) {
return executeWithTargetAndIndexUnchecked(target, (Object) index, receiver, defaultValue, root);
}
@Override
public boolean guard(Object target) {
return JSRuntime.isForeignObject(target);
}
private Object toArrayIndex(Object maybeIndex) {
if (toArrayIndexNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toArrayIndexNode = insert(ToArrayIndexNode.create());
}
return toArrayIndexNode.execute(maybeIndex);
}
private Object toPropertyKey(Object index) {
if (toPropertyKeyNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toPropertyKeyNode = insert(JSToPropertyKeyNode.create());
}
return toPropertyKeyNode.execute(index);
}
}
@Override
public final JavaScriptNode getTarget() {
return targetNode;
}
public final JavaScriptNode getElement() {
return getIndexNode();
}
public final JSContext getContext() {
return context;
}
@Override
protected JavaScriptNode copyUninitialized(Set> materializedTags) {
return create(cloneUninitialized(targetNode, materializedTags), cloneUninitialized(getIndexNode(), materializedTags), getContext());
}
@Override
public String expressionToString() {
if (targetNode != null && indexNode != null) {
return Objects.toString(targetNode.expressionToString(), INTERMEDIATE_VALUE) + "[" + Objects.toString(indexNode.expressionToString(), INTERMEDIATE_VALUE) + "]";
}
return null;
}
public JavaScriptNode getIndexNode() {
return indexNode;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy