String literals, caller frame access, eval function (#333)

This commit is contained in:
Marcin Kostrzewa 2019-11-15 15:49:57 +01:00 committed by GitHub
parent 8da25bec2d
commit 0ec41b5bbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 848 additions and 152 deletions

View File

@ -18,6 +18,11 @@ public class RecursionBenchmarks {
recursionFixtures.sumTCO().execute(recursionFixtures.hundredMillion()); recursionFixtures.sumTCO().execute(recursionFixtures.hundredMillion());
} }
@Benchmark
public void benchSumTCOWithEval() {
recursionFixtures.sumTCOWithEval().execute(recursionFixtures.hundredMillion());
}
@Benchmark @Benchmark
public void benchSumTCOFoldLike() { public void benchSumTCOFoldLike() {
recursionFixtures.sumTCOFoldLike().execute(recursionFixtures.hundredMillion()); recursionFixtures.sumTCOFoldLike().execute(recursionFixtures.hundredMillion());

View File

@ -83,4 +83,16 @@ class RecursionFixtures extends InterpreterRunner {
|""".stripMargin |""".stripMargin
val sumStateTCO = eval(sumStateTCOCode) val sumStateTCO = eval(sumStateTCOCode)
val sumTCOWithEvalCode =
"""
|{ |sumTo|
| summator = { |acc, current|
| @ifZero [current, acc, @eval [@Debug, "@summator [acc + current, current - 1]"]]
| };
| res = @summator [0, sumTo];
| res
|}
|""".stripMargin
val sumTCOWithEval = eval(sumTCOWithEvalCode)
} }

View File

@ -18,5 +18,6 @@ public class Constants {
public static final String ARGUMENT_SORTER_NODE = "10"; public static final String ARGUMENT_SORTER_NODE = "10";
public static final String FUNCTION_INTEROP_LIBRARY = "10"; public static final String FUNCTION_INTEROP_LIBRARY = "10";
public static final String THUNK_EXECUTOR_NODE = "10"; public static final String THUNK_EXECUTOR_NODE = "10";
public static final String EVAL_NODE = "10";
} }
} }

View File

@ -3,7 +3,6 @@ package org.enso.interpreter;
import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.instrumentation.ProvidedTags; import com.oracle.truffle.api.instrumentation.ProvidedTags;
import com.oracle.truffle.api.instrumentation.StandardTags; import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.nodes.RootNode;
@ -11,6 +10,8 @@ import org.enso.interpreter.builder.FileDetector;
import org.enso.interpreter.node.ProgramRootNode; import org.enso.interpreter.node.ProgramRootNode;
import org.enso.interpreter.runtime.Context; import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.RuntimeOptions; import org.enso.interpreter.runtime.RuntimeOptions;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.graalvm.options.OptionDescriptors; import org.graalvm.options.OptionDescriptors;
/** /**
@ -82,7 +83,8 @@ public final class Language extends TruffleLanguage<Context> {
@Override @Override
protected CallTarget parse(ParsingRequest request) { protected CallTarget parse(ParsingRequest request) {
RootNode root = RootNode root =
new ProgramRootNode(this, new FrameDescriptor(), "root", null, request.getSource()); new ProgramRootNode(
this, new LocalScope(), new ModuleScope(), "root", null, request.getSource());
return Truffle.getRuntime().createCallTarget(root); return Truffle.getRuntime().createCallTarget(root);
} }

View File

@ -61,7 +61,6 @@ public class CallArgFactory implements AstCallArgVisitor<CallArgument> {
name.orElse(null), name.orElse(null),
Truffle.getRuntime() Truffle.getRuntime()
.createCallTarget( .createCallTarget(
new ClosureRootNode( new ClosureRootNode(language, childScope, moduleScope, expr, null, displayName)));
language, childScope.getFrameDescriptor(), expr, null, displayName)));
} }
} }

View File

@ -17,6 +17,7 @@ import org.enso.interpreter.node.controlflow.*;
import org.enso.interpreter.node.expression.constant.ConstructorNode; import org.enso.interpreter.node.expression.constant.ConstructorNode;
import org.enso.interpreter.node.expression.constant.DynamicSymbolNode; import org.enso.interpreter.node.expression.constant.DynamicSymbolNode;
import org.enso.interpreter.node.expression.literal.IntegerLiteralNode; import org.enso.interpreter.node.expression.literal.IntegerLiteralNode;
import org.enso.interpreter.node.expression.literal.StringLiteralNode;
import org.enso.interpreter.node.expression.operator.*; import org.enso.interpreter.node.expression.operator.*;
import org.enso.interpreter.node.scope.AssignmentNode; import org.enso.interpreter.node.scope.AssignmentNode;
import org.enso.interpreter.node.scope.AssignmentNodeGen; import org.enso.interpreter.node.scope.AssignmentNodeGen;
@ -113,10 +114,22 @@ public class ExpressionFactory implements AstExpressionVisitor<ExpressionNode> {
* @param l the value to represent * @param l the value to represent
* @return a runtime node representing that value * @return a runtime node representing that value
*/ */
@Override
public ExpressionNode visitLong(long l) { public ExpressionNode visitLong(long l) {
return new IntegerLiteralNode(l); return new IntegerLiteralNode(l);
} }
/**
* Creates a runtime String literal value from an AST node.
*
* @param string the string value of this literal
* @return a runtime node representing this literal
*/
@Override
public ExpressionNode visitStringLiteral(String string) {
return new StringLiteralNode(string);
}
/** /**
* Creates runtime nodes representing arithmetic expressions. * Creates runtime nodes representing arithmetic expressions.
* *
@ -234,8 +247,7 @@ public class ExpressionFactory implements AstExpressionVisitor<ExpressionNode> {
FunctionBodyNode fnBodyNode = FunctionBodyNode fnBodyNode =
new FunctionBodyNode(allFnExpressions.toArray(new ExpressionNode[0]), returnExpr); new FunctionBodyNode(allFnExpressions.toArray(new ExpressionNode[0]), returnExpr);
RootNode fnRootNode = RootNode fnRootNode =
new ClosureRootNode( new ClosureRootNode(language, scope, moduleScope, fnBodyNode, null, "lambda::" + scopeName);
language, scope.getFrameDescriptor(), fnBodyNode, null, "lambda::" + scopeName);
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(fnRootNode); RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(fnRootNode);
return new CreateFunctionNode(callTarget, argDefinitions); return new CreateFunctionNode(callTarget, argDefinitions);

View File

@ -6,8 +6,8 @@ import org.enso.interpreter.node.callable.function.CreateFunctionNode;
import org.enso.interpreter.runtime.Context; import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor; import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.error.VariableDoesNotExistException; import org.enso.interpreter.runtime.error.VariableDoesNotExistException;
import org.enso.interpreter.runtime.scope.ModuleScope; import org.enso.interpreter.runtime.scope.ModuleScope;

View File

@ -2,13 +2,13 @@ package org.enso.interpreter.node;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.ReportPolymorphism; import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.frame.FrameUtil; import com.oracle.truffle.api.frame.FrameUtil;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.enso.interpreter.Language; import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.Stateful; import org.enso.interpreter.runtime.state.Stateful;
/** /**
@ -26,23 +26,20 @@ public class ClosureRootNode extends EnsoRootNode {
* Creates a new root node. * Creates a new root node.
* *
* @param language the language identifier * @param language the language identifier
* @param frameDescriptor a description of the stack frame * @param localScope a description of the local scope
* @param moduleScope a description of the module scope
* @param body the program body to be executed * @param body the program body to be executed
* @param section a mapping from {@code body} to the program source * @param section a mapping from {@code body} to the program source
* @param name a name for the node * @param name a name for the node
*/ */
public ClosureRootNode( public ClosureRootNode(
Language language, Language language,
FrameDescriptor frameDescriptor, LocalScope localScope,
ModuleScope moduleScope,
ExpressionNode body, ExpressionNode body,
SourceSection section, SourceSection section,
String name) { String name) {
super( super(language, localScope, moduleScope, name, section);
language,
frameDescriptor,
name,
section,
frameDescriptor.findOrAddFrameSlot("<<state>>", FrameSlotKind.Object));
this.body = body; this.body = body;
} }

View File

