package com.oracle.truffle.js.nodes.access;
import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.NodeCost;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.object.BooleanLocation;
import com.oracle.truffle.api.object.DoubleLocation;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.DynamicObjectLibrary;
import com.oracle.truffle.api.object.HiddenKey;
import com.oracle.truffle.api.object.IntLocation;
import com.oracle.truffle.api.object.LongLocation;
import com.oracle.truffle.api.object.ObjectLocation;
import com.oracle.truffle.api.object.Property;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.profiles.ValueProfile;
import com.oracle.truffle.js.nodes.JSGuards;
import com.oracle.truffle.js.nodes.JSTypesGen;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.access.PropertyGetNodeFactory.GetPropertyFromJSObjectNodeGen;
import com.oracle.truffle.js.nodes.array.ArrayLengthNode.ArrayLengthReadNode;
import com.oracle.truffle.js.nodes.cast.JSToObjectNode;
import com.oracle.truffle.js.nodes.function.CreateMethodPropertyNode;
import com.oracle.truffle.js.nodes.function.JSFunctionCallNode;
import com.oracle.truffle.js.nodes.interop.ForeignObjectPrototypeNode;
import com.oracle.truffle.js.nodes.interop.ImportValueNode;
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.JSNoSuchMethodAdapter;
import com.oracle.truffle.js.runtime.JSRealm;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.Symbol;
import com.oracle.truffle.js.runtime.array.dyn.LazyRegexResultIndicesArray;
import com.oracle.truffle.js.runtime.builtins.JSAbstractArray;
import com.oracle.truffle.js.runtime.builtins.JSAdapter;
import com.oracle.truffle.js.runtime.builtins.JSArray;
import com.oracle.truffle.js.runtime.builtins.JSArrayBufferView;
import com.oracle.truffle.js.runtime.builtins.JSClass;
import com.oracle.truffle.js.runtime.builtins.JSFunction;
import com.oracle.truffle.js.runtime.builtins.JSFunctionData;
import com.oracle.truffle.js.runtime.builtins.JSModuleNamespace;
import com.oracle.truffle.js.runtime.builtins.JSOrdinary;
import com.oracle.truffle.js.runtime.builtins.JSProxy;
import com.oracle.truffle.js.runtime.builtins.JSRegExp;
import com.oracle.truffle.js.runtime.builtins.JSRegExpGroupsObject;
import com.oracle.truffle.js.runtime.builtins.JSString;
import com.oracle.truffle.js.runtime.java.JavaImporter;
import com.oracle.truffle.js.runtime.java.JavaPackage;
import com.oracle.truffle.js.runtime.objects.Accessor;
import com.oracle.truffle.js.runtime.objects.JSDynamicObject;
import com.oracle.truffle.js.runtime.objects.JSObject;
import com.oracle.truffle.js.runtime.objects.JSObjectUtil;
import com.oracle.truffle.js.runtime.objects.JSProperty;
import com.oracle.truffle.js.runtime.objects.JSShape;
import com.oracle.truffle.js.runtime.objects.Null;
import com.oracle.truffle.js.runtime.objects.PropertyProxy;
import com.oracle.truffle.js.runtime.objects.Undefined;
import com.oracle.truffle.js.runtime.util.JSClassProfile;
import com.oracle.truffle.js.runtime.util.TRegexUtil;
import com.oracle.truffle.js.runtime.util.TRegexUtil.TRegexMaterializeResultNode;
import com.oracle.truffle.js.runtime.util.TRegexUtil.TRegexResultAccessor;
public class PropertyGetNode extends PropertyCacheNode<PropertyGetNode.GetCacheNode> {
protected final boolean isGlobal;
protected final boolean getOwnProperty;
@CompilationFinal protected boolean isMethod;
private boolean propertyAssumptionCheckEnabled = true;
public static PropertyGetNode create(Object key, JSContext context) {
return create(key, false, context);
}
public static PropertyGetNode create(Object key, boolean isGlobal, JSContext context) {
final boolean getOwnProperty = false;
final boolean isMethod = false;
return createImpl(key, isGlobal, context, getOwnProperty, isMethod);
}
public static PropertyGetNode create(Object key, boolean isGlobal, JSContext context, boolean getOwnProperty, boolean isMethod) {
return createImpl(key, isGlobal, context, getOwnProperty, isMethod);
}
private static PropertyGetNode createImpl(Object key, boolean isGlobal, JSContext context, boolean getOwnProperty, boolean isMethod) {
return new PropertyGetNode(key, context, isGlobal, getOwnProperty, isMethod);
}
public static PropertyGetNode createGetOwn(Object key, JSContext context) {
final boolean global = false;
final boolean getOwnProperty = true;
final boolean isMethod = false;
return createImpl(key, global, context, getOwnProperty, isMethod);
}
public static PropertyGetNode createGetHidden(HiddenKey key, JSContext context) {
return createGetOwn(key, context);
}
protected PropertyGetNode(Object key, JSContext context, boolean isGlobal, boolean getOwnProperty, boolean isMethod) {
super(key, context);
this.isGlobal = isGlobal;
this.getOwnProperty = getOwnProperty;
this.isMethod = isMethod;
}
public final Object getValue(Object obj) {
return getValueOrDefault(obj, obj, Undefined.instance);
}
public final int getValueInt(Object obj) throws UnexpectedResultException {
return getValueInt(obj, obj);
}
public final double getValueDouble(Object obj) throws UnexpectedResultException {
return getValueDouble(obj, obj);
}
public final boolean getValueBoolean(Object obj) throws UnexpectedResultException {
return getValueBoolean(obj, obj);
}
public final long getValueLong(Object obj) throws UnexpectedResultException {
return getValueLong(obj, obj);
}
public final Object getValueOrDefault(Object obj, Object defaultValue) {
return getValueOrDefault(obj, obj, defaultValue);
}
protected Object getValueOrUndefined(Object thisObj, Object receiver) {
return getValueOrDefault(thisObj, receiver, Undefined.instance);
}
@ExplodeLoop
protected int getValueInt(Object thisObj, Object receiver) throws UnexpectedResultException {
for (GetCacheNode c = cacheNode; c != null; c = c.next) {
if (c.isGeneric()) {
return c.getValueInt(thisObj, receiver, this, false);
}
if (!c.isValid()) {
CompilerDirectives.transferToInterpreterAndInvalidate();
break;
}
boolean guard = c.accepts(thisObj);
if (guard) {
return c.getValueInt(thisObj, receiver, this, guard);
}
}
deoptimize();
return getValueIntAndSpecialize(thisObj, receiver);
}
@TruffleBoundary
private int getValueIntAndSpecialize(Object thisObj, Object receiver) throws UnexpectedResultException {
return specialize(thisObj).getValueInt(thisObj, receiver, this, false);
}
@ExplodeLoop
protected double getValueDouble(Object thisObj, Object receiver) throws UnexpectedResultException {
for (GetCacheNode c = cacheNode; c != null; c = c.next) {
if (c.isGeneric()) {
return c.getValueDouble(thisObj, receiver, this, false);
}
if (!c.isValid()) {
CompilerDirectives.transferToInterpreterAndInvalidate();
break;
}
boolean guard = c.accepts(thisObj);
if (guard) {
return c.getValueDouble(thisObj, receiver, this, guard);
}
}
deoptimize();
return getValueDoubleAndSpecialize(thisObj, receiver);
}
@TruffleBoundary
private double getValueDoubleAndSpecialize(Object thisObj, Object receiver) throws UnexpectedResultException {
return specialize(thisObj).getValueDouble(thisObj, receiver, this, false);
}
@ExplodeLoop
protected boolean getValueBoolean(Object thisObj, Object receiver) throws UnexpectedResultException {
for (GetCacheNode c = cacheNode; c != null; c = c.next) {
if (c.isGeneric()) {
return c.getValueBoolean(thisObj, receiver, this, false);
}
if (!c.isValid()) {
CompilerDirectives.transferToInterpreterAndInvalidate();
break;
}
boolean guard = c.accepts(thisObj);
if (guard) {
return c.getValueBoolean(thisObj, receiver, this, guard);
}
}
deoptimize();
return getValueBooleanAndSpecialize(thisObj, receiver);
}
@TruffleBoundary
private boolean getValueBooleanAndSpecialize(Object thisObj, Object receiver) throws UnexpectedResultException {
return specialize(thisObj).getValueBoolean(thisObj, receiver, this, false);
}
@ExplodeLoop
protected long getValueLong(Object thisObj, Object receiver) throws UnexpectedResultException {
for (GetCacheNode c = cacheNode; c != null; c = c.next) {
if (c.isGeneric()) {
return c.getValueLong(thisObj, receiver, this, false);
}
if (!c.isValid()) {
CompilerDirectives.transferToInterpreterAndInvalidate();
break;
}
boolean guard = c.accepts(thisObj);
if (guard) {
return c.getValueLong(thisObj, receiver, this, guard);
}
}
deoptimize();
return getValueLongAndSpecialize(thisObj, receiver);
}
@TruffleBoundary
private long getValueLongAndSpecialize(Object thisObj, Object receiver) throws UnexpectedResultException {
return specialize(thisObj).getValueLong(thisObj, receiver, this, false);
}
@ExplodeLoop
protected Object getValueOrDefault(Object thisObj, Object receiver, Object defaultValue) {
for (GetCacheNode c = cacheNode; c != null; c = c.next) {
if (c.isGeneric()) {
return c.getValue(thisObj, receiver, defaultValue, this, false);
}
if (!c.isValid()) {
CompilerDirectives.transferToInterpreterAndInvalidate();
break;
}
boolean guard = c.accepts(thisObj);
if (guard) {
return c.getValue(thisObj, receiver, defaultValue, this, guard);
}
}
deoptimize();
return getValueAndSpecialize(thisObj, receiver, defaultValue);
}
@TruffleBoundary
private Object getValueAndSpecialize(Object thisObj, Object receiver, Object defaultValue) {
return specialize(thisObj).getValue(thisObj, receiver, defaultValue, this, false);
}
public abstract static class GetCacheNode extends PropertyCacheNode.CacheNode<GetCacheNode> {
protected GetCacheNode(ReceiverCheckNode receiverCheck) {
super(receiverCheck);
}
protected GetCacheNode(GetCacheNode next, ReceiverCheckNode receiverCheck) {
super(next, receiverCheck);
}
protected abstract Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard);
protected int getValueInt(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) throws UnexpectedResultException {
return JSTypesGen.expectInteger(getValue(thisObj, receiver, Undefined.instance, root, guard));
}
protected double getValueDouble(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) throws UnexpectedResultException {
return JSTypesGen.expectDouble(getValue(thisObj, receiver, Undefined.instance, root, guard));
}
protected boolean getValueBoolean(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) throws UnexpectedResultException {
return JSTypesGen.expectBoolean(getValue(thisObj, receiver, Undefined.instance, root, guard));
}
protected long getValueLong(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) throws UnexpectedResultException {
return JSTypesGen.expectLong(getValue(thisObj, receiver, Undefined.instance, root, guard));
}
}
public abstract static class LinkedPropertyGetNode extends GetCacheNode {
protected LinkedPropertyGetNode(ReceiverCheckNode receiverCheck) {
super(receiverCheck);
}
}
public static final class ObjectPropertyGetNode extends LinkedPropertyGetNode {
private final Property property;
public ObjectPropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isData(property);
this.property = property;
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
DynamicObject store = receiverCheck.getStore(thisObj);
Object value = property.get(store, guard);
if (JSProperty.isProxy(property)) {
return ((PropertyProxy) value).get(store);
} else {
assert JSProperty.isData(property);
return value;
}
}
}
protected abstract static class AbstractFinalDataPropertyGetNode extends LinkedPropertyGetNode {
private final Assumption finalAssumption;
protected AbstractFinalDataPropertyGetNode(Property property, AbstractShapeCheckNode shapeCheck) {
super(shapeCheck);
this.finalAssumption = property.getLocation().isFinal() ? null : property.getLocation().getFinalAssumption();
}
@Override
protected boolean isValid() {
return super.isValid() && (finalAssumption == null || finalAssumption.isValid());
}
protected final boolean assertFinalValue(Object finalValue, Object thisObj, PropertyGetNode root) {
if (!JSConfig.AssertFinalPropertySpecialization) {
return true;
}
int depth = ((AbstractShapeCheckNode) receiverCheck).getDepth();
DynamicObject store = (DynamicObject) thisObj;
for (int i = 0; i < depth; i++) {
store = JSObject.getPrototype(store);
}
return finalValue.equals(JSDynamicObject.getOrNull(store, root.getKey()));
}
}
public static final class FinalObjectPropertyGetNode extends AbstractFinalDataPropertyGetNode {
private final Object finalValue;
public FinalObjectPropertyGetNode(Property property, AbstractShapeCheckNode shapeCheck, Object value) {
super(property, shapeCheck);
assert JSProperty.isData(property);
this.finalValue = value;
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
assert assertFinalValue(finalValue, thisObj, root);
return finalValue;
}
}
public static final class IntPropertyGetNode extends LinkedPropertyGetNode {
private final IntLocation location;
public IntPropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isData(property);
this.location = (IntLocation) property.getLocation();
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return location.getInt(receiverCheck.getStore(thisObj), guard);
}
@Override
protected int getValueInt(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
return location.getInt(receiverCheck.getStore(thisObj), guard);
}
@Override
protected double getValueDouble(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
return location.getInt(receiverCheck.getStore(thisObj), guard);
}
}
public static final class FinalIntPropertyGetNode extends AbstractFinalDataPropertyGetNode {
private final int finalValue;
public FinalIntPropertyGetNode(Property property, AbstractShapeCheckNode shapeCheck, int value) {
super(property, shapeCheck);
assert JSProperty.isData(property);
this.finalValue = value;
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return getValueInt(thisObj, receiver, root, guard);
}
@Override
protected int getValueInt(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
assert assertFinalValue(finalValue, thisObj, root);
return finalValue;
}
@Override
protected double getValueDouble(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
return getValueInt(thisObj, receiver, root, guard);
}
}
public static final class DoublePropertyGetNode extends LinkedPropertyGetNode {
private final DoubleLocation location;
public DoublePropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isData(property);
this.location = (DoubleLocation) property.getLocation();
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return location.getDouble(receiverCheck.getStore(thisObj), guard);
}
@Override
protected double getValueDouble(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
return location.getDouble(receiverCheck.getStore(thisObj), guard);
}
}
public static final class FinalDoublePropertyGetNode extends AbstractFinalDataPropertyGetNode {
private final double finalValue;
public FinalDoublePropertyGetNode(Property property, AbstractShapeCheckNode shapeCheck, double value) {
super(property, shapeCheck);
assert JSProperty.isData(property);
this.finalValue = value;
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return getValueDouble(thisObj, receiver, root, guard);
}
@Override
protected double getValueDouble(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
assert assertFinalValue(finalValue, thisObj, root);
return finalValue;
}
}
public static final class BooleanPropertyGetNode extends LinkedPropertyGetNode {
private final BooleanLocation location;
public BooleanPropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isData(property);
this.location = (BooleanLocation) property.getLocation();
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return getValueBoolean(thisObj, receiver, root, guard);
}
@Override
protected boolean getValueBoolean(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
return location.getBoolean(receiverCheck.getStore(thisObj), guard);
}
}
public static final class FinalBooleanPropertyGetNode extends AbstractFinalDataPropertyGetNode {
private final boolean finalValue;
public FinalBooleanPropertyGetNode(Property property, AbstractShapeCheckNode shapeCheck, boolean value) {
super(property, shapeCheck);
assert JSProperty.isData(property);
this.finalValue = value;
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return getValueBoolean(thisObj, receiver, root, guard);
}
@Override
protected boolean getValueBoolean(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
assert assertFinalValue(finalValue, thisObj, root);
return finalValue;
}
}
public static final class LongPropertyGetNode extends LinkedPropertyGetNode {
private final LongLocation location;
public LongPropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isData(property);
this.location = (LongLocation) property.getLocation();
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return location.getLong(receiverCheck.getStore(thisObj), guard);
}
@Override
protected long getValueLong(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
return location.getLong(receiverCheck.getStore(thisObj), guard);
}
}
public static final class FinalLongPropertyGetNode extends AbstractFinalDataPropertyGetNode {
private final long finalValue;
public FinalLongPropertyGetNode(Property property, AbstractShapeCheckNode shapeCheck, long value) {
super(property, shapeCheck);
assert JSProperty.isData(property);
this.finalValue = value;
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return getValueLong(thisObj, receiver, root, guard);
}
@Override
protected long getValueLong(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
assert assertFinalValue(finalValue, thisObj, root);
return finalValue;
}
}
public static final class AccessorPropertyGetNode extends LinkedPropertyGetNode {
private final Property property;
@Child private JSFunctionCallNode callNode;
private final BranchProfile undefinedGetterBranch = BranchProfile.create();
public AccessorPropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isAccessor(property);
this.property = property;
this.callNode = JSFunctionCallNode.createCall();
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
DynamicObject store = receiverCheck.getStore(thisObj);
Accessor accessor = (Accessor) property.get(store, guard);
DynamicObject getter = accessor.getGetter();
if (getter != Undefined.instance) {
return callNode.executeCall(JSArguments.createZeroArg(receiver, getter));
} else {
undefinedGetterBranch.enter();
return Undefined.instance;
}
}
}
public static final class FinalAccessorPropertyGetNode extends LinkedPropertyGetNode {
@Child private JSFunctionCallNode callNode;
private final BranchProfile undefinedGetterBranch = BranchProfile.create();
private final Accessor finalAccessor;
private final Assumption finalAssumption;
public FinalAccessorPropertyGetNode(Property property, ReceiverCheckNode receiverCheck, Accessor finalAccessor) {
super(receiverCheck);
assert JSProperty.isAccessor(property);
this.callNode = JSFunctionCallNode.createCall();
this.finalAccessor = finalAccessor;
this.finalAssumption = property.getLocation().isFinal() ? null : property.getLocation().getFinalAssumption();
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
DynamicObject getter = finalAccessor.getGetter();
if (getter != Undefined.instance) {
return callNode.executeCall(JSArguments.createZeroArg(receiver, getter));
} else {
undefinedGetterBranch.enter();
return Undefined.instance;
}
}
@Override
protected boolean isValid() {
return super.isValid() && (finalAssumption == null || finalAssumption.isValid());
}
}
public static final class UndefinedPropertyGetNode extends LinkedPropertyGetNode {
public UndefinedPropertyGetNode(ReceiverCheckNode receiverCheck) {
super(receiverCheck);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return defaultValue;
}
}
public static final class UndefinedPropertyErrorNode extends LinkedPropertyGetNode {
public UndefinedPropertyErrorNode(ReceiverCheckNode receiverCheck) {
super(receiverCheck);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
throw Errors.createReferenceErrorNotDefined(root.getContext(), root.getKey(), this);
}
}
public static final class ArrayBufferViewNonIntegerIndexGetNode extends LinkedPropertyGetNode {
public ArrayBufferViewNonIntegerIndexGetNode(ReceiverCheckNode receiverCheck) {
super(receiverCheck);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
if (JSArrayBufferView.hasDetachedBuffer((DynamicObject) thisObj)) {
throw Errors.createTypeErrorDetachedBuffer();
} else {
return Undefined.instance;
}
}
}
public static final class CheckNoSuchPropertyNode extends LinkedPropertyGetNode {
private final JSContext context;
@Child private PropertyGetNode getNoSuchPropertyNode;
@Child private PropertyGetNode getNoSuchMethodNode;
@Child private JSHasPropertyNode hasPropertyNode;
@Child private JSFunctionCallNode callNoSuchNode;
public CheckNoSuchPropertyNode(Object key, ReceiverCheckNode receiverCheck, JSContext context) {
super(receiverCheck);
this.context = context;
assert !(key instanceof Symbol);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
if (JSRuntime.isObject(thisObj) && !JSAdapter.isJSAdapter(thisObj) && !JSProxy.isJSProxy(thisObj)) {
if (!context.getNoSuchMethodUnusedAssumption().isValid() && root.isMethod() && getHasProperty().executeBoolean(thisObj, JSObject.NO_SUCH_METHOD_NAME)) {
Object function = getNoSuchMethod().getValue(thisObj);
if (function != Undefined.instance) {
if (JSFunction.isJSFunction(function)) {
return callNoSuchHandler((DynamicObject) thisObj, (DynamicObject) function, root, false);
} else {
return getFallback(defaultValue, root);
}
}
}
if (!context.getNoSuchPropertyUnusedAssumption().isValid()) {
Object function = getNoSuchProperty().getValue(thisObj);
if (JSFunction.isJSFunction(function)) {
return callNoSuchHandler((DynamicObject) thisObj, (DynamicObject) function, root, true);
}
}
}
return getFallback(defaultValue, root);
}
private Object callNoSuchHandler(DynamicObject thisObj, DynamicObject function, PropertyGetNode root, boolean noSuchProperty) {
Object thisObject = root.isGlobal() ? Undefined.instance : thisObj;
if (noSuchProperty) {
return getCallNoSuch().executeCall(JSArguments.createOneArg(thisObject, function, root.getKey()));
} else {
return new JSNoSuchMethodAdapter(function, root.getKey(), thisObject);
}
}
private Object getFallback(Object defaultValue, PropertyGetNode root) {
if (root.isGlobal()) {
throw Errors.createReferenceErrorNotDefined(root.getContext(), root.getKey(), this);
} else {
return defaultValue;
}
}
public PropertyGetNode getNoSuchProperty() {
if (getNoSuchPropertyNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getNoSuchPropertyNode = insert(PropertyGetNode.create(JSObject.NO_SUCH_PROPERTY_NAME, context));
}
return getNoSuchPropertyNode;
}
public PropertyGetNode getNoSuchMethod() {
if (getNoSuchMethodNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getNoSuchMethodNode = insert(PropertyGetNode.create(JSObject.NO_SUCH_METHOD_NAME, context));
}
return getNoSuchMethodNode;
}
public JSHasPropertyNode getHasProperty() {
if (hasPropertyNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
hasPropertyNode = insert(JSHasPropertyNode.create());
}
return hasPropertyNode;
}
public JSFunctionCallNode getCallNoSuch() {
if (callNoSuchNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
callNoSuchNode = insert(JSFunctionCallNode.createCall());
}
return callNoSuchNode;
}
}
public static final class TypeErrorPropertyGetNode extends LinkedPropertyGetNode {
public TypeErrorPropertyGetNode(ReceiverCheckNode receiverCheck) {
super(receiverCheck);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
assert (thisObj == Undefined.instance || thisObj == Null.instance || thisObj == null) : thisObj;
throw Errors.createTypeErrorCannotGetProperty(root.getContext(), root.getKey(), thisObj, root.isMethod(), this);
}
}
public static final class JavaPackagePropertyGetNode extends LinkedPropertyGetNode {
public JavaPackagePropertyGetNode(ReceiverCheckNode receiverCheck) {
super(receiverCheck);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
Object key = root.getKey();
if (key instanceof String) {
return JavaPackage.getJavaClassOrConstructorOrSubPackage(root.getContext(), (DynamicObject) thisObj, (String) key);
} else {
return Undefined.instance;
}
}
}
public static class JavaStringMethodGetNode extends LinkedPropertyGetNode {
@Child private InteropLibrary interop;
public JavaStringMethodGetNode(ReceiverCheckNode receiverCheck) {
super(receiverCheck);
this.interop = InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
String thisStr = (String) thisObj;
if (root.getKey() instanceof String) {
Object boxedString = root.getContext().getRealm().getEnv().asBoxedGuestValue(thisStr);
try {
return interop.readMember(boxedString, (String) root.getKey());
} catch (UnknownIdentifierException | UnsupportedMessageException e) {
}
}
return Undefined.instance;
}
}
public static final class JSProxyDispatcherPropertyGetNode extends LinkedPropertyGetNode {
@Child private JSProxyPropertyGetNode proxyGet;
@SuppressWarnings("unused")
public JSProxyDispatcherPropertyGetNode(JSContext context, Object key, ReceiverCheckNode receiverCheck, boolean isMethod) {
super(receiverCheck);
this.proxyGet = JSProxyPropertyGetNode.create(context);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return proxyGet.executeWithReceiver(receiverCheck.getStore(thisObj), receiver, root.getKey());
}
}
public static final class JSProxyDispatcherRequiredPropertyGetNode extends LinkedPropertyGetNode {
@Child private JSProxyPropertyGetNode proxyGet;
@Child private JSProxyHasPropertyNode proxyHas;
@SuppressWarnings("unused")
public JSProxyDispatcherRequiredPropertyGetNode(JSContext context, Object key, ReceiverCheckNode receiverCheck, boolean isMethod) {
super(receiverCheck);
this.proxyGet = JSProxyPropertyGetNode.create(context);
this.proxyHas = JSProxyHasPropertyNode.create(context);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
Object key = root.getKey();
DynamicObject proxy = receiverCheck.getStore(thisObj);
if (proxyHas.executeWithTargetAndKeyBoolean(proxy, key)) {
return proxyGet.executeWithReceiver(proxy, receiver, key);
} else {
throw Errors.createReferenceErrorNotDefined(root.getContext(), key, this);
}
}
}
public static final class JSAdapterPropertyGetNode extends LinkedPropertyGetNode {
public JSAdapterPropertyGetNode(ReceiverCheckNode receiverCheck) {
super(receiverCheck);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
Object key = root.getKey();
DynamicObject obj = (DynamicObject) thisObj;
Object result = root.isMethod() ? JSAdapter.INSTANCE.getMethodHelper(obj, obj, key, root) : JSAdapter.INSTANCE.getHelper(obj, obj, key, root);
return JSRuntime.nullToUndefined(result);
}
}
public static final class UnspecializedPropertyGetNode extends LinkedPropertyGetNode {
public UnspecializedPropertyGetNode(ReceiverCheckNode receiverCheck) {
super(receiverCheck);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return JSObject.get((DynamicObject) thisObj, root.getKey());
}
}
public static final class ForeignPropertyGetNode extends LinkedPropertyGetNode {
@Child private ImportValueNode importValueNode;
@Child private JSToObjectNode toObjectNode;
@Child private ForeignObjectPrototypeNode foreignObjectPrototypeNode;
@Child private PropertyGetNode getFromJSObjectNode;
private final boolean isLength;
private final boolean isMethod;
private final boolean isGlobal;
private final JSContext context;
@Child private InteropLibrary interop;
@Child private InteropLibrary getterInterop;
private final BranchProfile errorBranch = BranchProfile.create();
@CompilationFinal private boolean optimistic = true;
public ForeignPropertyGetNode(Object key, boolean isMethod, boolean isGlobal, JSContext context) {
super(new ForeignLanguageCheckNode());
this.context = context;
this.importValueNode = ImportValueNode.create();
this.toObjectNode = JSToObjectNode.createToObject(context);
this.isLength = key.equals(JSAbstractArray.LENGTH);
this.isMethod = isMethod;
this.isGlobal = isGlobal;
this.interop = InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit);
}
private Object foreignGet(Object thisObj, PropertyGetNode root) {
Object key = root.getKey();
if (interop.isNull(thisObj)) {
errorBranch.enter();
throw Errors.createTypeErrorCannotGetProperty(context, key, thisObj, isMethod, this);
}
Object object = toObjectNode.execute(thisObj);
if (thisObj != object) {
assert JSObject.isJSObject(object);
return getFromJSObject(object, key);
}
if (!(key instanceof String)) {
return Undefined.instance;
}
String stringKey = (String) key;
if (context.isOptionNashornCompatibilityMode()) {
Object result = tryGetters(thisObj, root);
if (result != null) {
return importValueNode.executeWithTarget(result);
}
}
Object foreignResult;
if (optimistic) {
try {
foreignResult = interop.readMember(thisObj, stringKey);
} catch (UnknownIdentifierException | UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
optimistic = false;
foreignResult = maybeGetFromPrototype(thisObj, key);
}
} else {
if (interop.isMemberReadable(thisObj, stringKey)) {
try {
foreignResult = interop.readMember(thisObj, stringKey);
} catch (UnknownIdentifierException | UnsupportedMessageException e) {
return Undefined.instance;
}
} else {
foreignResult = maybeGetFromPrototype(thisObj, key);
}
}
return importValueNode.executeWithTarget(foreignResult);
}
private Object maybeGetFromPrototype(Object thisObj, Object key) {
if (context.getContextOptions().hasForeignObjectPrototype()) {
if (foreignObjectPrototypeNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
foreignObjectPrototypeNode = insert(ForeignObjectPrototypeNode.create());
}
DynamicObject prototype = foreignObjectPrototypeNode.executeDynamicObject(thisObj);
return getFromJSObject(prototype, key);
}
return Undefined.instance;
}
private Object getFromJSObject(Object object, Object key) {
assert JSObject.isJSObject(object);
if (getFromJSObjectNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getFromJSObjectNode = insert(PropertyGetNode.create(key, context));
}
assert key.equals(getFromJSObjectNode.getKey());
return getFromJSObjectNode.getValue(object);
}
private Object tryGetters(Object thisObj, PropertyGetNode root) {
assert context.isOptionNashornCompatibilityMode();
TruffleLanguage.Env env = context.getRealm().getEnv();
if (env.isHostObject(thisObj)) {
Object result = tryInvokeGetter(thisObj, "get", root);
if (result != null) {
return result;
}
result = tryInvokeGetter(thisObj, "is", root);
if (result != null) {
return result;
}
}
return null;
}
private Object tryInvokeGetter(Object thisObj, String prefix, PropertyGetNode root) {
assert context.isOptionNashornCompatibilityMode();
String getterKey = root.getAccessorKey(prefix);
if (getterKey == null) {
return null;
}
if (getterInterop == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getterInterop = insert(InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit));
}
if (!getterInterop.isMemberInvocable(thisObj, getterKey)) {
return null;
}
try {
return getterInterop.invokeMember(thisObj, getterKey, JSArguments.EMPTY_ARGUMENTS_ARRAY);
} catch (UnknownIdentifierException | UnsupportedMessageException | UnsupportedTypeException | ArityException e) {
return null;
}
}
private Object getSize(Object thisObj) {
try {
return JSRuntime.longToIntOrDouble(interop.getArraySize(thisObj));
} catch (UnsupportedMessageException e) {
errorBranch.enter();
throw Errors.createTypeErrorInteropException(thisObj, e, "getArraySize", this);
}
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
if (isMethod && !isGlobal) {
return thisObj;
}
if (isLength && interop.hasArrayElements(thisObj)) {
return getSize(thisObj);
}
return foreignGet(thisObj, root);
}
}
@NodeInfo(cost = NodeCost.MEGAMORPHIC)
public static class GenericPropertyGetNode extends GetCacheNode {
@Child private JSToObjectNode toObjectNode;
@Child private ForeignPropertyGetNode foreignGetNode;
@Child private GetPropertyFromJSObjectNode getFromJSObjectNode;
private final ConditionProfile isJSObject = ConditionProfile.createBinaryProfile();
private final ConditionProfile isForeignObject = ConditionProfile.createBinaryProfile();
private final BranchProfile notAJSObjectBranch = BranchProfile.create();
private final BranchProfile fallbackBranch = BranchProfile.create();
public GenericPropertyGetNode() {
super(null);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
if (isJSObject.profile(JSDynamicObject.isJSDynamicObject(thisObj))) {
return getPropertyFromJSObject((DynamicObject) thisObj, receiver, defaultValue, root);
} else {
if (isForeignObject.profile(JSGuards.isForeignObject(thisObj))) {
if (foreignGetNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
foreignGetNode = insert(new ForeignPropertyGetNode(root.getKey(), root.isMethod(), root.isGlobal(), root.getContext()));
}
return foreignGetNode.getValue(thisObj, receiver, defaultValue, root, guard);
} else {
if (toObjectNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toObjectNode = insert(JSToObjectNode.createToObjectNoCheck(root.getContext()));
}
DynamicObject object = JSRuntime.expectJSObject(toObjectNode.execute(thisObj), notAJSObjectBranch);
return getPropertyFromJSObject(object, receiver, defaultValue, root);
}
}
}
private Object getPropertyFromJSObject(DynamicObject thisObj, Object receiver, Object defaultValue, PropertyGetNode root) {
if (root.getKey() instanceof HiddenKey) {
Object result = JSDynamicObject.getOrNull(thisObj, root.getKey());
if (result != null) {
return result;
} else {
fallbackBranch.enter();
return getFallback(thisObj, root);
}
} else {
if (getFromJSObjectNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getFromJSObjectNode = insert(GetPropertyFromJSObjectNode.create(root));
}
return getFromJSObjectNode.executeWithJSObject(thisObj, receiver, defaultValue, root);
}
}
protected Object getFallback(@SuppressWarnings("unused") DynamicObject thisObj, PropertyGetNode root) {
if (root.isRequired()) {
throw Errors.createReferenceErrorNotDefined(root.getContext(), root.getKey(), this);
}
return Undefined.instance;
}
}
abstract static class GetPropertyFromJSObjectNode extends JavaScriptBaseNode {
private final Object key;
private final boolean isRequired;
private final JSContext context;
private final BranchProfile nullOrUndefinedBranch = BranchProfile.create();
private final BranchProfile fallbackBranch = BranchProfile.create();
GetPropertyFromJSObjectNode(PropertyGetNode root) {
this.key = root.getKey();
this.isRequired = root.isRequired();
this.context = root.getContext();
}
public abstract Object executeWithJSObject(DynamicObject thisObj, Object receiver, Object defaultValue, PropertyGetNode root);
public static GetPropertyFromJSObjectNode create(PropertyGetNode root) {
return GetPropertyFromJSObjectNodeGen.create(root);
}
@Specialization(limit = "2", guards = {"!isGlobal()", "cachedClass == getJSClass(object)"})
protected Object doJSObjectCached(DynamicObject object, Object receiver, Object defaultValue, PropertyGetNode root,
@Cached("getJSClass(object)") JSClass cachedClass) {
return getPropertyFromJSObjectIntl(cachedClass, object, receiver, defaultValue, root);
}
@Specialization(replaces = "doJSObjectCached", guards = {"!isGlobal()"})
protected Object doJSObjectDirect(DynamicObject object, Object receiver, Object defaultValue, PropertyGetNode root) {
return getPropertyFromJSObjectIntl(JSObject.getJSClass(object), object, receiver, defaultValue, root);
}
@Specialization(guards = {"isGlobal()"})
protected Object doRequired(DynamicObject object, Object receiver, Object defaultValue, PropertyGetNode root,
@Cached("create()") JSHasPropertyNode hasPropertyNode,
@Cached("create()") JSClassProfile classProfile) {
if (hasPropertyNode.executeBoolean(object, key)) {
return getPropertyFromJSObjectIntl(classProfile.profile(JSObject.getJSClass(object)), object, receiver, defaultValue, root);
} else {
fallbackBranch.enter();
return getNoSuchProperty(object, defaultValue, root);
}
}
protected JSClass getJSClass(DynamicObject object) {
return JSObject.getJSClass(object);
}
private Object getPropertyFromJSObjectIntl(JSClass jsclass, DynamicObject object, Object receiver, Object defaultValue, PropertyGetNode root) {
final boolean isMethod = root.isMethod();
assert !(key instanceof HiddenKey);
if (jsclass == Null.NULL_CLASS) {
nullOrUndefinedBranch.enter();
throw Errors.createTypeErrorCannotGetProperty(root.getContext(), key, object, isMethod, this);
}
Object value = isMethod ? jsclass.getMethodHelper(object, receiver, key, this) : jsclass.getHelper(object, receiver, key, this);
if (value != null) {
return value;
}
fallbackBranch.enter();
return getNoSuchProperty(object, defaultValue, root);
}
protected Object getNoSuchProperty(DynamicObject thisObj, Object defaultValue, PropertyGetNode root) {
if (root.getContext().isOptionNashornCompatibilityMode() &&
(!root.getContext().getNoSuchPropertyUnusedAssumption().isValid() || (root.isMethod() && !root.getContext().getNoSuchMethodUnusedAssumption().isValid()))) {
return getNoSuchPropertySlow(thisObj, defaultValue, root.isMethod());
}
return getFallback(defaultValue);
}
@TruffleBoundary
private Object getNoSuchPropertySlow(DynamicObject thisObj, Object defaultValue, boolean isMethod) {
if (!(key instanceof Symbol) && JSRuntime.isObject(thisObj) && !JSAdapter.isJSAdapter(thisObj) && !JSProxy.isJSProxy(thisObj)) {
if (isMethod) {
Object function = JSObject.get(thisObj, JSObject.NO_SUCH_METHOD_NAME);
if (function != Undefined.instance) {
if (JSFunction.isJSFunction(function)) {
return callNoSuchHandler(thisObj, (DynamicObject) function, false);
} else {
return getFallback(defaultValue);
}
}
}
Object function = JSObject.get(thisObj, JSObject.NO_SUCH_PROPERTY_NAME);
if (JSFunction.isJSFunction(function)) {
return callNoSuchHandler(thisObj, (DynamicObject) function, true);
}
}
return getFallback(defaultValue);
}
private Object callNoSuchHandler(DynamicObject thisObj, DynamicObject function, boolean noSuchProperty) {
Object thisObject = isGlobal() ? Undefined.instance : thisObj;
if (noSuchProperty) {
return JSFunction.call(function, thisObject, new Object[]{key});
} else {
return new JSNoSuchMethodAdapter(function, key, thisObject);
}
}
protected boolean isGlobal() {
return isRequired;
}
protected Object getFallback(Object defaultValue) {
if (isRequired) {
throw Errors.createReferenceErrorNotDefined(context, key, this);
}
return defaultValue;
}
}
public static final class ArrayLengthPropertyGetNode extends LinkedPropertyGetNode {
@Child private ArrayLengthReadNode arrayLengthRead;
@CompilationFinal private boolean longLength;
public ArrayLengthPropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isData(property);
assert isArrayLengthProperty(property);
this.arrayLengthRead = ArrayLengthReadNode.create();
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
if (!longLength) {
try {
return arrayLengthRead.executeInt(receiverCheck.getStore(thisObj));
} catch (UnexpectedResultException e) {
longLength = true;
return e.getResult();
}
} else {
return arrayLengthRead.executeDouble(receiverCheck.getStore(thisObj));
}
}
@Override
protected int getValueInt(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) throws UnexpectedResultException {
assert assertIsArray(thisObj);
return arrayLengthRead.executeInt(receiverCheck.getStore(thisObj));
}
@Override
protected double getValueDouble(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
assert assertIsArray(thisObj);
return arrayLengthRead.executeDouble(receiverCheck.getStore(thisObj));
}
private boolean assertIsArray(Object thisObj) {
DynamicObject store = receiverCheck.getStore(thisObj);
assert JSArray.isJSArray(store);
return true;
}
}
public static final class FunctionLengthPropertyGetNode extends LinkedPropertyGetNode {
private final BranchProfile isBoundBranch = BranchProfile.create();
private final JSFunction.FunctionLengthPropertyProxy property;
public FunctionLengthPropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isData(property);
assert isFunctionLengthProperty(property);
this.property = (JSFunction.FunctionLengthPropertyProxy) JSProperty.getConstantProxy(property);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return getValueInt(thisObj, receiver, root, guard);
}
@Override
protected int getValueInt(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
return property.getProfiled(receiverCheck.getStore(thisObj), isBoundBranch);
}
@Override
protected double getValueDouble(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
return getValueInt(thisObj, receiver, root, guard);
}
}
public static final class FunctionNamePropertyGetNode extends LinkedPropertyGetNode {
private final BranchProfile isBoundBranch = BranchProfile.create();
private final JSFunction.FunctionNamePropertyProxy property;
public FunctionNamePropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isData(property);
assert isFunctionNameProperty(property);
this.property = (JSFunction.FunctionNamePropertyProxy) JSProperty.getConstantProxy(property);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return property.getProfiled(receiverCheck.getStore(thisObj), isBoundBranch);
}
}
public static final class ClassPrototypePropertyGetNode extends LinkedPropertyGetNode {
@CompilationFinal private DynamicObject constantFunction;
@Child private CreateMethodPropertyNode setConstructor;
@CompilationFinal private int kind;
private final JSContext context;
private final ConditionProfile prototypeInitializedProfile = ConditionProfile.createCountingProfile();
private static final int UNKNOWN = 0;
private static final int CONSTRUCTOR = 1;
private static final int GENERATOR = 2;
private static final int ASYNC_GENERATOR = 3;
private static final DynamicObject UNKNOWN_FUN = Undefined.instance;
private static final DynamicObject GENERIC_FUN = null;
public ClassPrototypePropertyGetNode(Property property, ReceiverCheckNode receiverCheck, JSContext context) {
super(receiverCheck);
assert JSProperty.isData(property) && isClassPrototypeProperty(property);
this.context = context;
this.constantFunction = context.isMultiContext() ? GENERIC_FUN : UNKNOWN_FUN;
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
DynamicObject functionObj = receiverCheck.getStore(thisObj);
assert JSFunction.isJSFunction(functionObj);
DynamicObject constantFun = constantFunction;
if (constantFun == UNKNOWN_FUN) {
CompilerDirectives.transferToInterpreterAndInvalidate();
constantFunction = functionObj;
return JSFunction.getClassPrototype(functionObj);
} else if (constantFun != GENERIC_FUN) {
if (constantFun == functionObj) {
return JSFunction.getClassPrototypeInitialized(constantFun);
} else {
CompilerDirectives.transferToInterpreterAndInvalidate();
constantFunction = GENERIC_FUN;
}
}
if (prototypeInitializedProfile.profile(JSFunction.isClassPrototypeInitialized(functionObj))) {
return JSFunction.getClassPrototypeInitialized(functionObj);
} else {
return getPrototypeNotInitialized(functionObj);
}
}
private Object getPrototypeNotInitialized(DynamicObject functionObj) {
if (kind == UNKNOWN) {
CompilerDirectives.transferToInterpreterAndInvalidate();
JSFunctionData functionData = JSFunction.getFunctionData(functionObj);
if (functionData.isAsyncGenerator()) {
kind = ASYNC_GENERATOR;
} else if (functionData.isGenerator()) {
kind = GENERATOR;
} else {
kind = CONSTRUCTOR;
}
}
JSRealm realm = JSFunction.getRealm(functionObj, context);
DynamicObject prototype;
if (kind == CONSTRUCTOR) {
assert JSFunction.getFunctionData(functionObj).isConstructor();
if (setConstructor == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
setConstructor = insert(CreateMethodPropertyNode.create(context, JSObject.CONSTRUCTOR));
}
prototype = JSOrdinary.create(context, realm);
setConstructor.executeVoid(prototype, functionObj);
} else if (kind == GENERATOR) {
assert JSFunction.getFunctionData(functionObj).isGenerator();
prototype = JSOrdinary.createWithRealm(context, context.getGeneratorObjectFactory(), realm);
} else {
assert kind == ASYNC_GENERATOR;
assert JSFunction.getFunctionData(functionObj).isAsyncGenerator();
prototype = JSOrdinary.createWithRealm(context, context.getAsyncGeneratorObjectFactory(), realm);
}
JSFunction.setClassPrototype(functionObj, prototype);
return prototype;
}
}
public static final class StringLengthPropertyGetNode extends LinkedPropertyGetNode {
public StringLengthPropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isData(property) && isStringLengthProperty(property);
assert receiverCheck instanceof InstanceofCheckNode;
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return getValueInt(thisObj, receiver, root, guard);
}
@Override
protected int getValueInt(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
CharSequence charSequence = (CharSequence) ((InstanceofCheckNode) receiverCheck).type.cast(thisObj);
return JSRuntime.length(charSequence);
}
@Override
protected double getValueDouble(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
return getValueInt(thisObj, receiver, root, guard);
}
}
public static final class StringObjectLengthPropertyGetNode extends LinkedPropertyGetNode {
private final ValueProfile charSequenceClassProfile = ValueProfile.createClassProfile();
public StringObjectLengthPropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isData(property) && isStringLengthProperty(property);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return getValueInt(thisObj, receiver, root, guard);
}
@Override
protected int getValueInt(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
CharSequence charSequence = JSString.getCharSequence(receiverCheck.getStore(thisObj));
return JSRuntime.length(charSequenceClassProfile.profile(charSequence));
}
@Override
protected double getValueDouble(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
return getValueInt(thisObj, receiver, root, guard);
}
}
public static final class LazyRegexResultIndexPropertyGetNode extends LinkedPropertyGetNode {
@Child private TRegexUtil.InvokeGetGroupBoundariesMethodNode readStartNode = TRegexUtil.InvokeGetGroupBoundariesMethodNode.create();
@Child private DynamicObjectLibrary readLazyRegexResult = JSObjectUtil.createDispatched(JSAbstractArray.LAZY_REGEX_RESULT_ID);
public LazyRegexResultIndexPropertyGetNode(Property property, ReceiverCheckNode receiverCheck) {
super(receiverCheck);
assert JSProperty.isData(property);
assert isLazyRegexResultIndexProperty(property);
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
return getValueInt(thisObj, receiver, root, guard);
}
@Override
protected int getValueInt(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
DynamicObject store = receiverCheck.getStore(thisObj);
Object lazyRegexResult = readLazyRegexResult.getOrDefault(store, JSAbstractArray.LAZY_REGEX_RESULT_ID, null);
assert lazyRegexResult != null;
return readStartNode.execute(lazyRegexResult, TRegexUtil.Props.RegexResult.GET_START, 0);
}
@Override
protected double getValueDouble(Object thisObj, Object receiver, PropertyGetNode root, boolean guard) {
return getValueInt(thisObj, receiver, root, guard);
}
}
public static final class LazyNamedCaptureGroupPropertyGetNode extends LinkedPropertyGetNode {
private final int groupIndex;
@Child private TRegexMaterializeResultNode materializeNode = TRegexMaterializeResultNode.create();
@Child private TRegexResultAccessor resultAccessor = TRegexResultAccessor.create();
private final ConditionProfile isIndicesObject = ConditionProfile.createBinaryProfile();
public LazyNamedCaptureGroupPropertyGetNode(Property property, ReceiverCheckNode receiverCheck, int groupIndex) {
super(receiverCheck);
assert isLazyNamedCaptureGroupProperty(property);
this.groupIndex = groupIndex;
}
@Override
protected Object getValue(Object thisObj, Object receiver, Object defaultValue, PropertyGetNode root, boolean guard) {
DynamicObject store = receiverCheck.getStore(thisObj);
JSRegExpGroupsObject groups = (JSRegExpGroupsObject) store;
Object regexResult = groups.getRegexResult();
if (isIndicesObject.profile(groups.isIndices())) {
return LazyRegexResultIndicesArray.getIntIndicesArray(root.getContext(), resultAccessor, regexResult, groupIndex);
} else {
String input = groups.getInputString();
return materializeNode.materializeGroup(regexResult, groupIndex, input);
}
}
}
@Override
protected GetCacheNode createCachedPropertyNode(Property property, Object thisObj, int depth, Object value, GetCacheNode currentHead) {
assert !isOwnProperty() || depth == 0;
if (!(JSDynamicObject.isJSDynamicObject(thisObj))) {
return createCachedPropertyNodeNotJSObject(property, thisObj, depth);
}
JSDynamicObject thisJSObj = (JSDynamicObject) thisObj;
Shape cacheShape = thisJSObj.getShape();
if ((JSProperty.isData(property) && !JSProperty.isProxy(property) || JSProperty.isAccessor(property)) &&
(property.getLocation().isFinal() || property.getLocation().isAssumedFinal())) {
boolean isConstantObjectFinal = isPropertyAssumptionCheckEnabled();
for (GetCacheNode cur = currentHead; cur != null; cur = cur.next) {
if (isFinalSpecialization(cur)) {
if (cur.receiverCheck instanceof ConstantObjectReceiverCheck) {
((ConstantObjectReceiverCheck) cur.receiverCheck).clearExpectedObject();
setPropertyAssumptionCheckEnabled(false);
return null;
}
assert !(cur.receiverCheck instanceof ConstantObjectReceiverCheck) || ((ConstantObjectReceiverCheck) cur.receiverCheck).getExpectedObject() == thisObj;
}
}
if (isConstantObjectFinal && depth > 0 && !JSShape.getPropertyAssumption(cacheShape, key).isValid()) {
isConstantObjectFinal = false;
}
if (JSProperty.isData(property) && !JSProperty.isProxy(property)) {
if (isEligibleForFinalSpecialization(cacheShape, thisJSObj, depth, isConstantObjectFinal)) {
return createFinalSpecialization(property, cacheShape, thisJSObj, depth, isConstantObjectFinal);
}
} else if (JSProperty.isAccessor(property)) {
if (isEligibleForFinalSpecialization(cacheShape, thisJSObj, depth, isConstantObjectFinal)) {
return createFinalAccessorSpecialization(property, cacheShape, thisJSObj, depth, isConstantObjectFinal);
}
}
}
AbstractShapeCheckNode shapeCheck = createShapeCheckNode(cacheShape, thisJSObj, depth, false, false);
if (JSProperty.isData(property)) {
return createSpecializationFromDataProperty(property, shapeCheck, context);
} else {
assert JSProperty.isAccessor(property);
return new AccessorPropertyGetNode(property, shapeCheck);
}
}
private static boolean isFinalSpecialization(GetCacheNode existingNode) {
return existingNode instanceof AbstractFinalDataPropertyGetNode || existingNode instanceof FinalAccessorPropertyGetNode;
}
private boolean isEligibleForFinalSpecialization(Shape cacheShape, DynamicObject thisObj, int depth, boolean isConstantObjectFinal) {
if (depth == 0) {
return (JSConfig.SkipFinalShapeCheck && isPropertyAssumptionCheckEnabled() && JSShape.getPropertyAssumption(cacheShape, key).isValid());
} else {
return (prototypesInShape(thisObj, depth) && propertyAssumptionsValid(thisObj, depth, isConstantObjectFinal));
}
}
private GetCacheNode createCachedPropertyNodeNotJSObject(Property property, Object thisObj, int depth) {
final ReceiverCheckNode receiverCheck;
if (depth == 0) {
if (isMethod() && thisObj instanceof String && context.isOptionNashornCompatibilityMode()) {
GetCacheNode javaPropertyNode = createJavaPropertyNodeMaybe(thisObj, depth);
if (javaPropertyNode != null) {
return javaPropertyNode;
}
}
receiverCheck = new InstanceofCheckNode(thisObj.getClass(), context);
if (isStringLengthProperty(property)) {
return new StringLengthPropertyGetNode(property, receiverCheck);
}
} else {
receiverCheck = createPrimitiveReceiverCheck(thisObj, depth);
}
if (JSProperty.isData(property)) {
return createSpecializationFromDataProperty(property, receiverCheck, context);
} else {
assert JSProperty.isAccessor(property);
return new AccessorPropertyGetNode(property, receiverCheck);
}
}
private static GetCacheNode createSpecializationFromDataProperty(Property property, ReceiverCheckNode receiverCheck, JSContext context) {
Property dataProperty = property;
if (property.getLocation() instanceof IntLocation) {
return new IntPropertyGetNode(dataProperty, receiverCheck);
} else if (property.getLocation() instanceof DoubleLocation) {
return new DoublePropertyGetNode(dataProperty, receiverCheck);
} else if (property.getLocation() instanceof BooleanLocation) {
return new BooleanPropertyGetNode(dataProperty, receiverCheck);
} else if (property.getLocation() instanceof LongLocation) {
return new LongPropertyGetNode(dataProperty, receiverCheck);
} else {
if (isArrayLengthProperty(property)) {
return new ArrayLengthPropertyGetNode(dataProperty, receiverCheck);
} else if (isFunctionLengthProperty(property)) {
return new FunctionLengthPropertyGetNode(dataProperty, receiverCheck);
} else if (isFunctionNameProperty(property)) {
return new FunctionNamePropertyGetNode(dataProperty, receiverCheck);
} else if (isClassPrototypeProperty(property)) {
return new ClassPrototypePropertyGetNode(dataProperty, receiverCheck, context);
} else if (isStringLengthProperty(property)) {
return new StringObjectLengthPropertyGetNode(dataProperty, receiverCheck);
} else if (isLazyRegexResultIndexProperty(property)) {
return new LazyRegexResultIndexPropertyGetNode(dataProperty, receiverCheck);
} else if (isLazyNamedCaptureGroupProperty(property)) {
int groupIndex = ((JSRegExp.LazyNamedCaptureGroupProperty) JSProperty.getConstantProxy(property)).getGroupIndex();
return new LazyNamedCaptureGroupPropertyGetNode(dataProperty, receiverCheck, groupIndex);
} else {
return new ObjectPropertyGetNode(dataProperty, receiverCheck);
}
}
}
private GetCacheNode createFinalSpecialization(Property property, Shape cacheShape, JSDynamicObject thisObj, int depth, boolean isConstantObjectFinal) {
AbstractShapeCheckNode finalShapeCheckNode = createShapeCheckNode(cacheShape, thisObj, depth, isConstantObjectFinal, false);
finalShapeCheckNode.adoptChildren();
DynamicObject store = finalShapeCheckNode.getStore(thisObj);
return createFinalSpecializationImpl(property, finalShapeCheckNode, store);
}
private static GetCacheNode createFinalSpecializationImpl(Property property, AbstractShapeCheckNode shapeCheckNode, DynamicObject store) {
if (property.getLocation() instanceof IntLocation) {
return new FinalIntPropertyGetNode(property, shapeCheckNode, ((IntLocation) property.getLocation()).getInt(store, false));
} else if (property.getLocation() instanceof DoubleLocation) {
return new FinalDoublePropertyGetNode(property, shapeCheckNode, ((DoubleLocation) property.getLocation()).getDouble(store, false));
} else if (property.getLocation() instanceof BooleanLocation) {
return new FinalBooleanPropertyGetNode(property, shapeCheckNode, ((BooleanLocation) property.getLocation()).getBoolean(store, false));
} else if (property.getLocation() instanceof LongLocation) {
return new FinalLongPropertyGetNode(property, shapeCheckNode, ((LongLocation) property.getLocation()).getLong(store, false));
} else {
assert property.getLocation() instanceof ObjectLocation;
return new FinalObjectPropertyGetNode(property, shapeCheckNode, property.get(store, false));
}
}
private GetCacheNode createFinalAccessorSpecialization(Property property, Shape cacheShape, JSDynamicObject thisObj, int depth, boolean isConstantObjectFinal) {
AbstractShapeCheckNode finalShapeCheckNode = createShapeCheckNode(cacheShape, thisObj, depth, isConstantObjectFinal, false);
finalShapeCheckNode.adoptChildren();
DynamicObject store = finalShapeCheckNode.getStore(thisObj);
Accessor accessor = (Accessor) property.get(store, null);
return new FinalAccessorPropertyGetNode(property, finalShapeCheckNode, accessor);
}
@Override
protected GetCacheNode createJavaPropertyNodeMaybe(Object thisObj, int depth) {
if (JavaPackage.isJavaPackage(thisObj)) {
return new JavaPackagePropertyGetNode(new JSClassCheckNode(JSObject.getJSClass((DynamicObject) thisObj)));
} else if (JavaImporter.isJavaImporter(thisObj)) {
return new UnspecializedPropertyGetNode(new JSClassCheckNode(JSObject.getJSClass((DynamicObject) thisObj)));
}
if (JSConfig.SubstrateVM) {
return null;
}
if (context.isOptionNashornCompatibilityMode() && context.getRealm().isJavaInteropEnabled()) {
if (thisObj instanceof String && isMethod()) {
return new JavaStringMethodGetNode(createPrimitiveReceiverCheck(thisObj, depth));
}
}
return null;
}
@Override
protected GetCacheNode createUndefinedPropertyNode(Object thisObj, Object store, int depth, Object value) {
GetCacheNode javaPropertyNode = createJavaPropertyNodeMaybe(thisObj, depth);
if (javaPropertyNode != null) {
return javaPropertyNode;
}
if (JSDynamicObject.isJSDynamicObject(thisObj)) {
JSDynamicObject jsobject = (JSDynamicObject) thisObj;
Shape cacheShape = jsobject.getShape();
AbstractShapeCheckNode shapeCheck = createShapeCheckNode(cacheShape, jsobject, depth, false, false);
ReceiverCheckNode receiverCheck = (depth == 0) ? new JSClassCheckNode(JSObject.getJSClass(jsobject)) : shapeCheck;
if (JSAdapter.isJSAdapter(store)) {
return new JSAdapterPropertyGetNode(receiverCheck);
} else if (JSProxy.isJSProxy(store) && JSRuntime.isPropertyKey(key)) {
return createJSProxyCache(receiverCheck);
} else if (JSModuleNamespace.isJSModuleNamespace(store)) {
return new UnspecializedPropertyGetNode(receiverCheck);
} else if (JSArrayBufferView.isJSArrayBufferView(store) && isNonIntegerIndex(key)) {
return new ArrayBufferViewNonIntegerIndexGetNode(shapeCheck);
} else {
return createUndefinedJSObjectPropertyNode(jsobject, depth);
}
} else if (JSProxy.isJSProxy(store)) {
ReceiverCheckNode receiverCheck = createPrimitiveReceiverCheck(thisObj, depth);
return new JSProxyDispatcherPropertyGetNode(context, key, receiverCheck, isMethod());
} else {
if (thisObj == null) {
return new TypeErrorPropertyGetNode(new NullCheckNode());
} else {
ReceiverCheckNode receiverCheck = createPrimitiveReceiverCheck(thisObj, depth);
return createUndefinedOrErrorPropertyNode(receiverCheck);
}
}
}
protected GetCacheNode createJSProxyCache(ReceiverCheckNode receiverCheck) {
if (isRequired()) {
return new JSProxyDispatcherRequiredPropertyGetNode(context, key, receiverCheck, isMethod());
} else {
return new JSProxyDispatcherPropertyGetNode(context, key, receiverCheck, isMethod());
}
}
private GetCacheNode createUndefinedJSObjectPropertyNode(JSDynamicObject jsobject, int depth) {
AbstractShapeCheckNode shapeCheck = createShapeCheckNode(jsobject.getShape(), jsobject, depth, false, false);
if (JSRuntime.isObject(jsobject)) {
if (context.isOptionNashornCompatibilityMode() && !(key instanceof Symbol)) {
if ((!context.getNoSuchMethodUnusedAssumption().isValid() && JSObject.hasProperty(jsobject, JSObject.NO_SUCH_METHOD_NAME)) ||
(!context.getNoSuchPropertyUnusedAssumption().isValid() && JSObject.hasProperty(jsobject, JSObject.NO_SUCH_PROPERTY_NAME))) {
return new CheckNoSuchPropertyNode(key, shapeCheck, context);
}
}
return createUndefinedOrErrorPropertyNode(shapeCheck);
} else {
return new TypeErrorPropertyGetNode(shapeCheck);
}
}
protected GetCacheNode createUndefinedOrErrorPropertyNode(ReceiverCheckNode receiverCheck) {
if (isRequired()) {
return new UndefinedPropertyErrorNode(receiverCheck);
} else {
return new UndefinedPropertyGetNode(receiverCheck);
}
}
@Override
protected GetCacheNode createGenericPropertyNode() {
return new GenericPropertyGetNode();
}
protected final boolean isRequired() {
return isGlobal();
}
@Override
protected final boolean isGlobal() {
return isGlobal;
}
@Override
protected final boolean isOwnProperty() {
return getOwnProperty;
}
protected boolean isMethod() {
return isMethod;
}
protected void setMethod() {
CompilerAsserts.neverPartOfCompilation();
this.isMethod = true;
}
@Override
protected boolean isPropertyAssumptionCheckEnabled() {
return propertyAssumptionCheckEnabled && getContext().isSingleRealm();
}
@Override
protected void setPropertyAssumptionCheckEnabled(boolean value) {
CompilerAsserts.neverPartOfCompilation();
this.propertyAssumptionCheckEnabled = value;
}
@Override
protected GetCacheNode createTruffleObjectPropertyNode() {
return new ForeignPropertyGetNode(key, isMethod(), isGlobal(), context);
}
}