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

Send info about function values #7168

Merged
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,7 @@
- [Added retries when executing GraalVM updater][7079]
- [Add method call info for infix operators][7090]
- [`executionComplete` response is sent on successful execution only][7143]
- [Send info about function values][7168]

[3227]: https://github.com/enso-org/enso/pull/3227
[3248]: https://github.com/enso-org/enso/pull/3248
Expand Down Expand Up @@ -969,6 +970,7 @@
[7079]: https://github.com/enso-org/enso/pull/7079
[7090]: https://github.com/enso-org/enso/pull/7090
[7143]: https://github.com/enso-org/enso/pull/7143
[7168]: https://github.com/enso-org/enso/pull/7168

# Enso 2.0.0-alpha.18 (2021-10-12)

Expand Down
21 changes: 21 additions & 0 deletions docs/language-server/protocol-language-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,11 @@ interface Value {
* Information about attached warnings.
*/
warnings?: Warnings;

/**
* The schema of returned function value.
*/
functionSchema?: FunctionSchema;
}

/**
Expand Down Expand Up @@ -442,6 +447,22 @@ interface Warnings {
*/
value?: string;
}

/**
* Contains a method pointer with information on the partially applied argument
* positions.
*/
interface FunctionSchema {
/**
* The method pointer of this function.
*/
methodPointer: MethodPointer;

/**
* Indexes of arguments that have not been applied to this function.
*/
notAppliedArguments: number[];
}
```

### `VisualizationConfiguration`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,10 @@ final class ContextEventsListener(
payload: Api.ExpressionUpdate.Payload
): ContextRegistryProtocol.ExpressionUpdate.Payload =
payload match {
case Api.ExpressionUpdate.Payload.Value(warnings) =>
case Api.ExpressionUpdate.Payload.Value(warnings, functionSchema) =>
ContextRegistryProtocol.ExpressionUpdate.Payload.Value(
warnings.map(toProtocolWarnings)
warnings.map(toProtocolWarnings),
functionSchema.map(toProtocolFunctionSchema)
)

case Api.ExpressionUpdate.Payload.Pending(m, p) =>
Expand All @@ -236,6 +237,19 @@ final class ContextEventsListener(
ContextRegistryProtocol.ExpressionUpdate.Payload.Value
.Warnings(payload.count, payload.warning, payload.reachedMaxCount)

/** Convert the runtime function schema to the context registry protocol
* representation.
*
* @param functionSchema the function schema
*/
private def toProtocolFunctionSchema(
functionSchema: Api.FunctionSchema
): ContextRegistryProtocol.ExpressionUpdate.Payload.Value.FunctionSchema =
ContextRegistryProtocol.ExpressionUpdate.Payload.Value.FunctionSchema(
toProtocolMethodPointer(functionSchema.methodPointer),
functionSchema.notAppliedArguments
)

/** Convert the runtime profiling info to the context registry protocol
* representation.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,13 @@ object ContextRegistryProtocol {

/** An information about computed expression.
*
* @param warnings information about attached warnings.
* @param warnings information about attached warnings
* @param functionSchema the schema of returned function value
*/
case class Value(warnings: Option[Value.Warnings]) extends Payload
case class Value(
warnings: Option[Value.Warnings],
functionSchema: Option[Value.FunctionSchema]
) extends Payload
object Value {

/** Information about warnings associated with the value.
Expand All @@ -212,6 +216,18 @@ object ContextRegistryProtocol {
value: Option[String],
reachedMaxCount: Boolean
)

/** Contains a method pointer with information on the partially applied
* arguments positions.
*
* @param methodPointer the method pointer
* @param notAppliedArguments indexes of arguments that have not been applied
* to this method
*/
case class FunctionSchema(
methodPointer: MethodPointer,
notAppliedArguments: Vector[Int]
)
}

case class Pending(message: Option[String], progress: Option[Double])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ class ContextEventsListenerSpec
Vector(),
false,
true,
Api.ExpressionUpdate.Payload.Value()
Api.ExpressionUpdate.Payload.Value(
functionSchema = Some(
Api.FunctionSchema(methodPointer, Vector(1))
)
)
)
)
)
Expand All @@ -83,7 +87,17 @@ class ContextEventsListenerSpec
Some(toProtocolMethodCall(methodCall)),
Vector(),
false,
ContextRegistryProtocol.ExpressionUpdate.Payload.Value(None)
ContextRegistryProtocol.ExpressionUpdate.Payload
.Value(
None,
Some(
ContextRegistryProtocol.ExpressionUpdate.Payload.Value
.FunctionSchema(
toProtocolMethodPointer(methodPointer),
Vector(1)
)
)
)
)
)
)
Expand Down Expand Up @@ -216,15 +230,17 @@ class ContextEventsListenerSpec
None,
Vector(),
false,
ContextRegistryProtocol.ExpressionUpdate.Payload.Value(None)
ContextRegistryProtocol.ExpressionUpdate.Payload
.Value(None, None)
),
ContextRegistryProtocol.ExpressionUpdate(
Suggestions.local.externalId.get,
None,
None,
Vector(),
false,
ContextRegistryProtocol.ExpressionUpdate.Payload.Value(None)
ContextRegistryProtocol.ExpressionUpdate.Payload
.Value(None, None)
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,18 @@ object Runtime {
MethodCall(methodPointer, Vector())
}

/** Contains a method pointer with information on the partially applied
* arguments positions.
*
* @param methodPointer the method pointer
* @param notAppliedArguments indexes of arguments that have not been applied
* to this method
*/
case class FunctionSchema(
methodPointer: MethodPointer,
notAppliedArguments: Vector[Int]
)

/** A representation of an executable position in code.
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
Expand Down Expand Up @@ -434,9 +446,12 @@ object Runtime {
/** Indicates that the expression was computed to a value.
*
* @param warnings information about attached warnings.
* @param functionSchema the function schema
4e6 marked this conversation as resolved.
Show resolved Hide resolved
*/
case class Value(warnings: Option[Value.Warnings] = None)
extends Payload
case class Value(
warnings: Option[Value.Warnings] = None,
functionSchema: Option[FunctionSchema] = None
) extends Payload

object Value {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
import org.enso.interpreter.runtime.Module;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.data.Type;
import org.enso.logger.masking.MaskedString;
import org.enso.pkg.QualifiedName;
Expand Down Expand Up @@ -196,22 +198,15 @@ public boolean isFunctionCallChanged() {
}
}

/** Information about the function call. */
class FunctionCallInfo {

private final QualifiedName moduleName;
private final QualifiedName typeName;
private final String functionName;
/** Points to the definition of a runtime function. */
record FunctionPointer(QualifiedName moduleName, QualifiedName typeName, String functionName) {

private final int[] notAppliedArguments;
public static FunctionPointer fromFunction(Function function) {
RootNode rootNode = function.getCallTarget().getRootNode();

/**
* Creates a new instance of this class.
*
* @param call the function call.
*/
public FunctionCallInfo(FunctionCallInstrumentationNode.FunctionCall call) {
RootNode rootNode = call.getFunction().getCallTarget().getRootNode();
QualifiedName moduleName;
QualifiedName typeName;
String functionName;

switch (rootNode) {
case MethodRootNode methodNode -> {
Expand All @@ -237,9 +232,31 @@ public FunctionCallInfo(FunctionCallInstrumentationNode.FunctionCall call) {
}
}

notAppliedArguments = collectNotAppliedArguments(call);
return new FunctionPointer(moduleName, typeName, functionName);
}

public static int[] collectNotAppliedArguments(Function function) {
FunctionSchema functionSchema = function.getSchema();
Object[] preAppliedArguments = function.getPreAppliedArguments();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the preAppliedArguments array has zero size? Will the next line not throw an ArrayIndexOutOfBoundsException?

boolean isStatic = preAppliedArguments[0] instanceof Type;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this reliable? We could have an argument which is a type but the function is not a static?

Copy link
Contributor Author

@4e6 4e6 Jun 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, to resolve the function the self argument has to be present. And at this point, the function is resolved.
If the function is not static, the self argument will be something like UnboxingAtom I believe.

int selfArgumentPosition = isStatic ? -1 : 0;
int[] notAppliedArguments = new int[functionSchema.getArgumentsCount()];
int notAppliedArgumentsLength = 0;

for (int i = 0; i < functionSchema.getArgumentsCount(); i++) {
if (!functionSchema.hasPreAppliedAt(i)) {
notAppliedArguments[notAppliedArgumentsLength] = i + selfArgumentPosition;
notAppliedArgumentsLength += 1;
}
}

return Arrays.copyOf(notAppliedArguments, notAppliedArgumentsLength);
}
}

/** Information about the function call. */
record FunctionCallInfo(FunctionPointer functionPointer, int[] notAppliedArguments) {

@Override
public boolean equals(Object o) {
if (this == o) {
Expand All @@ -249,39 +266,27 @@ public boolean equals(Object o) {
return false;
}
FunctionCallInfo that = (FunctionCallInfo) o;
return Objects.equals(moduleName, that.moduleName)
&& Objects.equals(typeName, that.typeName)
&& Objects.equals(functionName, that.functionName);
return Objects.equals(functionPointer, that.functionPointer) && Arrays.equals(
notAppliedArguments, that.notAppliedArguments);
}

@Override
public int hashCode() {
return Objects.hash(moduleName, typeName, functionName);
}

@Override
public String toString() {
return moduleName + "::" + typeName + "::" + functionName;
}

/** @return the name of the module this function was defined in, or null if not available. */
public QualifiedName getModuleName() {
return moduleName;
int result = Objects.hash(functionPointer);
result = 31 * result + Arrays.hashCode(notAppliedArguments);
return result;
4e6 marked this conversation as resolved.
Show resolved Hide resolved
}

/** @return the name of the type this method was defined for, or null if not a method. */
public QualifiedName getTypeName() {
return typeName;
}

/** @return the name of this function. */
public String getFunctionName() {
return functionName;
}
/**
* Creates a new instance of this record from a function call.
*
* @param call the function call.
*/
public static FunctionCallInfo fromFunctionCall(FunctionCallInstrumentationNode.FunctionCall call) {
FunctionPointer functionPointer = FunctionPointer.fromFunction(call.getFunction());
int[] notAppliedArguments = collectNotAppliedArguments(call);

/** @return the arguments of this function that have not yet been applied. */
public int[] getNotAppliedArguments() {
return notAppliedArguments;
return new FunctionCallInfo(functionPointer, notAppliedArguments);
}

private static int[] collectNotAppliedArguments(FunctionCallInstrumentationNode.FunctionCall call) {
Expand Down
Loading
Loading