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

State rework & IO Contexts #3828

Merged
merged 11 commits into from
Oct 26, 2022
Merged

State rework & IO Contexts #3828

merged 11 commits into from
Oct 26, 2022

Conversation

kustosz
Copy link
Contributor

@kustosz kustosz commented Oct 25, 2022

Pull Request Description

  1. Changes how we do monadic state – rather than a haskelly solution, we now have an implicit env with mutable data inside. It's better for the JVM. It also opens the possibility to have state ratained on exceptions (previously not possible) – both can now be implemented.
  2. Introduces permission check system for IO actions.

Important Notes

Checklist

Please include the following checklist in your PR:

  • The documentation has been updated if necessary.
  • All code conforms to the
    Scala,
    Java,
    and
    Rust
    style guides.
  • All code has been tested:
    • Unit tests have been written where possible.
    • If GUI codebase was changed: Enso GUI was tested when built using BOTH
      ./run ide build and ./run ide watch.

Copy link
Member

@JaroslavTulach JaroslavTulach left a comment

Choose a reason for hiding this comment

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

Very nice simplification.

@@ -87,9 +86,7 @@ public Object execute(VirtualFrame frame) {
}
Object state = Function.ArgumentsHelper.getState(frame.getArguments());
frame.setObject(this.getStateFrameSlot(), state);
Object result = body.executeGeneric(frame);
state = FrameUtil.getObjectSafe(frame, this.getStateFrameSlot());
return new Stateful(state, result);
Copy link
Member

Choose a reason for hiding this comment

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

Yes, this is simpler.

this.callable.executeGeneric(frame), frame, state, evaluateArguments(frame));
frame.setObject(getStateFrameSlot(), result.getState());
return result.getValue();
State state = (State) FrameUtil.getObjectSafe(frame, getStateFrameSlot());
Copy link
Member

Choose a reason for hiding this comment

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

State is a class. It is in a getStateFromSlot().

Stateful result = thunkExecutorNode.executeThunk(thunk, state, getTailStatus());
frame.setObject(getStateFrameSlot(), result.getState());
return result.getValue();
State state = (State) FrameUtil.getObjectSafe(frame, getStateFrameSlot());
Copy link
Member

Choose a reason for hiding this comment

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

I suggest to introduce:

static State.getState(Frame frame, BaseNode where) { ... }

which could encapsulate this code into a nicer API.


@Override
public Object executeGeneric(VirtualFrame frame) {
State state = (State) FrameUtil.getObjectSafe(frame, getStateFrameSlot());
Copy link
Member

Choose a reason for hiding this comment

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

While evaluating I found :

  protected FrameSlot getStateFrameSlot() {
    if (stateFrameSlot == null) {
      CompilerDirectives.transferToInterpreterAndInvalidate();
      stateFrameSlot = ((EnsoRootNode) getRootNode()).getStateFrameSlot();
    }
    return stateFrameSlot;
  }

I'd like to ask? Does it make sense to have per node local stateFrameSlot? It might be enough to:

  protected FrameSlot getStateFrameSlot() {
    return((EnsoRootNode) getRootNode()).getStateFrameSlot();
  }

Should have the same performance (on fast path) because getRootNode() is a compilation constant.

Stateful execute(
VirtualFrame frame, @MonadicState Object state, Object self, @Suspend Object argument) {
Object execute(
VirtualFrame frame, @MonadicState State state, Object self, @Suspend Object argument) {
Copy link
Member

Choose a reason for hiding this comment

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

Do we need the @MonadicState annotation? Isn't it enough to check for State and treat each State as a carrier for the @MonadicState?

@@ -0,0 +1,31 @@
package org.enso.interpreter.runtime.state;

public record IOPermissions(String name, boolean isInputAllowed, boolean isOutputAllowed) {
Copy link
Member

Choose a reason for hiding this comment

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

Using record as enum! You don't want this class to have public constructor. Try:

private IOPermissions {
}

import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.Shape;

public record State(Container container, IOPermissions ioPermissions) {
Copy link
Member

Choose a reason for hiding this comment

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

Probably you also want a private constructor with a factory method to get the default State.

Comment on lines 37 to 38
allow_input_in env = @Builtin_Method "Runtime.allow_input_in"
allow_output_in env = @Builtin_Method "Runtime.allow_output_in"
Copy link
Member

Choose a reason for hiding this comment

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

It feels like this should be placed in a file Runtime.enso and re-exported here, no?

Copy link
Member

Choose a reason for hiding this comment

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

And btw. shouldn't these definitions be placed also in the main distribution too? Otherwise, they are unreachable in the normal operations outside of the tests that replace the distribution with micro-distribution.

Because of that - I'd also suggest to add a copy of at least one tests from IOContextTest.scala to test/Tests/src/Runtime/IO_Context_Spec.enso so that we can ensure that they work outside of the micro-distribution framework properly (as I assume currently they wouldn't).

Or is that planned for some separate future PR?

Text_Utils.take_prefix (kshi+kshi+kshi) 2 . should_equal kshi+kshi
Text_Utils.take_suffix (kshi+kshi+'a'+kshi) 2 . should_equal 'a'+kshi
Text_Utils.take_suffix (kshi+kshi+'a'+kshi) 1 . should_equal kshi
Test.specify "should correctly take prefix and suffix of a string" <|
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for catching this!

Comment on lines +8 to +9
override def contextModifiers: Option[Context#Builder => Context#Builder] =
Some(_.option("enso.IOEnvironment", "development"))
Copy link
Member

Choose a reason for hiding this comment

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

Is this the reason why these tests need to be in Scala and cannot be just pure Enso tests? Can we get a comment explaining that?

@kustosz kustosz added the CI: Ready to merge This PR is eligible for automatic merge label Oct 26, 2022
@mergify mergify bot merged commit 9017608 into develop Oct 26, 2022
@mergify mergify bot deleted the wip/mk/state-refactor branch October 26, 2022 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: Ready to merge This PR is eligible for automatic merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants