Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct expression updates of partially applied constructors #6939

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,21 @@
import com.oracle.truffle.api.interop.InteropException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.nodes.Node;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.UUID;
import java.util.Set;
import java.util.function.Consumer;

import org.enso.interpreter.instrument.execution.Timer;
import org.enso.interpreter.instrument.profiling.ExecutionTime;
import org.enso.interpreter.instrument.profiling.ProfilingInfo;
import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
import org.enso.interpreter.node.expression.atom.QualifiedAccessorNode;
import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.function.Function;
Expand All @@ -28,14 +35,11 @@
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.state.State;
import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;
import org.enso.interpreter.runtime.tag.IdentifiedTag;
import org.enso.interpreter.runtime.type.Constants;
import org.enso.interpreter.runtime.Module;

import java.util.function.Consumer;
import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;

/** An instrument for getting values from AST-identified expressions. */
@TruffleInstrument.Registration(
id = IdExecutionService.INSTRUMENT_ID,
Expand Down Expand Up @@ -68,6 +72,7 @@ private static class IdEventNodeFactory implements ExecutionEventNodeFactory {
private final UpdatesSynchronizationState syncState;
private final UUID nextExecutionItem;
private final Map<UUID, FunctionCallInfo> calls = new HashMap<>();
private final Set<UUID> constructorCalls = new HashSet<>();
private final Timer timer;

/**
Expand Down Expand Up @@ -109,8 +114,20 @@ public IdEventNodeFactory(

@Override
public ExecutionEventNode create(EventContext context) {
return new IdExecutionEventNode(context, entryCallTarget, cache, methodCallsCache, syncState,
nextExecutionItem, calls, functionCallCallback, onComputedCallback, onCachedCallback, onExceptionalCallback, timer);
return new IdExecutionEventNode(
context,
entryCallTarget,
cache,
methodCallsCache,
syncState,
nextExecutionItem,
calls,
constructorCalls,
functionCallCallback,
onComputedCallback,
onCachedCallback,
onExceptionalCallback,
timer);
}
}

Expand All @@ -127,6 +144,7 @@ private static class IdExecutionEventNode extends ExecutionEventNode {
private final UpdatesSynchronizationState syncState;
private final UUID nextExecutionItem;
private final Map<UUID, FunctionCallInfo> calls;
private final Set<UUID> constructorCalls;
private final Timer timer;
private long nanoTimeElapsed = 0;
private @Child TypeOfNode typeOfNode = TypeOfNode.build();
Expand All @@ -139,6 +157,8 @@ private static class IdExecutionEventNode extends ExecutionEventNode {
* @param methodCallsCache the storage tracking the executed method calls.
* @param syncState the synchronization state of runtime updates.
* @param nextExecutionItem the next item scheduled for execution.
* @param calls the list of instrumented method calls.
* @param constructorCalls the list of instrumented constructor calls.
* @param functionCallCallback the consumer of function call events.
* @param onComputedCallback the consumer of the computed value events.
* @param onCachedCallback the consumer of the cached value events.
Expand All @@ -153,6 +173,7 @@ public IdExecutionEventNode(
UpdatesSynchronizationState syncState,
UUID nextExecutionItem, // The expression ID
Map<UUID, FunctionCallInfo> calls,
Set<UUID> constructorCalls,
Consumer<ExpressionCall> functionCallCallback,
Consumer<ExpressionValue> onComputedCallback,
Consumer<ExpressionValue> onCachedCallback,
Expand All @@ -162,6 +183,7 @@ public IdExecutionEventNode(
this.entryCallTarget = entryCallTarget;
this.cache = cache;
this.calls = calls;
this.constructorCalls = constructorCalls;
this.callsCache = methodCallsCache;
this.syncState = syncState;
this.nextExecutionItem = nextExecutionItem;
Expand Down Expand Up @@ -256,7 +278,7 @@ private void onExpressionReturn(Object result, Node node, EventContext context)

String resultType = typeOf(result);
String cachedType = cache.getType(nodeId);
FunctionCallInfo call = functionCallInfoById(nodeId);
FunctionCallInfo call = functionCallInfoById(nodeId, result);
FunctionCallInfo cachedCall = cache.getCall(nodeId);
ProfilingInfo[] profilingInfo = new ProfilingInfo[] {new ExecutionTime(nanoTimeElapsed)};

Expand Down Expand Up @@ -302,14 +324,21 @@ private void passExpressionValueToCallback(ExpressionValue expressionValue) {
}

@CompilerDirectives.TruffleBoundary
private FunctionCallInfo functionCallInfoById(UUID nodeId) {
private FunctionCallInfo functionCallInfoById(UUID nodeId, Object result) {
if (constructorCalls.contains(nodeId) && result instanceof Function function) {
FunctionCallInfo call = calls.get(nodeId);
return new FunctionCallInfo(call, function);
}
return calls.get(nodeId);
}

@CompilerDirectives.TruffleBoundary
private void onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCall result, EventContext context) throws ThreadDeath {
calls.put(nodeId, new FunctionCallInfo(result));
functionCallCallback.accept(new ExpressionCall(nodeId, result));
private void onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCall functionCall, EventContext context) throws ThreadDeath {
if (functionCall.getFunction().getCallTarget().getRootNode() instanceof QualifiedAccessorNode) {
constructorCalls.add(nodeId);
}
calls.put(nodeId, new FunctionCallInfo(functionCall));
functionCallCallback.accept(new ExpressionCall(nodeId, functionCall));
// Return cached value after capturing the enterable function call in `functionCallCallback`
Object cachedResult = cache.get(nodeId);
if (cachedResult != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,6 @@ class RuntimeServerTest
}

it should "send method pointer updates of partially applied constructors" in {
pending
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
val moduleName = "Enso_Test.Test.Main"
Expand Down Expand Up @@ -867,9 +866,62 @@ class RuntimeServerTest
Vector(1)
)
),
TestMessages.update(contextId, id_x_2, "Enso_Test.Test.Main.T"),
context.executionComplete(contextId)
)
}

it should "send method pointer updates of partially applied constructors with default arguments" in {
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
val moduleName = "Enso_Test.Test.Main"

val metadata = new Metadata
val id_x_1 = metadata.addItem(39, 3, "aa")

val code =
"""type T
| A x=1 y=2
|
|main =
| x_1 = T.A
| x_1
|""".stripMargin.linesIterator.mkString("\n")
val contents = metadata.appendToCode(code)
val mainFile = context.writeMain(contents)

// create context
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
context.receive shouldEqual Some(
Api.Response(requestId, Api.CreateContextResponse(contextId))
)

// open file
context.send(
Api.Request(Api.OpenFileNotification(mainFile, contents))
)
context.receiveNone shouldEqual None

// push main
context.send(
Api.Request(
requestId,
Api.PushContextRequest(
contextId,
Api.StackItem.ExplicitCall(
Api.MethodPointer(moduleName, moduleName, "main"),
None,
Vector()
)
)
)
)
context.receiveN(4) should contain theSameElementsAs Seq(
Api.Response(Api.BackgroundJobsStartedNotification()),
Api.Response(requestId, Api.PushContextResponse(contextId)),
TestMessages.update(
contextId,
id_x_2,
id_x_1,
"Enso_Test.Test.Main.T",
Api.MethodCall(
Api.MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main.T", "A")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.oracle.truffle.api.instrumentation.EventBinding;
import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory;
import com.oracle.truffle.api.nodes.RootNode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.UUID;
Expand All @@ -16,9 +15,8 @@
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
import org.enso.interpreter.node.expression.atom.QualifiedAccessorNode;
import org.enso.interpreter.runtime.Module;
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.data.Type;
import org.enso.logger.masking.MaskedString;
import org.enso.pkg.QualifiedName;
Expand Down Expand Up @@ -243,6 +241,19 @@ public FunctionCallInfo(FunctionCallInstrumentationNode.FunctionCall call) {
notAppliedArguments = collectNotAppliedArguments(call);
}

/**
* Create a new function call info for partially applied constructors.
*
* @param call the function call returning the constructor function.
* @param function the constructor function creating the type instance.
*/
public FunctionCallInfo(FunctionCallInfo call, Function function) {
this.moduleName = call.moduleName;
this.typeName = call.typeName;
this.functionName = call.functionName;
this.notAppliedArguments = collectNotAppliedArguments(function);
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand Down Expand Up @@ -303,5 +314,20 @@ private static int[] collectNotAppliedArguments(FunctionCallInstrumentationNode.

return Arrays.copyOf(notAppliedArgs, notAppliedArgsSize);
}

private static int[] collectNotAppliedArguments(Function function) {
Object[] preAppliedArguments = function.getPreAppliedArguments();
int[] notAppliedArgs = new int[preAppliedArguments.length];
int notAppliedArgsSize = 0;

for (int i = 0; i < preAppliedArguments.length; i++) {
if (preAppliedArguments[i] == null) {
notAppliedArgs[notAppliedArgsSize] = i;
notAppliedArgsSize += 1;
}
}

return Arrays.copyOf(notAppliedArgs, notAppliedArgsSize);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import com.oracle.truffle.api.source.SourceSection;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.enso.interpreter.Constants;
import org.enso.interpreter.EnsoLanguage;
import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.node.ExpressionNode;
Expand Down Expand Up @@ -181,7 +183,8 @@ private void generateQualifiedAccessor() {
callTarget,
null,
new FunctionSchema(
new ArgumentDefinition(0, "self", ArgumentDefinition.ExecutionMode.EXECUTE)));
new ArgumentDefinition(
0, Constants.Names.SELF_ARGUMENT, ArgumentDefinition.ExecutionMode.EXECUTE)));
definitionScope.registerMethod(type.getEigentype(), this.name, function);
}

Expand Down