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
crystal i #10910
crystal i #10910
Conversation
Can you try to pinpoint where that error happens? The easiest way would be to put a |
@straight-shoota Also, you can try pulling and applying your patch again. I made a lot of fixes these past days. |
Okay, so the issue seems to be with calling fstat. I jerry-rigged something together because the symbol The first exception is raised when initializing |
My suspicion was correct, the following code triggers the @[Primitive(:interpreter_raise_without_backtrace)]
def raise(exception : Exception) : NoReturn
end
def foo
raise Exception.new
end
begin
foo
rescue exc
foo
end |
I think the issue is that when an exception escapes then we print it using |
I don't think it's necessary tampering with exception printing. Knowing that that's the reason is enough. We should be able to investigate the actual error cause in isolation. But on a more general perspective, we might want to make sure to make the kernel setup in general (not just for the interpreter) more resilient to issues with setting up the standard streams. |
@straight-shoota Done! Can you try again? That said, I think we should figure out what's the problem with |
As I said in my previous comment, the last comment isn't necessary. It doesn't help me with debugging. |
@straight-shoota Maybe we could pair on this one day to see why |
@straight-shoota I think You could also try and see if |
Yeah, I've figured as much. But even with assumingly correct arguments, it still doesn't work. The Ruby example fails for me as well. It appears that different versions of libc (I assume it's glibc because carc.in runs on Arch Linux) may have an |
I've updated the gist with a patch that now works for me on x86_64-linux-gnu |
@straight-shoota Great! I just pushed that diff minus the change to Thinking about next steps, this is what comes to my mind:
After that, there's still a lot more to do but then it will be easier because the PRs will be smaller and more focused. In particular:
|
@straight-shoota Oh, maybe it's better if we make another branch so that any of us can push to it? Otherwise... how are you going to make changes to see if CI works? (if that's what you eventually intend to do) |
Yeah, it might be a good idea to develop this feature in a branch on this repo. We can even make pull requests against that. I think we have still some work for getting this in a shippable state. |
FWIW, the default options on a PR allow maintainers to push into PR branches. If you, when creating the original PR, hadn't changed this checkbox, then anyone with commit access on Once you've created a PR, you can change this in the bottom right of the PR: |
@Fryguy Thanks! I still think it's better to have a branch in this repo. Then for example you can crate a PR that targets that's branch, and letting others push to it is just simpler. |
(also checking out the branch locally is simpler, no need to add remotes...) |
There's one drawback - it duplicates the CI jobs. |
@Sija How? |
@asterite They're being run on both, |
The experimental
crystal i
(interactive) command is here!What is
crystal i
crystal i
stands for "interactive crystal". It can be used in two modes right now:crystal i
: opens an interactive crystal session (REPL) similar toirb
from Rubycrystal i file.cr
: runs a file in interpreted modeIn any of these two modes, you can use
debugger
in your code to debug it at that point. This is similar tu Ruby's pry. There you can use these commands (similar topry
too):step
: go to the next line/instruction, possibly going inside a methodnext
: go to the next line/instruction, doesn't enter into methodsfinish
: exit the current methodcontinue
: resume executionwhereami
: show where the debugger isBy the way, before this PR you could also use
debugger
and lldb or gdb would stop at that point, if compiled with-d
(otherwise it seems the program just crashes, maybe we should just ignoredebugger
in that case: something to improve outside of this PR)Overview and some technical details
When running in interpreted mode, semantic analysis is done as usual, but instead of then using LLVM to generate code, we compile code to bytecode (custom bytecode defined in this PR, totally unrelated to LLVM). Then there's an interpreter that understands this bytecode. All of this for a stack-based machine. If you know nothing about interpreters and you want to learn more, I recommend reading the excellent https://www.craftinginterpreters.com/a-bytecode-virtual-machine.html
The memory layout of things in interpreted mode matches the memory layout of compiled mode: a union of
Int32 | String
occupies 16 bytes in both modes, and there's an 8 bytes tag for the type ID. That said, the values for type IDs might not match between the two versions, and this makes the two worlds kind of incompatible (they can't interact with each other.)C calls, and C callbacks, are implemented using libffi together with dlopen.
Status
I'm just opening this PR to give a chance for others to start looking and commenting at the code, and ask questions if things are not clear. That said, I'd still like to take some time to clean up and comment a lot of the code before merging this. A lot of this was rushed because I wanted to have a big chunk ready for the conference.
Some things that are missing to implement related to the language:
And then some things that I know don't work fine right now:
crystal i
doesn't work super well, specially if you redefine methods or if you extend types (this is the real challenge, and we could maybe just postpone this one for how long as we want)debugger
mode (let's call itpry
from now on) doesn't work. I think this one should be easy to fix but I didn't have time to look into itpry
don't work well (I'm not sure how to fix this, I think it requires a change also to the "real" Crystal)finished
isn't executed yetat_exit
isn't executed yet (becausemain
isn't executed at all)Focus
For now, I'd like to focus on polishing the existing code and making sure it's 100% understandable and documented (some comments are already outdated, some method names are long and not nice, there might be some redundant code, and I'm sure there's already unclear code). After that we can merge this, put an "experimental" label next to the command, and then implement the missing things or fix existing bugs in separate PRs, which are going to be easier to review and merge like that.
I'm also open for suggestions on how to improve the code, be it for code clarity of for performance.
Also sorry beforehand for the commit (mis)organization and commit messages, this was all very experimental.
Should this be in a separate repository?
This PR has some changes to the standard library, mainly some methods and functions need to be emulated in interpreted mode, and there's a new
flag?(:interpreted)
thing you can use. These could be in a separate PR, and then we could maybe extract the tool to a separate repository. But I think that having interpreted mode in the main repository, like every other tool, is fine from now. I believe moving tools outside of this repository is a separate discussion. But for instance, eventuallycrystal play
could start using interpreted mode under the hood, or maybe you will be able to docrystal spec -i ...
to run in interpreted mode. So, again, having all of this in this repository is probably fine.About FFI, dlopen and readline
Right now I put them in the top-level just for simplicity. We should probably move them under theCrystal
namespace because we don't want to publicly support these.There are now inside the✅
Crystal
namespaceMore thoughts
Steps before merging this PR (will be updated as we work on it)