package com.oracle.truffle.js.nodes.access;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Executed;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.Tag;
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.object.Property;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.objects.Dead;
import com.oracle.truffle.js.runtime.objects.JSDynamicObject;
import java.util.Set;
@NodeInfo(cost = NodeCost.NONE)
public class GlobalScopeNode extends JavaScriptNode {
protected final JSContext context;
protected GlobalScopeNode(JSContext context) {
this.context = context;
}
public static JavaScriptNode create(JSContext context) {
return new GlobalScopeNode(context);
}
public static JavaScriptNode createWithTDZCheck(JSContext context, String varName) {
return GlobalScopeTDZCheckNodeGen.create(context, varName);
}
@Override
public Object execute(VirtualFrame frame) {
return context.getRealm().getGlobalScope();
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return copy();
}
}
abstract class GlobalScopeTDZCheckNode extends GlobalScopeNode {
final String varName;
@Executed @Child JavaScriptNode scopeNode;
GlobalScopeTDZCheckNode(JSContext context, String varName) {
super(context);
this.varName = varName;
this.scopeNode = GlobalScopeNode.create(context);
}
@SuppressWarnings("unused")
@Specialization(guards = {"scope.getShape() == cachedShape"}, assumptions = {"cachedShape.getValidAssumption()"}, limit = "context.getPropertyCacheLimit()")
final Object doCached(DynamicObject scope,
@Cached("scope.getShape()") Shape cachedShape,
@Cached("isDead(cachedShape)") boolean dead) {
assert dead == (JSDynamicObject.getOrNull(scope, varName) == Dead.instance());
if (dead) {
throw Errors.createReferenceErrorNotDefined(context, varName, this);
}
return scope;
}
@Specialization(replaces = "doCached")
final Object doUncached(Object scope,
@Cached("create(varName, context)") PropertyGetNode getNode,
@Cached("create()") BranchProfile deadBranch) {
if (getNode.getValue(scope) == Dead.instance()) {
deadBranch.enter();
throw Errors.createReferenceErrorNotDefined(context, varName, this);
}
return scope;
}
final boolean isDead(Shape shape) {
Property property = shape.getProperty(varName);
return property != null && property.getLocation().isValue() && property.getLocation().get(null) == Dead.instance();
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return GlobalScopeTDZCheckNodeGen.create(context, varName);
}
}