package com.oracle.truffle.js.nodes.access;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.NodeCost;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.cast.JSToBooleanNode;
import com.oracle.truffle.js.nodes.cast.JSToPropertyKeyNode;
import com.oracle.truffle.js.nodes.function.JSFunctionCallNode;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSArguments;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.builtins.JSProxy;
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.Undefined;
@NodeInfo(cost = NodeCost.NONE)
@ImportStatic({JSProxy.class})
public abstract class JSProxyHasPropertyNode extends JavaScriptBaseNode {
@Child protected GetMethodNode trapGetter;
@Child private JSFunctionCallNode callNode;
@Child private JSToBooleanNode toBooleanNode;
@Child private JSToPropertyKeyNode toPropertyKeyNode;
private final BranchProfile errorBranch = BranchProfile.create();
public JSProxyHasPropertyNode(JSContext context) {
this.callNode = JSFunctionCallNode.createCall();
this.trapGetter = GetMethodNode.create(context, null, JSProxy.HAS);
this.toPropertyKeyNode = JSToPropertyKeyNode.create();
this.toBooleanNode = JSToBooleanNode.create();
}
public static JSProxyHasPropertyNode create(JSContext context) {
return JSProxyHasPropertyNodeGen.create(context);
}
public abstract boolean executeWithTargetAndKeyBoolean(Object shared, Object key);
@Specialization
protected boolean doGeneric(DynamicObject proxy, Object key,
@Cached("createBinaryProfile()") ConditionProfile trapFunProfile) {
assert JSProxy.isJSProxy(proxy);
Object propertyKey = toPropertyKeyNode.execute(key);
DynamicObject handler = JSProxy.getHandlerChecked(proxy, errorBranch);
Object target = JSProxy.getTarget(proxy);
Object trapFun = trapGetter.executeWithTarget(handler);
if (trapFunProfile.profile(trapFun == Undefined.instance)) {
if (JSDynamicObject.isJSDynamicObject(target)) {
return JSObject.hasProperty((DynamicObject) target, propertyKey);
} else {
return JSInteropUtil.hasProperty(target, propertyKey);
}
} else {
Object callResult = callNode.executeCall(JSArguments.create(handler, trapFun, target, propertyKey));
boolean trapResult = toBooleanNode.executeBoolean(callResult);
if (!trapResult) {
errorBranch.enter();
if (!JSProxy.checkPropertyIsSettable(target, propertyKey)) {
throw Errors.createTypeError("Proxy can't successfully access a non-writable, non-configurable property", this);
}
}
return trapResult;
}
}
}