package com.oracle.truffle.js.nodes.access;
import java.util.Objects;
import java.util.Set;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.ReadNode;
import com.oracle.truffle.js.nodes.instrumentation.JSTags;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.ReadVariableTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.WriteVariableTag;
import com.oracle.truffle.js.nodes.instrumentation.NodeObjectDescriptor;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.objects.Undefined;
public final class EvalVariableNode extends JSTargetableNode implements ReadNode, WriteNode {
@Child private JavaScriptNode defaultDelegate;
private final String varName;
@Child private JavaScriptNode dynamicScopeNode;
@Child private HasPropertyCacheNode hasPropertyNode;
@Child private JSTargetableNode scopeAccessNode;
private final JSContext context;
public EvalVariableNode(JSContext context, String varName, JavaScriptNode defaultDelegate, JavaScriptNode dynamicScope, JSTargetableNode scopeAccessNode) {
this.varName = varName;
this.defaultDelegate = Objects.requireNonNull(defaultDelegate);
this.dynamicScopeNode = dynamicScope;
this.hasPropertyNode = HasPropertyCacheNode.create(varName, context);
this.scopeAccessNode = scopeAccessNode;
this.context = context;
}
public String getPropertyName() {
return varName;
}
public JavaScriptNode getDefaultDelegate() {
return defaultDelegate;
}
private boolean isWrite() {
return scopeAccessNode instanceof WritePropertyNode;
}
@Override
public boolean hasTag(Class<? extends Tag> tag) {
if ((tag == ReadVariableTag.class || tag == StandardTags.ReadVariableTag.class) && !isWrite()) {
return true;
} else if ((tag == WriteVariableTag.class || tag == StandardTags.WriteVariableTag.class) && isWrite()) {
return true;
} else {
return super.hasTag(tag);
}
}
@Override
public Object getNodeObject() {
NodeObjectDescriptor descriptor = JSTags.createNodeObjectDescriptor("name", varName);
if (isWrite()) {
descriptor.addProperty(StandardTags.WriteVariableTag.NAME, varName);
} else {
descriptor.addProperty(StandardTags.ReadVariableTag.NAME, varName);
}
return descriptor;
}
@Override
public JavaScriptNode getTarget() {
return dynamicScopeNode;
}
@Override
public Object evaluateTarget(VirtualFrame frame) {
Object dynamicScope = dynamicScopeNode.execute(frame);
if (dynamicScope != Undefined.instance && hasPropertyNode.hasProperty(dynamicScope)) {
return dynamicScope;
}
return Undefined.instance;
}
@Override
public Object execute(VirtualFrame frame) {
Object dynamicScope = evaluateTarget(frame);
return executeWithTarget(frame, dynamicScope);
}
@Override
public Object executeWithTarget(VirtualFrame frame, Object dynamicScope) {
if (dynamicScope != Undefined.instance) {
if (isWrite()) {
Object value = ((WriteNode) defaultDelegate).getRhs().execute(frame);
((WritePropertyNode) scopeAccessNode).executeWithValue(dynamicScope, value);
return value;
} else {
return scopeAccessNode.executeWithTarget(frame, dynamicScope);
}
}
return defaultDelegate.execute(frame);
}
@Override
public Object executeWrite(VirtualFrame frame, Object value) {
throw Errors.shouldNotReachHere();
}
@Override
public JavaScriptNode getRhs() {
return ((WriteNode) defaultDelegate).getRhs();
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return new EvalVariableNode(context, varName, cloneUninitialized(defaultDelegate, materializedTags), cloneUninitialized(dynamicScopeNode, materializedTags),
cloneUninitialized(scopeAccessNode, materializedTags));
}
}