mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 22:10:15 +03:00
String literals, caller frame access, eval function (#333)
This commit is contained in:
parent
8da25bec2d
commit
0ec41b5bbd
@ -18,6 +18,11 @@ public class RecursionBenchmarks {
|
||||
recursionFixtures.sumTCO().execute(recursionFixtures.hundredMillion());
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void benchSumTCOWithEval() {
|
||||
recursionFixtures.sumTCOWithEval().execute(recursionFixtures.hundredMillion());
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void benchSumTCOFoldLike() {
|
||||
recursionFixtures.sumTCOFoldLike().execute(recursionFixtures.hundredMillion());
|
||||
|
@ -83,4 +83,16 @@ class RecursionFixtures extends InterpreterRunner {
|
||||
|""".stripMargin
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -18,5 +18,6 @@ public class Constants {
|
||||
public static final String ARGUMENT_SORTER_NODE = "10";
|
||||
public static final String FUNCTION_INTEROP_LIBRARY = "10";
|
||||
public static final String THUNK_EXECUTOR_NODE = "10";
|
||||
public static final String EVAL_NODE = "10";
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package org.enso.interpreter;
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.Truffle;
|
||||
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.StandardTags;
|
||||
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.runtime.Context;
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -82,7 +83,8 @@ public final class Language extends TruffleLanguage<Context> {
|
||||
@Override
|
||||
protected CallTarget parse(ParsingRequest request) {
|
||||
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);
|
||||
}
|
||||
|
@ -61,7 +61,6 @@ public class CallArgFactory implements AstCallArgVisitor<CallArgument> {
|
||||
name.orElse(null),
|
||||
Truffle.getRuntime()
|
||||
.createCallTarget(
|
||||
new ClosureRootNode(
|
||||
language, childScope.getFrameDescriptor(), expr, null, displayName)));
|
||||
new ClosureRootNode(language, childScope, moduleScope, expr, null, displayName)));
|
||||
}
|
||||
}
|
||||
|
@ -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.DynamicSymbolNode;
|
||||
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.scope.AssignmentNode;
|
||||
import org.enso.interpreter.node.scope.AssignmentNodeGen;
|
||||
@ -113,10 +114,22 @@ public class ExpressionFactory implements AstExpressionVisitor<ExpressionNode> {
|
||||
* @param l the value to represent
|
||||
* @return a runtime node representing that value
|
||||
*/
|
||||
@Override
|
||||
public ExpressionNode visitLong(long 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.
|
||||
*
|
||||
@ -234,8 +247,7 @@ public class ExpressionFactory implements AstExpressionVisitor<ExpressionNode> {
|
||||
FunctionBodyNode fnBodyNode =
|
||||
new FunctionBodyNode(allFnExpressions.toArray(new ExpressionNode[0]), returnExpr);
|
||||
RootNode fnRootNode =
|
||||
new ClosureRootNode(
|
||||
language, scope.getFrameDescriptor(), fnBodyNode, null, "lambda::" + scopeName);
|
||||
new ClosureRootNode(language, scope, moduleScope, fnBodyNode, null, "lambda::" + scopeName);
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(fnRootNode);
|
||||
|
||||
return new CreateFunctionNode(callTarget, argDefinitions);
|
||||
|
@ -6,8 +6,8 @@ import org.enso.interpreter.node.callable.function.CreateFunctionNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
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.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.error.VariableDoesNotExistException;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
|
||||
|
@ -2,13 +2,13 @@ package org.enso.interpreter.node;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
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.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.enso.interpreter.Language;
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -26,23 +26,20 @@ public class ClosureRootNode extends EnsoRootNode {
|
||||
* Creates a new root node.
|
||||
*
|
||||
* @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 section a mapping from {@code body} to the program source
|
||||
* @param name a name for the node
|
||||
*/
|
||||
public ClosureRootNode(
|
||||
Language language,
|
||||
FrameDescriptor frameDescriptor,
|
||||
LocalScope localScope,
|
||||
ModuleScope moduleScope,
|
||||
ExpressionNode body,
|
||||
SourceSection section,
|
||||
String name) {
|
||||
super(
|
||||
language,
|
||||
frameDescriptor,
|
||||
name,
|
||||
section,
|
||||
frameDescriptor.findOrAddFrameSlot("<<state>>", FrameSlotKind.Object));
|
||||
super(language, localScope, moduleScope, name, section);
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
|
@ -2,18 +2,22 @@ package org.enso.interpreter.node;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
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.FrameSlotKind;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.enso.interpreter.Language;
|
||||
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. */
|
||||
public abstract class EnsoRootNode extends RootNode {
|
||||
private final String name;
|
||||
private final SourceSection sourceSection;
|
||||
private final FrameSlot stateFrameSlot;
|
||||
private final LocalScope localScope;
|
||||
private final ModuleScope moduleScope;
|
||||
private @CompilerDirectives.CompilationFinal TruffleLanguage.ContextReference<Context>
|
||||
contextReference;
|
||||
private @CompilerDirectives.CompilationFinal TruffleLanguage.LanguageReference<Language>
|
||||
@ -23,21 +27,24 @@ public abstract class EnsoRootNode extends RootNode {
|
||||
* Constructs the root node.
|
||||
*
|
||||
* @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 sourceSection a reference to the source code being executed
|
||||
* @param stateFrameSlot the code to compile and execute
|
||||
*/
|
||||
public EnsoRootNode(
|
||||
Language language,
|
||||
FrameDescriptor frameDescriptor,
|
||||
LocalScope localScope,
|
||||
ModuleScope moduleScope,
|
||||
String name,
|
||||
SourceSection sourceSection,
|
||||
FrameSlot stateFrameSlot) {
|
||||
super(language, frameDescriptor);
|
||||
SourceSection sourceSection) {
|
||||
super(language, localScope.getFrameDescriptor());
|
||||
this.name = name;
|
||||
this.localScope = localScope;
|
||||
this.moduleScope = moduleScope;
|
||||
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() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
package org.enso.interpreter.node;
|
||||
|
||||
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.source.Source;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.enso.interpreter.Language;
|
||||
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
|
||||
@ -27,23 +27,25 @@ public class ProgramRootNode extends EnsoRootNode {
|
||||
* Constructs the root node.
|
||||
*
|
||||
* @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 sourceSection a reference to the source code being executed
|
||||
* @param sourceCode the code to compile and execute
|
||||
*/
|
||||
public ProgramRootNode(
|
||||
Language language,
|
||||
FrameDescriptor frameDescriptor,
|
||||
LocalScope localScope,
|
||||
ModuleScope moduleScope,
|
||||
String name,
|
||||
SourceSection sourceSection,
|
||||
Source sourceCode) {
|
||||
super(
|
||||
language,
|
||||
frameDescriptor,
|
||||
localScope,
|
||||
moduleScope,
|
||||
name,
|
||||
sourceSection,
|
||||
frameDescriptor.findOrAddFrameSlot("<<state>>", FrameSlotKind.Object));
|
||||
sourceSection);
|
||||
this.sourceCode = sourceCode;
|
||||
}
|
||||
|
||||
|
@ -90,8 +90,10 @@ public class ApplicationNode extends ExpressionNode {
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
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());
|
||||
return result.getValue();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -6,14 +6,14 @@ import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||
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.state.Stateful;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* fall back to performing a lookup if necessary.
|
||||
* <p>Where possible, it will make the call as a direct call, with potential for inlining.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param function the function to execute
|
||||
* @param callerInfo the caller info to pass to the function
|
||||
* @param state the current state value
|
||||
* @param arguments the arguments passed to {@code function} in the expected positional order
|
||||
* @param cachedTarget the cached call target for {@code function}
|
||||
@ -33,12 +34,14 @@ public abstract class ExecuteCallNode extends Node {
|
||||
@Specialization(guards = "function.getCallTarget() == cachedTarget")
|
||||
protected Stateful callDirect(
|
||||
Function function,
|
||||
CallerInfo callerInfo,
|
||||
Object state,
|
||||
Object[] arguments,
|
||||
@Cached("function.getCallTarget()") RootCallTarget cachedTarget,
|
||||
@Cached("create(cachedTarget)") DirectCallNode callNode) {
|
||||
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.
|
||||
*
|
||||
* @param function the function to execute
|
||||
* @param callerInfo the caller info to pass to the function
|
||||
* @param state the current state value
|
||||
* @param arguments the arguments passed to {@code function} in the expected positional order
|
||||
* @param callNode the cached call node for making indirect calls
|
||||
@ -55,23 +59,34 @@ public abstract class ExecuteCallNode extends Node {
|
||||
*/
|
||||
@Specialization(replaces = "callDirect")
|
||||
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)
|
||||
callNode.call(
|
||||
function.getCallTarget(),
|
||||
Function.ArgumentsHelper.buildArguments(function, state, arguments));
|
||||
Function.ArgumentsHelper.buildArguments(function, callerInfo, state, arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the function call.
|
||||
*
|
||||
* @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 arguments the arguments to be passed to {@code function}
|
||||
* @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() {
|
||||
return ExecuteCallNodeGen.create();
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
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.NodeChild;
|
||||
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.VirtualFrame;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
|
@ -3,6 +3,7 @@ package org.enso.interpreter.node.callable;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.Fallback;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.enso.interpreter.Constants;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
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.
|
||||
*
|
||||
* @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 arguments the arguments to the function
|
||||
* @return the result of executing {@code callable} on the known arguments
|
||||
*/
|
||||
@Specialization
|
||||
public Stateful invokeFunction(Function function, Object state, Object[] arguments) {
|
||||
return this.argumentSorter.execute(function, state, arguments);
|
||||
Stateful invokeFunction(
|
||||
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.
|
||||
*
|
||||
* @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 arguments the arguments to the constructor
|
||||
* @return the result of executing {@code constructor} on the known arguments
|
||||
*/
|
||||
@Specialization
|
||||
public Stateful invokeConstructor(AtomConstructor constructor, Object state, Object[] arguments) {
|
||||
return invokeFunction(constructor.getConstructorFunction(), state, arguments);
|
||||
Stateful invokeConstructor(
|
||||
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.
|
||||
*
|
||||
* @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 arguments the arguments to the dynamic symbol
|
||||
* @return the result of resolving and executing the symbol for the {@code this} argument
|
||||
*/
|
||||
@Specialization
|
||||
public Stateful invokeDynamicSymbol(UnresolvedSymbol symbol, Object state, Object[] arguments) {
|
||||
public Stateful invokeDynamicSymbol(
|
||||
UnresolvedSymbol symbol, VirtualFrame callerFrame, Object state, Object[] arguments) {
|
||||
if (canApplyThis) {
|
||||
Object selfArgument = arguments[thisArgumentPosition];
|
||||
if (argumentsExecutionMode.shouldExecute()) {
|
||||
@ -159,7 +166,7 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
state = selfResult.getState();
|
||||
}
|
||||
Function function = methodResolverNode.execute(symbol, selfArgument);
|
||||
return this.argumentSorter.execute(function, state, arguments);
|
||||
return this.argumentSorter.execute(function, callerFrame, state, arguments);
|
||||
} else {
|
||||
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.
|
||||
*
|
||||
* @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 arguments the arguments to the callable
|
||||
* @return error
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
@ -185,11 +194,13 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
* Executes the provided {@code callable} on the supplied {@code arguments}.
|
||||
*
|
||||
* @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 arguments the arguments to evaluate {@code callable} on
|
||||
* @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.
|
||||
|
@ -38,15 +38,13 @@ public abstract class ThunkExecutorNode extends Node {
|
||||
@Cached("createLoopingOptimizerIfNeeded()")
|
||||
LoopingCallOptimiserNode loopingCallOptimiserNode) {
|
||||
if (getIsTail()) {
|
||||
return (Stateful)
|
||||
callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
|
||||
return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
|
||||
} else {
|
||||
try {
|
||||
return (Stateful)
|
||||
callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
|
||||
return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
|
||||
} catch (TailCallException e) {
|
||||
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("createLoopingOptimizerIfNeeded()")
|
||||
LoopingCallOptimiserNode loopingCallOptimiserNode) {
|
||||
try {
|
||||
if (getIsTail()) {
|
||||
return (Stateful)
|
||||
callNode.call(
|
||||
thunk.getCallTarget(),
|
||||
Function.ArgumentsHelper.buildArguments(thunk, state));
|
||||
} catch (TailCallException e) {
|
||||
return loopingCallOptimiserNode.executeDispatch(
|
||||
e.getFunction(), e.getState(), e.getArguments());
|
||||
thunk.getCallTarget(), Function.ArgumentsHelper.buildArguments(thunk, state));
|
||||
} else {
|
||||
try {
|
||||
return (Stateful)
|
||||
callNode.call(
|
||||
thunk.getCallTarget(), Function.ArgumentsHelper.buildArguments(thunk, state));
|
||||
} catch (TailCallException e) {
|
||||
return loopingCallOptimiserNode.executeDispatch(
|
||||
e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,11 @@ package org.enso.interpreter.node.callable.argument.sorter;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import org.enso.interpreter.Constants;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
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.function.Function;
|
||||
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.
|
||||
*
|
||||
* @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 arguments the arguments being passed to {@code callable}
|
||||
* @param mappingNode a cached node that tracks information about the mapping to enable a fast
|
||||
* 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
|
||||
*/
|
||||
@Specialization(
|
||||
@ -62,12 +62,13 @@ public abstract class ArgumentSorterNode extends BaseNode {
|
||||
limit = Constants.CacheSizes.ARGUMENT_SORTER_NODE)
|
||||
public Stateful invokeCached(
|
||||
Function function,
|
||||
VirtualFrame callerFrame,
|
||||
Object state,
|
||||
Object[] arguments,
|
||||
@Cached(
|
||||
"build(function, getSchema(), getDefaultsExecutionMode(), getArgumentsExecutionMode(), isTail())")
|
||||
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.
|
||||
*
|
||||
* @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 arguments the arguments to reorder and supply to the {@code function}.
|
||||
* @return the result of calling {@code function} with the supplied {@code arguments}.
|
||||
*/
|
||||
@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(
|
||||
function,
|
||||
callerFrame,
|
||||
state,
|
||||
arguments,
|
||||
CachedArgumentSorterNode.build(
|
||||
@ -97,11 +101,13 @@ public abstract class ArgumentSorterNode extends BaseNode {
|
||||
* Executes the {@link ArgumentSorterNode} to reorder the arguments.
|
||||
*
|
||||
* @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 arguments the arguments being passed to {@code function}
|
||||
* @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() {
|
||||
return schema;
|
||||
|
@ -1,20 +1,23 @@
|
||||
package org.enso.interpreter.node.callable.argument.sorter;
|
||||
|
||||
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.NodeInfo;
|
||||
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.InvokeCallableNode;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNodeGen;
|
||||
import org.enso.interpreter.node.callable.argument.ThunkExecutorNode;
|
||||
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.ArgumentMapping;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo.ArgumentMappingBuilder;
|
||||
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.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.control.TailCallException;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@ -35,6 +38,7 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
private final InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode;
|
||||
private @Child ExecuteCallNode directCall;
|
||||
private @Child CallOptimiserNode loopingCall;
|
||||
private @Child CaptureCallerInfoNode captureCallerInfoNode;
|
||||
|
||||
/**
|
||||
* Creates a node that generates and then caches the argument mapping.
|
||||
@ -64,8 +68,11 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
initializeOversaturatedCallNode(defaultsExecutionMode, argumentsExecutionMode);
|
||||
|
||||
argumentShouldExecute = this.mapping.getArgumentShouldExecute();
|
||||
|
||||
initializeCallNodes();
|
||||
|
||||
if (originalFunction.getSchema().getCallerFrameAccess().shouldFrameBePassed()) {
|
||||
this.captureCallerInfoNode = CaptureCallerInfoNode.build();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeCallNodes() {
|
||||
@ -156,11 +163,17 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
* Reorders the provided arguments into the necessary order for the cached callable.
|
||||
*
|
||||
* @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 arguments the arguments to reorder
|
||||
* @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()) {
|
||||
state = executeArguments(arguments, state);
|
||||
}
|
||||
@ -169,13 +182,14 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
|
||||
if (this.appliesFully()) {
|
||||
if (!postApplicationSchema.hasOversaturatedArgs()) {
|
||||
return doCall(function, state, mappedAppliedArguments);
|
||||
return doCall(function, callerInfo, state, mappedAppliedArguments);
|
||||
} else {
|
||||
Stateful evaluatedVal =
|
||||
loopingCall.executeDispatch(function, state, mappedAppliedArguments);
|
||||
loopingCall.executeDispatch(function, callerInfo, state, mappedAppliedArguments);
|
||||
|
||||
return this.oversaturatedCallableNode.execute(
|
||||
evaluatedVal.getValue(),
|
||||
callerFrame,
|
||||
evaluatedVal.getState(),
|
||||
generateOversaturatedArguments(function, arguments));
|
||||
}
|
||||
@ -232,13 +246,14 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
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())) {
|
||||
return directCall.executeCall(function, state, arguments);
|
||||
return directCall.executeCall(function, callerInfo, state, arguments);
|
||||
} else if (isTail()) {
|
||||
throw new TailCallException(function, state, arguments);
|
||||
throw new TailCallException(function, callerInfo, state, arguments);
|
||||
} else {
|
||||
return loopingCall.executeDispatch(function, state, arguments);
|
||||
return loopingCall.executeDispatch(function, callerInfo, state, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.enso.interpreter.node.callable.dispatch;
|
||||
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.runtime.callable.CallerInfo;
|
||||
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}.
|
||||
*
|
||||
* @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}
|
||||
* @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}.
|
||||
|
@ -7,6 +7,7 @@ import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.RepeatingNode;
|
||||
import org.enso.interpreter.node.callable.ExecuteCallNode;
|
||||
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.state.Stateful;
|
||||
|
||||
@ -33,14 +34,17 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
* Calls the provided {@code function} using the provided {@code arguments}.
|
||||
*
|
||||
* @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 arguments the arguments to {@code function}
|
||||
* @return the result of executing {@code function} using {@code arguments}
|
||||
*/
|
||||
@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);
|
||||
((RepeatedCallNode) loopNode.getRepeatingNode()).setNextCall(frame, function, state, arguments);
|
||||
((RepeatedCallNode) loopNode.getRepeatingNode())
|
||||
.setNextCall(frame, function, callerInfo, state, arguments);
|
||||
loopNode.executeLoop(frame);
|
||||
|
||||
return ((RepeatedCallNode) loopNode.getRepeatingNode()).getResult(frame);
|
||||
@ -56,6 +60,7 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
private final FrameSlot functionSlot;
|
||||
private final FrameSlot argsSlot;
|
||||
private final FrameSlot stateSlot;
|
||||
private final FrameSlot callerInfoSlot;
|
||||
@Child private ExecuteCallNode dispatchNode;
|
||||
|
||||
/**
|
||||
@ -68,6 +73,7 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
resultSlot = descriptor.findOrAddFrameSlot("<TCO Result>", FrameSlotKind.Object);
|
||||
argsSlot = descriptor.findOrAddFrameSlot("<TCO Arguments>", FrameSlotKind.Object);
|
||||
stateSlot = descriptor.findOrAddFrameSlot("<TCO State>", FrameSlotKind.Object);
|
||||
callerInfoSlot = descriptor.findOrAddFrameSlot("<TCO Caller Info>", FrameSlotKind.Object);
|
||||
dispatchNode = ExecuteCallNodeGen.create();
|
||||
}
|
||||
|
||||
@ -76,11 +82,18 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
*
|
||||
* @param frame the stack frame for execution
|
||||
* @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 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(callerInfoSlot, callerInfo);
|
||||
frame.setObject(stateSlot, state);
|
||||
frame.setObject(argsSlot, arguments);
|
||||
}
|
||||
@ -95,6 +108,12 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
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.
|
||||
*
|
||||
@ -143,10 +162,12 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
Object function = getNextFunction(frame);
|
||||
Object state = getNextState(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;
|
||||
} catch (TailCallException e) {
|
||||
setNextCall(frame, e.getFunction(), e.getState(), e.getArguments());
|
||||
setNextCall(frame, e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package org.enso.interpreter.node.callable.dispatch;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import org.enso.interpreter.node.callable.ExecuteCallNode;
|
||||
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.state.Stateful;
|
||||
|
||||
@ -19,19 +20,22 @@ public class SimpleCallOptimiserNode extends CallOptimiserNode {
|
||||
* Calls the provided {@code function} using the provided {@code arguments}.
|
||||
*
|
||||
* @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 arguments the arguments to {@code function}
|
||||
* @return the result of executing {@code function} using {@code arguments}
|
||||
*/
|
||||
@Override
|
||||
public Stateful executeDispatch(Object function, Object state, Object[] arguments) {
|
||||
public Stateful executeDispatch(
|
||||
Object function, CallerInfo callerInfo, Object state, Object[] arguments) {
|
||||
try {
|
||||
return executeCallNode.executeCall(function, state, arguments);
|
||||
return executeCallNode.executeCall(function, callerInfo, state, arguments);
|
||||
} catch (TailCallException e) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
CallOptimiserNode replacement = new LoopingCallOptimiserNode();
|
||||
this.replace(replacement);
|
||||
return replacement.executeDispatch(e.getFunction(), e.getState(), e.getArguments());
|
||||
return replacement.executeDispatch(
|
||||
e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.enso.interpreter.node.ClosureRootNode;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
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.FunctionSchema;
|
||||
|
||||
/**
|
||||
* This node is responsible for representing the definition of a function. It contains information
|
||||
|
@ -56,10 +56,19 @@ public class ConstructorCaseNode extends CaseNode {
|
||||
if (profile.profile(matcherVal == target.getConstructor())) {
|
||||
Function function = branch.executeFunction(frame);
|
||||
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.
|
||||
*
|
||||
|
@ -29,7 +29,9 @@ public class FallbackNode extends CaseNode {
|
||||
private void execute(VirtualFrame frame, Object target) throws UnexpectedResultException {
|
||||
Function function = functionNode.executeFunction(frame);
|
||||
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]
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
@ -6,10 +6,13 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode;
|
||||
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.CallArgumentInfo;
|
||||
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.state.Stateful;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
@ -45,7 +48,7 @@ public class CatchErrorNode extends BuiltinRootNode {
|
||||
Object handler = arguments[1];
|
||||
if (executionProfile.profile(TypesGen.isRuntimeError(scrutinee))) {
|
||||
return invokeCallableNode.execute(
|
||||
handler, state, new Object[] {TypesGen.asRuntimeError(scrutinee).getPayload()});
|
||||
handler, frame, state, new Object[] {TypesGen.asRuntimeError(scrutinee).getPayload()});
|
||||
} else {
|
||||
return new Stateful(state, scrutinee);
|
||||
}
|
||||
|
@ -40,7 +40,9 @@ public class RunStateNode extends BuiltinRootNode {
|
||||
if (thunksProfile.profile(TypesGen.isThunk(maybeThunk))) {
|
||||
return new Stateful(
|
||||
state,
|
||||
thunkExecutorNode.executeThunk(TypesGen.asThunk(maybeThunk), localState).getValue());
|
||||
thunkExecutorNode
|
||||
.executeThunk(TypesGen.asThunk(maybeThunk), localState)
|
||||
.getValue());
|
||||
} else {
|
||||
return new Stateful(state, maybeThunk);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -2,10 +2,11 @@ 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;
|
||||
|
||||
/** A representation of integer literals in Enso. */
|
||||
@NodeInfo(shortName = "IntegerLiteral")
|
||||
public final class IntegerLiteralNode extends LiteralNode {
|
||||
public final class IntegerLiteralNode extends ExpressionNode {
|
||||
private final long value;
|
||||
|
||||
/**
|
||||
|
@ -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 {}
|
@ -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;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package org.enso.interpreter.runtime;
|
||||
|
||||
import org.enso.interpreter.Language;
|
||||
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.CatchPanicNode;
|
||||
import org.enso.interpreter.node.expression.builtin.error.PanicNode;
|
||||
@ -38,6 +39,7 @@ public class Builtins {
|
||||
AtomConstructor panic = new AtomConstructor("Panic", scope).initializeFields();
|
||||
AtomConstructor error = new AtomConstructor("Error", scope).initializeFields();
|
||||
AtomConstructor state = new AtomConstructor("State", scope).initializeFields();
|
||||
AtomConstructor debug = new AtomConstructor("Debug", scope).initializeFields();
|
||||
|
||||
scope.registerConstructor(cons);
|
||||
scope.registerConstructor(nil);
|
||||
@ -46,6 +48,7 @@ public class Builtins {
|
||||
scope.registerConstructor(panic);
|
||||
scope.registerConstructor(error);
|
||||
scope.registerConstructor(state);
|
||||
scope.registerConstructor(debug);
|
||||
|
||||
scope.registerMethod(io, "println", PrintNode.makeFunction(language));
|
||||
|
||||
@ -59,6 +62,8 @@ public class Builtins {
|
||||
scope.registerMethod(state, "get", GetStateNode.makeFunction(language));
|
||||
scope.registerMethod(state, "put", PutStateNode.makeFunction(language));
|
||||
scope.registerMethod(state, "run", RunStateNode.makeFunction(language));
|
||||
|
||||
scope.registerMethod(debug, "eval", DebugEvalNode.makeFunction(language));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -73,4 +73,9 @@ public class UnresolvedSymbol implements TruffleObject {
|
||||
public Function resolveForError() {
|
||||
return scope.lookupMethodDefinitionForAny(name).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UnresolvedSymbol<" + this.name + ">";
|
||||
}
|
||||
}
|
||||
|
@ -77,8 +77,8 @@ public class CallArgumentInfo {
|
||||
private CallArgumentInfo[] existingOversaturatedArgs;
|
||||
private boolean[] argumentUsed;
|
||||
private boolean[] callSiteArgApplied;
|
||||
private FunctionSchema originalSchema;
|
||||
private int oversaturatedWritePosition = 0;
|
||||
private FunctionSchema.CallStrategy callStrategy;
|
||||
|
||||
/**
|
||||
* 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.argumentUsed = schema.cloneHasPreApplied();
|
||||
this.existingOversaturatedArgs = schema.cloneOversaturatedArgs();
|
||||
this.callStrategy = schema.getCallStrategy();
|
||||
this.originalSchema = schema;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,7 +214,12 @@ public class CallArgumentInfo {
|
||||
this.existingOversaturatedArgs.length,
|
||||
newOversaturatedArgInfo.length);
|
||||
|
||||
return new FunctionSchema(callStrategy, definitions, argumentUsed, oversaturatedArgInfo);
|
||||
return new FunctionSchema(
|
||||
originalSchema.getCallStrategy(),
|
||||
originalSchema.getCallerFrameAccess(),
|
||||
definitions,
|
||||
argumentUsed,
|
||||
oversaturatedArgInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,15 +3,15 @@ package org.enso.interpreter.runtime.callable.atom;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.RootCallTarget;
|
||||
import com.oracle.truffle.api.Truffle;
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
import org.enso.interpreter.node.ClosureRootNode;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.node.callable.argument.ReadArgumentNode;
|
||||
import org.enso.interpreter.node.expression.atom.InstantiateNode;
|
||||
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.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.scope.LocalScope;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
|
||||
/** A representation of an Atom constructor. */
|
||||
@ -66,7 +66,12 @@ public class AtomConstructor implements TruffleObject {
|
||||
ExpressionNode instantiateNode = new InstantiateNode(this, argumentReaders);
|
||||
ClosureRootNode rootNode =
|
||||
new ClosureRootNode(
|
||||
null, new FrameDescriptor(), instantiateNode, null, "<constructor>:" + name);
|
||||
null,
|
||||
new LocalScope(),
|
||||
new ModuleScope(),
|
||||
instantiateNode,
|
||||
null,
|
||||
"<constructor>:" + name);
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
|
||||
return new Function(
|
||||
callTarget, null, new FunctionSchema(FunctionSchema.CallStrategy.ALWAYS_DIRECT, args));
|
||||
|
@ -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.expression.builtin.BuiltinRootNode;
|
||||
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.CallArgumentInfo;
|
||||
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
|
||||
* must be conforming to the {@code schema}. {@code null} is allowed if the function does not
|
||||
* 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(
|
||||
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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -194,7 +217,9 @@ public final class Function implements TruffleObject {
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached(value = "arguments.length") int cachedArgsLength,
|
||||
@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
|
||||
*/
|
||||
public static Object[] buildArguments(
|
||||
Function function, Object state, Object[] positionalArguments) {
|
||||
return new Object[] {function.getScope(), state, positionalArguments};
|
||||
Function function, CallerInfo callerInfo, Object state, Object[] 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
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function, Object,
|
||||
* Object[])}
|
||||
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function,
|
||||
* CallerInfo, Object, Object[])}
|
||||
* @return the positional arguments to the function
|
||||
*/
|
||||
public static Object[] getPositionalArguments(Object[] arguments) {
|
||||
return (Object[]) arguments[2];
|
||||
return (Object[]) arguments[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state out of the array.
|
||||
*
|
||||
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function, Object,
|
||||
* Object[])}
|
||||
* @param arguments an array produced by {@link
|
||||
* ArgumentsHelper#buildArguments(Function,CallerInfo, Object, Object[])}
|
||||
* @return the state for the function
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function, Object,
|
||||
* Object[])}
|
||||
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function,
|
||||
* CallerInfo, Object, Object[])}
|
||||
* @return the local scope for the associated function
|
||||
*/
|
||||
public static MaterializedFrame getLocalScope(Object[] arguments) {
|
||||
|
@ -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) boolean[] hasPreApplied;
|
||||
private final @CompilationFinal(dimensions = 1) CallArgumentInfo[] oversaturatedArguments;
|
||||
private final CallStrategy callStrategy;
|
||||
private final boolean hasAnyPreApplied;
|
||||
private final boolean hasOversaturatedArguments;
|
||||
private final CallerFrameAccess callerFrameAccess;
|
||||
|
||||
/**
|
||||
* Creates an {@link FunctionSchema} instance.
|
||||
*
|
||||
* @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 hasPreApplied A flags collection such that {@code hasPreApplied[i]} is true iff a
|
||||
* function has a partially applied argument at position {@code i}
|
||||
@ -58,6 +79,7 @@ public class FunctionSchema {
|
||||
*/
|
||||
public FunctionSchema(
|
||||
CallStrategy callStrategy,
|
||||
CallerFrameAccess callerFrameAccess,
|
||||
ArgumentDefinition[] argumentInfos,
|
||||
boolean[] hasPreApplied,
|
||||
CallArgumentInfo[] oversaturatedArguments) {
|
||||
@ -65,8 +87,8 @@ public class FunctionSchema {
|
||||
this.argumentInfos = argumentInfos;
|
||||
this.oversaturatedArguments = oversaturatedArguments;
|
||||
this.hasPreApplied = hasPreApplied;
|
||||
this.callerFrameAccess = callerFrameAccess;
|
||||
boolean hasAnyPreApplied = false;
|
||||
|
||||
for (boolean b : hasPreApplied) {
|
||||
if (b) {
|
||||
hasAnyPreApplied = true;
|
||||
@ -82,10 +104,33 @@ public class FunctionSchema {
|
||||
* Creates an {@link FunctionSchema} instance assuming the function has no partially applied
|
||||
* 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
|
||||
*/
|
||||
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() {
|
||||
return callStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the caller frame access declaration for this function.
|
||||
*
|
||||
* @return the caller frame access declaration
|
||||
*/
|
||||
public CallerFrameAccess getCallerFrameAccess() {
|
||||
return callerFrameAccess;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.enso.interpreter.runtime.control;
|
||||
|
||||
import com.oracle.truffle.api.nodes.ControlFlowException;
|
||||
import org.enso.interpreter.runtime.callable.CallerInfo;
|
||||
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 {
|
||||
private final Function function;
|
||||
private final CallerInfo callerInfo;
|
||||
private final Object state;
|
||||
private final Object[] arguments;
|
||||
|
||||
@ -20,8 +22,10 @@ public class TailCallException extends ControlFlowException {
|
||||
* @param state the state to pass to the 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.callerInfo = callerInfo;
|
||||
this.arguments = arguments;
|
||||
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
|
||||
*/
|
||||
public Object getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the caller info to pass to the function.
|
||||
*
|
||||
* @return the state to pass for next call
|
||||
*/
|
||||
public CallerInfo getCallerInfo() {
|
||||
return callerInfo;
|
||||
}
|
||||
}
|
||||
|
@ -14,24 +14,23 @@ import java.util.Optional;
|
||||
* frames.
|
||||
*/
|
||||
public class LocalScope {
|
||||
private Map<String, FrameSlot> items;
|
||||
private FrameDescriptor frameDescriptor;
|
||||
private LocalScope parent;
|
||||
public final Map<String, FrameSlot> items;
|
||||
private final FrameDescriptor frameDescriptor;
|
||||
public final LocalScope parent;
|
||||
|
||||
/** Creates a new local scope with defaulted arguments. */
|
||||
/** Creates a root local scope. */
|
||||
public LocalScope() {
|
||||
items = new HashMap<>();
|
||||
frameDescriptor = new FrameDescriptor();
|
||||
parent = null;
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new local scope with a known parent.
|
||||
* Creates a child local scope with a given parent.
|
||||
*
|
||||
* @param parent the parent scope
|
||||
*/
|
||||
public LocalScope(LocalScope parent) {
|
||||
this();
|
||||
items = new HashMap<>();
|
||||
frameDescriptor = new FrameDescriptor();
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,17 @@ import com.oracle.truffle.api.source.Source
|
||||
import org.enso.compiler.generate.AstToIr
|
||||
import org.enso.compiler.ir.IR
|
||||
import org.enso.flexer.Reader
|
||||
import org.enso.interpreter.AstExpression
|
||||
import org.enso.interpreter.Constants
|
||||
import org.enso.interpreter.EnsoParser
|
||||
import org.enso.interpreter.Language
|
||||
import org.enso.interpreter.builder.ExpressionFactory
|
||||
import org.enso.interpreter.builder.ModuleScopeExpressionFactory
|
||||
import org.enso.interpreter.node.ExpressionNode
|
||||
import org.enso.interpreter.runtime.Context
|
||||
import org.enso.interpreter.runtime.Module
|
||||
import org.enso.interpreter.runtime.error.ModuleDoesNotExistException
|
||||
import org.enso.interpreter.runtime.scope.LocalScope
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope
|
||||
import org.enso.syntax.text.AST
|
||||
import org.enso.syntax.text.Parser
|
||||
@ -88,6 +91,27 @@ class Compiler(
|
||||
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.
|
||||
*
|
||||
@ -119,6 +143,17 @@ class Compiler(
|
||||
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
|
||||
* representation.
|
||||
|
@ -2,6 +2,8 @@ package org.enso.interpreter
|
||||
|
||||
import java.util.Optional
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.language.postfixOps
|
||||
import scala.util.parsing.combinator._
|
||||
@ -42,6 +44,8 @@ trait AstExpressionVisitor[+T] {
|
||||
): T
|
||||
|
||||
def visitDesuspend(target: AstExpression): T
|
||||
|
||||
def visitStringLiteral(string: String): T
|
||||
}
|
||||
|
||||
trait AstModuleScopeVisitor[+T] {
|
||||
@ -144,6 +148,11 @@ case class AstLong(l: Long) extends AstExpression {
|
||||
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)
|
||||
extends AstExpression {
|
||||
override def visit[T](visitor: AstExpressionVisitor[T]): T =
|
||||
@ -261,6 +270,12 @@ class EnsoParserInternal extends JavaTokenParsers {
|
||||
|
||||
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 operand: Parser[AstExpression] =
|
||||
@ -273,7 +288,7 @@ class EnsoParserInternal extends JavaTokenParsers {
|
||||
}
|
||||
|
||||
def expression: Parser[AstExpression] =
|
||||
desuspend | matchClause | arith | function
|
||||
desuspend | matchClause | arith | function | string
|
||||
|
||||
def functionCall: Parser[AstApply] =
|
||||
"@" ~> expression ~ (argList ?) ~ defaultSuspend ^^ {
|
||||
@ -351,6 +366,10 @@ class EnsoParserInternal extends JavaTokenParsers {
|
||||
def parse(code: String): AstExpression = {
|
||||
parseAll(expression | function, code).get
|
||||
}
|
||||
|
||||
def parseLine(code: String): AstExpression = {
|
||||
parseAll(statement, code).get
|
||||
}
|
||||
}
|
||||
|
||||
class EnsoParser {
|
||||
@ -358,4 +377,8 @@ class EnsoParser {
|
||||
def parseEnso(code: String): AstModuleScope = {
|
||||
new EnsoParserInternal().parseGlobalScope(code)
|
||||
}
|
||||
|
||||
def parseEnsoInline(code: String): AstExpression = {
|
||||
new EnsoParserInternal().parseLine(code)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.enso.interpreter.test
|
||||
|
||||
import org.graalvm.polyglot.PolyglotException
|
||||
import org.graalvm.polyglot.Value
|
||||
|
||||
case class InterpreterException(
|
||||
@transient polyglotException: PolyglotException
|
||||
|
@ -3,10 +3,8 @@ package org.enso.interpreter.test
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
import org.enso.interpreter.Constants
|
||||
import org.graalvm.polyglot.Context
|
||||
import org.graalvm.polyglot.Value
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.Matchers
|
||||
import org.graalvm.polyglot.{Context, Value}
|
||||
import org.scalatest.{FlatSpec, Matchers}
|
||||
|
||||
trait InterpreterRunner {
|
||||
implicit class RichValue(value: Value) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.enso.interpreter.test.semantic
|
||||
|
||||
import org.enso.interpreter.test.InterpreterException
|
||||
import org.enso.interpreter.test.InterpreterTest
|
||||
import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
|
||||
|
||||
class ErrorsTest extends InterpreterTest {
|
||||
"Panics" should "be thrown and stop evaluation" in {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package org.enso.interpreter.test.semantic
|
||||
|
||||
import org.enso.interpreter.test.InterpreterException
|
||||
import org.enso.interpreter.test.InterpreterTest
|
||||
import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
|
||||
|
||||
class GlobalScopeTest extends InterpreterTest {
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.enso.interpreter.test.semantic
|
||||
|
||||
import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
|
||||
import org.graalvm.polyglot.PolyglotException
|
||||
|
||||
class NamedArgumentsTest extends InterpreterTest {
|
||||
"Functions" should "take arguments by name and use them in their bodies" in {
|
||||
|
@ -4,14 +4,10 @@ import java.io.File
|
||||
|
||||
import org.enso.interpreter.Constants
|
||||
import org.enso.interpreter.runtime.RuntimeOptions
|
||||
import org.enso.interpreter.test.InterpreterException
|
||||
import org.enso.interpreter.test.ValueEquality
|
||||
import org.enso.interpreter.test.{InterpreterException, ValueEquality}
|
||||
import org.enso.pkg.Package
|
||||
import org.graalvm.polyglot.Context
|
||||
import org.graalvm.polyglot.Source
|
||||
import org.graalvm.polyglot.Value
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.Matchers
|
||||
import org.graalvm.polyglot.{Context, Source, Value}
|
||||
import org.scalatest.{FlatSpec, Matchers}
|
||||
|
||||
trait PackageTest extends FlatSpec with Matchers with ValueEquality {
|
||||
|
||||
|
@ -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!")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user