A typical use case would be in a parser, which can use the
+ * {@link #inline(Object, List, ScopeBuilder, SourceSection)} to request inlining.
+ * For this purpose, {@link InlinableNodes} takes a list of node classes and factories as
+ * candidates for inlining.
+ *
+ * @param The potential node is identified with a {@code selector} and it is determined whether
+ * inlining is applicable by using the data from {@link Inline} and matching it with the
+ * {@code argNodes}.
+ *
+ * @param The rest is treated as normal, first the args, then the inlined args,
+ * then possibly to be introduced temps, and finally possible additional args.
+ *
+ * @param Node state can typically include information about lexical bindings, for instance in form
+ * of unique identifiers, which might be class names or ids of mixins or similar lexical
+ * constructs.
+ */
+public interface NodeState {
+
+}
diff --git a/src/bd/inlining/NodeVisitorUtil.java b/src/bd/inlining/NodeVisitorUtil.java
new file mode 100644
index 0000000..47c65b5
--- /dev/null
+++ b/src/bd/inlining/NodeVisitorUtil.java
@@ -0,0 +1,22 @@
+package bd.inlining;
+
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeVisitor;
+
+import bd.basic.nodes.DummyParent;
+
+
+final class NodeVisitorUtil {
+
+ @SuppressWarnings("unchecked")
+ public static Many languages use lexical scopes, i.e., grouping of variables based on textual elements.
+ * Often such groups are delimited by parentheses or other indicators for textual blocks.
+ *
+ * Scopes are expected to form a chain from the inner to the outer scopes.
+ *
+ * @param The set excludes variables that are defined in other scopes, even if they might be
+ * logically part of the set from language perspective, perhaps because of nesting scopes.
+ *
+ * @param A candidate for a concrete scope builder could be for instance the class that creates
+ * methods or lambdas when parsing code.
+ *
+ * @param Some code elements require additional temporary variables when they are inlined.
+ * An example is the lambda-body for a counting loop, which needs a temporary variable to
+ * perform the counting and communicate it to the lambda.
+ *
+ * @param node the node using a variable that needs to be represented as a new temporary
+ * @param source the synthetic source location for the new variable definition
+ * @return the introduced variable representing the temporary
+ * @throws ProgramDefinitionError when there is a consistency problem caused by introducing
+ * the variable. This is supposed to be used to indicate errors in the user
+ * program.
+ */
+ Variable> introduceTempForInlinedVersion(Inlinable Generally, we expect variables to be read or written, but do not require an
+ * implementation the writing operation, since variables might be immutable and initialized
+ * otherwise.
+ *
+ * Some special variables, such as Note that variable access are typically associated with a This operation should only be used on variables that are This operation should only be used on variables that are Note, {@link Inlinable} is different from {@link Inline}. Nodes marked
+ * with @{@link Inline} are replacing more general nodes, which typically have
+ * In most cases, the replacement nodes for an An example for an A reference to the scope is typically an access to some type of variable, which might be
+ * realized as an access to a {@link Frame} with a {@link FrameSlot}. More generally, it is any
+ * reference that might need to be adjusted after scopes have been changed. Scope changes can
+ * be cause by inlining, which usually means that some scopes get merged, or because of
+ * splitting, which separates scopes. In either case, nodes with scope references need to be
+ * adapted, which is done with {@link #replaceAfterScopeChange(ScopeAdaptationVisitor)}.
+ */
+public interface ScopeReference {
+
+ /**
+ * Replaces the current node with one that is adapted to match the a changed scope.
+ *
+ * A scope change might have been caused by splitting or inlining. The
+ * {@link ScopeAdaptationVisitor} provides access to the scope information, to obtain updated
+ * scope references, e.g., valid variable or frame slot objects, or for convenience also
+ * complete updated read/write nodes.
+ *
+ * @param visitor the {@link ScopeAdaptationVisitor} that manages the scope adaptation and
+ * provides access to scope information
+ */
+ void replaceAfterScopeChange(ScopeAdaptationVisitor visitor);
+}
diff --git a/src/bd/inlining/nodes/WithSource.java b/src/bd/inlining/nodes/WithSource.java
new file mode 100644
index 0000000..51763cf
--- /dev/null
+++ b/src/bd/inlining/nodes/WithSource.java
@@ -0,0 +1,22 @@
+package bd.inlining.nodes;
+
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.source.SourceSection;
+
+
+/**
+ * All nodes that are handled by inlining are expected to implement {@link WithSource}, which
+ * is used to make sure they have the source section attribution after inlining.
+ */
+public interface WithSource {
+
+ /**
+ * Initialize the node with the source section.
+ *
+ * @param the type of the {@link ScopeBuilder}
+ *
+ * @param selector to identify a potential inline replacement
+ * @param argNodes the argument/child nodes to the potential node
+ * @param builder used for providing context to the inlining operation
+ * @param source the source section of the potential node
+ * @return the inlined version of the potential node, or {@code null}, if inlining is not
+ * applicable
+ * @throws ProgramDefinitionError in case the inlining would result in a structural violation
+ * of program definition constraints. The error is language specific and not
+ * triggered by the inlining logic.
+ */
+ public
+ * @param the scope type
+ * @param , MethodT> S getScope(final MethodT method) {
+ return ((S) scope).getScope(method);
+ }
+
+ /**
+ * The current scope, which had been adapted before instantiating the visitor.
+ *
+ * @param the scope type
+ * @param , MethodT> S getCurrentScope() {
+ return (S) scope;
+ }
+
+ /**
+ * Adapt the given node.
+ *
+ * @return true, if the process should continue
+ */
+ @Override
+ public boolean visit(final Node node) {
+ if (node instanceof ScopeReference) {
+ ((ScopeReference) node).replaceAfterScopeChange(this);
+ }
+ return true;
+ }
+
+ /**
+ * Factory method to update a read node with an appropriate version for the adapted scope.
+ *
+ * @param this
-read node with an appropriate version for the
+ * adapted scope.
+ *
+ * @param super
-read node with an appropriate version for
+ * the adapted scope.
+ *
+ * @param this
can require extra handling and can thus
+ * require the use of special nodes. We provide here factory methods for this
-like
+ * variables as well as super
-like reads.
+ *
+ * contextLevel
. The
+ * precise semantics is specific to your language's use of {@link Scope}s. But generally, we
+ * assume that scopes are defined lexically, and a context level of 0 means the local scope,
+ * and every increment represents one step outwards in a scope chain.
+ *
+ * @param this
variable.
+ *
+ * this
-like
+ * variables.
+ *
+ * @param contextLevel references the scope in which the variable is defined,
+ * relative to the scope in which the read is done
+ * @param state to be used to initialize this node
+ * @param source of the read operation
+ * @return a node to read this
+ */
+ default N getThisReadNode(final int contextLevel, final NodeState state,
+ final SourceSection source) {
+ throw new UnsupportedOperationException(
+ "Variable.getThisReadNode not supported on this type of variable: "
+ + getClass().getSimpleName());
+ }
+
+ /**
+ * Create a node to read the special super
variable.
+ *
+ * this
-like
+ * variables supporting super
reads.
+ *
+ * @param contextLevel references the scope in which the variable is defined,
+ * relative to the scope in which the read is done
+ * @param state to be used to initialize this node
+ * @param source of the read operation
+ * @return a node to read super
+ */
+ default N getSuperReadNode(final int contextLevel, final NodeState state,
+ final SourceSection source) {
+ throw new UnsupportedOperationException(
+ "Variable.getSuperReadNode not supported on this type of variable: "
+ + getClass().getSimpleName());
+ }
+}
diff --git a/src/bd/inlining/nodes/Inlinable.java b/src/bd/inlining/nodes/Inlinable.java
new file mode 100644
index 0000000..64a7814
--- /dev/null
+++ b/src/bd/inlining/nodes/Inlinable.java
@@ -0,0 +1,37 @@
+package bd.inlining.nodes;
+
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+
+import bd.inlining.Inline;
+import bd.inlining.ScopeBuilder;
+
+
+/**
+ * Inlinable
nodes can be replaced, inlined with arbitrary other nodes.
+ *
+ * Inlinable
nodes as their children.
+ *
+ * Inlinable
node will be at least
+ * logically sub-nodes of the inlinable node.
+ *
+ * Inlinable
node is a lambda (closure, block) that
+ * represents for instance the body of a for-each operation. The for-each operation can be
+ * annotated with @{@link Inline}, so that it's children, which include the node representing
+ * the lambda are then inlined. Concretely, the node to replace the Inlinable
node
+ * would likely be the {@link RootNode} of the lambda.
+ *
+ * @param "+"
. The name should be
* understandable by humans, and might be shown in a user interface.
+ *
+ * @return name of the operation
*/
String getOperation();
/**
* The number of arguments on which the operation depends.
+ *
+ * @return number of required arguments
*/
int getNumArguments();
}
diff --git a/tests/bd/inlining/InliningTests.java b/tests/bd/inlining/InliningTests.java
new file mode 100644
index 0000000..9b28122
--- /dev/null
+++ b/tests/bd/inlining/InliningTests.java
@@ -0,0 +1,86 @@
+package bd.inlining;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+
+import bd.basic.ProgramDefinitionError;
+import bd.testsetup.AddNodeFactory;
+import bd.testsetup.ExprNode;
+import bd.testsetup.LambdaNode;
+import bd.testsetup.StringId;
+import bd.testsetup.ValueNode;
+import bd.testsetup.ValueSpecializedNode;
+
+
+public class InliningTests {
+
+ private final SourceSection source =
+ Source.newBuilder("test").name("test").mimeType("x/test").build().createSection(1);
+
+ private final InlinableNodes