package com.oracle.truffle.js.nodes.access;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.cast.ToArrayIndexNode;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.objects.JSObject;
import com.oracle.truffle.js.runtime.util.JSClassProfile;
@ImportStatic(JSRuntime.class)
abstract class CachedGetPropertyNode extends JavaScriptBaseNode {
static final int MAX_DEPTH = 2;
protected final JSContext context;
CachedGetPropertyNode(JSContext context) {
this.context = context;
}
public abstract Object execute(DynamicObject target, Object propertyKey, Object receiver, Object defaultValue);
static CachedGetPropertyNode create(JSContext context) {
return CachedGetPropertyNodeGen.create(context);
}
@SuppressWarnings("unused")
@Specialization(guards = {"cachedKey != null", "!isArrayIndex(cachedKey)", "propertyKeyEquals(cachedKey, key)"}, limit = "MAX_DEPTH")
Object doCachedKey(DynamicObject target, Object key, Object receiver, Object defaultValue,
@Cached("cachedPropertyKey(key)") Object cachedKey,
@Cached("create(cachedKey, context)") PropertyGetNode propertyNode) {
return propertyNode.getValueOrDefault(target, receiver, defaultValue);
}
@Specialization(guards = {"isArrayIndex(index)", "!isJSProxy(target)"})
Object doIntIndex(DynamicObject target, int index, Object receiver, Object defaultValue,
@Cached("create()") JSClassProfile jsclassProfile) {
return JSObject.getOrDefault(target, index, receiver, defaultValue, jsclassProfile, this);
}
@Specialization(guards = {"!isJSProxy(target)", "toArrayIndexNode.isResultArrayIndex(maybeIndex)"}, replaces = {"doIntIndex"})
Object doArrayIndex(DynamicObject target, @SuppressWarnings("unused") Object key, Object receiver, Object defaultValue,
@Cached("create()") RequireObjectCoercibleNode requireObjectCoercibleNode,
@Cached("createNoToPropertyKey()") @SuppressWarnings("unused") ToArrayIndexNode toArrayIndexNode,
@Bind("toArrayIndexNode.execute(key)") Object maybeIndex,
@Cached("create()") JSClassProfile jsclassProfile) {
requireObjectCoercibleNode.executeVoid(target);
long index = (long) maybeIndex;
return JSObject.getOrDefault(target, index, receiver, defaultValue, jsclassProfile, this);
}
@Specialization(guards = {"isJSProxy(target)"})
protected Object doProxy(DynamicObject target, Object index, Object receiver, @SuppressWarnings("unused") Object defaultValue,
@Cached("create(context)") JSProxyPropertyGetNode proxyGet) {
return proxyGet.executeWithReceiver(target, receiver, index);
}
@ReportPolymorphism.Megamorphic
@Specialization(replaces = {"doCachedKey", "doArrayIndex", "doProxy"})
Object doGeneric(DynamicObject target, Object key, Object receiver, Object defaultValue,
@Cached("create()") RequireObjectCoercibleNode requireObjectCoercibleNode,
@Cached("create()") ToArrayIndexNode toArrayIndexNode,
@Cached("createBinaryProfile()") ConditionProfile getType,
@Cached("create()") JSClassProfile jsclassProfile) {
requireObjectCoercibleNode.executeVoid(target);
Object arrayIndex = toArrayIndexNode.execute(key);
if (getType.profile(arrayIndex instanceof Long)) {
return JSObject.getOrDefault(target, (long) arrayIndex, receiver, defaultValue, jsclassProfile, this);
} else {
assert JSRuntime.isPropertyKey(arrayIndex);
return JSObject.getOrDefault(target, arrayIndex, receiver, defaultValue, jsclassProfile, this);
}
}
public static Object cachedPropertyKey(Object key) {
CompilerAsserts.neverPartOfCompilation();
if (JSRuntime.isPropertyKey(key)) {
return key;
} else if (JSRuntime.isLazyString(key)) {
return key.toString();
} else {
return null;
}
}
}