Kotlin K2 FIR Quickstart Guide

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).

Step 1

Add the Kotlin Embedded Compiler Dependency

implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:2.0.0")

Step 2

Use the code in the Gist: https://gist.github.com/handstandsam/9a561fc78b593039d1dd500fae14b355

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.

Credits

This implementation is based on:

Fetch and Render GitHub Markdown without CORS

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….

<iframe src="https://github.com/handstandsam/ShoppingApp"></iframe>

Roadblock: GitHub blocks iframes for its content.

Idea 2: Fetch the HTML, and Render it Manually

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.

Roadblock: CORS Browser Security Policies

Idea 3: Use the GitHub API to fetch the README File

GitHub has an awesome API that we can use to access the contents of a repository! I can’t use it to get the rendering of the entire project page, but I can access individual files like my README.md.

https://api.github.com/repos/handstandsam/ShoppingApp/contents/README.md

This allows me to pull down the contents of the file. The problem is that I can’t do that just in the frontend browser itself due to CORS.

Are you sensing a theme? Doing things in a browser is hard, but it helps make us safer on the web, so I can’t argue with that.

Roadblock: CORS Browser Security Policies

Idea 4: Use JSONP with the GitHub API

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.

Well, GitHub has support for JSONP! We will load JavaScript into our page from https://api.github.com/repos/handstandsam/ShoppingApp/contents/README.md?callback=myCallback and when the loading is done, it will invoke myCallback(results) assuming that the remote server has support for JSONP.

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.

<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
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>