Skip to content

Commit

Permalink
WiP: add block-level variables to the debugger
Browse files Browse the repository at this point in the history
  • Loading branch information
skinny85 committed Dec 1, 2024
1 parent 59605d7 commit c0ac900
Show file tree
Hide file tree
Showing 10 changed files with 964 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
* Identical to the class with the same name from part 14.
*/
@NodeChild("initializerExpr")
@NodeField(name = "slotName", type = String.class)
@NodeField(name = "frameSlot", type = int.class)
@ImportStatic(FrameSlotKind.class)
public abstract class LocalVarAssignmentExprNode extends EasyScriptExprNode {
protected abstract int getFrameSlot();
public abstract String getSlotName();
public abstract int getFrameSlot();

@Specialization(guards = "frame.getFrameDescriptor().getSlotKind(getFrameSlot()) == Illegal || " +
"frame.getFrameDescriptor().getSlotKind(getFrameSlot()) == Int")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
package com.endoflineblog.truffle.part_15.nodes.root;

import com.endoflineblog.truffle.part_15.EasyScriptTruffleLanguage;
import com.endoflineblog.truffle.part_15.nodes.exprs.variables.LocalVarAssignmentExprNode;
import com.endoflineblog.truffle.part_15.nodes.exprs.variables.LocalVarReferenceExprNode;
import com.endoflineblog.truffle.part_15.nodes.stmts.EasyScriptStmtNode;
import com.endoflineblog.truffle.part_15.nodes.stmts.blocks.BlockStmtNode;
import com.endoflineblog.truffle.part_15.nodes.stmts.blocks.UserFuncBodyStmtNode;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.InstrumentableNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeUtil;
import com.oracle.truffle.api.nodes.NodeVisitor;
import com.oracle.truffle.api.nodes.RootNode;

import java.util.ArrayList;
import java.util.List;

/**
* A {@link RootNode} that represents the execution of a block of statements.
* Used as the {@link RootNode} for the entire EasyScript program,
Expand All @@ -28,6 +39,9 @@ public final class StmtBlockRootNode extends RootNode {

private final String name;

@CompilationFinal(dimensions = 1)
private LocalVarAssignmentExprNode[] localVariableAssignmentsCache;

public StmtBlockRootNode(EasyScriptTruffleLanguage truffleLanguage,
FrameDescriptor frameDescriptor, BlockStmtNode blockStmt, String name) {
this(truffleLanguage, frameDescriptor, (EasyScriptStmtNode) blockStmt, name);
Expand Down Expand Up @@ -55,4 +69,43 @@ public Object execute(VirtualFrame frame) {
public String getName() {
return this.name;
}

public LocalVarAssignmentExprNode[] getLocalVariableAssignments() {
if (this.localVariableAssignmentsCache == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.localVariableAssignmentsCache = this.findLocalVariableAssignments();
}
return this.localVariableAssignmentsCache;
}

private LocalVarAssignmentExprNode[] findLocalVariableAssignments() {
List<LocalVarAssignmentExprNode> varAssignments = new ArrayList<>(4);
NodeUtil.forEachChild(this.blockStmt, new NodeVisitor() {
private LocalVarAssignmentExprNode varAssignment; // The current write node containing a slot

@Override
public boolean visit(Node visitedNode) {
if (visitedNode instanceof InstrumentableNode.WrapperNode) {
return NodeUtil.forEachChild(visitedNode, this);
}
if (visitedNode instanceof LocalVarAssignmentExprNode) {
varAssignment = (LocalVarAssignmentExprNode) visitedNode;
boolean all = NodeUtil.forEachChild(visitedNode, this);
varAssignment = null;
return all;
} else if (varAssignment != null && visitedNode instanceof LocalVarReferenceExprNode) {
// When there is a write node, search for SLReadArgumentNode among its children:
varAssignments.add(varAssignment);
return true;
} else if (varAssignment == null && (visitedNode instanceof EasyScriptStmtNode &&
!(visitedNode instanceof BlockStmtNode || visitedNode instanceof UserFuncBodyStmtNode))) {
// A different SL node - we're done.
return false;
} else {
return NodeUtil.forEachChild(visitedNode, this);
}
}
});
return varAssignments.toArray(new LocalVarAssignmentExprNode[varAssignments.size()]);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
package com.endoflineblog.truffle.part_15.nodes.stmts;

import com.endoflineblog.truffle.part_15.nodes.EasyScriptNode;
import com.endoflineblog.truffle.part_15.nodes.root.StmtBlockRootNode;
import com.endoflineblog.truffle.part_15.nodes.stmts.blocks.BlockStmtNode;
import com.endoflineblog.truffle.part_15.runtime.nodes.ArgumentsObject;
import com.endoflineblog.truffle.part_15.runtime.nodes.VariablesObject;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.GenerateWrapper;
import com.oracle.truffle.api.instrumentation.InstrumentableNode;
import com.oracle.truffle.api.instrumentation.ProbeNode;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.interop.NodeLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.SourceSection;

/**
Expand All @@ -15,6 +26,7 @@
* Identical to the class with the same name from part 14.
*/
@GenerateWrapper
@ExportLibrary(value = NodeLibrary.class)
public abstract class EasyScriptStmtNode extends EasyScriptNode implements InstrumentableNode {
private final SourceSection sourceSection;

Expand Down Expand Up @@ -46,4 +58,49 @@ public boolean hasTag(Class<? extends Tag> tag) {
public SourceSection getSourceSection() {
return this.sourceSection;
}

/**
* Find block of this node. Traverse the parent chain and find the first {@link BlockStmtNode}.
* If none is found, {@link RootNode} is returned.
*
* @return the block node, always non-null. Either SLBlockNode, or SLRootNode.
*/
public final Node findBlock() {
Node parent = getParent();
while (parent != null) {
if (parent instanceof BlockStmtNode) {
break;
}
Node grandParent = parent.getParent();
if (grandParent == null) {
assert parent instanceof RootNode : String.format("Not adopted node under %s.", parent);
return parent;
}
parent = grandParent;
}
return parent;
}

/**
* The scope depends on the current node and the node's block. Cache the node and its block for
* fast access. Depending on the block node, we create either block variables, or function
* arguments (in the RootNode, but outside of a block).
*/
@ExportMessage
Object getScope(
Frame frame, boolean nodeEnter,
@Cached(value = "this", adopt = false) EasyScriptStmtNode cachedNode,
@Cached(value = "this.findBlock()", adopt = false, allowUncached = true) Node blockNode) {
return blockNode instanceof BlockStmtNode
? new VariablesObject(frame, cachedNode, nodeEnter, (BlockStmtNode) blockNode)
: new ArgumentsObject(frame, (StmtBlockRootNode) blockNode);
}

/**
* We do provide a scope.
*/
@ExportMessage
boolean hasScope(@SuppressWarnings("unused") Frame frame) {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package com.endoflineblog.truffle.part_15.nodes.stmts.blocks;

import com.endoflineblog.truffle.part_15.nodes.exprs.variables.LocalVarAssignmentExprNode;
import com.endoflineblog.truffle.part_15.nodes.stmts.EasyScriptStmtNode;
import com.endoflineblog.truffle.part_15.nodes.stmts.SkippedStmtNode;
import com.endoflineblog.truffle.part_15.runtime.Undefined;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
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.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeUtil;
import com.oracle.truffle.api.nodes.NodeVisitor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
Expand All @@ -24,6 +32,9 @@ public final class BlockStmtNode extends SkippedStmtNode {
@Children
private final EasyScriptStmtNode[] stmts;

@CompilationFinal(dimensions = 1)
private LocalVarAssignmentExprNode[] writeNodesCache;

public BlockStmtNode(List<EasyScriptStmtNode> stmts) {
this.stmts = stmts.toArray(new EasyScriptStmtNode[]{});
}
Expand All @@ -49,4 +60,72 @@ public Object executeStatement(VirtualFrame frame) {
public boolean hasTag(Class<? extends Tag> tag) {
return tag == StandardTags.RootTag.class;
}

public LocalVarAssignmentExprNode[] getDeclaredLocalVariables() {
LocalVarAssignmentExprNode[] writeNodes = this.writeNodesCache;
if (writeNodes == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
writeNodesCache = writeNodes = this.findDeclaredLocalVariables();
}
return writeNodes;
}

private LocalVarAssignmentExprNode[] findDeclaredLocalVariables() {
// Search for those write nodes, which declare variables
List<LocalVarAssignmentExprNode> writeNodes = new ArrayList<>(4);
// int[] varsIndex = new int[]{0};
NodeUtil.forEachChild(this, new NodeVisitor() {
@Override
public boolean visit(Node visitedNode) {
if (visitedNode instanceof WrapperNode) {
NodeUtil.forEachChild(visitedNode, this);
return true;
}
// if (visitedNode instanceof SLScopedNode) {
// SLScopedNode scopedNode = (SLScopedNode) visitedNode;
// scopedNode.setVisibleVariablesIndexOnEnter(varsIndex[0]);
// }
// Do not enter any nested blocks.
if (!(visitedNode instanceof BlockStmtNode)) {
NodeUtil.forEachChild(visitedNode, this);
}
// Write to a variable is a declaration unless it exists already in a parent scope.
if (visitedNode instanceof LocalVarAssignmentExprNode) {
LocalVarAssignmentExprNode wn = (LocalVarAssignmentExprNode) visitedNode;
// if (wn.isDeclaration()) {
writeNodes.add(wn);
// varsIndex[0]++;
// }
}
// if (visitedNode instanceof SLScopedNode) {
// SLScopedNode scopedNode = (SLScopedNode) visitedNode;
// scopedNode.setVisibleVariablesIndexOnExit(varsIndex[0]);
// }
return true;
}
});
LocalVarAssignmentExprNode[] variables = writeNodes.toArray(new LocalVarAssignmentExprNode[0]);
// this.parentBlockIndex = variables.length;

Node parentBlock = this.findBlock();
LocalVarAssignmentExprNode[] parentVariables = null;
if (parentBlock instanceof BlockStmtNode) {
parentVariables = ((BlockStmtNode) parentBlock).getDeclaredLocalVariables();
}
if (parentVariables == null || parentVariables.length == 0) {
return variables;
} else {
// int parentVariablesIndex = ((BlockStmtNode) parentBlock).getParentBlockIndex();
// int visibleVarsIndex = getVisibleVariablesIndexOnEnter();
// int allVarsLength = variables.length + visibleVarsIndex + parentVariables.length - parentVariablesIndex;
// LocalVarAssignmentExprNode[] allVariables = Arrays.copyOf(variables, allVarsLength);
// System.arraycopy(parentVariables, 0, allVariables, variables.length, visibleVarsIndex);
// System.arraycopy(parentVariables, parentVariablesIndex, allVariables, variables.length + visibleVarsIndex, parentVariables.length - parentVariablesIndex);

int allVarsLength = variables.length + parentVariables.length;
LocalVarAssignmentExprNode[] allVariables = Arrays.copyOf(variables, allVarsLength);
System.arraycopy(parentVariables, 0, allVariables, variables.length, allVarsLength - variables.length);
return allVariables;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ private List<EasyScriptStmtNode> parseStmtsList(List<EasyScriptParser.StmtContex
if (this.localScopes.peek().putIfAbsent(variableId, new LocalVariable(frameSlot, declarationKind)) != null) {
throw new EasyScriptException("Identifier '" + variableId + "' has already been declared");
}
LocalVarAssignmentExprNode assignmentExpr = LocalVarAssignmentExprNodeGen.create(initializerExpr, frameSlot);
LocalVarAssignmentExprNode assignmentExpr = LocalVarAssignmentExprNodeGen.create(initializerExpr, variableId, frameSlot);
nonFuncDeclStmts.add(new ExprStmtNode(assignmentExpr,
this.createSourceSection(varDeclStmt), /* discardExpressionValue */ true));
}
Expand Down Expand Up @@ -518,7 +518,7 @@ private EasyScriptExprNode parseAssignmentExpr(EasyScriptParser.AssignmentExpr1C
if (localVariable.declarationKind == DeclarationKind.CONST) {
throw new EasyScriptException("Assignment to constant variable '" + variableId + "'");
}
return LocalVarAssignmentExprNodeGen.create(initializerExpr, localVariable.variableIndex);
return LocalVarAssignmentExprNodeGen.create(initializerExpr, variableId, localVariable.variableIndex);
}
}
}
Expand Down
Loading

0 comments on commit c0ac900

Please sign in to comment.