[JS IR] JS AST serialization

This commit is contained in:
Anton Bannykh
2021-09-28 12:09:46 +03:00
committed by teamcityserver
parent 95ab5e1b7e
commit b1b88a0d11
10 changed files with 8107 additions and 5943 deletions
@@ -175,7 +175,8 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
override fun visitWhileLoop(loop: IrWhileLoop, context: JsGenerationContext): JsStatement {
//TODO what if body null?
val label = context.getNameForLoop(loop)
val loopStatement = JsWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context))
val loopStatement = JsWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context),
loop.body?.accept(this, context) ?: JsEmpty)
return label?.let { JsLabel(it, loopStatement) } ?: loopStatement
}
@@ -183,7 +184,7 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
//TODO what if body null?
val label = context.getNameForLoop(loop)
val loopStatement =
JsDoWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context))
JsDoWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context) ?: JsEmpty)
return label?.let { JsLabel(it, loopStatement) } ?: loopStatement
}
}
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.ir.backend.js.eliminateDeadDeclarations
import org.jetbrains.kotlin.ir.backend.js.export.*
import org.jetbrains.kotlin.ir.backend.js.lower.StaticMembersLowering
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.backend.js.utils.serialization.JsIrAstDeserializer
import org.jetbrains.kotlin.ir.backend.js.utils.serialization.JsIrAstSerializer
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.ir.util.isInterface
@@ -26,6 +28,9 @@ import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver
import org.jetbrains.kotlin.js.sourceMap.SourceMap3Builder
import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilderConsumer
import org.jetbrains.kotlin.js.util.TextOutputImpl
import org.jetbrains.kotlin.serialization.js.ast.JsAstSerializer
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
class IrModuleToJsTransformer(
@@ -84,7 +89,26 @@ class IrModuleToJsTransformer(
}
}
return fragments
// TODO remove serialization -> deserialization work
val serialized = mutableListOf<Pair<IrFile, ByteArray>>()
val serializer = JsIrAstSerializer()
fragments.entries.forEach { (file, fragment) ->
val output = ByteArrayOutputStream()
serializer.serialize(fragment, output)
val binaryAst = output.toByteArray()
serialized += file to binaryAst
}
val restoredMap = mutableMapOf<IrFile, JsIrProgramFragment>()
val deserializer = JsIrAstDeserializer()
serialized.forEach { (file, binaryAst) ->
restoredMap[file] = deserializer.deserialize(ByteArrayInputStream(binaryAst))
}
return restoredMap
}
private fun generateWrappedModuleBody(
@@ -0,0 +1,98 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.ir.backend.js.utils.serialization
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrClassModel
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.JsImportedModule
import org.jetbrains.kotlin.protobuf.CodedInputStream
import org.jetbrains.kotlin.serialization.js.ast.JsAstDeserializerBase
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.*
import java.io.InputStream
class JsIrAstDeserializer : JsAstDeserializerBase() {
override val scope = emptyScope
fun deserialize(input: InputStream): JsIrProgramFragment {
return deserialize(Chunk.parseFrom(CodedInputStream.newInstance(input).apply { setRecursionLimit(4096) }))
}
fun deserialize(proto: Chunk): JsIrProgramFragment {
stringTable += proto.stringTable.entryList
nameTable += proto.nameTable.entryList
nameCache += nameTable.map { null }
try {
return deserialize(proto.fragment)
} finally {
stringTable.clear()
nameTable.clear()
nameCache.clear()
}
}
private fun deserialize(proto: Fragment): JsIrProgramFragment {
val fragment = JsIrProgramFragment(proto.packageFqn)
fragment.importedModules += proto.importedModuleList.map { importedModuleProto ->
JsImportedModule(
deserializeString(importedModuleProto.externalNameId),
deserializeName(importedModuleProto.internalNameId),
if (importedModuleProto.hasPlainReference()) deserialize(importedModuleProto.plainReference) else null
)
}
proto.importEntryList.associateTo(fragment.imports) { importProto ->
deserializeString(importProto.signatureId) to deserialize(importProto.expression)
}
if (proto.hasDeclarationBlock()) {
fragment.declarations.statements += deserializeGlobalBlock(proto.declarationBlock).statements
}
if (proto.hasInitializerBlock()) {
fragment.initializers.statements += deserializeGlobalBlock(proto.initializerBlock).statements
}
if (proto.hasExportBlock()) {
fragment.exports.statements += deserializeGlobalBlock(proto.exportBlock).statements
}
proto.nameBindingList.associateTo(fragment.nameBindings) { nameBindingProto ->
deserializeString(nameBindingProto.signatureId) to deserializeName(nameBindingProto.nameId)
}
proto.irClassModelList.associateTo(fragment.classes) { clsProto -> deserialize(clsProto) }
if (proto.hasTestsInvocation()) {
fragment.testFunInvocation = deserialize(proto.testsInvocation)
}
if (proto.hasMainInvocation()) {
fragment.mainFunction = deserialize(proto.mainInvocation)
}
if (proto.hasSuiteFunction()) {
fragment.suiteFn = deserializeName(proto.suiteFunction)
}
fragment.dts = proto.dts
return fragment
}
private fun deserialize(proto: IrClassModel): Pair<JsName, JsIrClassModel> {
return deserializeName(proto.nameId) to JsIrClassModel(proto.superClassesList.map { deserializeName(it) }).apply {
if (proto.hasPreDeclarationBlock()) {
preDeclarationBlock.statements += deserializeGlobalBlock(proto.preDeclarationBlock).statements
}
if (proto.hasPostDeclarationBlock()) {
postDeclarationBlock.statements += deserializeGlobalBlock(proto.postDeclarationBlock).statements
}
}
}
override fun embedSources(deserializedLocation: JsLocation, file: String): JsLocationWithSource? = null
}
@@ -0,0 +1,107 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.ir.backend.js.utils.serialization
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrClassModel
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.*
import org.jetbrains.kotlin.serialization.js.ast.JsAstSerializerBase
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.io.OutputStream
class JsIrAstSerializer: JsAstSerializerBase() {
fun serialize(fragment: JsIrProgramFragment, output: OutputStream) {
importedNames.clear()
importedNames += fragment.imports.map { fragment.nameBindings[it.key]!! }
serialize(fragment).writeTo(output)
}
fun serialize(fragment: JsIrProgramFragment): Chunk {
try {
val chunkBuilder = Chunk.newBuilder()
chunkBuilder.fragment = serializeFragment(fragment)
chunkBuilder.nameTable = nameTableBuilder.build()
chunkBuilder.stringTable = stringTableBuilder.build()
return chunkBuilder.build()
} finally {
nameTableBuilder.clear()
stringTableBuilder.clear()
nameMap.clear()
stringMap.clear()
}
}
private fun serializeFragment(fragment: JsIrProgramFragment): Fragment {
val fragmentBuilder = Fragment.newBuilder()
fragmentBuilder.packageFqn = fragment.packageFqn
for (importedModule in fragment.importedModules) {
val importedModuleBuilder = ImportedModule.newBuilder()
importedModuleBuilder.externalNameId = serialize(importedModule.externalName)
importedModuleBuilder.internalNameId = serialize(importedModule.internalName)
importedModule.plainReference?.let { importedModuleBuilder.plainReference = serialize(it) }
fragmentBuilder.addImportedModule(importedModuleBuilder)
}
for ((signature, expression) in fragment.imports) {
val importBuilder = Import.newBuilder()
importBuilder.signatureId = serialize(signature)
importBuilder.expression = serialize(expression)
fragmentBuilder.addImportEntry(importBuilder)
}
fragmentBuilder.declarationBlock = serializeBlock(fragment.declarations)
fragmentBuilder.initializerBlock = serializeBlock(fragment.initializers)
fragmentBuilder.exportBlock = serializeBlock(fragment.exports)
for ((key, name) in fragment.nameBindings.entries) {
val nameBindingBuilder = NameBinding.newBuilder()
nameBindingBuilder.signatureId = serialize(key)
nameBindingBuilder.nameId = serialize(name)
fragmentBuilder.addNameBinding(nameBindingBuilder)
}
fragment.classes.entries.forEach { (name, model) -> fragmentBuilder.addIrClassModel(serialize(name, model)) }
fragment.testFunInvocation?.let {
fragmentBuilder.setTestsInvocation(serialize(it))
}
fragment.mainFunction?.let {
fragmentBuilder.setMainInvocation(serialize(it))
}
fragment.dts?.let {
fragmentBuilder.dts = it
}
fragment.suiteFn?.let {
fragmentBuilder.setSuiteFunction(serialize(it))
}
return fragmentBuilder.build()
}
private fun serialize(name: JsName, classModel: JsIrClassModel): IrClassModel {
val builder = IrClassModel.newBuilder()
builder.nameId = serialize(name)
classModel.superClasses.forEach { builder.addSuperClasses(serialize(it)) }
if (classModel.preDeclarationBlock.statements.isNotEmpty()) {
builder.preDeclarationBlock = serializeBlock(classModel.preDeclarationBlock)
}
if (classModel.postDeclarationBlock.statements.isNotEmpty()) {
builder.postDeclarationBlock = serializeBlock(classModel.postDeclarationBlock)
}
return builder.build()
}
override fun extractLocation(node: JsNode): JsLocation? {
return node.source.safeAs<JsLocationWithSource>()?.asSimpleLocation()
}
}