package com.oracle.truffle.js.nodes.instrumentation;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.function.BlockScopeNode.FrameBlockScopeNode;
import com.oracle.truffle.js.nodes.function.FunctionBodyNode;
import com.oracle.truffle.js.runtime.JSFrameUtil;
import com.oracle.truffle.js.runtime.objects.Undefined;
public final class DeclareTagProvider {
public static JavaScriptNode createMaterializedFunctionBodyNode(JavaScriptNode body, SourceSection sourceSection, FrameDescriptor frameDescriptor) {
return new MaterializedFunctionBodyNode(body, sourceSection, frameDescriptor);
}
public static JavaScriptNode createMaterializedBlockNode(JavaScriptNode block, FrameDescriptor frameDescriptor, FrameSlot parentSlot, SourceSection sourceSection) {
return new MaterializedFrameBlockScopeNode(block, frameDescriptor, parentSlot, sourceSection);
}
public static boolean isMaterializedFrameProvider(JavaScriptNode node) {
return node instanceof MaterializedFrameBlockScopeNode || node instanceof MaterializedFunctionBodyNode;
}
public static NodeObjectDescriptor createDeclareNodeObject(Object name, Object type) {
NodeObjectDescriptor descriptor = JSTags.createNodeObjectDescriptor();
descriptor.addProperty(JSTags.DeclareTag.NAME, name);
descriptor.addProperty(JSTags.DeclareTag.TYPE, type);
return descriptor;
}
private DeclareTagProvider() {
}
private static JavaScriptNode[] initDeclarations(FrameDescriptor frameDescriptor, SourceSection sourceSection) {
assert sourceSection != null;
if (frameDescriptor != null) {
List<FrameSlot> slots = new ArrayList<>();
for (FrameSlot slot : frameDescriptor.getSlots()) {
if (!JSFrameUtil.isInternal(slot) && !JSFrameUtil.isHoistable(slot)) {
slots.add(slot);
}
}
JavaScriptNode[] declarations = new JavaScriptNode[slots.size()];
for (int i = 0; i < slots.size(); i++) {
declarations[i] = new DeclareProviderNode(slots.get(i));
declarations[i].setSourceSection(sourceSection);
}
return declarations;
} else {
return new JavaScriptNode[0];
}
}
private static class MaterializedFrameBlockScopeNode extends FrameBlockScopeNode {
@Children private JavaScriptNode[] declarations;
protected MaterializedFrameBlockScopeNode(JavaScriptNode block, FrameDescriptor frameDescriptor, FrameSlot parentSlot, SourceSection sourceSection) {
super(block, frameDescriptor, parentSlot);
this.declarations = initDeclarations(frameDescriptor, sourceSection);
}
@ExplodeLoop
private void executeDeclarations(VirtualFrame frame) {
for (JavaScriptNode declaration : declarations) {
declaration.execute(frame);
}
}
@Override
public Object execute(VirtualFrame frame) {
executeDeclarations(frame);
return super.execute(frame);
}
@Override
public void executeVoid(VirtualFrame frame) {
executeDeclarations(frame);
super.executeVoid(frame);
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return new MaterializedFrameBlockScopeNode(cloneUninitialized(block, materializedTags), frameDescriptor, parentSlot, getSourceSection());
}
}
private static class MaterializedFunctionBodyNode extends FunctionBodyNode {
@Children private JavaScriptNode[] declarations;
private final FrameDescriptor frameDescriptor;
protected MaterializedFunctionBodyNode(JavaScriptNode body, SourceSection sourceSection, FrameDescriptor frameDescriptor) {
super(body);
this.frameDescriptor = frameDescriptor;
this.declarations = initDeclarations(frameDescriptor, sourceSection);
}
@ExplodeLoop
@Override
public Object execute(VirtualFrame frame) {
for (JavaScriptNode declaration : declarations) {
declaration.execute(frame);
}
return super.execute(frame);
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return new MaterializedFunctionBodyNode(cloneUninitialized(getBody(), materializedTags), getSourceSection(), frameDescriptor);
}
}
private static class DeclareProviderNode extends JavaScriptNode {
private final FrameSlot slot;
DeclareProviderNode(FrameSlot slot) {
this.slot = slot;
}
@Override
public Object execute(VirtualFrame frame) {
return Undefined.instance;
}
@Override
public boolean hasTag(Class<? extends Tag> tag) {
if (tag == JSTags.DeclareTag.class) {
return true;
} else {
return super.hasTag(tag);
}
}
@Override
public boolean isInstrumentable() {
return true;
}
@Override
public Object getNodeObject() {
String type;
if (JSFrameUtil.isConst(slot)) {
type = "const";
} else if (JSFrameUtil.isLet(slot)) {
type = "let";
} else {
type = "var";
}
return createDeclareNodeObject(slot.getIdentifier(), type);
}
@Override
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return new DeclareProviderNode(slot);
}
}
}