mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 06:01:37 +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
|
ADVANCED
|
||||||
|
|
||||||
Registers callback to be executed when a node/expression evaluation
|
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 value is _cached_ and returned from `on_enter` callback next time
|
||||||
the same expression is evaluated.
|
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:
|
Arguments:
|
||||||
- fn: The callback function accepting UUID and computed value
|
- fn: The callback function accepting UUID and computed value
|
||||||
on_return self (fn : Text -> Any -> Nothing) =
|
- expression: Expression to evaluate on_return - by default Nothing -
|
||||||
new = instrumentor_builtin "onReturn" [ self.impl, fn ]
|
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
|
Instrumentor.Value new
|
||||||
|
|
||||||
## PRIVATE
|
## PRIVATE
|
||||||
|
@ -21,6 +21,7 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
|||||||
private final Module module;
|
private final Module module;
|
||||||
private final Object onEnter;
|
private final Object onEnter;
|
||||||
private final Object onReturn;
|
private final Object onReturn;
|
||||||
|
private final Object onReturnExpr;
|
||||||
private final Object onCall;
|
private final Object onCall;
|
||||||
private final EventBinding<?> handle;
|
private final EventBinding<?> handle;
|
||||||
|
|
||||||
@ -30,16 +31,18 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
|||||||
this.target = target;
|
this.target = target;
|
||||||
this.onEnter = null;
|
this.onEnter = null;
|
||||||
this.onReturn = null;
|
this.onReturn = null;
|
||||||
|
this.onReturnExpr = null;
|
||||||
this.onCall = null;
|
this.onCall = null;
|
||||||
this.handle = 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.module = orig.module;
|
||||||
this.service = orig.service;
|
this.service = orig.service;
|
||||||
this.target = orig.target;
|
this.target = orig.target;
|
||||||
this.onEnter = onEnter != null ? onEnter : orig.onEnter;
|
this.onEnter = onEnter != null ? onEnter : orig.onEnter;
|
||||||
this.onReturn = onReturn != null ? onReturn : orig.onReturn;
|
this.onReturn = onReturn != null ? onReturn : orig.onReturn;
|
||||||
|
this.onReturnExpr = onReturnExpr != null ? onReturnExpr : orig.onReturnExpr;
|
||||||
this.onCall = onCall != null ? onCall : orig.onCall;
|
this.onCall = onCall != null ? onCall : orig.onCall;
|
||||||
this.handle = !activate ? null : service.bind(
|
this.handle = !activate ? null : service.bind(
|
||||||
module, target, this, new Timer.Disabled()
|
module, target, this, new Timer.Disabled()
|
||||||
@ -73,7 +76,12 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
|||||||
public void updateCachedResult(IdExecutionService.Info info) {
|
public void updateCachedResult(IdExecutionService.Info info) {
|
||||||
try {
|
try {
|
||||||
if (onReturn != null) {
|
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) {
|
} catch (InteropException ignored) {
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
package org.enso.interpreter.node.expression.builtin.meta;
|
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.dsl.BuiltinMethod;
|
||||||
import org.enso.interpreter.runtime.EnsoContext;
|
import org.enso.interpreter.runtime.EnsoContext;
|
||||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||||
import org.enso.interpreter.runtime.callable.function.Function;
|
import org.enso.interpreter.runtime.callable.function.Function;
|
||||||
import org.enso.interpreter.runtime.data.text.Text;
|
import org.enso.interpreter.runtime.data.text.Text;
|
||||||
import org.enso.interpreter.runtime.data.vector.ArrayLikeAtNode;
|
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.data.vector.ArrayLikeLengthNode;
|
||||||
import org.enso.interpreter.runtime.error.PanicException;
|
import org.enso.interpreter.runtime.error.PanicException;
|
||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
|
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
|
||||||
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
|
|
||||||
@BuiltinMethod(
|
@BuiltinMethod(
|
||||||
type = "Meta",
|
type = "Meta",
|
||||||
@ -33,7 +31,7 @@ public class InstrumentorBuiltin extends Node {
|
|||||||
if (atNode.executeAt(args, 0) instanceof Instrumentor b) {
|
if (atNode.executeAt(args, 0) instanceof Instrumentor b) {
|
||||||
ret = switch (op) {
|
ret = switch (op) {
|
||||||
case "onEnter" -> onEnter(b, atNode.executeAt(args, 1));
|
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 "onCall" -> onCall(b, atNode.executeAt(args, 1));
|
||||||
case "activate" -> activate(b, atNode.executeAt(args, 1));
|
case "activate" -> activate(b, atNode.executeAt(args, 1));
|
||||||
case "deactivate" -> b.deactivate();
|
case "deactivate" -> b.deactivate();
|
||||||
@ -70,16 +68,16 @@ public class InstrumentorBuiltin extends Node {
|
|||||||
@CompilerDirectives.TruffleBoundary
|
@CompilerDirectives.TruffleBoundary
|
||||||
private Object onEnter(Instrumentor b, Object arg) {
|
private Object onEnter(Instrumentor b, Object arg) {
|
||||||
if (arg instanceof Function fn) {
|
if (arg instanceof Function fn) {
|
||||||
return new Instrumentor(b, fn, null, null, false);
|
return new Instrumentor(b, fn, null, null, null, false);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@CompilerDirectives.TruffleBoundary
|
@CompilerDirectives.TruffleBoundary
|
||||||
private Object onReturn(Instrumentor b, Object arg) {
|
private Object onReturn(Instrumentor b, Object arg, Object expr) {
|
||||||
if (arg instanceof Function fn) {
|
if (arg instanceof Function fn) {
|
||||||
return new Instrumentor(b, null, fn, null, false);
|
return new Instrumentor(b, null, fn, expr, null, false);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -88,7 +86,7 @@ public class InstrumentorBuiltin extends Node {
|
|||||||
@CompilerDirectives.TruffleBoundary
|
@CompilerDirectives.TruffleBoundary
|
||||||
private Object onCall(Instrumentor b, Object arg) {
|
private Object onCall(Instrumentor b, Object arg) {
|
||||||
if (arg instanceof Function fn) {
|
if (arg instanceof Function fn) {
|
||||||
return new Instrumentor(b, null, null, fn, false);
|
return new Instrumentor(b, null, null, null, fn, false);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -97,7 +95,7 @@ public class InstrumentorBuiltin extends Node {
|
|||||||
@CompilerDirectives.TruffleBoundary
|
@CompilerDirectives.TruffleBoundary
|
||||||
private Object activate(Instrumentor b, Object arg) {
|
private Object activate(Instrumentor b, Object arg) {
|
||||||
if (arg instanceof Function fn) {
|
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);
|
var ctx = EnsoContext.get(this);
|
||||||
return ctx.getResourceManager().register(builder, fn);
|
return ctx.getResourceManager().register(builder, fn);
|
||||||
} else {
|
} 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 =
|
spec =
|
||||||
Test.group "Instrument fibonacci" <|
|
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" <|
|
Test.specify "collect and filter on return updates" <|
|
||||||
b = Vector.new_builder
|
b = Vector.new_builder
|
||||||
|
|
||||||
collect uuid:Text result =
|
collect uuid:Text result =
|
||||||
a_plus_b_uuid = "00000000-aaaa-bbbb-0000-000000000000" # UUID for a+b
|
|
||||||
if uuid == a_plus_b_uuid then
|
if uuid == a_plus_b_uuid then
|
||||||
b.append result
|
b.append result
|
||||||
Nothing
|
Nothing
|
||||||
@ -44,6 +45,38 @@ spec =
|
|||||||
# no more instrumenting after finalize
|
# no more instrumenting after finalize
|
||||||
b.to_vector.length . should_equal 1
|
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" <|
|
Test.specify "replay with caches and specify different result" <|
|
||||||
replay uuid:Text = case uuid of
|
replay uuid:Text = case uuid of
|
||||||
"00000000-ffff-bbbb-0000-000000000000" -> 42
|
"00000000-ffff-bbbb-0000-000000000000" -> 42
|
||||||
|
Loading…
Reference in New Issue
Block a user