package com.oracle.truffle.js.nodes.access;
import java.util.Set;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.InstrumentableNode;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
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.ReadPropertyTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.ReadVariableTag;
import com.oracle.truffle.js.nodes.instrumentation.NodeObjectDescriptor;
import com.oracle.truffle.js.runtime.JSContext;
public class GlobalPropertyNode extends JSTargetableNode implements ReadNode {
private final String propertyName;
private final JSContext context;
@Child private PropertyGetNode cache;
@Child private JavaScriptNode globalObjectNode;
protected GlobalPropertyNode(JSContext context, String propertyName, JavaScriptNode globalObjectNode) {
this.propertyName = propertyName;
this.context = context;
this.globalObjectNode = globalObjectNode;
}
public static JSTargetableNode createPropertyNode(JSContext ctx, String propertyName) {
if (ctx != null && ctx.isOptionNashornCompatibilityMode()) {
if (propertyName.equals("__LINE__")) {
return new GlobalConstantNode(ctx, propertyName, new GlobalConstantNode.LineNumberNode());
} else if (propertyName.equals("__FILE__")) {
return new GlobalConstantNode(ctx, propertyName, new GlobalConstantNode.FileNameNode());
} else if (propertyName.equals("__DIR__")) {
return new GlobalConstantNode(ctx, propertyName, new GlobalConstantNode.DirNameNode(ctx));
}
}
return new GlobalPropertyNode(ctx, propertyName, null);
}
public static JSTargetableNode createLexicalGlobal(JSContext ctx, String propertyName, boolean checkTDZ) {
JavaScriptNode globalScope = checkTDZ ? GlobalScopeNode.createWithTDZCheck(ctx, propertyName) : GlobalScopeNode.create(ctx);
return new GlobalPropertyNode(ctx, propertyName, globalScope);
}
@Override
public boolean hasTag(Class<? extends Tag> tag) {
if ((tag == ReadVariableTag.class || tag == StandardTags.ReadVariableTag.class) && isScopeAccess()) {
return true;
} else if (tag == ReadPropertyTag.class && !isScopeAccess()) {
return true;
} else {
return super.hasTag(tag);
}
}
private boolean isScopeAccess() {
return globalObjectNode instanceof GlobalScopeNode;
}
@Override
public Object getNodeObject() {
if (isScopeAccess()) {
NodeObjectDescriptor descriptor = JSTags.createNodeObjectDescriptor("name", getPropertyKey());
descriptor.addProperty(StandardTags.ReadVariableTag.NAME, getPropertyKey());
return descriptor;
}
return JSTags.createNodeObjectDescriptor("key", getPropertyKey());
}
@Override
public InstrumentableNode materializeInstrumentableNodes(Set<Class<? extends Tag>> materializedTags) {
if (materializedTags.contains(ReadPropertyTag.class) && !isScopeAccess() && globalObjectNode == null) {
GlobalObjectNode global = GlobalObjectNode.create(context);
GlobalPropertyNode materialized = new GlobalPropertyNode(context, propertyName, global);
if (this.cache != null && this.cache.isMethod()) {
materialized.getCache().setMethod();
}
transferSourceSectionAndTags(this, materialized);
transferSourceSection(this, global);
return materialized;
}
return this;
}
@Override
public Object executeWithTarget(VirtualFrame frame, Object target) {
return getCache().getValue(target);
}
@Override
public final Object evaluateTarget(VirtualFrame frame) {
if (globalObjectNode != null) {
return globalObjectNode.execute(frame);
}
return GlobalObjectNode.getGlobalObject(context);
}
@Override
public JavaScriptNode getTarget() {
return getGlobalObjectNode();
}
@Override
public Object execute(VirtualFrame frame) {
return getCache().getValue(evaluateTarget(frame));
}
@Override
public int executeInt(VirtualFrame frame) throws UnexpectedResultException {
return getCache().getValueInt(evaluateTarget(frame));
}
@Override
public double executeDouble(VirtualFrame frame) throws UnexpectedResultException {
return getCache().getValueDouble(evaluateTarget(frame));
}
public String getPropertyKey() {
return propertyName;
}
public void setMethod() {
getCache().setMethod();
}
public void setPropertyAssumptionCheckEnabled(boolean enabled) {
getCache().setPropertyAssumptionCheckEnabled(enabled);
}
private PropertyGetNode getCache() {
if (cache == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.cache = insert(PropertyGetNode.create(propertyName, true, context));
}
return cache;
}
private JavaScriptNode getGlobalObjectNode() {
if (globalObjectNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.globalObjectNode = insert(GlobalObjectNode.create(context));
}
return globalObjectNode;
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
GlobalPropertyNode copy = new GlobalPropertyNode(context, propertyName, cloneUninitialized(globalObjectNode, materializedTags));
if (this.cache != null && this.cache.isMethod()) {
copy.getCache().setMethod();
}
return copy;
}
@Override
public String expressionToString() {
return getPropertyKey();
}
}