mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 00:52:09 +03:00
Specify expression to get more advanced results on_return callback (#8331)
This commit is contained in:
parent
53d1f727da
commit
1138dfe147
@ -616,14 +616,25 @@ type Instrumentor
|
||||
ADVANCED
|
||||
|
||||
Registers callback to be executed when a node/expression evaluation
|
||||
is over. The callback `fn` gets UUID and the computed value. Usually
|
||||
is over. The callback `fn` gets UUID and the computed value (or value
|
||||
of `expression` if specified). Usually
|
||||
the value is _cached_ and returned from `on_enter` callback next time
|
||||
the same expression is evaluated.
|
||||
|
||||
> Example
|
||||
Specify `expression` to _"inline evaluate"_ it.
|
||||
see_a_b uuid:Text ~result =
|
||||
if uuid == "expected-uuid" then
|
||||
IO.println "evalutated to "+result.to_text
|
||||
|
||||
Meta.meta .fn . instrument . on_return fn=see_a_b expression="a+b" . activate
|
||||
|
||||
Arguments:
|
||||
- fn: The callback function accepting UUID and computed value
|
||||
on_return self (fn : Text -> Any -> Nothing) =
|
||||
new = instrumentor_builtin "onReturn" [ self.impl, fn ]
|
||||
- expression: Expression to evaluate on_return - by default Nothing -
|
||||
e.g. to provide the return value of the function
|
||||
on_return self (fn : Text -> Any -> Nothing) (expression : Text|Nothing)=Nothing =
|
||||
new = instrumentor_builtin "onReturn" [ self.impl, fn, expression ]
|
||||
Instrumentor.Value new
|
||||
|
||||
## PRIVATE
|
||||
|
@ -21,6 +21,7 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
||||
private final Module module;
|
||||
private final Object onEnter;
|
||||
private final Object onReturn;
|
||||
private final Object onReturnExpr;
|
||||
private final Object onCall;
|
||||
private final EventBinding<?> handle;
|
||||
|
||||
@ -30,16 +31,18 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
||||
this.target = target;
|
||||
this.onEnter = null;
|
||||
this.onReturn = null;
|
||||
this.onReturnExpr = null;
|
||||
this.onCall = null;
|
||||
this.handle = null;
|
||||
}
|
||||
|
||||
Instrumentor(Instrumentor orig, Object onEnter, Object onReturn, Object onCall, boolean activate) {
|
||||
Instrumentor(Instrumentor orig, Object onEnter, Object onReturn, Object onReturnExpr, Object onCall, boolean activate) {
|
||||
this.module = orig.module;
|
||||
this.service = orig.service;
|
||||
this.target = orig.target;
|
||||
this.onEnter = onEnter != null ? onEnter : orig.onEnter;
|
||||
this.onReturn = onReturn != null ? onReturn : orig.onReturn;
|
||||
this.onReturnExpr = onReturnExpr != null ? onReturnExpr : orig.onReturnExpr;
|
||||
this.onCall = onCall != null ? onCall : orig.onCall;
|
||||
this.handle = !activate ? null : service.bind(
|
||||
module, target, this, new Timer.Disabled()
|
||||
@ -73,7 +76,12 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
||||
public void updateCachedResult(IdExecutionService.Info info) {
|
||||
try {
|
||||
if (onReturn != null) {
|
||||
InteropLibrary.getUncached().execute(onReturn, info.getId().toString(), info.getResult());
|
||||
var iop = InteropLibrary.getUncached();
|
||||
var result = onReturnExpr == null || !iop.isString(onReturnExpr) ?
|
||||
info.getResult()
|
||||
:
|
||||
InstrumentorEvalNode.asSuspendedEval(onReturnExpr, info);
|
||||
iop.execute(onReturn, info.getId().toString(), result);
|
||||
}
|
||||
} catch (InteropException ignored) {
|
||||
}
|
||||
|
@ -1,20 +1,18 @@
|
||||
package org.enso.interpreter.node.expression.builtin.meta;
|
||||
|
||||
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.EnsoContext;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.data.vector.ArrayLikeAtNode;
|
||||
import org.enso.interpreter.runtime.data.vector.ArrayLikeCoerceToArrayNode;
|
||||
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Meta",
|
||||
@ -33,7 +31,7 @@ public class InstrumentorBuiltin extends Node {
|
||||
if (atNode.executeAt(args, 0) instanceof Instrumentor b) {
|
||||
ret = switch (op) {
|
||||
case "onEnter" -> onEnter(b, atNode.executeAt(args, 1));
|
||||
case "onReturn" -> onReturn(b, atNode.executeAt(args, 1));
|
||||
case "onReturn" -> onReturn(b, atNode.executeAt(args, 1), atNode.executeAt(args, 2));
|
||||
case "onCall" -> onCall(b, atNode.executeAt(args, 1));
|
||||
case "activate" -> activate(b, atNode.executeAt(args, 1));
|
||||
case "deactivate" -> b.deactivate();
|
||||
@ -70,16 +68,16 @@ public class InstrumentorBuiltin extends Node {
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private Object onEnter(Instrumentor b, Object arg) {
|
||||
if (arg instanceof Function fn) {
|
||||
return new Instrumentor(b, fn, null, null, false);
|
||||
return new Instrumentor(b, fn, null, null, null, false);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private Object onReturn(Instrumentor b, Object arg) {
|
||||
private Object onReturn(Instrumentor b, Object arg, Object expr) {
|
||||
if (arg instanceof Function fn) {
|
||||
return new Instrumentor(b, null, fn, null, false);
|
||||
return new Instrumentor(b, null, fn, expr, null, false);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -88,7 +86,7 @@ public class InstrumentorBuiltin extends Node {
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private Object onCall(Instrumentor b, Object arg) {
|
||||
if (arg instanceof Function fn) {
|
||||
return new Instrumentor(b, null, null, fn, false);
|
||||
return new Instrumentor(b, null, null, null, fn, false);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -97,7 +95,7 @@ public class InstrumentorBuiltin extends Node {
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private Object activate(Instrumentor b, Object arg) {
|
||||
if (arg instanceof Function fn) {
|
||||
var builder = new Instrumentor(b, null, null, null, true);
|
||||
var builder = new Instrumentor(b, null, null, null, null, true);
|
||||
var ctx = EnsoContext.get(this);
|
||||
return ctx.getResourceManager().register(builder, fn);
|
||||
} else {
|
||||
|
@ -0,0 +1,54 @@
|
||||
package org.enso.interpreter.node.expression.builtin.meta;
|
||||
|
||||
import com.oracle.truffle.api.RootCallTarget;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.EnsoLanguage;
|
||||
import org.enso.interpreter.runtime.callable.Annotation;
|
||||
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.error.PanicException;
|
||||
import org.enso.polyglot.debugger.IdExecutionService;
|
||||
|
||||
final class InstrumentorEvalNode extends RootNode {
|
||||
private static final FunctionSchema SUSPENDED_EVAL =
|
||||
new FunctionSchema(
|
||||
FunctionSchema.CallerFrameAccess.NONE,
|
||||
new ArgumentDefinition[] {
|
||||
new ArgumentDefinition(0, "expr", null, null, ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "info", null, null, ArgumentDefinition.ExecutionMode.EXECUTE)
|
||||
},
|
||||
new boolean[] {true, true},
|
||||
new CallArgumentInfo[0],
|
||||
new Annotation[0]);
|
||||
private static final RootCallTarget CALL = new InstrumentorEvalNode().getCallTarget();
|
||||
|
||||
private InstrumentorEvalNode() {
|
||||
super(EnsoLanguage.get(null));
|
||||
}
|
||||
|
||||
static Function asSuspendedEval(Object expr, IdExecutionService.Info info) {
|
||||
return new Function(CALL, null, SUSPENDED_EVAL, new Object[] {expr, info}, new Object[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Instrumentor.eval";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame) {
|
||||
var args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments());
|
||||
try {
|
||||
var expr = InteropLibrary.getUncached().asString(args[0]);
|
||||
var info = (IdExecutionService.Info) args[1];
|
||||
return info.eval(expr);
|
||||
} catch (UnsupportedMessageException ex) {
|
||||
throw new PanicException(args[0], this);
|
||||
}
|
||||
}
|
||||
}
|
@ -14,11 +14,12 @@ fib2 n f=1 s=1 =
|
||||
|
||||
spec =
|
||||
Test.group "Instrument fibonacci" <|
|
||||
a_plus_b_uuid = "00000000-aaaa-bbbb-0000-000000000000" # UUID for a+b
|
||||
|
||||
Test.specify "collect and filter on return updates" <|
|
||||
b = Vector.new_builder
|
||||
|
||||
collect uuid:Text result =
|
||||
a_plus_b_uuid = "00000000-aaaa-bbbb-0000-000000000000" # UUID for a+b
|
||||
if uuid == a_plus_b_uuid then
|
||||
b.append result
|
||||
Nothing
|
||||
@ -44,6 +45,38 @@ spec =
|
||||
# no more instrumenting after finalize
|
||||
b.to_vector.length . should_equal 1
|
||||
|
||||
Test.specify "access local variables " <|
|
||||
b = Vector.new_builder
|
||||
|
||||
collect uuid:Text ~result =
|
||||
if uuid == a_plus_b_uuid then
|
||||
case result of
|
||||
v : Vector -> b.append_vector_range v
|
||||
anything -> Test.fail <| "Should be a number: " + anything.to_text
|
||||
Nothing
|
||||
|
||||
expr = "[a, b, a-b]"
|
||||
instrumenter = Meta.meta .fib . instrument . on_return collect expression=expr . activate
|
||||
|
||||
instrumenter . with _->
|
||||
result = fib 10
|
||||
|
||||
v = b.to_vector
|
||||
|
||||
v.length . should_equal 3
|
||||
result . should_equal 89
|
||||
v . should_equal [55, 34, 21]
|
||||
|
||||
instrumenter.finalize
|
||||
# no op:
|
||||
instrumenter.finalize
|
||||
|
||||
result = fib 10
|
||||
result . should_equal 89
|
||||
|
||||
# no more instrumenting after finalize
|
||||
b.to_vector.length . should_equal 3
|
||||
|
||||
Test.specify "replay with caches and specify different result" <|
|
||||
replay uuid:Text = case uuid of
|
||||
"00000000-ffff-bbbb-0000-000000000000" -> 42
|
||||
|
Loading…
Reference in New Issue
Block a user