-
-
Notifications
You must be signed in to change notification settings - Fork 147
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
Generate source maps #356
Generate source maps #356
Conversation
a47ec64
to
1707fb3
Compare
First of all thank you very much for this! 🙏 It's the biggest feature PR yet! 🎉 Since there are many files changed, and also I need to fully understand how it works so it will take a while to fully review. I've tested it on the realworld project https://github.com/mint-lang/mint-realworld and fixed the encoder to support (it threw an error). There are a couple of places I see breakpoints which work well like this: There are a lot of places there is no breakpoint at all: I guess because it's not I'll probably will have more questions as I am progressing. |
<3
👍
Great! Some specs fail now, though. We can't have breakpoints inside the JS interop code since we don't analyse it, simply copy-paste. However, I'd have to debug why there is not even a single breakpoint at where the first backtick appears (inside function). Maybe there is no Some explanation
So, there are 2 methods:
The biggest issue with mapping is that the source map format only takes positions without lengths. So if you call Now, some things fail. Example 1.For example the
would be compiled to: return (some_condition ? 2 : 3); Now, you would expect that it's possible to put a breakpoint at The only reason why you can put a breakpoint at Example 2.There is another issue, in some cases the Mint compiler puts things into IIF (immediately-invoked function). Look at this example:
Probably not exactly this example but in it's formula it should compile into something like: return (() => { // try
let a = some_condition ? 2 : 3;
return (a ? (() => { // <--- here's our problem
return 4;
} : 5);
}); For the
The first one is generated because it's a top view from the if/else perspective. The second is naturally what we would want to have. And because there are 2 things generated for exactly one source segment, the debugger loses a position and I think it actually makes impossible put a breakpoint in there or it moves. SolutionsThe mentioned two issues can be solved but I didn't want to go with too many changes into the compiler. For the if/else (the The really good solution to IIFs would be outputting JavaScript AST instead of the text directly, then process the JS AST to figure out where mappings should go. But I felt it would be a larger change to the compiler (not sure atm though).
Sure |
3086b1a
to
975b212
Compare
I've just resolved the conflicts. I have overwritten the branch after rebasing onto master because I don't like merging |
f055213
to
b1b704c
Compare
@Sija please have a look at my comment above. |
@Namek My bad, excuse my hastiness 🙇♂️ You can still remove it and rebase it manually if u like :) |
Sorry for taking this long to check this, will do in the next couple of days, and it will definitely be part of the next release! |
@gdotdesign no problem, didn't have much time myself. I will rebase and solve the conflicts this weekend |
36f3317
to
7d19afd
Compare
@gdotdesign It's rebased again :) |
def self.empty?(node : Node) | ||
case node | ||
in NodeContainer | ||
[email protected]? { |n| empty?(n) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please avoid accessing @Ivars from outside of the object, use getters for such properties.
src/compiler.cr
Outdated
end | ||
|
||
def compile(nodes : Array(Ast::Node)) | ||
nodes.map { |node| compile(node).as(String) }.reject!(&.empty?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest implementing Codegen::Node#empty?
instead.
@Sija thanks for review, keep posting when you spot bad practices. I will refactor such stuff when general algorithm gets accepted by @gdotdesign Beause maybe there's a better approach to achieve source mapping or this one somehow destroys idea of how code is generated. Also I am not sure about performance since I didn't benchmark-compared it. |
I've taken a deeper look mostly on the mapping side, I've played around with adding def compile(node : Ast::Node) : Codegen::Node
if checked.includes?(node)
Codegen.source_mapped(node, _compile(node)).as(Codegen::Node)
else
""
end
end added most of the breakpoints that I was missing. There are some cases where it still needs manually adding the mapping but it's a lot better then it was. Now I'm wondering if it would be sufficient to only have that, but I'm guessing not. On the side of understanding the actual implementation of the In general I think this adds a lot of value in it's current state so we should merge it. I feel that a refactor of the compiler will happen in the future and with that we can create a better architecture for this as well. |
fwiw I've found some possibly useful materials:
|
end | ||
end | ||
|
||
src_from_line, src_from_col = file.get_line_column_position src_from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be refactored to use the Ast::Node#location
instead (added in #443).
@Namek Hi, could you please resolve the merge conflicts, so this could be possibly merged for the upcoming ( |
Rebased from master and tested the build times of https://github.com/mint-lang/mint-ui-website (27K LOC) and it slows down compiling considerably:
The development server is affected as well (obviously), so before merging it we need to see what where the bottleneck is. |
The culprit seems to be the IndentedStringBuilder using @Namek any idea why it could be slow? |
hey guys, big thanks for continuation of the work on this feature. Unfortunately, some unforeseen stuff happened in my life so I didn't have much time during last months. However, I'm ok now, so don't worry :)
@gdotdesign well, I only remember I wrote that class because it covers a lot of small cases. One was actually a fix covered by change in tests with incorrect indentation. So during code generation you'd have to fall back from indentation in current line, then add empty line, then start with correct indentation size on the next line. Where exactly did you replace using One suggestion I'd have for now would be to reduce the initial size of buffer in line 6 (inside I picked 16k because I was probably thinking about size of a cacheline in CPU. But maybe Garbage Collection hurts too much because of this? Anyway, I wanted to pick the number big enough so I wouldn't have too many resizes of a buffer. However, I didn't benchmark it, I only knew it had to be done because I've seen the performance drop in there. |
I didn't run the tests, just wanted to find what slows it down 😂
Thanks I'll try that 😉 |
any news? |
The compiler is being rewritten, so this PR will not be compatible with the new one. We will see if we can use parts of this for the new compiler. |
I think this should be a priority one |
I agree, that's why I started working on it a couple of days ago. See #702 |
Superseded by #702 |
(tackles issue #184)
Source map is a piece of data for a (browser's) debugger which enables to put breakpoints in actual Mint code instead of the generated JavaScript code for arbitrary Mint application.
Technical explanation
Currently JS code is generated by concatenating strings to generate the JS code. This makes it hard to find out connections between a string represting e.g. variable name and the origins of the variable inside original Mint code. Thus, this PR changes string concatenation to building a new tree of nodes which are mainly two things:
SourceMappable
class which identify whichAst::Node
is an origin of strings that those nodes containSo, during compilation whole tree is built and then we need to build it as a string which happens in
Codegen.build
function (seesrc/utils/codegen.cr
). The source map JSON itself is generated by code insrc/utils/source_map.cr
Resources:
How to use
For commands
compile
,build
andstart
there is a new flag boolean--source_map
(-m
for short).mint start
has this flag enabled by default (since it's good for development experience) so you don't have to do anything new.Just open the website for your mint app (
127.0.0.1:3000
by default). Then, in the browser's DevTools (F12) you can go toSources
tab, hit CTRL+P and type in a filename, e.g.Main.mint
. The rest is just normal debugging - breakpoints and looking at variables.Call for Review
Debugging experience is not perfect but I believe it's already valuable. I tested it only with Chrome. I would highly appreciate a review and some testing to see whether this is appropriate enough for merging.