@ -2,18 +2,22 @@ package org.enso.interpreter.node;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.enso.interpreter.Language; import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.Context; import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
/** A common base class for all kinds of root node in Enso. */ /** A common base class for all kinds of root node in Enso. */
public abstract class EnsoRootNode extends RootNode { public abstract class EnsoRootNode extends RootNode {
private final String name; private final String name;
private final SourceSection sourceSection; private final SourceSection sourceSection;
private final FrameSlot stateFrameSlot; private final FrameSlot stateFrameSlot;
private final LocalScope localScope;
private final ModuleScope moduleScope;
private @CompilerDirectives.CompilationFinal TruffleLanguage.ContextReference<Context> private @CompilerDirectives.CompilationFinal TruffleLanguage.ContextReference<Context>
contextReference; contextReference;
private @CompilerDirectives.CompilationFinal TruffleLanguage.LanguageReference<Language> private @CompilerDirectives.CompilationFinal TruffleLanguage.LanguageReference<Language>
@ -23,21 +27,24 @@ public abstract class EnsoRootNode extends RootNode {
* Constructs the root node. * Constructs the root node.
* *
* @param language the language instance in which this will execute * @param language the language instance in which this will execute
* @param frameDescriptor a reference to the construct root frame * @param localScope a reference to the construct local scope
* @param moduleScope a reference to the construct module scope
* @param name the name of the construct * @param name the name of the construct
* @param sourceSection a reference to the source code being executed * @param sourceSection a reference to the source code being executed
* @param stateFrameSlot the code to compile and execute
*/ */
public EnsoRootNode( public EnsoRootNode(
Language language, Language language,
FrameDescriptor frameDescriptor, LocalScope localScope,
ModuleScope moduleScope,
String name, String name,
SourceSection sourceSection, SourceSection sourceSection) {
FrameSlot stateFrameSlot) { super(language, localScope.getFrameDescriptor());
super(language, frameDescriptor);
this.name = name; this.name = name;
this.localScope = localScope;
this.moduleScope = moduleScope;
this.sourceSection = sourceSection; this.sourceSection = sourceSection;
this.stateFrameSlot = stateFrameSlot; this.stateFrameSlot =
localScope.getFrameDescriptor().findOrAddFrameSlot("<<state>>", FrameSlotKind.Object);
} }
/** /**
@ -89,4 +96,22 @@ public abstract class EnsoRootNode extends RootNode {
public SourceSection getSourceSection() { public SourceSection getSourceSection() {
return sourceSection; return sourceSection;
} }
/**
* Gets the local scope this node expects to work with
*
* @return the local scope for this node
*/
public LocalScope getLocalScope() {
return localScope;
}
/**
* Gets the module scope this node was defined with
*
* @return the module scope for this node
*/
public ModuleScope getModuleScope() {
return moduleScope;
}
} }

View File

@ -1,13 +1,13 @@
package org.enso.interpreter.node; package org.enso.interpreter.node;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.enso.interpreter.Language; import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.Context; import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
/** /**
* This node handles static transformation of the input AST before execution and represents the root * This node handles static transformation of the input AST before execution and represents the root
@ -27,23 +27,25 @@ public class ProgramRootNode extends EnsoRootNode {
* Constructs the root node. * Constructs the root node.
* *
* @param language the language instance in which this will execute * @param language the language instance in which this will execute
* @param frameDescriptor a reference to the program root frame * @param localScope a reference to the program local scope
* @param moduleScope a reference to the program module scope
* @param name the name of the program * @param name the name of the program
* @param sourceSection a reference to the source code being executed * @param sourceSection a reference to the source code being executed
* @param sourceCode the code to compile and execute * @param sourceCode the code to compile and execute
*/ */
public ProgramRootNode( public ProgramRootNode(
Language language, Language language,
FrameDescriptor frameDescriptor, LocalScope localScope,
ModuleScope moduleScope,
String name, String name,
SourceSection sourceSection, SourceSection sourceSection,
Source sourceCode) { Source sourceCode) {
super( super(
language, language,
frameDescriptor, localScope,
moduleScope,
name, name,
sourceSection, sourceSection);
frameDescriptor.findOrAddFrameSlot("<<state>>", FrameSlotKind.Object));
this.sourceCode = sourceCode; this.sourceCode = sourceCode;
} }

View File

@ -90,8 +90,10 @@ public class ApplicationNode extends ExpressionNode {
@Override @Override
public Object executeGeneric(VirtualFrame frame) { public Object executeGeneric(VirtualFrame frame) {
Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot()); Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot());
Stateful result = this.invokeCallableNode.execute(
this.callable.executeGeneric(frame), state, evaluateArguments(frame)); Stateful result =
this.invokeCallableNode.execute(
this.callable.executeGeneric(frame), frame, state, evaluateArguments(frame));
frame.setObject(getStateFrameSlot(), result.getState()); frame.setObject(getStateFrameSlot(), result.getState());
return result.getValue(); return result.getValue();
} }

View File

@ -0,0 +1,48 @@
package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.EnsoRootNode;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
/**
* Captures the current caller info to pass to functions requiring it.
*
* <p>The information captured includes current execution frame, as well as static context,
* including the local and module scope metadata.
*/
@NodeInfo(
description = "Captures the caller info for use in functions called from this node's scope")
public class CaptureCallerInfoNode extends Node {
private @CompilerDirectives.CompilationFinal LocalScope localScope;
private @CompilerDirectives.CompilationFinal ModuleScope moduleScope;
/**
* Captures the caller info for use in functions called from the current scope.
*
* @param frame current execution frame
* @return caller information for the current scope
*/
public CallerInfo execute(VirtualFrame frame) {
if (localScope == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
EnsoRootNode rootNode = (EnsoRootNode) getRootNode();
localScope = rootNode.getLocalScope();
moduleScope = rootNode.getModuleScope();
}
return new CallerInfo(frame.materialize(), localScope, moduleScope);
}
/**
* Creates an instance of this node.
*
* @return an instance of this node
*/
public static CaptureCallerInfoNode build() {
return new CaptureCallerInfoNode();
}
}

View File

