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

feat: call dictu from native code #755

Merged
merged 13 commits into from
Nov 15, 2024

Conversation

liz3
Copy link
Contributor

@liz3 liz3 commented Nov 13, 2024

feat: call dictu from native code

This uses a stack loaded piece of code to interrupt the current vm execution in order to invoke a function called by native code.

It creates a new callframe for that and then makes sure the vm loop breaks after that frame was reached again, this way the result is returned to the caller. It then resets the vm state and continues normally.

Type of Change:

  • Bug fix
  • New feature
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Housekeeping:

  • Tests have been updated to reflect the changes done within this PR (if applicable).
  • Documentation has been updated to reflect the changes done within this PR (if applicable).

Screenshots (If Applicable):

const entry = "test";

def testFunc (a,b=30) {
  print("Hello world {}".format(entry));
  if(a == 30)
    return nil;
   return a+b;
}

print(entry.testThing(
testFunc, 30
));

This uses a stack loaded piece of code to interrupt the current
vm execution in order to invoke a function called by native code.

It creates a new callframe for that and then makes sure the vm loop
breaks after that frame was reached again, this way the result is
returned tp the caller. It then resets the vm state and continues
normally.
@liz3
Copy link
Contributor Author

liz3 commented Nov 13, 2024

@Jason2605 would be super thrilled to get some feedback here

Copy link
Member

@Jason2605 Jason2605 left a comment

Choose a reason for hiding this comment

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

This seems really promising so far! A couple of comments to think about but this would be huge 😄

return NIL_VAL;
int currentFrameCount = vm->frameCount;
Value* currentStack = vm->stackTop;
CallFrame *frame = &vm->frames[vm->frameCount++];
Copy link
Member

Choose a reason for hiding this comment

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

Here we need to be careful we actually have enough frames, we may need to reallocate if not

src/vm/vm.c Outdated
@@ -2457,3 +2469,21 @@ DictuInterpretResult dictuInterpret(DictuVM *vm, char *moduleName, char *source)

return result;
}
Value callFunction(DictuVM* vm, Value function, int argCount, Value* args) {
if(!IS_FUNCTION(function) && !IS_CLOSURE(function))
Copy link
Member

Choose a reason for hiding this comment

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

Here it would be nice to return an EMPTY val and throw a runtime error (happy you're just testing so may not have put that in yet etc)

Copy link
Member

Choose a reason for hiding this comment

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

Also be nice to handle natives here too, that should be a lot easier as we don't need to handle call frames

src/vm/vm.c Outdated
for(int i = argCount -1; i >= 0; i--) {
push(vm, args[i]);
}
runWithBreakFrame(vm, currentFrameCount+1);
Copy link
Member

Choose a reason for hiding this comment

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

Here we would need to handle a runtime error as at the minute (without testing) I'm assuming this will probably dump

@liz3
Copy link
Contributor Author

liz3 commented Nov 14, 2024

@Jason2605 I've addressed the review points.
The next step will be adding tests but i am yet to decide how to do that.

I am very thankful you do not seam against this approach because i was very uncertain whether this would have other side effects but it seams that it does give a good way to callback into dictu code from natives, the overhead is manageable i hope.

@liz3
Copy link
Contributor Author

liz3 commented Nov 14, 2024

added tests!

@Jason2605
Copy link
Member

I think we're almost there! I think the runtime stack trace does segfault out though if a runtime error is actually raised, so we will need to have a look into that

@liz3
Copy link
Contributor Author

liz3 commented Nov 14, 2024

@Jason2605 ive fixed the issue, i had to read the error logic to make sense of the issue but after that it appeared logical.
I intentionally set the closure to NULL and then have a special case within the error stack trace, should there be more "types of callframes" in the future we might need to change the approach but for now this should work

@Jason2605
Copy link
Member

I actually wonder if it's worth silencing it for the synthetic callframe. I'm not sure it gives the end user a whole lot of benefit and may cause confusion, what do you think?

Screenshot 2024-11-15 at 00 46 02 Screenshot 2024-11-15 at 00 45 28

@liz3
Copy link
Contributor Author

liz3 commented Nov 15, 2024

Silencing should be fine too, it is not real code afterall.

@liz3
Copy link
Contributor Author

liz3 commented Nov 15, 2024

http test service returning 502 🫠

@Jason2605
Copy link
Member

Aha yeah the tests rely on an external service (not great). I’ve re-ran them now

@Jason2605
Copy link
Member

It’s 1am for me right now so I’ll give this a proper look over tomorrow! Thank you again for this :)

Copy link
Member

@Jason2605 Jason2605 left a comment

Choose a reason for hiding this comment

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

We have the setUp method we can use here that is implicitly called before each test run!

https://dictu-lang.com/docs/standard-lib/unittest/#setup

tests/ffi/ffi.du Outdated
@@ -38,18 +38,51 @@ import FFI;
import Path;

class TestFFIModule < UnitTest {
var mod = nil;
doSetup() {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
doSetup() {
setUp() {

tests/ffi/ffi.du Outdated
FFI.suffix
));
const mod = FFI.load(path);
this.doSetup();
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
this.doSetup();

tests/ffi/ffi.du Outdated
this.assertEquals(mod.test, "Dictu!");
for(var i = 0; i < 40; i+=2) {
this.assertEquals(mod.dictuFFITestAdd(i, i), i+i);
}
this.assertEquals(mod.dictuFFITestStr(), "Hello From Dictu FFI module!");
}
testFFIModuleCallback() {
this.doSetup();
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
this.doSetup();

@liz3
Copy link
Contributor Author

liz3 commented Nov 15, 2024

We have the setUp method we can use here that is implicitly called before each test run!

https://dictu-lang.com/docs/standard-lib/unittest/#setup

done

@Jason2605
Copy link
Member

God damn HTTP tests 😠

@Jason2605
Copy link
Member

It's getting merged. Good find with this! Thank you for the PR 😃

@Jason2605 Jason2605 merged commit ac22490 into dictu-lang:develop Nov 15, 2024
7 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants