diff --git a/compiler/fir/dump/build.gradle.kts b/compiler/fir/dump/build.gradle.kts
new file mode 100644
index 00000000000..39d96734fee
--- /dev/null
+++ b/compiler/fir/dump/build.gradle.kts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+plugins {
+ kotlin("jvm")
+ id("jps-compatible")
+}
+
+repositories {
+ maven { setUrl("https://dl.bintray.com/kotlin/kotlinx.html/") }
+}
+
+dependencies {
+ compile(project(":core:descriptors"))
+ compile(project(":core:deserialization"))
+ compile(project(":compiler:fir:cones"))
+ compile(project(":compiler:fir:tree"))
+ compile(project(":compiler:fir:resolve"))
+ compile(project(":compiler:fir:java"))
+ compile(project(":compiler:cli"))
+
+ compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
+ compile("org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.12")
+}
+
+sourceSets {
+ "main" { projectDefault() }
+}
+
+
+testsJar()
\ No newline at end of file
diff --git a/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.dark.css b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.dark.css
new file mode 100644
index 00000000000..4dd1fd7bc98
--- /dev/null
+++ b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.dark.css
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+body.dark {
+ background-color: #2b2b2b;
+ color: #bababa;
+}
+
+.dark .toggle-theme {
+ background-color: #6a7878;
+}
+
+.dark .return-label {
+ color: #6a7878;
+ background-color: #333333;
+}
+
+.dark .label {
+ color: dodgerblue;
+ background-color: #333333;
+}
+
+.dark .expression-type {
+ color: #6a7878;
+ background-color: #333333;
+}
+
+.dark .keyword {
+ color: #8ba558;
+}
+
+.dark a.ref.selected {
+ background-color: rgba(77, 255, 27, 0.44);
+}
+
+.dark .unresolved {
+ text-decoration-color: blue;
+}
+
+.dark .error {
+ text-decoration-color: red;
+}
+
+.dark .error.unsupported {
+ text-decoration-color: cyan;
+}
+
+.dark a.ref.external {
+ color: #bababa;
+}
+
+.dark a.container-ref,
+.dark a.ref,
+.dark a.ref:visited,
+.dark a.package-fqn,
+.dark a.package-fqn:visited {
+ color: rgb(223, 190, 149);
+}
+
+.dark a.container-ref:visited {
+ color: rgb(223, 185, 67);
+}
+
+.dark :target {
+ background-color: #9f5000;
+}
+
+.dark .string-literal {
+ color: #98b884;
+}
+
+.dark .declaration .simple-name {
+ color: #dfb943
+}
+
+.dark .line {
+ background-color: #313335;
+ border-right: thin solid darkgray;
+}
\ No newline at end of file
diff --git a/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.white.css b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.white.css
new file mode 100644
index 00000000000..54f01bd82e1
--- /dev/null
+++ b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.white.css
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+.dark .toggle-theme {
+ background-color: #dfdfdf;
+}
+
+.white .return-label {
+ background-color: lightgray;
+}
+
+.white .label {
+ color: dodgerblue;
+ background-color: lightgray;
+}
+
+.white .expression-type {
+ background-color: lightgray;
+}
+
+.white .keyword {
+ color: #8ba558;
+}
+
+.white a.ref.selected {
+ background-color: rgba(77, 255, 27, 0.44);
+}
+
+.white .unresolved {
+ text-decoration-color: blue;
+}
+
+.white .error {
+ text-decoration-color: red;
+}
+
+.white .error.unsupported {
+ text-decoration-color: cyan;
+}
+
+.white a.ref.external {
+ color: black;
+}
+
+.white a.container-ref,
+.white a.ref,
+.white a.ref:visited,
+.white a.package-fqn,
+.white a.package-fqn:visited {
+ color: rgb(0, 0, 238);
+}
+
+.white :target {
+ background-color: yellow;
+}
+
+.white .string-literal {
+ color: #98b884;
+}
+
+.white .declaration .simple-name {
+ font-weight: bold;
+}
+
+.white .line {
+ background-color: lightgray;
+ border-right: thin solid darkgray;
+}
\ No newline at end of file
diff --git a/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/logic.js b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/logic.js
new file mode 100644
index 00000000000..f161180ade9
--- /dev/null
+++ b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/logic.js
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+function updateSelectedRefs() {
+ $("a.ref.selected").removeClass("selected");
+ $("a.ref").each(function () {
+ if ($(this).attr("jshref") == window.location.href) {
+ $(this).addClass("selected");
+ }
+ });
+}
+
+var key = "fir-dump-theme";
+
+
+var theme = localStorage.getItem(key);
+if (!theme) {
+ theme = "white"
+}
+
+function toggleTheme() {
+ var body = $("body");
+ body.removeClass(theme);
+ if (theme === "white") {
+ theme = "dark"
+ }
+ else {
+ theme = "white"
+ }
+ body.addClass(theme);
+
+ localStorage.setItem(key, theme);
+}
+
+$(document).ready(function () {
+ $(".fold-container").click(function (event) {
+ $(this).toggleClass("unfold");
+ event.stopPropagation();
+ });
+ var refs = $('a.ref:not(.external)');
+ refs.each(function () {
+ var href = $(this).prop('href');
+ $(this).attr('href', 'javascript:void(0);');
+ $(this).attr('jshref', href);
+ });
+ refs.bind('click', function (e) {
+ var href = $(this).attr('jshref');
+ if (!e.metaKey && e.ctrlKey) {
+ e.metaKey = e.ctrlKey;
+ }
+ if (e.metaKey) {
+ location.href = href;
+ return false;
+ }
+ });
+ updateSelectedRefs();
+ $(window).on('hashchange', function (e) {
+ updateSelectedRefs()
+ });
+
+
+ var body = $("body");
+ body.prepend("");
+ body.addClass(theme);
+
+
+});
+
+window.onload = function () {
+ var $line = $('.line');
+ var maxWidth = Math.max.apply(Math, $line.map(function () {
+ return $(this).width();
+ }).get());
+ $line.each(function () {
+ $(this).width(maxWidth)
+ })
+};
\ No newline at end of file
diff --git a/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/style.css b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/style.css
new file mode 100644
index 00000000000..b60ebff552f
--- /dev/null
+++ b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/style.css
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+pre {
+ counter-reset: line;
+}
+
+.line:after {
+ content: counter(line);
+ -webkit-user-select: none;
+ padding-right: 5px;
+ margin-right: 0;
+ margin-left: auto;
+}
+
+.line {
+ counter-increment: line;
+ display: inline-flex;
+}
+
+.unresolved {
+ text-decoration-style: wavy;
+ text-decoration: underline;
+}
+
+.error {
+ text-decoration-style: wavy;
+ text-decoration: underline;
+}
+
+a.container-ref,
+a.package-fqn,
+a.ref {
+ text-decoration: none;
+}
+
+.expression-type, .label, .return-label {
+ border-radius: 5px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+.fold-region {
+ display: none;
+}
+
+.fold-container.unfold > .fold-region {
+ display: inline;
+}
+
+.toggle-theme {
+ border-style: solid;
+ border-width: thin;
+ border-radius: 10px;
+}
+
+.line {
+ margin-right: 5px;
+}
\ No newline at end of file
diff --git a/compiler/fir/dump/src/org/jetbrains/kotlin/fir/dump/HtmlFirDump.kt b/compiler/fir/dump/src/org/jetbrains/kotlin/fir/dump/HtmlFirDump.kt
new file mode 100644
index 00000000000..e58a475bc22
--- /dev/null
+++ b/compiler/fir/dump/src/org/jetbrains/kotlin/fir/dump/HtmlFirDump.kt
@@ -0,0 +1,1252 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.fir.dump
+
+import kotlinx.html.*
+import kotlinx.html.stream.appendHTML
+import org.apache.commons.lang.StringEscapeUtils
+import org.jetbrains.kotlin.descriptors.ClassKind
+import org.jetbrains.kotlin.descriptors.Modality
+import org.jetbrains.kotlin.descriptors.Visibilities
+import org.jetbrains.kotlin.descriptors.Visibility
+import org.jetbrains.kotlin.fir.*
+import org.jetbrains.kotlin.fir.declarations.*
+import org.jetbrains.kotlin.fir.expressions.*
+import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition
+import org.jetbrains.kotlin.fir.expressions.FirWhenSubjectExpression
+import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
+import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference
+import org.jetbrains.kotlin.fir.symbols.ConeSymbol
+import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
+import org.jetbrains.kotlin.fir.symbols.FirSymbolOwner
+import org.jetbrains.kotlin.fir.types.*
+import org.jetbrains.kotlin.fir.types.impl.FirTypePlaceholderProjection
+import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
+import org.jetbrains.kotlin.ir.expressions.IrConstKind
+import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.types.Variance
+import java.io.File
+import java.io.Writer
+
+
+internal interface FirLinkResolver {
+ fun nearPackage(fqName: FqName): String?
+
+ fun symbolSignature(symbol: ConeSymbol): String
+ fun nearSymbolLocation(symbol: ConeSymbol): String?
+
+ fun classLocation(classId: ClassId): String?
+
+ fun HEAD.supplementary()
+}
+
+
+private const val PACKAGE_INDEX = "package-index.html"
+private const val MODULE_INDEX = "module-index.html"
+
+
+private fun HEAD.commonHead() {
+ meta { charset = "utf-8" }
+ script(src = "https://code.jquery.com/jquery-3.4.0.slim.min.js") {
+ integrity = "sha256-ZaXnYkHGqIhqTbJ6MB4l9Frs/r7U4jlx7ir8PJYBqbI="
+ @Suppress("SpellCheckingInspection")
+ attributes["crossorigin"] = "anonymous"
+ }
+}
+
+
+private class PackageInfo(val fqName: FqName, val moduleInfo: ModuleInfo) {
+ val contents = LinkedHashSet()
+ val packageRoot = fqName.pathSegments().fold(moduleInfo.moduleRoot) { dir, segment -> dir.resolve(segment.asString()) }.also {
+ it.mkdirs()
+ }
+
+}
+
+private class ModuleInfo(val name: String, outputRoot: File) {
+ val packages = mutableMapOf()
+ val moduleRoot = outputRoot.resolve(name).also {
+ it.mkdirs()
+ }
+}
+
+private class SupplementaryGenerator(val outputRoot: File) {
+ fun generateIndex(moduleInfo: ModuleInfo, writer: Writer) {
+ writer.appendHTML().html {
+ head {
+ title { +moduleInfo.name }
+ commonHead()
+ supplementary(this, moduleInfo.moduleRoot)
+ }
+ body {
+ h2 { +moduleInfo.name }
+ ul {
+ for (packageInfo in moduleInfo.packages.values) {
+ li {
+ a(
+ href = linkToIndex(packageInfo, moduleInfo.moduleRoot),
+ classes = "container-ref"
+ ) {
+ +packageInfo.fqName.asString()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fun linkToIndex(moduleInfo: ModuleInfo, from: File): String {
+ return moduleInfo.moduleRoot.resolve(MODULE_INDEX).relativeTo(from).path
+ }
+
+ fun linkToIndex(packageInfo: PackageInfo, from: File): String {
+ return packageInfo.packageRoot.resolve(PACKAGE_INDEX).relativeTo(from).path
+ }
+
+ fun generateIndex(packageInfo: PackageInfo, writer: Writer) {
+ writer.appendHTML().html {
+ head {
+ title { +packageInfo.fqName.asString() }
+ commonHead()
+ supplementary(this, packageInfo.packageRoot)
+ }
+ body {
+ h4 {
+ +"In module: "
+ a(
+ href = linkToIndex(packageInfo.moduleInfo, packageInfo.packageRoot),
+ classes = "container-ref"
+ ) {
+ +packageInfo.moduleInfo.name
+ }
+ }
+ h2 { +packageInfo.fqName.asString() }
+
+ ul {
+ for (file in packageInfo.contents) {
+ li {
+ a(href = "./$file.fir.html", classes = "container-ref") { +file }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ fun supplementary(head: HEAD, originDir: File) = with(head) {
+ for (file in jsFiles) {
+ script(src = outputRoot.resolve(file).relativeTo(originDir).path) {}
+ }
+ for (styleSheet in cssFiles) {
+ styleLink(outputRoot.resolve(styleSheet).relativeTo(originDir).path)
+ }
+ }
+}
+
+
+private val jsFiles = listOf("logic.js")
+private val cssFiles = listOf("style.css", "colors.white.css", "colors.dark.css")
+
+
+class MultiModuleHtmlFirDump(private val outputRoot: File) {
+
+ private val modules = mutableMapOf()
+
+ private lateinit var currentModule: ModuleInfo
+ private var inModule = false
+ private var finished = false
+ private var index = Index()
+
+ fun module(module: String, block: () -> Unit) {
+ require(!finished)
+ inModule = true
+ currentModule = modules.getOrPut(module) {
+ ModuleInfo(module, outputRoot)
+ }
+
+ block()
+ inModule = false
+ index = Index()
+ }
+
+ private val supplementaryGenerator = SupplementaryGenerator(outputRoot)
+
+ fun finish() {
+ require(!finished)
+ finished = true
+ for (module in modules.values) {
+ module.moduleRoot.resolve(MODULE_INDEX)
+ .writer().use {
+ supplementaryGenerator.generateIndex(module, it)
+ }
+ for (packageInfo in module.packages.values) {
+ packageInfo.packageRoot.resolve(PACKAGE_INDEX)
+ .writer().use {
+ supplementaryGenerator.generateIndex(packageInfo, it)
+ }
+ }
+ }
+ val supplementaryFiles = jsFiles + cssFiles
+
+ for (file in supplementaryFiles) {
+ val stream = this::class.java.getResourceAsStream(file)
+ outputRoot.resolve(file).outputStream().use { outputStream ->
+ stream.copyTo(outputStream)
+ }
+ }
+ }
+
+ private fun locationForFile(firFile: FirFile, packageInfo: PackageInfo = packageForFile(firFile)): File {
+ var name = firFile.name
+ var index = 0
+ while (!packageInfo.contents.add(name)) {
+ name = firFile.name + ".${index++}"
+ }
+ return packageInfo.packageRoot.resolve("$name.fir.html")
+ }
+
+ private fun packageForFile(firFile: FirFile): PackageInfo {
+ val packageFqName = firFile.packageFqName
+ return currentModule.packages.getOrPut(packageFqName) {
+ PackageInfo(packageFqName, currentModule)
+ }
+ }
+
+ fun indexFile(file: FirFile) {
+ val location = locationForFile(file)
+ index.files[file] = location
+ file.accept(index.visitor(location))
+ }
+
+ fun generateFile(file: FirFile) {
+ require(inModule)
+
+ val dumpOutput = index.files[file] ?: error("No location for ${file.name}")
+ val dumper = HtmlFirDump(LinkResolver(dumpOutput))
+ val builder = StringBuilder()
+ dumper.generate(file, builder)
+
+ dumpOutput.writeText(builder.toString())
+ }
+
+
+ private inner class LinkResolver(origin: File) : FirLinkResolver {
+
+ override fun HEAD.supplementary() {
+ supplementaryGenerator.supplementary(this, originDir)
+ }
+
+ override fun symbolSignature(symbol: ConeSymbol): String {
+ val id = index.symbolIds[symbol] ?: error("Not found $symbol")
+ return "id$id"
+ }
+
+ private val originDir = origin.parentFile
+
+ override fun nearPackage(fqName: FqName): String? {
+ val packageInfo = currentModule.packages[fqName] ?: return null
+ return packageInfo.packageRoot.resolve(PACKAGE_INDEX).relativeTo(originDir).path
+ }
+
+ override fun classLocation(classId: ClassId): String? {
+ val location = index.classes[classId] ?: return null
+ return location.relativeTo(originDir).path + "#$classId"
+ }
+
+ override fun nearSymbolLocation(symbol: ConeSymbol): String? {
+ val location = index.symbols[symbol] ?: return null
+ return location.relativeTo(originDir).path + "#${symbolSignature(symbol)}"
+ }
+ }
+
+ private inner class Index {
+ val files = mutableMapOf()
+ val classes = mutableMapOf()
+ val symbols = mutableMapOf, File>()
+ val symbolIds = mutableMapOf, Int>()
+ private var symbolCounter = 0
+
+ fun visitor(location: File): FirVisitorVoid {
+ return object : FirVisitorVoid() {
+ override fun visitElement(element: FirElement) {
+ element.acceptChildren(this)
+ }
+
+ override fun visitRegularClass(regularClass: FirRegularClass) {
+ classes[regularClass.classId] = location
+ super.visitRegularClass(regularClass)
+ }
+
+ fun indexDeclaration(callableDeclaration: FirCallableDeclaration) {
+ symbols[callableDeclaration.symbol] = location
+ symbolIds[callableDeclaration.symbol] = symbolCounter++
+ }
+
+ override fun visitVariable(variable: FirVariable) {
+ indexDeclaration(variable)
+ super.visitVariable(variable)
+ }
+
+ override fun visitValueParameter(valueParameter: FirValueParameter) {
+ indexDeclaration(valueParameter)
+ super.visitValueParameter(valueParameter)
+ }
+
+ override fun visitNamedFunction(namedFunction: FirNamedFunction) {
+ indexDeclaration(namedFunction)
+ super.visitNamedFunction(namedFunction)
+ }
+
+ override fun visitProperty(property: FirProperty) {
+ indexDeclaration(property)
+ super.visitProperty(property)
+ }
+
+ override fun visitConstructor(constructor: FirConstructor) {
+ indexDeclaration(constructor)
+ super.visitConstructor(constructor)
+ }
+ }
+ }
+
+ }
+}
+
+class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver) {
+
+
+ fun generate(element: FirFile, builder: StringBuilder) {
+ builder.appendHTML().html {
+ generate(element)
+ }
+ }
+
+ private fun FlowContent.keyword(text: String) {
+ span(classes = "keyword") { +text }
+ }
+
+ private fun FlowContent.line(block: FlowContent.() -> Unit) {
+ nl()
+ block()
+ br
+ }
+
+ private fun FlowContent.nl(block: FlowContent.() -> Unit = {}) {
+ span(classes = "line") { block() }
+ }
+
+ private fun FlowContent.inl() {
+ nl()
+ ident()
+ }
+
+ private fun FlowContent.simpleName(name: Name) {
+ span(classes = "simple-name") {
+ +name.asString()
+ }
+ }
+
+ private fun FlowContent.unresolved(block: FlowContent.() -> Unit) {
+ span(classes = "unresolved") {
+ block()
+ }
+ }
+
+ private fun FlowContent.resolved(block: FlowContent.() -> Unit) {
+ span(classes = "resolved") {
+ block()
+ }
+ }
+
+ private fun FlowContent.error(block: SPAN.() -> Unit) {
+ span(classes = "error") {
+ block()
+ }
+ }
+
+ private fun FlowContent.packageName(fqName: FqName) {
+ line {
+ keyword("package")
+ ws
+ val link = linkResolver.nearPackage(fqName)!!
+ a(href = link, classes = "package-fqn") { +fqName.asString() }
+ }
+ }
+
+ private val FlowContent.ws get() = +" "
+
+ private fun FlowContent.fqn(name: FqName) {
+ +name.asString()
+ }
+
+ private fun FlowContent.inlineUnsupported(element: Any) {
+ span(classes = "fold-container") {
+ span(classes = "error unsupported") {
+ +"Unsupported: ${element::class}"
+ }
+ span("fold-region") {
+ val content = when (element) {
+ is FirElement -> element.render()
+ else -> element.toString()
+ }
+ +"\n"
+ +content
+ +"\n"
+ }
+ }
+ }
+
+ private fun FlowContent.unsupported(element: FirElement) {
+ line {
+ inlineUnsupported(element)
+ }
+ }
+
+ private fun FlowContent.modality(modality: Modality?) {
+ if (modality == null) return
+ keyword(modality.name.toLowerCase())
+ }
+
+ private fun FlowContent.visibility(visibility: Visibility) {
+ if (visibility == Visibilities.UNKNOWN)
+ return unresolved { keyword("public?") }
+ return keyword(visibility.toString())
+ }
+
+ private fun FlowContent.declarationStatus(status: FirDeclarationStatus) {
+ visibility(status.visibility)
+ ws
+ modality(status.modality)
+ ws
+ if (status.isCompanion) {
+ keyword("companion ")
+ }
+ if (status.isInline) {
+ keyword("inline ")
+ }
+ if (status.isInner) {
+ keyword("inner ")
+ }
+ if (status.isExpect) {
+ keyword("expect ")
+ }
+ if (status.isActual) {
+ keyword("actual ")
+ }
+ if (status.isOverride) {
+ keyword("override ")
+ }
+ if (status.isOperator) {
+ keyword("operator ")
+ }
+ if (status.isInfix) {
+ keyword("infix ")
+ }
+ if (status.isInline) {
+ keyword("inline ")
+ }
+ if (status.isTailRec) {
+ keyword("tailrec ")
+ }
+ if (status.isExternal) {
+ keyword("external ")
+ }
+ if (status.isConst) {
+ keyword("const ")
+ }
+ if (status.isLateInit) {
+ keyword("lateinit ")
+ }
+ if (status.isInner) {
+ keyword("inner ")
+ }
+ if (status.isCompanion) {
+ keyword("companion ")
+ }
+ if (status.isData) {
+ keyword("data ")
+ }
+ if (status.isSuspend) {
+ keyword("suspend ")
+ }
+ if (status.isStatic) {
+ keyword("static ")
+ }
+ }
+
+
+ private fun FlowContent.anchoredName(name: Name, signature: String) {
+ span(classes = "declaration") {
+ id = signature
+ simpleName(name)
+ }
+ }
+
+ private var currentIdent = 0
+
+ private fun FlowContent.ident(level: Int = currentIdent) {
+ +" ".repeat(level * 4)
+ }
+
+ @Suppress("SpellCheckingInspection")
+ private fun FlowContent.iline(block: FlowContent.() -> Unit) {
+ inl()
+ block()
+ br
+ }
+
+ private fun FlowContent.withIdentLevel(block: FlowContent.() -> Unit) {
+ currentIdent++
+ block()
+ currentIdent--
+ }
+
+ private fun FlowContent.generateList(list: List, separator: String = ", ", generate: FlowContent.(E) -> Unit) {
+ if (list.isEmpty()) return
+ generate(list.first())
+ for (element in list.drop(1)) {
+ +separator
+ generate(element)
+ }
+ }
+
+
+ private fun FlowContent.generate(klass: FirRegularClass) {
+ inl()
+
+ declarationStatus(klass.status)
+
+
+ when (klass.classKind) {
+ ClassKind.CLASS -> keyword("class")
+ ClassKind.INTERFACE -> keyword("interface")
+ ClassKind.ENUM_CLASS -> keyword("enum class")
+ ClassKind.ENUM_ENTRY -> Unit // ?
+ ClassKind.ANNOTATION_CLASS -> keyword("annotation class")
+ ClassKind.OBJECT -> keyword("object")
+ }
+ ws
+ anchoredName(klass.name, klass.classId.asString())
+ if (klass.superTypeRefs.isNotEmpty()) {
+ +": "
+ generateList(klass.superTypeRefs) {
+ generate(it)
+ }
+ }
+ if (klass.declarations.isNotEmpty()) {
+ +" {"
+ br
+
+ withIdentLevel {
+ for (declaration in klass.declarations) {
+ generate(declaration)
+ line {}
+ }
+ }
+
+
+ inl()
+ +"}"
+ }
+ br
+
+ }
+
+ private fun FlowContent.generate(flexibleType: ConeFlexibleType) {
+ generate(flexibleType.lowerBound)
+ +".."
+ generate(flexibleType.upperBound)
+ }
+
+ private fun FlowContent.generate(type: ConeClassType) {
+ resolved {
+ val link = linkResolver.classLocation(type.lookupTag.classId)
+ declarationRef(link, setOf("class-fqn")) {
+ title = type.lookupTag.classId.asString()
+ fqn(type.lookupTag.classId.relativeClassName)
+ }
+ }
+ }
+
+ private fun FlowContent.generate(variableAssignment: FirVariableAssignment) {
+ generateReceiver(variableAssignment)
+ generate(variableAssignment.lValue)
+ +" "
+ +variableAssignment.operation.operator
+ +" "
+ generate(variableAssignment.rValue)
+ }
+
+ private fun FlowContent.generate(projection: ConeKotlinTypeProjection) {
+ when (projection) {
+ is ConeStarProjection -> +"*"
+ is ConeTypedProjection -> {
+ when (projection.kind) {
+ ProjectionKind.IN -> keyword("in ")
+ ProjectionKind.OUT -> keyword("out ")
+ else -> {
+ }
+ }
+
+ generate(projection.type)
+ }
+ }
+ }
+
+ private fun FlowContent.stringLiteral(value: Any?) {
+ val text = StringEscapeUtils.escapeJava(value.toString())
+ span("string-literal") {
+ when (value) {
+ is String -> +"\"$text\""
+ is Char -> +"'$text'"
+ else -> error("Unknown string literal: \"$value\" ${value?.let { it::class }}")
+ }
+ }
+ }
+
+ private fun FlowContent.generate(expression: FirConstExpression<*>) {
+ val value = expression.value
+ if (value == null && expression.kind != IrConstKind.Null) {
+ return error {
+ +"null value"
+ }
+ }
+
+ when (expression.kind) {
+ IrConstKind.Null -> keyword("null")
+ IrConstKind.Boolean -> keyword(value.toString())
+ IrConstKind.String, IrConstKind.Char ->
+ stringLiteral(value)
+ IrConstKind.Byte -> {
+ +value.toString()
+ keyword("B")
+ }
+ IrConstKind.Short -> {
+ +value.toString()
+ keyword("S")
+ }
+ IrConstKind.Int -> {
+ +value.toString()
+ keyword("I")
+ }
+ IrConstKind.Long -> {
+ +value.toString()
+ keyword("L")
+ }
+ IrConstKind.Float -> {
+ +value.toString()
+ keyword("F")
+ }
+ IrConstKind.Double -> {
+ +value.toString()
+ keyword("D")
+ }
+ }
+
+ }
+
+ private fun FlowContent.generate(type: ConeKotlinType) {
+ when (type) {
+ is ConeClassErrorType -> error { +type.reason }
+ is ConeClassType -> generate(type)
+ is ConeAbbreviatedType -> inlineUnsupported(type)
+ is ConeTypeParameterType -> resolved { +type.lookupTag.name.asString() }
+ is ConeTypeVariableType -> resolved { +type.lookupTag.name.asString() }
+ is ConeFlexibleType -> resolved { generate(type) }
+ is ConeCapturedType -> inlineUnsupported(type)
+ is ConeDefinitelyNotNullType -> inlineUnsupported(type)
+ }
+ if (type.typeArguments.isNotEmpty()) {
+ +"<"
+ generateList(type.typeArguments.toList()) {
+ generate(it)
+ }
+ +">"
+ }
+ if (type.isMarkedNullable) {
+ +"?"
+ }
+ }
+
+ private fun FlowContent.generate(typeProjection: FirTypeProjection) {
+ when (typeProjection) {
+ is FirTypeProjectionWithVariance -> {
+ generate(typeProjection.variance)
+ generate(typeProjection.typeRef)
+ }
+ is FirStarProjection -> +"*"
+ is FirTypePlaceholderProjection -> +"_"
+ }
+ }
+
+ private fun FlowContent.generate(variance: Variance) {
+ when (variance) {
+ Variance.INVARIANT -> Unit
+ Variance.IN_VARIANCE -> keyword("in ")
+ Variance.OUT_VARIANCE -> keyword("out ")
+ }
+ }
+
+ private fun FlowContent.generateTypeProjections(typeProjections: List) {
+ if (typeProjections.isEmpty()) return
+ +"<"
+ generateList(typeProjections) {
+ generate(it)
+ }
+ +">"
+ }
+
+ private fun FlowContent.generate(typeRef: FirTypeRef) {
+ when (typeRef) {
+ is FirErrorTypeRef -> error { +typeRef.reason }
+ is FirResolvedTypeRef -> generate(typeRef.type)
+ is FirImplicitTypeRef -> unresolved { keyword("") }
+ is FirUserTypeRef -> unresolved {
+ generateList(typeRef.qualifier, separator = ".") {
+ simpleName(it.name)
+ generateTypeProjections(it.typeArguments)
+ }
+ if (typeRef.isMarkedNullable) {
+ +"?"
+ }
+ }
+ else -> inlineUnsupported(typeRef)
+ }
+ }
+
+ private fun FlowContent.generate(memberDeclaration: FirMemberDeclaration) {
+ when (memberDeclaration) {
+ is FirRegularClass -> generate(memberDeclaration)
+ is FirNamedFunction -> generate(memberDeclaration)
+ is FirProperty -> generate(memberDeclaration)
+ is FirConstructor -> generate(memberDeclaration)
+ else -> unsupported(memberDeclaration)
+ }
+ }
+
+ private fun FlowContent.generateTypeParameters(typeParameterContainer: FirTypeParameterContainer) {
+ if (typeParameterContainer.typeParameters.isEmpty()) return
+ +"<"
+ generateList(typeParameterContainer.typeParameters) {
+ generate(it.variance)
+ if (it.isReified) {
+ keyword("reified ")
+ }
+ simpleName(it.name)
+ if (it.bounds.isNotEmpty()) {
+ +": "
+ generateList(it.bounds) { bound ->
+ generate(bound)
+ }
+ }
+ }
+ +"> "
+ }
+
+ private fun FlowContent.generateReceiver(declaration: FirCallableDeclaration) {
+ generateReceiver(declaration.receiverTypeRef)
+ }
+
+ private fun FlowContent.generateReceiver(receiverTypeRef: FirTypeRef?) {
+ receiverTypeRef ?: return
+ generate(receiverTypeRef)
+ +"."
+ }
+
+ private fun FlowContent.generate(property: FirProperty) {
+ //anchor
+ iline {
+ declarationStatus(property.status)
+ if (property.isVal) {
+ keyword("val ")
+ } else {
+ keyword("var ")
+ }
+ generateTypeParameters(property)
+ generateReceiver(property)
+ symbolRef(property.symbol) {
+ simpleName(property.name)
+ }
+ +": "
+ generate(property.returnTypeRef)
+
+
+ val initializer = property.initializer
+ if (initializer != null) {
+ +" = "
+ generate(initializer)
+ }
+ }
+
+ }
+
+
+ private fun FlowContent.exprType(type: FirTypeRef, block: SPAN.() -> Unit) {
+ span(classes = "typed-expression fold-container") {
+ block()
+ span(classes = "expression-type fold-region") {
+ +": "
+ generate(type)
+ }
+ }
+ }
+
+ private fun FlowContent.generate(statement: FirStatement) {
+ when (statement) {
+ is FirAnonymousFunction -> generate(statement, isStatement = true)
+ is FirWhileLoop -> generate(statement)
+ is FirWhenExpression -> generate(statement, isStatement = true)
+ is FirTryExpression -> generate(statement, isStatement = true)
+ is FirExpression -> iline { generate(statement) }
+ is FirVariable -> iline { generate(statement) }
+ is FirVariableAssignment -> iline { generate(statement) }
+ else -> unsupported(statement)
+ }
+ }
+
+ private fun FlowContent.generate(variable: FirVariable) {
+ if (variable.isVal) {
+ keyword("val ")
+ } else {
+ keyword("var ")
+ }
+
+ symbolAnchor(variable.symbol) { simpleName(variable.name) }
+ +": "
+ generate(variable.returnTypeRef)
+ val initializer = variable.initializer
+ if (initializer != null) {
+ +" = "
+ generate(initializer)
+ }
+ }
+
+ private fun FlowContent.declarationRef(
+ href: String?,
+ classes: Set = emptySet(),
+ body: A.() -> Unit
+ ) {
+ a(href = href, classes = "ref") {
+ this.classes += classes
+ if (href == null) {
+ this.classes += "external"
+ }
+ body()
+ }
+ }
+
+ private fun FlowContent.symbolRef(symbol: ConeSymbol?, body: FlowContent.() -> Unit) {
+ val link = if (symbol == null) null else linkResolver.nearSymbolLocation(symbol)
+ declarationRef(link, setOf("symbol")) {
+ body()
+ }
+ }
+
+ private fun FlowContent.generate(reference: FirReference) {
+ when (reference) {
+ is FirSuperReference -> keyword("super")
+ is FirThisReference -> {
+ keyword("this")
+ val label = reference.labelName ?: ""
+ span("label") {
+ +"@"
+ +label
+ }
+ }
+ is FirErrorNamedReference -> {
+ error {
+ title = reference.errorReason
+ simpleName(reference.name)
+ }
+ }
+ is FirSimpleNamedReference -> {
+ unresolved {
+ simpleName(reference.name)
+ }
+ }
+ is FirResolvedCallableReference -> {
+ resolved {
+ symbolRef(reference.coneSymbol) {
+ simpleName(reference.name)
+ }
+ }
+ }
+ }
+ }
+
+ private fun FlowContent.generateReceiver(access: FirQualifiedAccess) {
+ val explicitReceiver = access.explicitReceiver
+ if (explicitReceiver != null) {
+ generate(explicitReceiver)
+ if (access.safe) {
+ +"?."
+ } else {
+ +"."
+ }
+ }
+ }
+
+ private fun FlowContent.generate(functionCall: FirFunctionCall) {
+ generateReceiver(functionCall)
+
+ generate(functionCall.calleeReference)
+ generateTypeProjections(functionCall.typeArguments)
+ +"("
+ generateList(functionCall.arguments) {
+ generate(it)
+ }
+ +")"
+ }
+
+ private fun FlowContent.generateBinary(expression: FirOperatorCall) {
+ val (first, second) = expression.arguments
+ generate(first)
+ ws
+ unresolved { +expression.operation.operator }
+ ws
+ generate(second)
+ }
+
+ private fun FlowContent.generateUnary(expression: FirOperatorCall) {
+ val (first) = expression.arguments
+ unresolved { +expression.operation.operator }
+ ws
+ generate(first)
+ }
+
+ private fun FlowContent.generate(expression: FirOperatorCall) {
+ when (expression.arguments.size) {
+ 1 -> generateUnary(expression)
+ 2 -> generateBinary(expression)
+ else -> inlineUnsupported(expression)
+ }
+ }
+
+ private fun FlowContent.generate(typeOperatorCall: FirTypeOperatorCall) {
+ val (expression) = typeOperatorCall.arguments
+ generate(expression)
+ ws
+ keyword(typeOperatorCall.operation.operator)
+ ws
+ generate(typeOperatorCall.conversionTypeRef)
+ }
+
+ @Suppress("UNUSED_PARAMETER")
+ private fun FlowContent.generate(elseIfTrueCondition: FirElseIfTrueCondition) {
+ keyword("else")
+ }
+
+ @Suppress("UNUSED_PARAMETER")
+ private fun FlowContent.generate(whenSubjectExpression: FirWhenSubjectExpression) {
+ +""
+ }
+
+ private fun FlowContent.generateMultiLineExpression(isStatement: Boolean, body: FlowContent.() -> Unit) {
+ if (!isStatement) {
+ br
+ currentIdent++
+ }
+ body()
+ if (!isStatement) {
+ currentIdent--
+ inl()
+ }
+ }
+
+ private fun FlowContent.generate(tryExpression: FirTryExpression, isStatement: Boolean) {
+ generateMultiLineExpression(!isStatement) {
+ iline {
+ keyword("try ")
+ generateBlockIfAny(tryExpression.tryBlock)
+ }
+
+ for (catch in tryExpression.catches) {
+ keyword(" catch ")
+ +"("
+ generate(catch.parameter)
+ +") "
+ generateBlockIfAny(catch.block)
+ }
+ val finallyBlock = tryExpression.finallyBlock
+ if (finallyBlock != null) {
+ keyword(" finally ")
+ generateBlockIfAny(finallyBlock)
+ }
+ }
+ }
+
+ private fun FlowContent.generate(whileLoop: FirWhileLoop) {
+ iline {
+ keyword("while ")
+ +"("
+ generate(whileLoop.condition)
+ +") "
+ generateBlockIfAny(whileLoop.block)
+ }
+ }
+
+ private fun FlowContent.generate(whenExpression: FirWhenExpression, isStatement: Boolean) {
+ generateMultiLineExpression(isStatement) {
+ iline {
+ keyword("when")
+ +" ("
+ whenExpression.subjectVariable?.let { generate(it) }
+ ?: whenExpression.subject?.let { generate(it) }
+ +") {"
+ }
+ withIdentLevel {
+ for (branch in whenExpression.branches) {
+ inl()
+ generate(branch.condition)
+ +" -> {"
+ if (branch.result.statements.isNotEmpty()) {
+ br
+ withIdentLevel {
+ generateBlockContent(branch.result)
+ }
+ inl()
+ }
+ +"}"
+ br
+ }
+ }
+ iline {
+ +"}"
+ }
+ }
+ }
+
+ private fun FlowContent.generate(throwExpression: FirThrowExpression) {
+ keyword("throw ")
+ generate(throwExpression.exception)
+ }
+
+ private fun FlowContent.generate(expression: FirExpression) {
+ exprType(expression.typeRef) {
+ when (expression) {
+ is FirAnonymousFunction -> generate(expression, isStatement = false)
+ is FirThrowExpression -> generate(expression)
+ is FirWhenSubjectExpression -> generate(expression)
+ is FirElseIfTrueCondition -> generate(expression)
+ is FirWhenExpression -> generate(expression, isStatement = false)
+ is FirTryExpression -> generate(expression, isStatement = false)
+ is FirConstExpression<*> -> generate(expression)
+ is FirReturnExpression -> {
+ span("return-label") {
+ symbolRef((expression.target.labeledElement as? FirSymbolOwner<*>)?.symbol) {
+ +"^"
+ +(expression.target.labelName ?: "")
+ }
+ }
+ generate(expression.result)
+ }
+ is FirFunctionCall -> {
+ generate(expression)
+ }
+ is FirQualifiedAccessExpression -> generate(expression)
+ is FirNamedArgumentExpression -> {
+ simpleName(expression.name)
+ +" = "
+ generate(expression.expression)
+ }
+ is FirTypeOperatorCall -> generate(expression)
+ is FirOperatorCall -> generate(expression)
+ else -> inlineUnsupported(expression)
+ }
+ }
+ }
+
+ private fun FlowContent.generate(qualifiedAccessExpression: FirQualifiedAccessExpression) {
+ generateReceiver(qualifiedAccessExpression)
+ generate(qualifiedAccessExpression.calleeReference)
+ }
+
+ private fun FlowContent.generateBlockContent(block: FirBlock) {
+ for (statement in block.statements) {
+ generate(statement)
+ }
+ }
+
+ private fun FlowContent.symbolAnchor(symbol: ConeSymbol, body: FlowContent.() -> Unit) {
+ span(classes = "declaration") {
+ id = linkResolver.symbolSignature(symbol)
+ body()
+ }
+ }
+
+ private fun FlowContent.generate(valueParameter: FirValueParameter) {
+ symbolAnchor(valueParameter.symbol) { simpleName(valueParameter.name) }
+ +": "
+ generate(valueParameter.returnTypeRef)
+ val defaultValue = valueParameter.defaultValue
+ if (defaultValue != null) {
+ +" = "
+ generate(defaultValue)
+ }
+ }
+
+ private fun FlowContent.generateBlockIfAny(block: FirBlock?) {
+ if (block == null) return
+ +" {"
+ generateMultiLineExpression(isStatement = false) {
+ generateBlockContent(block)
+ }
+ +"}"
+ }
+
+ private fun FlowContent.generate(function: FirNamedFunction) {
+ iline {
+ declarationStatus(function.status)
+ keyword("fun ")
+ generateTypeParameters(function)
+ generateReceiver(function)
+ symbolRef(function.symbol) {
+ simpleName(function.name)
+ }
+ +"("
+ generateList(function.valueParameters) {
+ generate(it)
+ }
+ +"): "
+ generate(function.returnTypeRef)
+ generateBlockIfAny(function.body)
+ }
+
+ }
+
+ private fun FlowContent.generate(function: FirConstructor) {
+ iline {
+ declarationStatus(function.status)
+ symbolAnchor(function.symbol) {
+ keyword("constructor")
+ }
+ generateTypeParameters(function)
+ +"("
+ generateList(function.valueParameters) {
+ generate(it)
+ }
+ +"): "
+ generate(function.returnTypeRef)
+ generateBlockIfAny(function.body)
+ }
+ }
+
+ private fun FlowContent.generate(anonymousFunction: FirAnonymousFunction, isStatement: Boolean) {
+ generateMultiLineExpression(isStatement) {
+ iline {
+ keyword("fun ")
+ generateReceiver(anonymousFunction.receiverTypeRef)
+
+ +"("
+ generateList(anonymousFunction.valueParameters) {
+ generate(it)
+ }
+ +"): "
+ generate(anonymousFunction.returnTypeRef)
+ generateBlockIfAny(anonymousFunction.body)
+ }
+ }
+ }
+
+ private fun FlowContent.generate(declaration: FirDeclaration) {
+ when (declaration) {
+ is FirMemberDeclaration -> generate(declaration)
+ else -> unsupported(declaration)
+ }
+ }
+
+ private fun FlowContent.generate(import: FirImport) {
+ fun simpleRender() {
+ val fqName = import.importedFqName
+ if (fqName != null) {
+ fqn(fqName)
+ if (import.isAllUnder)
+ +"."
+ }
+ if (import.isAllUnder) {
+ +"*"
+ }
+ }
+
+ line {
+ keyword("import")
+ ws
+ when (import) {
+ is FirResolvedImport -> {
+ val classId = import.resolvedClassId
+ if (classId == null) {
+ val importedFqName = import.importedFqName
+ if (importedFqName != null) {
+ fqn(importedFqName)
+ } else {
+ error { +"no fqn" }
+ }
+ } else {
+ +classId.asString()
+ }
+ if (import.isAllUnder) {
+ +".*"
+ }
+ }
+ else -> {
+ unresolved {
+ simpleRender()
+ }
+ }
+ }
+ val alias = import.aliasName
+ if (alias != null) {
+ ws
+ keyword("as")
+ ws
+ simpleName(alias)
+ }
+ }
+ }
+
+ private fun HTML.generate(file: FirFile) {
+ head {
+ title { +file.name }
+ commonHead()
+ with(linkResolver) {
+ supplementary()
+ }
+ }
+ body {
+ h4 {
+ +"Source: "
+ val vFile = file.psi?.containingFile?.virtualFile
+ if (vFile != null) {
+ a(href = vFile.url, classes = "container-ref") {
+ +vFile.path
+ }
+ } else {
+ +"No source"
+ }
+ }
+ h2 {
+ +file.name
+ }
+ pre {
+ packageName(file.packageFqName)
+ line {}
+ for (import in file.imports) {
+ generate(import)
+ }
+ line {}
+ for (declaration in file.declarations) {
+ generate(declaration)
+ line {}
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 8414c16dd69..200edaa64a2 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -36,6 +36,7 @@ include ":kotlin-build-common",
":compiler:fir:resolve",
":compiler:fir:java",
":compiler:fir:modularized-tests",
+ ":compiler:fir:dump",
":compiler:frontend",
":compiler:frontend.common",
":compiler:frontend.java",