@ -6,14 +6,14 @@ import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.state.Stateful; import org.enso.interpreter.runtime.state.Stateful;
/** /**
* This node is responsible for optimising function calls. * This node is responsible for optimising function calls.
* *
* <p>Where possible, it will make the call as a 'direct' call, one with no lookup needed, but will * <p>Where possible, it will make the call as a direct call, with potential for inlining.
* fall back to performing a lookup if necessary.
*/ */
public abstract class ExecuteCallNode extends Node { public abstract class ExecuteCallNode extends Node {
@ -24,6 +24,7 @@ public abstract class ExecuteCallNode extends Node {
* already cached. THis means that the call can be made quickly. * already cached. THis means that the call can be made quickly.
* *
* @param function the function to execute * @param function the function to execute
* @param callerInfo the caller info to pass to the function
* @param state the current state value * @param state the current state value
* @param arguments the arguments passed to {@code function} in the expected positional order * @param arguments the arguments passed to {@code function} in the expected positional order
* @param cachedTarget the cached call target for {@code function} * @param cachedTarget the cached call target for {@code function}
@ -33,12 +34,14 @@ public abstract class ExecuteCallNode extends Node {
@Specialization(guards = "function.getCallTarget() == cachedTarget") @Specialization(guards = "function.getCallTarget() == cachedTarget")
protected Stateful callDirect( protected Stateful callDirect(
Function function, Function function,
CallerInfo callerInfo,
Object state, Object state,
Object[] arguments, Object[] arguments,
@Cached("function.getCallTarget()") RootCallTarget cachedTarget, @Cached("function.getCallTarget()") RootCallTarget cachedTarget,
@Cached("create(cachedTarget)") DirectCallNode callNode) { @Cached("create(cachedTarget)") DirectCallNode callNode) {
return (Stateful) return (Stateful)
callNode.call(Function.ArgumentsHelper.buildArguments(function, state, arguments)); callNode.call(
Function.ArgumentsHelper.buildArguments(function, callerInfo, state, arguments));
} }
/** /**
@ -48,6 +51,7 @@ public abstract class ExecuteCallNode extends Node {
* provided function. This is much slower and should, in general, be avoided. * provided function. This is much slower and should, in general, be avoided.
* *
* @param function the function to execute * @param function the function to execute
* @param callerInfo the caller info to pass to the function
* @param state the current state value * @param state the current state value
* @param arguments the arguments passed to {@code function} in the expected positional order * @param arguments the arguments passed to {@code function} in the expected positional order
* @param callNode the cached call node for making indirect calls * @param callNode the cached call node for making indirect calls
@ -55,23 +59,34 @@ public abstract class ExecuteCallNode extends Node {
*/ */
@Specialization(replaces = "callDirect") @Specialization(replaces = "callDirect")
protected Stateful callIndirect( protected Stateful callIndirect(
Function function, Object state, Object[] arguments, @Cached IndirectCallNode callNode) { Function function,
CallerInfo callerInfo,
Object state,
Object[] arguments,
@Cached IndirectCallNode callNode) {
return (Stateful) return (Stateful)
callNode.call( callNode.call(
function.getCallTarget(), function.getCallTarget(),
Function.ArgumentsHelper.buildArguments(function, state, arguments)); Function.ArgumentsHelper.buildArguments(function, callerInfo, state, arguments));
} }
/** /**
* Executes the function call. * Executes the function call.
* *
* @param function the function to execute * @param function the function to execute
* @param callerInfo the caller info to pass to the function
* @param state the state value to pass to the function * @param state the state value to pass to the function
* @param arguments the arguments to be passed to {@code function} * @param arguments the arguments to be passed to {@code function}
* @return the result of executing {@code function} on {@code arguments} * @return the result of executing {@code function} on {@code arguments}
*/ */
public abstract Stateful executeCall(Object function, Object state, Object[] arguments); public abstract Stateful executeCall(
Object function, CallerInfo callerInfo, Object state, Object[] arguments);
/**
* Creates an instance of this node.
*
* @return an instance of this node
*/
public static ExecuteCallNode build() { public static ExecuteCallNode build() {
return ExecuteCallNodeGen.create(); return ExecuteCallNodeGen.create();
} }

View File

@ -1,10 +1,8 @@
package org.enso.interpreter.node.callable; package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameUtil; import com.oracle.truffle.api.frame.FrameUtil;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.ExpressionNode;

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.enso.interpreter.Constants; import org.enso.interpreter.Constants;
import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.argument.ThunkExecutorNode; import org.enso.interpreter.node.callable.argument.ThunkExecutorNode;
@ -114,26 +115,30 @@ public abstract class InvokeCallableNode extends BaseNode {
* Invokes a function directly on the arguments contained in this node. * Invokes a function directly on the arguments contained in this node.
* *
* @param function the function to be executed * @param function the function to be executed
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments to the function * @param arguments the arguments to the function
* @return the result of executing {@code callable} on the known arguments * @return the result of executing {@code callable} on the known arguments
*/ */
@Specialization @Specialization
public Stateful invokeFunction(Function function, Object state, Object[] arguments) { Stateful invokeFunction(
return this.argumentSorter.execute(function, state, arguments); Function function, VirtualFrame callerFrame, Object state, Object[] arguments) {
return this.argumentSorter.execute(function, callerFrame, state, arguments);
} }
/** /**
* Invokes a constructor directly on the arguments contained in this node. * Invokes a constructor directly on the arguments contained in this node.
* *
* @param constructor the constructor to be executed * @param constructor the constructor to be executed
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments to the constructor * @param arguments the arguments to the constructor
* @return the result of executing {@code constructor} on the known arguments * @return the result of executing {@code constructor} on the known arguments
*/ */
@Specialization @Specialization
public Stateful invokeConstructor(AtomConstructor constructor, Object state, Object[] arguments) { Stateful invokeConstructor(
return invokeFunction(constructor.getConstructorFunction(), state, arguments); AtomConstructor constructor, VirtualFrame callerFrame, Object state, Object[] arguments) {
return invokeFunction(constructor.getConstructorFunction(), callerFrame, state, arguments);
} }
/** /**
@ -141,12 +146,14 @@ public abstract class InvokeCallableNode extends BaseNode {
* argument. * argument.
* *
* @param symbol the name of the requested symbol * @param symbol the name of the requested symbol
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments to the dynamic symbol * @param arguments the arguments to the dynamic symbol
* @return the result of resolving and executing the symbol for the {@code this} argument * @return the result of resolving and executing the symbol for the {@code this} argument
*/ */
@Specialization @Specialization
public Stateful invokeDynamicSymbol(UnresolvedSymbol symbol, Object state, Object[] arguments) { public Stateful invokeDynamicSymbol(
UnresolvedSymbol symbol, VirtualFrame callerFrame, Object state, Object[] arguments) {
if (canApplyThis) { if (canApplyThis) {
Object selfArgument = arguments[thisArgumentPosition]; Object selfArgument = arguments[thisArgumentPosition];
if (argumentsExecutionMode.shouldExecute()) { if (argumentsExecutionMode.shouldExecute()) {
@ -159,7 +166,7 @@ public abstract class InvokeCallableNode extends BaseNode {
state = selfResult.getState(); state = selfResult.getState();
} }
Function function = methodResolverNode.execute(symbol, selfArgument); Function function = methodResolverNode.execute(symbol, selfArgument);
return this.argumentSorter.execute(function, state, arguments); return this.argumentSorter.execute(function, callerFrame, state, arguments);
} else { } else {
throw new RuntimeException("Currying without `this` argument is not yet supported."); throw new RuntimeException("Currying without `this` argument is not yet supported.");
} }
@ -172,12 +179,14 @@ public abstract class InvokeCallableNode extends BaseNode {
* NotInvokableException} to signal this. * NotInvokableException} to signal this.
* *
* @param callable the callable to be executed * @param callable the callable to be executed
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments to the callable * @param arguments the arguments to the callable
* @return error * @return error
*/ */
@Fallback @Fallback
public Stateful invokeGeneric(Object callable, Object state, Object[] arguments) { public Stateful invokeGeneric(
Object callable, VirtualFrame callerFrame, Object state, Object[] arguments) {
throw new NotInvokableException(callable, this); throw new NotInvokableException(callable, this);
} }
@ -185,11 +194,13 @@ public abstract class InvokeCallableNode extends BaseNode {
* Executes the provided {@code callable} on the supplied {@code arguments}. * Executes the provided {@code callable} on the supplied {@code arguments}.
* *
* @param callable the callable to evaluate * @param callable the callable to evaluate
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments to evaluate {@code callable} on * @param arguments the arguments to evaluate {@code callable} on
* @return the result of executing {@code callable} on the supplied {@code arguments} * @return the result of executing {@code callable} on the supplied {@code arguments}
*/ */
public abstract Stateful execute(Object callable, Object state, Object[] arguments); public abstract Stateful execute(
Object callable, VirtualFrame callerFrame, Object state, Object[] arguments);
/** /**
* Sets whether or not the current node is tail-recursive. * Sets whether or not the current node is tail-recursive.

View File

@ -38,15 +38,13 @@ public abstract class ThunkExecutorNode extends Node {
@Cached("createLoopingOptimizerIfNeeded()") @Cached("createLoopingOptimizerIfNeeded()")
LoopingCallOptimiserNode loopingCallOptimiserNode) { LoopingCallOptimiserNode loopingCallOptimiserNode) {
if (getIsTail()) { if (getIsTail()) {
return (Stateful) return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
} else { } else {
try { try {
return (Stateful) return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
} catch (TailCallException e) { } catch (TailCallException e) {
return loopingCallOptimiserNode.executeDispatch( return loopingCallOptimiserNode.executeDispatch(
e.getFunction(), e.getState(), e.getArguments()); e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
} }
} }
} }
@ -58,14 +56,19 @@ public abstract class ThunkExecutorNode extends Node {
@Cached IndirectCallNode callNode, @Cached IndirectCallNode callNode,
@Cached("createLoopingOptimizerIfNeeded()") @Cached("createLoopingOptimizerIfNeeded()")
LoopingCallOptimiserNode loopingCallOptimiserNode) { LoopingCallOptimiserNode loopingCallOptimiserNode) {
try { if (getIsTail()) {
return (Stateful) return (Stateful)
callNode.call( callNode.call(
thunk.getCallTarget(), thunk.getCallTarget(), Function.ArgumentsHelper.buildArguments(thunk, state));
Function.ArgumentsHelper.buildArguments(thunk, state)); } else {
} catch (TailCallException e) { try {
return loopingCallOptimiserNode.executeDispatch( return (Stateful)
e.getFunction(), e.getState(), e.getArguments()); callNode.call(
thunk.getCallTarget(), Function.ArgumentsHelper.buildArguments(thunk, state));
} catch (TailCallException e) {
return loopingCallOptimiserNode.executeDispatch(
e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
}
} }
} }

View File

@ -3,11 +3,11 @@ package org.enso.interpreter.node.callable.argument.sorter;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.Constants; import org.enso.interpreter.Constants;
import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.InvokeCallableNode; import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.node.callable.dispatch.CallOptimiserNode;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.state.Stateful; import org.enso.interpreter.runtime.state.Stateful;
@ -50,11 +50,11 @@ public abstract class ArgumentSorterNode extends BaseNode {
* matches with the one stored in the cached argument sorter object. * matches with the one stored in the cached argument sorter object.
* *
* @param function the function to sort arguments for * @param function the function to sort arguments for
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments being passed to {@code callable} * @param arguments the arguments being passed to {@code callable}
* @param mappingNode a cached node that tracks information about the mapping to enable a fast * @param mappingNode a cached node that tracks information about the mapping to enable a fast
* path * path
* @param optimiser a cached call optimizer node, capable of performing the actual function call
* @return the result of applying the function with remapped arguments * @return the result of applying the function with remapped arguments
*/ */
@Specialization( @Specialization(
@ -62,12 +62,13 @@ public abstract class ArgumentSorterNode extends BaseNode {
limit = Constants.CacheSizes.ARGUMENT_SORTER_NODE) limit = Constants.CacheSizes.ARGUMENT_SORTER_NODE)
public Stateful invokeCached( public Stateful invokeCached(
Function function, Function function,
VirtualFrame callerFrame,
Object state, Object state,
Object[] arguments, Object[] arguments,
@Cached( @Cached(
"build(function, getSchema(), getDefaultsExecutionMode(), getArgumentsExecutionMode(), isTail())") "build(function, getSchema(), getDefaultsExecutionMode(), getArgumentsExecutionMode(), isTail())")
CachedArgumentSorterNode mappingNode) { CachedArgumentSorterNode mappingNode) {
return mappingNode.execute(function, state, arguments); return mappingNode.execute(function, callerFrame, state, arguments);
} }
/** /**
@ -75,14 +76,17 @@ public abstract class ArgumentSorterNode extends BaseNode {
* perform any caching and is thus a slow-path operation. * perform any caching and is thus a slow-path operation.
* *
* @param function the function to execute. * @param function the function to execute.
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments to reorder and supply to the {@code function}. * @param arguments the arguments to reorder and supply to the {@code function}.
* @return the result of calling {@code function} with the supplied {@code arguments}. * @return the result of calling {@code function} with the supplied {@code arguments}.
*/ */
@Specialization(replaces = "invokeCached") @Specialization(replaces = "invokeCached")
public Stateful invokeUncached(Function function, Object state, Object[] arguments) { public Stateful invokeUncached(
Function function, VirtualFrame callerFrame, Object state, Object[] arguments) {
return invokeCached( return invokeCached(
function, function,
callerFrame,
state, state,
arguments, arguments,
CachedArgumentSorterNode.build( CachedArgumentSorterNode.build(
@ -97,11 +101,13 @@ public abstract class ArgumentSorterNode extends BaseNode {
* Executes the {@link ArgumentSorterNode} to reorder the arguments. * Executes the {@link ArgumentSorterNode} to reorder the arguments.
* *
* @param callable the function to sort arguments for * @param callable the function to sort arguments for
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments being passed to {@code function} * @param arguments the arguments being passed to {@code function}
* @return the result of executing the {@code function} with reordered {@code arguments} * @return the result of executing the {@code function} with reordered {@code arguments}
*/ */
public abstract Stateful execute(Function callable, Object state, Object[] arguments); public abstract Stateful execute(
Function callable, VirtualFrame callerFrame, Object state, Object[] arguments);
CallArgumentInfo[] getSchema() { CallArgumentInfo[] getSchema() {
return schema; return schema;

View File

@ -1,20 +1,23 @@
package org.enso.interpreter.node.callable.argument.sorter; package org.enso.interpreter.node.callable.argument.sorter;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.CaptureCallerInfoNode;
import org.enso.interpreter.node.callable.ExecuteCallNode; import org.enso.interpreter.node.callable.ExecuteCallNode;
import org.enso.interpreter.node.callable.InvokeCallableNode; import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.node.callable.InvokeCallableNodeGen; import org.enso.interpreter.node.callable.InvokeCallableNodeGen;
import org.enso.interpreter.node.callable.argument.ThunkExecutorNode; import org.enso.interpreter.node.callable.argument.ThunkExecutorNode;
import org.enso.interpreter.node.callable.dispatch.CallOptimiserNode; import org.enso.interpreter.node.callable.dispatch.CallOptimiserNode;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo.ArgumentMapping; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo.ArgumentMapping;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo.ArgumentMappingBuilder; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo.ArgumentMappingBuilder;
import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.callable.argument.Thunk;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.control.TailCallException; import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.state.Stateful; import org.enso.interpreter.runtime.state.Stateful;
@ -35,6 +38,7 @@ public class CachedArgumentSorterNode extends BaseNode {
private final InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode; private final InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode;
private @Child ExecuteCallNode directCall; private @Child ExecuteCallNode directCall;
private @Child CallOptimiserNode loopingCall; private @Child CallOptimiserNode loopingCall;
private @Child CaptureCallerInfoNode captureCallerInfoNode;
/** /**
* Creates a node that generates and then caches the argument mapping. * Creates a node that generates and then caches the argument mapping.
@ -64,8 +68,11 @@ public class CachedArgumentSorterNode extends BaseNode {
initializeOversaturatedCallNode(defaultsExecutionMode, argumentsExecutionMode); initializeOversaturatedCallNode(defaultsExecutionMode, argumentsExecutionMode);
argumentShouldExecute = this.mapping.getArgumentShouldExecute(); argumentShouldExecute = this.mapping.getArgumentShouldExecute();
initializeCallNodes(); initializeCallNodes();
if (originalFunction.getSchema().getCallerFrameAccess().shouldFrameBePassed()) {
this.captureCallerInfoNode = CaptureCallerInfoNode.build();
}
} }
private void initializeCallNodes() { private void initializeCallNodes() {
@ -156,11 +163,17 @@ public class CachedArgumentSorterNode extends BaseNode {
* Reorders the provided arguments into the necessary order for the cached callable. * Reorders the provided arguments into the necessary order for the cached callable.
* *
* @param function the function this node is reordering arguments for * @param function the function this node is reordering arguments for
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments to reorder * @param arguments the arguments to reorder
* @return the provided {@code arguments} in the order expected by the cached {@link Function} * @return the provided {@code arguments} in the order expected by the cached {@link Function}
*/ */
public Stateful execute(Function function, Object state, Object[] arguments) { public Stateful execute(
Function function, VirtualFrame callerFrame, Object state, Object[] arguments) {
CallerInfo callerInfo = null;
if (captureCallerInfoNode != null) {
callerInfo = captureCallerInfoNode.execute(callerFrame);
}
if (argumentsExecutionMode.shouldExecute()) { if (argumentsExecutionMode.shouldExecute()) {
state = executeArguments(arguments, state); state = executeArguments(arguments, state);
} }
@ -169,13 +182,14 @@ public class CachedArgumentSorterNode extends BaseNode {
if (this.appliesFully()) { if (this.appliesFully()) {
if (!postApplicationSchema.hasOversaturatedArgs()) { if (!postApplicationSchema.hasOversaturatedArgs()) {
return doCall(function, state, mappedAppliedArguments); return doCall(function, callerInfo, state, mappedAppliedArguments);
} else { } else {
Stateful evaluatedVal = Stateful evaluatedVal =
loopingCall.executeDispatch(function, state, mappedAppliedArguments); loopingCall.executeDispatch(function, callerInfo, state, mappedAppliedArguments);
return this.oversaturatedCallableNode.execute( return this.oversaturatedCallableNode.execute(
evaluatedVal.getValue(), evaluatedVal.getValue(),
callerFrame,
evaluatedVal.getState(), evaluatedVal.getState(),
generateOversaturatedArguments(function, arguments)); generateOversaturatedArguments(function, arguments));
} }
@ -232,13 +246,14 @@ public class CachedArgumentSorterNode extends BaseNode {
return oversaturatedArguments; return oversaturatedArguments;
} }
private Stateful doCall(Function function, Object state, Object[] arguments) { private Stateful doCall(
Function function, CallerInfo callerInfo, Object state, Object[] arguments) {
if (getOriginalFunction().getCallStrategy().shouldCallDirect(isTail())) { if (getOriginalFunction().getCallStrategy().shouldCallDirect(isTail())) {
return directCall.executeCall(function, state, arguments); return directCall.executeCall(function, callerInfo, state, arguments);
} else if (isTail()) { } else if (isTail()) {
throw new TailCallException(function, state, arguments); throw new TailCallException(function, callerInfo, state, arguments);
} else { } else {
return loopingCall.executeDispatch(function, state, arguments); return loopingCall.executeDispatch(function, callerInfo, state, arguments);
} }
} }

View File

@ -1,6 +1,7 @@
package org.enso.interpreter.node.callable.dispatch; package org.enso.interpreter.node.callable.dispatch;
import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.state.Stateful; import org.enso.interpreter.runtime.state.Stateful;
/** /**
@ -13,10 +14,13 @@ public abstract class CallOptimiserNode extends Node {
* Calls the provided {@code callable} using the provided {@code arguments}. * Calls the provided {@code callable} using the provided {@code arguments}.
* *
* @param callable the callable to execute * @param callable the callable to execute
* @param callerInfo the caller info to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to {@code callable} * @param arguments the arguments to {@code callable}
* @return the result of executing {@code callable} using {@code arguments} * @return the result of executing {@code callable} using {@code arguments}
*/ */
public abstract Stateful executeDispatch(Object callable, Object state, Object[] arguments); public abstract Stateful executeDispatch(
Object callable, CallerInfo callerInfo, Object state, Object[] arguments);
/** /**
* Creates an instance of default implementation of {@link CallOptimiserNode}. * Creates an instance of default implementation of {@link CallOptimiserNode}.

View File

@ -7,6 +7,7 @@ import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RepeatingNode; import com.oracle.truffle.api.nodes.RepeatingNode;
import org.enso.interpreter.node.callable.ExecuteCallNode; import org.enso.interpreter.node.callable.ExecuteCallNode;
import org.enso.interpreter.node.callable.ExecuteCallNodeGen; import org.enso.interpreter.node.callable.ExecuteCallNodeGen;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.control.TailCallException; import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.state.Stateful; import org.enso.interpreter.runtime.state.Stateful;
@ -33,14 +34,17 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
* Calls the provided {@code function} using the provided {@code arguments}. * Calls the provided {@code function} using the provided {@code arguments}.
* *
* @param function the function to execute * @param function the function to execute
* @param callerInfo the caller info to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments to {@code function} * @param arguments the arguments to {@code function}
* @return the result of executing {@code function} using {@code arguments} * @return the result of executing {@code function} using {@code arguments}
*/ */
@Override @Override
public Stateful executeDispatch(Object function, Object state, Object[] arguments) { public Stateful executeDispatch(
Object function, CallerInfo callerInfo, Object state, Object[] arguments) {
VirtualFrame frame = Truffle.getRuntime().createVirtualFrame(null, loopFrameDescriptor); VirtualFrame frame = Truffle.getRuntime().createVirtualFrame(null, loopFrameDescriptor);
((RepeatedCallNode) loopNode.getRepeatingNode()).setNextCall(frame, function, state, arguments); ((RepeatedCallNode) loopNode.getRepeatingNode())
.setNextCall(frame, function, callerInfo, state, arguments);
loopNode.executeLoop(frame); loopNode.executeLoop(frame);
return ((RepeatedCallNode) loopNode.getRepeatingNode()).getResult(frame); return ((RepeatedCallNode) loopNode.getRepeatingNode()).getResult(frame);
@ -56,6 +60,7 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
private final FrameSlot functionSlot; private final FrameSlot functionSlot;
private final FrameSlot argsSlot; private final FrameSlot argsSlot;
private final FrameSlot stateSlot; private final FrameSlot stateSlot;
private final FrameSlot callerInfoSlot;
@Child private ExecuteCallNode dispatchNode; @Child private ExecuteCallNode dispatchNode;
/** /**
@ -68,6 +73,7 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
resultSlot = descriptor.findOrAddFrameSlot("<TCO Result>", FrameSlotKind.Object); resultSlot = descriptor.findOrAddFrameSlot("<TCO Result>", FrameSlotKind.Object);
argsSlot = descriptor.findOrAddFrameSlot("<TCO Arguments>", FrameSlotKind.Object); argsSlot = descriptor.findOrAddFrameSlot("<TCO Arguments>", FrameSlotKind.Object);
stateSlot = descriptor.findOrAddFrameSlot("<TCO State>", FrameSlotKind.Object); stateSlot = descriptor.findOrAddFrameSlot("<TCO State>", FrameSlotKind.Object);
callerInfoSlot = descriptor.findOrAddFrameSlot("<TCO Caller Info>", FrameSlotKind.Object);
dispatchNode = ExecuteCallNodeGen.create(); dispatchNode = ExecuteCallNodeGen.create();
} }
@ -76,11 +82,18 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
* *
* @param frame the stack frame for execution * @param frame the stack frame for execution
* @param function the function to execute in {@code frame} * @param function the function to execute in {@code frame}
* @param callerInfo the caller info to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments to execute {@code function} with * @param arguments the arguments to execute {@code function} with
*/ */
public void setNextCall(VirtualFrame frame, Object function, Object state, Object[] arguments) { public void setNextCall(
VirtualFrame frame,
Object function,
CallerInfo callerInfo,
Object state,
Object[] arguments) {
frame.setObject(functionSlot, function); frame.setObject(functionSlot, function);
frame.setObject(callerInfoSlot, callerInfo);
frame.setObject(stateSlot, state); frame.setObject(stateSlot, state);
frame.setObject(argsSlot, arguments); frame.setObject(argsSlot, arguments);
} }
@ -95,6 +108,12 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
return (Stateful) FrameUtil.getObjectSafe(frame, resultSlot); return (Stateful) FrameUtil.getObjectSafe(frame, resultSlot);
} }
private CallerInfo getCallerInfo(VirtualFrame frame) {
CallerInfo result = (CallerInfo) FrameUtil.getObjectSafe(frame, callerInfoSlot);
frame.setObject(callerInfoSlot, null);
return result;
}
/** /**
* Generates the next call to be made during looping execution. * Generates the next call to be made during looping execution.
* *
@ -143,10 +162,12 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
Object function = getNextFunction(frame); Object function = getNextFunction(frame);
Object state = getNextState(frame); Object state = getNextState(frame);
Object[] arguments = getNextArgs(frame); Object[] arguments = getNextArgs(frame);
frame.setObject(resultSlot, dispatchNode.executeCall(function, state, arguments)); CallerInfo callerInfo = getCallerInfo(frame);
frame.setObject(
resultSlot, dispatchNode.executeCall(function, callerInfo, state, arguments));
return false; return false;
} catch (TailCallException e) { } catch (TailCallException e) {
setNextCall(frame, e.getFunction(), e.getState(), e.getArguments()); setNextCall(frame, e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
return true; return true;
} }
} }

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.node.callable.dispatch;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import org.enso.interpreter.node.callable.ExecuteCallNode; import org.enso.interpreter.node.callable.ExecuteCallNode;
import org.enso.interpreter.node.callable.ExecuteCallNodeGen; import org.enso.interpreter.node.callable.ExecuteCallNodeGen;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.control.TailCallException; import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.state.Stateful; import org.enso.interpreter.runtime.state.Stateful;
@ -19,19 +20,22 @@ public class SimpleCallOptimiserNode extends CallOptimiserNode {
* Calls the provided {@code function} using the provided {@code arguments}. * Calls the provided {@code function} using the provided {@code arguments}.
* *
* @param function the function to execute * @param function the function to execute
* @param callerInfo the caller info to pass to the function
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments to {@code function} * @param arguments the arguments to {@code function}
* @return the result of executing {@code function} using {@code arguments} * @return the result of executing {@code function} using {@code arguments}
*/ */
@Override @Override
public Stateful executeDispatch(Object function, Object state, Object[] arguments) { public Stateful executeDispatch(
Object function, CallerInfo callerInfo, Object state, Object[] arguments) {
try { try {
return executeCallNode.executeCall(function, state, arguments); return executeCallNode.executeCall(function, callerInfo, state, arguments);
} catch (TailCallException e) { } catch (TailCallException e) {
CompilerDirectives.transferToInterpreterAndInvalidate(); CompilerDirectives.transferToInterpreterAndInvalidate();
CallOptimiserNode replacement = new LoopingCallOptimiserNode(); CallOptimiserNode replacement = new LoopingCallOptimiserNode();
this.replace(replacement); this.replace(replacement);
return replacement.executeDispatch(e.getFunction(), e.getState(), e.getArguments()); return replacement.executeDispatch(
e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
} }
} }
} }

View File

@ -6,8 +6,8 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import org.enso.interpreter.node.ClosureRootNode; import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
/** /**
* This node is responsible for representing the definition of a function. It contains information * This node is responsible for representing the definition of a function. It contains information

View File

@ -56,10 +56,19 @@ public class ConstructorCaseNode extends CaseNode {
if (profile.profile(matcherVal == target.getConstructor())) { if (profile.profile(matcherVal == target.getConstructor())) {
Function function = branch.executeFunction(frame); Function function = branch.executeFunction(frame);
throw new BranchSelectedException( throw new BranchSelectedException(
executeCallNode.executeCall(function, state, target.getFields())); executeCallNode.executeCall(
function, null, state, target.getFields())); // Note [Caller Info For Case Branches]
} }
} }
/* Note [Caller Info For Case Branches]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* It is assumed that functions serving as pattern match logic branches are always function
* literals, not references, curried functions etc. Therefore, as function literals, they
* have no way of accessing the caller frame and can safely be passed null.
*/
/** /**
* Handles the function scrutinee case, by not matching it at all. * Handles the function scrutinee case, by not matching it at all.
* *

View File

@ -29,7 +29,9 @@ public class FallbackNode extends CaseNode {
private void execute(VirtualFrame frame, Object target) throws UnexpectedResultException { private void execute(VirtualFrame frame, Object target) throws UnexpectedResultException {
Function function = functionNode.executeFunction(frame); Function function = functionNode.executeFunction(frame);
Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot()); Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot());
throw new BranchSelectedException(executeCallNode.executeCall(function, state, new Object[0])); throw new BranchSelectedException(
executeCallNode.executeCall(
function, null, state, new Object[0])); // Note [Caller Info For Case Branches]
} }
/** /**

View File

@ -0,0 +1,55 @@
package org.enso.interpreter.node.expression.builtin.debug;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
import org.enso.interpreter.node.expression.debug.EvalNode;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.state.Stateful;
/** Root node for the builtin Debug.eval function. */
@NodeInfo(shortName = "Debug.eval", description = "Root node for the builtin Debug.eval function")
public class DebugEvalNode extends BuiltinRootNode {
private @Child EvalNode evalNode = EvalNode.build();
private DebugEvalNode(Language language) {
super(language);
evalNode.markTail();
}
/**
* Executes the function in the given execution frame.
*
* <p>Requires a non-null {@link CallerInfo} passed to it. The string argument containing code to
* evaluate is this function's second (non-this) argument.
*
* @param frame current execution frame
* @return
*/
@Override
public Stateful execute(VirtualFrame frame) {
CallerInfo callerInfo = Function.ArgumentsHelper.getCallerInfo(frame.getArguments());
Object state = Function.ArgumentsHelper.getState(frame.getArguments());
String code = (String) Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[1];
return evalNode.execute(callerInfo, state, code);
}
/**
* Returns a two argument function wrapping this node.
*
* @param language current language instance
* @return a function wrapping this node's logic
*/
public static Function makeFunction(Language language) {
return Function.fromBuiltinRootNodeWithCallerFrameAccess(
new DebugEvalNode(language),
FunctionSchema.CallStrategy.DIRECT_WHEN_TAIL,
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(1, "expression", ArgumentDefinition.ExecutionMode.EXECUTE));
}
}

View File

@ -6,10 +6,13 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.Language; import org.enso.interpreter.Language;
import org.enso.interpreter.node.callable.InvokeCallableNode; import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema; import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.Stateful; import org.enso.interpreter.runtime.state.Stateful;
import org.enso.interpreter.runtime.type.TypesGen; import org.enso.interpreter.runtime.type.TypesGen;
@ -45,7 +48,7 @@ public class CatchErrorNode extends BuiltinRootNode {
Object handler = arguments[1]; Object handler = arguments[1];
if (executionProfile.profile(TypesGen.isRuntimeError(scrutinee))) { if (executionProfile.profile(TypesGen.isRuntimeError(scrutinee))) {
return invokeCallableNode.execute( return invokeCallableNode.execute(
handler, state, new Object[] {TypesGen.asRuntimeError(scrutinee).getPayload()}); handler, frame, state, new Object[] {TypesGen.asRuntimeError(scrutinee).getPayload()});
} else { } else {
return new Stateful(state, scrutinee); return new Stateful(state, scrutinee);
} }

View File

@ -40,7 +40,9 @@ public class RunStateNode extends BuiltinRootNode {
if (thunksProfile.profile(TypesGen.isThunk(maybeThunk))) { if (thunksProfile.profile(TypesGen.isThunk(maybeThunk))) {
return new Stateful( return new Stateful(
state, state,
thunkExecutorNode.executeThunk(TypesGen.asThunk(maybeThunk), localState).getValue()); thunkExecutorNode
.executeThunk(TypesGen.asThunk(maybeThunk), localState)
.getValue());
} else { } else {
return new Stateful(state, maybeThunk); return new Stateful(state, maybeThunk);
} }

View File

@ -0,0 +1,95 @@
package org.enso.interpreter.node.expression.debug;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.Constants;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.node.callable.argument.ThunkExecutorNode;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.argument.Thunk;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.Stateful;
/** Node running Enso expressions passed to it as strings. */
@NodeInfo(description = "Evaluates code passed to it as string")
public abstract class EvalNode extends BaseNode {
/**
* Creates an instance of this node.
*
* @return an instance of this node
*/
public static EvalNode build() {
return EvalNodeGen.create();
}
/**
* Executes a string expression in the given execution context
*
* @param callerInfo captured information about the execution context
* @param state current value of the monadic state
* @param expression the string containing expression to evaluate
* @return the result of evaluating {@code expression} in the {@code callerInfo} context
*/
public abstract Stateful execute(CallerInfo callerInfo, Object state, String expression);
RootCallTarget parseExpression(LocalScope scope, ModuleScope moduleScope, String expression) {
LocalScope localScope = new LocalScope(scope);
Language language = lookupLanguageReference(Language.class).get();
ExpressionNode expr =
lookupContextReference(Language.class)
.get()
.compiler()
.runInline(expression, language, localScope, moduleScope);
ClosureRootNode framedNode =
new ClosureRootNode(
lookupLanguageReference(Language.class).get(),
localScope,
moduleScope,
expr,
null,
"<dynamic_eval>");
framedNode.setTail(isTail());
return Truffle.getRuntime().createCallTarget(framedNode);
}
@Specialization(
guards = {
"expression == cachedExpression",
"callerInfo.getLocalScope() == cachedCallerInfo.getLocalScope()",
"callerInfo.getModuleScope() == cachedCallerInfo.getModuleScope()",
},
limit = Constants.CacheSizes.EVAL_NODE)
Stateful doCached(
CallerInfo callerInfo,
Object state,
String expression,
@Cached("expression") String cachedExpression,
@Cached("callerInfo") CallerInfo cachedCallerInfo,
@Cached(
"parseExpression(callerInfo.getLocalScope(), callerInfo.getModuleScope(), expression)")
RootCallTarget cachedCallTarget,
@Cached("build(isTail())") ThunkExecutorNode thunkExecutorNode) {
Thunk thunk = new Thunk(cachedCallTarget, callerInfo.getFrame());
return thunkExecutorNode.executeThunk(thunk, state);
}
@Specialization
Stateful doUncached(
CallerInfo callerInfo,
Object state,
String expression,
@Cached("build(isTail())") ThunkExecutorNode thunkExecutorNode) {
RootCallTarget callTarget =
parseExpression(callerInfo.getLocalScope(), callerInfo.getModuleScope(), expression);
Thunk thunk = new Thunk(callTarget, callerInfo.getFrame());
return thunkExecutorNode.executeThunk(thunk, state);
}
}

View File

@ -2,10 +2,11 @@ package org.enso.interpreter.node.expression.literal;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.ExpressionNode;
/** A representation of integer literals in Enso. */ /** A representation of integer literals in Enso. */
@NodeInfo(shortName = "IntegerLiteral") @NodeInfo(shortName = "IntegerLiteral")
public final class IntegerLiteralNode extends LiteralNode { public final class IntegerLiteralNode extends ExpressionNode {
private final long value; private final long value;
/** /**

View File

@ -1,12 +0,0 @@
package org.enso.interpreter.node.expression.literal;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.ExpressionNode;
/**
* A base class for all Enso literals.
*
* <p>It exists only to enable working with literal values as an aggregate.
*/
@NodeInfo(shortName = "Literal", description = "A representation of literal values.")
public abstract class LiteralNode extends ExpressionNode {}

View File

@ -0,0 +1,31 @@
package org.enso.interpreter.node.expression.literal;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.ExpressionNode;
/** Node representing a constant String value. */
@NodeInfo(shortName = "StringLiteral", description = "Constant string literal expression")
public class StringLiteralNode extends ExpressionNode {
private final String value;
/**
* Creates a new instance of this node.
*
* @param value the literal value this node represents
*/
public StringLiteralNode(String value) {
this.value = value;
}
/**
* Returns the constant value of this string literal.
*
* @param frame the stack frame for execution
* @return the string value this node was created with
*/
@Override
public Object executeGeneric(VirtualFrame frame) {
return value;
}
}

View File

@ -2,6 +2,7 @@ package org.enso.interpreter.runtime;
import org.enso.interpreter.Language; import org.enso.interpreter.Language;
import org.enso.interpreter.node.expression.builtin.IfZeroNode; import org.enso.interpreter.node.expression.builtin.IfZeroNode;
import org.enso.interpreter.node.expression.builtin.debug.DebugEvalNode;
import org.enso.interpreter.node.expression.builtin.error.CatchErrorNode; import org.enso.interpreter.node.expression.builtin.error.CatchErrorNode;
import org.enso.interpreter.node.expression.builtin.error.CatchPanicNode; import org.enso.interpreter.node.expression.builtin.error.CatchPanicNode;
import org.enso.interpreter.node.expression.builtin.error.PanicNode; import org.enso.interpreter.node.expression.builtin.error.PanicNode;
@ -38,6 +39,7 @@ public class Builtins {
AtomConstructor panic = new AtomConstructor("Panic", scope).initializeFields(); AtomConstructor panic = new AtomConstructor("Panic", scope).initializeFields();
AtomConstructor error = new AtomConstructor("Error", scope).initializeFields(); AtomConstructor error = new AtomConstructor("Error", scope).initializeFields();
AtomConstructor state = new AtomConstructor("State", scope).initializeFields(); AtomConstructor state = new AtomConstructor("State", scope).initializeFields();
AtomConstructor debug = new AtomConstructor("Debug", scope).initializeFields();
scope.registerConstructor(cons); scope.registerConstructor(cons);
scope.registerConstructor(nil); scope.registerConstructor(nil);
@ -46,6 +48,7 @@ public class Builtins {
scope.registerConstructor(panic); scope.registerConstructor(panic);
scope.registerConstructor(error); scope.registerConstructor(error);
scope.registerConstructor(state); scope.registerConstructor(state);
scope.registerConstructor(debug);
scope.registerMethod(io, "println", PrintNode.makeFunction(language)); scope.registerMethod(io, "println", PrintNode.makeFunction(language));
@ -59,6 +62,8 @@ public class Builtins {
scope.registerMethod(state, "get", GetStateNode.makeFunction(language)); scope.registerMethod(state, "get", GetStateNode.makeFunction(language));
scope.registerMethod(state, "put", PutStateNode.makeFunction(language)); scope.registerMethod(state, "put", PutStateNode.makeFunction(language));
scope.registerMethod(state, "run", RunStateNode.makeFunction(language)); scope.registerMethod(state, "run", RunStateNode.makeFunction(language));
scope.registerMethod(debug, "eval", DebugEvalNode.makeFunction(language));
} }
/** /**

View File

@ -0,0 +1,54 @@
package org.enso.interpreter.runtime.callable;
import com.oracle.truffle.api.frame.MaterializedFrame;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
/**
* Represents the caller execution context, to be passed to functions that declare the need for it.
*/
public class CallerInfo {
private final MaterializedFrame frame;
private final LocalScope localScope;
private final ModuleScope moduleScope;
/**
* Creates a new instance of caller information
*
* @param frame the caller's execution frame
* @param localScope the local scope caller uses
* @param moduleScope the module scope caller was defined in
*/
public CallerInfo(MaterializedFrame frame, LocalScope localScope, ModuleScope moduleScope) {
this.frame = frame;
this.localScope = localScope;
this.moduleScope = moduleScope;
}
/**
* Gets the caller's execution frame.
*
* @return the caller's execution frame
*/
public MaterializedFrame getFrame() {
return frame;
}
/**
* Gets the caller's local scope metadata.
*
* @return the caller's local scope metadata
*/
public LocalScope getLocalScope() {
return localScope;
}
/**
* Gets the caller's module scope.
*
* @return the caller's module scope.
*/
public ModuleScope getModuleScope() {
return moduleScope;
}
}

View File

@ -73,4 +73,9 @@ public class UnresolvedSymbol implements TruffleObject {
public Function resolveForError() { public Function resolveForError() {
return scope.lookupMethodDefinitionForAny(name).orElse(null); return scope.lookupMethodDefinitionForAny(name).orElse(null);
} }
@Override
public String toString() {
return "UnresolvedSymbol<" + this.name + ">";
}
} }

View File

@ -77,8 +77,8 @@ public class CallArgumentInfo {
private CallArgumentInfo[] existingOversaturatedArgs; private CallArgumentInfo[] existingOversaturatedArgs;
private boolean[] argumentUsed; private boolean[] argumentUsed;
private boolean[] callSiteArgApplied; private boolean[] callSiteArgApplied;
private FunctionSchema originalSchema;
private int oversaturatedWritePosition = 0; private int oversaturatedWritePosition = 0;
private FunctionSchema.CallStrategy callStrategy;
/** /**
* Creates an unitialised object of this class. This instance is not safe for external use and * Creates an unitialised object of this class. This instance is not safe for external use and
@ -97,7 +97,7 @@ public class CallArgumentInfo {
this.definitions = schema.getArgumentInfos(); this.definitions = schema.getArgumentInfos();
this.argumentUsed = schema.cloneHasPreApplied(); this.argumentUsed = schema.cloneHasPreApplied();
this.existingOversaturatedArgs = schema.cloneOversaturatedArgs(); this.existingOversaturatedArgs = schema.cloneOversaturatedArgs();
this.callStrategy = schema.getCallStrategy(); this.originalSchema = schema;
} }
/** /**
@ -214,7 +214,12 @@ public class CallArgumentInfo {
this.existingOversaturatedArgs.length, this.existingOversaturatedArgs.length,
newOversaturatedArgInfo.length); newOversaturatedArgInfo.length);
return new FunctionSchema(callStrategy, definitions, argumentUsed, oversaturatedArgInfo); return new FunctionSchema(
originalSchema.getCallStrategy(),
originalSchema.getCallerFrameAccess(),
definitions,
argumentUsed,
oversaturatedArgInfo);
} }
} }

View File

@ -3,15 +3,15 @@ package org.enso.interpreter.runtime.callable.atom;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.TruffleObject;
import org.enso.interpreter.node.ClosureRootNode; import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.node.callable.argument.ReadArgumentNode; import org.enso.interpreter.node.callable.argument.ReadArgumentNode;
import org.enso.interpreter.node.expression.atom.InstantiateNode; import org.enso.interpreter.node.expression.atom.InstantiateNode;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope; import org.enso.interpreter.runtime.scope.ModuleScope;
/** A representation of an Atom constructor. */ /** A representation of an Atom constructor. */
@ -66,7 +66,12 @@ public class AtomConstructor implements TruffleObject {
ExpressionNode instantiateNode = new InstantiateNode(this, argumentReaders); ExpressionNode instantiateNode = new InstantiateNode(this, argumentReaders);
ClosureRootNode rootNode = ClosureRootNode rootNode =
new ClosureRootNode( new ClosureRootNode(
null, new FrameDescriptor(), instantiateNode, null, "<constructor>:" + name); null,
new LocalScope(),
new ModuleScope(),
instantiateNode,
null,
"<constructor>:" + name);
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode); RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
return new Function( return new Function(
callTarget, null, new FunctionSchema(FunctionSchema.CallStrategy.ALWAYS_DIRECT, args)); callTarget, null, new FunctionSchema(FunctionSchema.CallStrategy.ALWAYS_DIRECT, args));

View File

@ -21,6 +21,7 @@ import org.enso.interpreter.node.callable.argument.sorter.ArgumentSorterNode;
import org.enso.interpreter.node.callable.argument.sorter.ArgumentSorterNodeGen; import org.enso.interpreter.node.callable.argument.sorter.ArgumentSorterNodeGen;
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
import org.enso.interpreter.runtime.Context; import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.argument.Thunk; import org.enso.interpreter.runtime.callable.argument.Thunk;
@ -43,6 +44,9 @@ public final class Function implements TruffleObject {
* @param preappliedArguments the preapplied arguments for this function. The layout of this array * @param preappliedArguments the preapplied arguments for this function. The layout of this array
* must be conforming to the {@code schema}. {@code null} is allowed if the function does not * must be conforming to the {@code schema}. {@code null} is allowed if the function does not
* have any partially applied arguments. * have any partially applied arguments.
* @param oversaturatedArguments the oversaturated arguments this function may have accumulated.
* The layout of this array must be conforming to the {@code schema}. @{code null} is allowed
* if the function does not carry any oversaturated arguments.
*/ */
public Function( public Function(
RootCallTarget callTarget, RootCallTarget callTarget,
@ -69,7 +73,7 @@ public final class Function implements TruffleObject {
} }
/** /**
* Creates a Function object from a {@link RootNode} and argument definitions. * Creates a Function object from a {@link BuiltinRootNode} and argument definitions.
* *
* @param node the {@link RootNode} for the function logic * @param node the {@link RootNode} for the function logic
* @param callStrategy the {@link FunctionSchema.CallStrategy} to use for this function * @param callStrategy the {@link FunctionSchema.CallStrategy} to use for this function
@ -83,6 +87,25 @@ public final class Function implements TruffleObject {
return new Function(callTarget, null, schema); return new Function(callTarget, null, schema);
} }
/**
* Creates a Function object from a {@link BuiltinRootNode} and argument definitions.
*
* <p>The root node wrapped using this method can safely assume the {@link CallerInfo} argument
* will be non-null.
*
* @param node the {@link RootNode} for the function logic
* @param callStrategy the {@link FunctionSchema.CallStrategy} to use for this function
* @param args argument definitons
* @return a Function object with specified behavior and arguments
*/
public static Function fromBuiltinRootNodeWithCallerFrameAccess(
BuiltinRootNode node, FunctionSchema.CallStrategy callStrategy, ArgumentDefinition... args) {
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
FunctionSchema schema =
new FunctionSchema(callStrategy, FunctionSchema.CallerFrameAccess.FULL, args);
return new Function(callTarget, null, schema);
}
/** /**
* Gets the target containing the function's code. * Gets the target containing the function's code.
* *
@ -194,7 +217,9 @@ public final class Function implements TruffleObject {
@CachedContext(Language.class) Context context, @CachedContext(Language.class) Context context,
@Cached(value = "arguments.length") int cachedArgsLength, @Cached(value = "arguments.length") int cachedArgsLength,
@Cached(value = "buildSorter(cachedArgsLength)") ArgumentSorterNode sorterNode) { @Cached(value = "buildSorter(cachedArgsLength)") ArgumentSorterNode sorterNode) {
return sorterNode.execute(function, context.getUnit().newInstance(), arguments).getValue(); return sorterNode
.execute(function, null, context.getUnit().newInstance(), arguments)
.getValue();
} }
/** /**
@ -234,8 +259,8 @@ public final class Function implements TruffleObject {
* @return an array containing the necessary information to call an Enso function * @return an array containing the necessary information to call an Enso function
*/ */
public static Object[] buildArguments( public static Object[] buildArguments(
Function function, Object state, Object[] positionalArguments) { Function function, CallerInfo callerInfo, Object state, Object[] positionalArguments) {
return new Object[] {function.getScope(), state, positionalArguments}; return new Object[] {function.getScope(), callerInfo, state, positionalArguments};
} }
/** /**
@ -246,36 +271,51 @@ public final class Function implements TruffleObject {
* @return an array containing the necessary information to call an Enso thunk * @return an array containing the necessary information to call an Enso thunk
*/ */
public static Object[] buildArguments(Thunk thunk, Object state) { public static Object[] buildArguments(Thunk thunk, Object state) {
return new Object[] {thunk.getScope(), state, new Object[0]}; return new Object[] {thunk.getScope(), null, state, new Object[0]};
} }
/** /**
* Gets the positional arguments out of the array. * Gets the positional arguments out of the array.
* *
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function, Object, * @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function,
* Object[])} * CallerInfo, Object, Object[])}
* @return the positional arguments to the function * @return the positional arguments to the function
*/ */
public static Object[] getPositionalArguments(Object[] arguments) { public static Object[] getPositionalArguments(Object[] arguments) {
return (Object[]) arguments[2]; return (Object[]) arguments[3];
} }
/** /**
* Gets the state out of the array. * Gets the state out of the array.
* *
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function, Object, * @param arguments an array produced by {@link
* Object[])} * ArgumentsHelper#buildArguments(Function,CallerInfo, Object, Object[])}
* @return the state for the function * @return the state for the function
*/ */
public static Object getState(Object[] arguments) { public static Object getState(Object[] arguments) {
return arguments[1]; return arguments[2];
}
/**
* Gets the caller info out of the array.
*
* <p>Any function using this method should declare {@link
* FunctionSchema.CallerFrameAccess#FULL} in its schema for the result to be guaranteed
* non-null.
*
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function,
* CallerInfo, Object, Object[])}
* @return the caller info for the function
*/
public static CallerInfo getCallerInfo(Object[] arguments) {
return (CallerInfo) arguments[1];
} }
/** /**
* Gets the function's local scope out of the array. * Gets the function's local scope out of the array.
* *
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function, Object, * @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function,
* Object[])} * CallerInfo, Object, Object[])}
* @return the local scope for the associated function * @return the local scope for the associated function
*/ */
public static MaterializedFrame getLocalScope(Object[] arguments) { public static MaterializedFrame getLocalScope(Object[] arguments) {

View File

@ -39,17 +39,38 @@ public class FunctionSchema {
} }
} }
/** Denotes the caller frame access functions with this schema require to run properly. */
public enum CallerFrameAccess {
/** Requires full access to the (materialized) caller frame. */
FULL,
/** Does not use the caller frame at all. */
NONE;
/**
* Is there any level of caller frame access required by the function?
*
* @return {@code true} if the function must be passed the caller frame, {@code false}
* otherwise.
*/
public boolean shouldFrameBePassed() {
return this != NONE;
}
}
private final @CompilationFinal(dimensions = 1) ArgumentDefinition[] argumentInfos; private final @CompilationFinal(dimensions = 1) ArgumentDefinition[] argumentInfos;
private final @CompilationFinal(dimensions = 1) boolean[] hasPreApplied; private final @CompilationFinal(dimensions = 1) boolean[] hasPreApplied;
private final @CompilationFinal(dimensions = 1) CallArgumentInfo[] oversaturatedArguments; private final @CompilationFinal(dimensions = 1) CallArgumentInfo[] oversaturatedArguments;
private final CallStrategy callStrategy; private final CallStrategy callStrategy;
private final boolean hasAnyPreApplied; private final boolean hasAnyPreApplied;
private final boolean hasOversaturatedArguments; private final boolean hasOversaturatedArguments;
private final CallerFrameAccess callerFrameAccess;
/** /**
* Creates an {@link FunctionSchema} instance. * Creates an {@link FunctionSchema} instance.
* *
* @param callStrategy the call strategy to use for functions having this schema * @param callStrategy the call strategy to use for functions having this schema
* @param callerFrameAccess the declaration of whether access to caller frame is required for this
* function
* @param argumentInfos Definition site arguments information * @param argumentInfos Definition site arguments information
* @param hasPreApplied A flags collection such that {@code hasPreApplied[i]} is true iff a * @param hasPreApplied A flags collection such that {@code hasPreApplied[i]} is true iff a
* function has a partially applied argument at position {@code i} * function has a partially applied argument at position {@code i}
@ -58,6 +79,7 @@ public class FunctionSchema {
*/ */
public FunctionSchema( public FunctionSchema(
CallStrategy callStrategy, CallStrategy callStrategy,
CallerFrameAccess callerFrameAccess,
ArgumentDefinition[] argumentInfos, ArgumentDefinition[] argumentInfos,
boolean[] hasPreApplied, boolean[] hasPreApplied,
CallArgumentInfo[] oversaturatedArguments) { CallArgumentInfo[] oversaturatedArguments) {
@ -65,8 +87,8 @@ public class FunctionSchema {
this.argumentInfos = argumentInfos; this.argumentInfos = argumentInfos;
this.oversaturatedArguments = oversaturatedArguments; this.oversaturatedArguments = oversaturatedArguments;
this.hasPreApplied = hasPreApplied; this.hasPreApplied = hasPreApplied;
this.callerFrameAccess = callerFrameAccess;
boolean hasAnyPreApplied = false; boolean hasAnyPreApplied = false;
for (boolean b : hasPreApplied) { for (boolean b : hasPreApplied) {
if (b) { if (b) {
hasAnyPreApplied = true; hasAnyPreApplied = true;
@ -82,10 +104,33 @@ public class FunctionSchema {
* Creates an {@link FunctionSchema} instance assuming the function has no partially applied * Creates an {@link FunctionSchema} instance assuming the function has no partially applied
* arguments. * arguments.
* *
* @param callStrategy the call strategy to use for this function
* @param callerFrameAccess the declaration of need to access the caller frame from the function
* @param argumentInfos Definition site arguments information
*/
public FunctionSchema(
CallStrategy callStrategy,
CallerFrameAccess callerFrameAccess,
ArgumentDefinition... argumentInfos) {
this(
callStrategy,
callerFrameAccess,
argumentInfos,
new boolean[argumentInfos.length],
new CallArgumentInfo[0]);
}
/**
* Creates an {@link FunctionSchema} instance assuming the function has no partially applied
* arguments.
*
* <p>Caller frame access is assumed to be {@link CallerFrameAccess#NONE}.
*
* @param callStrategy the call strategy to use for this function
* @param argumentInfos Definition site arguments information * @param argumentInfos Definition site arguments information
*/ */
public FunctionSchema(CallStrategy callStrategy, ArgumentDefinition... argumentInfos) { public FunctionSchema(CallStrategy callStrategy, ArgumentDefinition... argumentInfos) {
this(callStrategy, argumentInfos, new boolean[argumentInfos.length], new CallArgumentInfo[0]); this(callStrategy, CallerFrameAccess.NONE, argumentInfos);
} }
/** /**
@ -183,4 +228,13 @@ public class FunctionSchema {
public CallStrategy getCallStrategy() { public CallStrategy getCallStrategy() {
return callStrategy; return callStrategy;
} }
/**
* Returns the caller frame access declaration for this function.
*
* @return the caller frame access declaration
*/
public CallerFrameAccess getCallerFrameAccess() {
return callerFrameAccess;
}
} }

View File

@ -1,6 +1,7 @@
package org.enso.interpreter.runtime.control; package org.enso.interpreter.runtime.control;
import com.oracle.truffle.api.nodes.ControlFlowException; import com.oracle.truffle.api.nodes.ControlFlowException;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
/** /**
@ -10,6 +11,7 @@ import org.enso.interpreter.runtime.callable.function.Function;
*/ */
public class TailCallException extends ControlFlowException { public class TailCallException extends ControlFlowException {
private final Function function; private final Function function;
private final CallerInfo callerInfo;
private final Object state; private final Object state;
private final Object[] arguments; private final Object[] arguments;
@ -20,8 +22,10 @@ public class TailCallException extends ControlFlowException {
* @param state the state to pass to the function * @param state the state to pass to the function
* @param arguments the arguments to {@code function} * @param arguments the arguments to {@code function}
*/ */
public TailCallException(Function function, Object state, Object[] arguments) { public TailCallException(
Function function, CallerInfo callerInfo, Object state, Object[] arguments) {
this.function = function; this.function = function;
this.callerInfo = callerInfo;
this.arguments = arguments; this.arguments = arguments;
this.state = state; this.state = state;
} }
@ -45,11 +49,20 @@ public class TailCallException extends ControlFlowException {
} }
/** /**
* Gets the state to pass to the function * Gets the state to pass to the function.
* *
* @return the state to pass for next call * @return the state to pass for next call
*/ */
public Object getState() { public Object getState() {
return state; return state;
} }
/**
* Gets the caller info to pass to the function.
*
* @return the state to pass for next call
*/
public CallerInfo getCallerInfo() {
return callerInfo;
}
} }

View File

@ -14,24 +14,23 @@ import java.util.Optional;
* frames. * frames.
*/ */
public class LocalScope { public class LocalScope {
private Map<String, FrameSlot> items; public final Map<String, FrameSlot> items;
private FrameDescriptor frameDescriptor; private final FrameDescriptor frameDescriptor;
private LocalScope parent; public final LocalScope parent;
/** Creates a new local scope with defaulted arguments. */ /** Creates a root local scope. */
public LocalScope() { public LocalScope() {
items = new HashMap<>(); this(null);
frameDescriptor = new FrameDescriptor();
parent = null;
} }
/** /**
* Creates a new local scope with a known parent. * Creates a child local scope with a given parent.
* *
* @param parent the parent scope * @param parent the parent scope
*/ */
public LocalScope(LocalScope parent) { public LocalScope(LocalScope parent) {
this(); items = new HashMap<>();
frameDescriptor = new FrameDescriptor();
this.parent = parent; this.parent = parent;
} }

View File

@ -5,14 +5,17 @@ import com.oracle.truffle.api.source.Source
import org.enso.compiler.generate.AstToIr import org.enso.compiler.generate.AstToIr
import org.enso.compiler.ir.IR import org.enso.compiler.ir.IR
import org.enso.flexer.Reader import org.enso.flexer.Reader
import org.enso.interpreter.AstExpression
import org.enso.interpreter.Constants import org.enso.interpreter.Constants
import org.enso.interpreter.EnsoParser import org.enso.interpreter.EnsoParser
import org.enso.interpreter.Language import org.enso.interpreter.Language
import org.enso.interpreter.builder.ExpressionFactory
import org.enso.interpreter.builder.ModuleScopeExpressionFactory import org.enso.interpreter.builder.ModuleScopeExpressionFactory
import org.enso.interpreter.node.ExpressionNode import org.enso.interpreter.node.ExpressionNode
import org.enso.interpreter.runtime.Context import org.enso.interpreter.runtime.Context
import org.enso.interpreter.runtime.Module import org.enso.interpreter.runtime.Module
import org.enso.interpreter.runtime.error.ModuleDoesNotExistException import org.enso.interpreter.runtime.error.ModuleDoesNotExistException
import org.enso.interpreter.runtime.scope.LocalScope
import org.enso.interpreter.runtime.scope.ModuleScope import org.enso.interpreter.runtime.scope.ModuleScope
import org.enso.syntax.text.AST import org.enso.syntax.text.AST
import org.enso.syntax.text.Parser import org.enso.syntax.text.Parser
@ -88,6 +91,27 @@ class Compiler(
run(Source.newBuilder(Constants.LANGUAGE_ID, file).build) run(Source.newBuilder(Constants.LANGUAGE_ID, file).build)
} }
/**
* Processes the language source, interpreting it as an expression.
* Processes the source in the context of given local and module scopes.
*
* @param source string representing the expression to process
* @param language current language instance
* @param localScope local scope to process the source in
* @param moduleScope module scope to process the source in
* @return an expression node representing the parsed and analyzed source
*/
def runInline(
source: String,
language: Language,
localScope: LocalScope,
moduleScope: ModuleScope
): ExpressionNode = {
val parsed = parseInline(source)
new ExpressionFactory(language, localScope, "<inline_source>", moduleScope)
.run(parsed)
}
/** /**
* Finds and processes a language source by its qualified name. * Finds and processes a language source by its qualified name.
* *
@ -119,6 +143,17 @@ class Compiler(
resolvedAST resolvedAST
} }
/**
* Parses the provided language source expression in inline mode.
*
* @param source the code to parse
* @return an AST representation of `source`
*/
def parseInline(source: String): AstExpression = {
val parsed = new EnsoParser().parseEnsoInline(source)
parsed
}
/** /**
* Lowers the input AST to the compiler's high-level intermediate * Lowers the input AST to the compiler's high-level intermediate
* representation. * representation.

View File

@ -2,6 +2,8 @@ package org.enso.interpreter
import java.util.Optional import java.util.Optional
import org.apache.commons.lang3.StringEscapeUtils
import scala.collection.JavaConverters._ import scala.collection.JavaConverters._
import scala.language.postfixOps import scala.language.postfixOps
import scala.util.parsing.combinator._ import scala.util.parsing.combinator._
@ -42,6 +44,8 @@ trait AstExpressionVisitor[+T] {
): T ): T
def visitDesuspend(target: AstExpression): T def visitDesuspend(target: AstExpression): T
def visitStringLiteral(string: String): T
} }
trait AstModuleScopeVisitor[+T] { trait AstModuleScopeVisitor[+T] {
@ -144,6 +148,11 @@ case class AstLong(l: Long) extends AstExpression {
visitor.visitLong(l) visitor.visitLong(l)
} }
case class AstStringLiteral(string: String) extends AstExpression {
override def visit[T](visitor: AstExpressionVisitor[T]): T =
visitor.visitStringLiteral(string)
}
case class AstArithOp(op: String, left: AstExpression, right: AstExpression) case class AstArithOp(op: String, left: AstExpression, right: AstExpression)
extends AstExpression { extends AstExpression {
override def visit[T](visitor: AstExpressionVisitor[T]): T = override def visit[T](visitor: AstExpressionVisitor[T]): T =
@ -261,6 +270,12 @@ class EnsoParserInternal extends JavaTokenParsers {
def foreignLiteral: Parser[String] = "**" ~> "[^\\*]*".r <~ "**" def foreignLiteral: Parser[String] = "**" ~> "[^\\*]*".r <~ "**"
def string: Parser[AstStringLiteral] = stringLiteral ^^ { lit =>
AstStringLiteral(
StringEscapeUtils.unescapeJava(lit.substring(1, lit.length - 1))
)
}
def variable: Parser[AstVariable] = ident ^^ AstVariable def variable: Parser[AstVariable] = ident ^^ AstVariable
def operand: Parser[AstExpression] = def operand: Parser[AstExpression] =
@ -273,7 +288,7 @@ class EnsoParserInternal extends JavaTokenParsers {
} }
def expression: Parser[AstExpression] = def expression: Parser[AstExpression] =
desuspend | matchClause | arith | function desuspend | matchClause | arith | function | string
def functionCall: Parser[AstApply] = def functionCall: Parser[AstApply] =
"@" ~> expression ~ (argList ?) ~ defaultSuspend ^^ { "@" ~> expression ~ (argList ?) ~ defaultSuspend ^^ {
@ -351,6 +366,10 @@ class EnsoParserInternal extends JavaTokenParsers {
def parse(code: String): AstExpression = { def parse(code: String): AstExpression = {
parseAll(expression | function, code).get parseAll(expression | function, code).get
} }
def parseLine(code: String): AstExpression = {
parseAll(statement, code).get
}
} }
class EnsoParser { class EnsoParser {
@ -358,4 +377,8 @@ class EnsoParser {
def parseEnso(code: String): AstModuleScope = { def parseEnso(code: String): AstModuleScope = {
new EnsoParserInternal().parseGlobalScope(code) new EnsoParserInternal().parseGlobalScope(code)
} }
def parseEnsoInline(code: String): AstExpression = {
new EnsoParserInternal().parseLine(code)
}
} }

View File

@ -1,7 +1,6 @@
package org.enso.interpreter.test package org.enso.interpreter.test
import org.graalvm.polyglot.PolyglotException import org.graalvm.polyglot.PolyglotException
import org.graalvm.polyglot.Value
case class InterpreterException( case class InterpreterException(
@transient polyglotException: PolyglotException @transient polyglotException: PolyglotException

View File

@ -3,10 +3,8 @@ package org.enso.interpreter.test
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import org.enso.interpreter.Constants import org.enso.interpreter.Constants
import org.graalvm.polyglot.Context import org.graalvm.polyglot.{Context, Value}
import org.graalvm.polyglot.Value import org.scalatest.{FlatSpec, Matchers}
import org.scalatest.FlatSpec
import org.scalatest.Matchers
trait InterpreterRunner { trait InterpreterRunner {
implicit class RichValue(value: Value) { implicit class RichValue(value: Value) {

View File

@ -1,7 +1,6 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.InterpreterException import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
import org.enso.interpreter.test.InterpreterTest
class ErrorsTest extends InterpreterTest { class ErrorsTest extends InterpreterTest {
"Panics" should "be thrown and stop evaluation" in { "Panics" should "be thrown and stop evaluation" in {

View File

@ -0,0 +1,85 @@
package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.InterpreterTest
class EvalTest extends InterpreterTest {
"Debug.eval" should "evaluate a string expression" in {
val code =
"""
|@{
| @eval [@Debug, "@println[@IO, \"foo\"]"]
|}
|""".stripMargin
eval(code)
consumeOut shouldEqual List("foo")
}
"Debug.eval" should "have access to the caller scope" in {
val code =
"""
|@{
| x = "Hello World!";
| @eval [@Debug, "@println[@IO, x]"]
|}
|""".stripMargin
eval(code)
consumeOut shouldEqual List("Hello World!")
}
"Debug.eval" should "have access to the caller module scope" in {
val code =
"""
|type MyType x;
|
|@{
| x = 10;
| @eval [@Debug, "@println[@IO, @MyType[x]]"]
|}
|""".stripMargin
eval(code)
consumeOut shouldEqual List("MyType<10>")
}
"Debug.eval" should "return a value usable in the caller scope" in {
val code =
"""
|@{
| x = 1;
| y = 2;
| res = @eval [@Debug, "x + y"];
| res + 1
|}
|""".stripMargin
eval(code) shouldEqual 4
}
"Debug.eval" should "work in a recursive setting" in {
val code =
"""
|{ |sumTo|
| summator = { |acc, current|
| @eval [@Debug, "@ifZero [current, acc, @summator [acc + current, current - 1]]"]
| };
| res = @summator [0, sumTo];
| res
|}
|""".stripMargin
val fun = eval(code)
fun.call(100) shouldEqual 5050
}
"Debug.eval" should "work inside a thunk passed to another function" in {
val code =
"""
|{ |sumTo|
| summator = { |acc, current|
| @ifZero [current, acc, @eval [@Debug, "@summator [acc + current, current - 1]"]]
| };
| res = @summator [0, sumTo];
| res
|}
|""".stripMargin
val fun = eval(code)
fun.call(100) shouldEqual 5050
}
}

View File

@ -1,7 +1,6 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.InterpreterException import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
import org.enso.interpreter.test.InterpreterTest
class GlobalScopeTest extends InterpreterTest { class GlobalScopeTest extends InterpreterTest {

View File

@ -1,7 +1,6 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.{InterpreterException, InterpreterTest} import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
import org.graalvm.polyglot.PolyglotException
class NamedArgumentsTest extends InterpreterTest { class NamedArgumentsTest extends InterpreterTest {
"Functions" should "take arguments by name and use them in their bodies" in { "Functions" should "take arguments by name and use them in their bodies" in {

View File

@ -4,14 +4,10 @@ import java.io.File
import org.enso.interpreter.Constants import org.enso.interpreter.Constants
import org.enso.interpreter.runtime.RuntimeOptions import org.enso.interpreter.runtime.RuntimeOptions
import org.enso.interpreter.test.InterpreterException import org.enso.interpreter.test.{InterpreterException, ValueEquality}
import org.enso.interpreter.test.ValueEquality
import org.enso.pkg.Package import org.enso.pkg.Package
import org.graalvm.polyglot.Context import org.graalvm.polyglot.{Context, Source, Value}
import org.graalvm.polyglot.Source import org.scalatest.{FlatSpec, Matchers}
import org.graalvm.polyglot.Value
import org.scalatest.FlatSpec
import org.scalatest.Matchers
trait PackageTest extends FlatSpec with Matchers with ValueEquality { trait PackageTest extends FlatSpec with Matchers with ValueEquality {

View File

@ -0,0 +1,15 @@
package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.InterpreterTest
class StringTest extends InterpreterTest {
"Strings" should "exist in the language and be printable" in {
val code =
"""
|@println [@IO, "hello world!"]
|""".stripMargin
noException shouldBe thrownBy(eval(code))
consumeOut shouldEqual List("hello world!")
}
}