I wrote this Kotlin K2 FIR guide because I was not able to find any guides or examples to get started with FIR for static analysis. K2 is now finally stable in Kotlin 2.0.0 and future versions, so FIR will be the recommended way of doing static analysis.
Background
Starting Kotlin 1.x you could only use PSI (Program Structure Interface) to create an AST (Abstract Syntax Tree) to run static analysis.
Kotlin’s K2 Compiler is powered by a new Frontend Intermediate Representation (FIR). It still uses PSI to create the initial model, but transforms that into FIR which is a semantic model that is independent from any compiler backend (JVM, JS, Native, etc).
Yes, this is a really short post, but it’s more so here so that you’ll discover the Gist from a search and be able to get started using K2’s FIR model!
Conclusion
FIR is much more useful in understanding what the code is instead of the original syntax. That being said, you can still reach back into the code that the FIR model is derived from to gain more context as needed. If you are building any Static Analysis tooling, it’s HIGHLY suggested to start building it on FIR going forward.
I wanted to embed the contents of my GitHub project on another website, but the path to get there wasn’t straightforward. Here are the roadblocks I hit, and how I got around them.
Skip to the end if you just want the final solution.
Idea 1: Render in an iframe
I’d love to just create an iframe to show everything in my GitHub project on another website. An iframe seemed like a beautiful solution, but….
I thought I could just scrape the content from the website for my GitHub project and then render it on my site. However, I couldn’t pull arbitrary content from another web host due to CORS.
Note: If I had a server I could do this because I wouldn’t have CORS issues, but I was trying to do this completely in a frontend web page without a server.
JSONP (JSON with Padding) is a workaround for CORS. You basically load an arbitrary bit of JavaScript from a 3rd party site, and have it call an arbitrary function that you know the name of.
This exposes us to so many security vulnerabilities so PLEASE only do this with trusted sites. They could arbitrarily execute code within the context of your website and you wouldn’t know.
Note: JSONP uses the same functionality of loading in 3rd party javascript to do things like Analytics tracking, or fancy animations with JS libraries like BootStrap JS. It’s just programatically creating a <script> tag.
Roadblock: The response to the API contains Base64 encoded content.
Idea 5: Decode Base64 File Contents and Show README
I was able to define my callback for the GitHub API and then render the text into a pre (preformatted text) element. I did this by Base64 Decoding the GitHub API response’s “content” field, and then programmatically creating a pre element and setting its textContent.
function myCallback(response) {
// Decode the Base64 Encoded Content
let decodedContent = atob(response.data.content);
// Create a "pre" HTML tag and render the content
let pre = document.createElement("pre");
pre.textContent = decodedContent;
document.getElementsByTagName('body')[0].append(pre);
}
Roadblock: The contents weren’t formatted, just plain markdown.
Idea 6: Use a JS Library to Render the Markdown
There is a JavaScript library for everything. In this case I found markedjs/marked. I just give it a string of Markdown, and it’ll give me back the rendered HTML.
function githubMarkdownCallback(response) {
// Decode the Base64 Encoded Content
let rawMarkdown = atob(response.data.content);
// Use the markedjs library to transform markdown -> html
let markdownHtml = marked.parse(rawMarkdown)
// Add the new div to the body of the html page
let div = document.createElement("div");
div.innerHTML = markdownHtml;
document.getElementsByTagName('body')[0].append(div);
}
FINAL SOLUTION!
Fetch my project’s README.md contents from GitHub’s public API using JSONP and use markedjs to render the Markdown into HTML.
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script type="text/javascript">
function myCallback(response) {
// Decode the Base64 Encoded Content
let rawMarkdown = atob(response.data.content);
// Use the markedjs library to transform markdown -> html
let markdownHtml = marked.parse(rawMarkdown)
// Add the new div to the body of the html page
let div = document.createElement("div");
div.innerHTML = markdownHtml;
document.getElementsByTagName('body')[0].append(div);
}
let script = document.createElement('script');
script.src = 'https://api.github.com/repos/handstandsam/ShoppingApp/contents/README.md?callback=myCallback';
document.getElementsByTagName('head')[0].appendChild(script);
</script>