package com.oracle.truffle.js.nodes.access;
import java.util.Objects;
import java.util.Set;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Executed;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.library.CachedLibrary;
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.profiles.BranchProfile;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.ReadNode;
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.objects.Accessor;
import com.oracle.truffle.js.runtime.objects.Undefined;
public abstract class PrivateFieldGetNode extends JSTargetableNode implements ReadNode {
@Child @Executed protected JavaScriptNode targetNode;
@Child @Executed protected JavaScriptNode keyNode;
protected final JSContext context;
public static PrivateFieldGetNode create(JavaScriptNode targetNode, JavaScriptNode keyNode, JSContext context) {
return PrivateFieldGetNodeGen.create(targetNode, keyNode, context);
}
protected PrivateFieldGetNode(JavaScriptNode targetNode, JavaScriptNode keyNode, JSContext context) {
this.targetNode = targetNode;
this.keyNode = keyNode;
this.context = context;
}
@Specialization(guards = {"isJSObject(target)"}, limit = "3")
Object doField(DynamicObject target, HiddenKey key,
@CachedLibrary("target") DynamicObjectLibrary access,
@Cached BranchProfile errorBranch) {
if (access.containsKey(target, key)) {
return access.getOrDefault(target, key, Undefined.instance);
} else {
errorBranch.enter();
return missing(target, key);
}
}
@Specialization(guards = {"isJSObject(target)", "isJSFunction(method)"})
Object doMethod(@SuppressWarnings("unused") DynamicObject target, DynamicObject method) {
return method;
}
@Specialization(guards = {"isJSObject(target)"})
Object doAccessor(DynamicObject target, Accessor accessor,
@Cached("createCall()") JSFunctionCallNode callNode,
@Cached BranchProfile errorBranch) {
DynamicObject getter = accessor.getGetter();
if (getter == Undefined.instance) {
errorBranch.enter();
throw Errors.createTypeErrorCannotGetAccessorProperty(keyAsString(), target, this);
}
return callNode.executeCall(JSArguments.createZeroArg(target, getter));
}
@TruffleBoundary
@Fallback
Object missing(@SuppressWarnings("unused") Object target, @SuppressWarnings("unused") Object key) {
throw Errors.createTypeErrorCannotGetPrivateMember(keyAsString(), this);
}
@TruffleBoundary
private String keyAsString() {
return keyNode.expressionToString();
}
@Override
public final JavaScriptNode getTarget() {
return targetNode;
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return create(cloneUninitialized(targetNode, materializedTags), cloneUninitialized(keyNode, materializedTags), context);
}
@Override
public String expressionToString() {
if (targetNode != null && keyNode != null) {
return Objects.toString(targetNode.expressionToString(), INTERMEDIATE_VALUE) + "." + Objects.toString(keyAsString(), INTERMEDIATE_VALUE);
}
return null;
}
}