JS: add -Xmetadata-only flag for JS common code modules (KT-33142 fixed)

This commit is contained in:
Anton Bannykh
2019-09-11 15:58:34 +03:00
parent 6a808ce842
commit ead8379c50
16 changed files with 1140 additions and 89 deletions
@@ -69,4 +69,7 @@ public class JSConfigurationKeys {
public static final CompilerConfigurationKey<List<String>> FRIEND_PATHS =
CompilerConfigurationKey.create("friend module paths");
public static final CompilerConfigurationKey<Boolean> METADATA_ONLY =
CompilerConfigurationKey.create("generate .meta.js and .kjsm files only");
}
@@ -48,7 +48,7 @@ import org.jetbrains.kotlin.js.backend.ast.JsProgramFragment
import org.jetbrains.kotlin.js.backend.ast.JsStatement
import org.jetbrains.kotlin.js.coroutine.transformCoroutines
import org.jetbrains.kotlin.js.inline.util.collectDefinedNamesInAllScopes
import org.jetbrains.kotlin.js.translate.general.AstGenerationResult
import org.jetbrains.kotlin.js.translate.general.SourceFileTranslationResult
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.BindingContext
@@ -101,7 +101,47 @@ class K2JSTranslator @JvmOverloads constructor(
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
return translate(reporter, files, units, mainCallParameters, actualAnalysisResult, packageMetadata)
if (config.configuration.getBoolean(JSConfigurationKeys.METADATA_ONLY)) {
return translateWithoutCode(files, actualAnalysisResult, packageMetadata)
} else {
return translate(reporter, files, units, mainCallParameters, actualAnalysisResult, packageMetadata)
}
}
private fun translateWithoutCode(
files: List<KtFile>,
analysisResult: JsAnalysisResult,
packageMetadata: MutableMap<FqName, ByteArray>
): TranslationResult {
val bindingTrace = analysisResult.bindingTrace
TopDownAnalyzerFacadeForJS.checkForErrors(files, bindingTrace.bindingContext)
val moduleDescriptor = analysisResult.moduleDescriptor
val diagnostics = bindingTrace.bindingContext.diagnostics
val pathResolver = SourceFilePathResolver.create(config)
val bindingContext = bindingTrace.bindingContext
checkCanceled()
updateMetadataMap(bindingTrace.bindingContext, moduleDescriptor, packageMetadata)
trySaveIncrementalData(files, pathResolver, bindingTrace, moduleDescriptor) { sourceFile ->
TranslationData(sourceFile.declarations.map {
BindingUtils.getDescriptorForElement(bindingContext, it)
})
}
return if (hasError(diagnostics)) {
TranslationResult.Fail(diagnostics)
} else {
TranslationResult.SuccessNoCode(
config,
files,
diagnostics,
emptyList(),
moduleDescriptor,
bindingTrace.bindingContext,
packageMetadata
)
}
}
@Throws(TranslationException::class)
@@ -142,7 +182,9 @@ class K2JSTranslator @JvmOverloads constructor(
checkCanceled()
updateMetadataMap(bindingTrace.bindingContext, moduleDescriptor, packageMetadata)
trySaveIncrementalData(translationResult, pathResolver, bindingTrace, moduleDescriptor)
trySaveIncrementalData(translationResult.translatedSourceFiles.keys, pathResolver, bindingTrace, moduleDescriptor) { sourceFile ->
toTranslationData(translationResult.translatedSourceFiles[sourceFile]!!)
}
checkCanceled()
// Global phases
@@ -177,7 +219,8 @@ class K2JSTranslator @JvmOverloads constructor(
bindingContext,
moduleDescriptor,
config.configuration.languageVersionSettings,
config.configuration.get(CommonConfigurationKeys.METADATA_VERSION) as? JsMetadataVersion ?: JsMetadataVersion.INSTANCE)
config.configuration.get(CommonConfigurationKeys.METADATA_VERSION) as? JsMetadataVersion ?: JsMetadataVersion.INSTANCE
)
for ((packageName, metadata) in additionalMetadata) {
incrementalResults?.processPackageMetadata(packageName.asString(), metadata)
@@ -207,11 +250,33 @@ class K2JSTranslator @JvmOverloads constructor(
)
}
private class TranslationData(
val memberScope: Collection<DeclarationDescriptor>,
val binaryAst: ByteArray? = null,
val inlineData: ByteArray? = null
)
private fun JsAstSerializer.toTranslationData(fileTranslationResult: SourceFileTranslationResult): TranslationData {
val fragment = fileTranslationResult.fragment
val output = ByteArrayOutputStream()
serialize(fragment, output)
val binaryAst = output.toByteArray()
val inlineData = serializeInlineData(fileTranslationResult.inlineFunctionTags)
return TranslationData(
memberScope = fileTranslationResult.memberScope,
binaryAst = binaryAst,
inlineData = inlineData
)
}
private fun trySaveIncrementalData(
translationResult: AstGenerationResult,
sourceFiles: Iterable<KtFile>,
pathResolver: SourceFilePathResolver,
bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor
moduleDescriptor: ModuleDescriptor,
translationData: JsAstSerializer.(KtFile) -> TranslationData
) {
// TODO Maybe switch validation on for recompile
if (incrementalResults == null && !shouldValidateJsAst) return
@@ -224,19 +289,19 @@ class K2JSTranslator @JvmOverloads constructor(
}
}
for ((sourceUnit, fileTranslationResult) in translationResult.translatedSourceFiles) {
val file = sourceUnit.file
val fragment = fileTranslationResult.fragment
val output = ByteArrayOutputStream()
serializer.serialize(fragment, output)
val binaryAst = output.toByteArray()
for (file in sourceFiles) {
val fileTranslationData = serializer.translationData(file)
val packagePart = serializeScope(bindingTrace.bindingContext, moduleDescriptor, file.packageFqName, fileTranslationResult.memberScope)
val inlineData = serializeInlineData(fileTranslationResult.inlineFunctionTags)
val packagePart =
serializeScope(bindingTrace.bindingContext, moduleDescriptor, file.packageFqName, fileTranslationData.memberScope)
val ioFile = VfsUtilCore.virtualToIoFile(file.virtualFile)
incrementalResults?.processPackagePart(ioFile, packagePart.toByteArray(), binaryAst, inlineData)
incrementalResults?.processPackagePart(
ioFile,
packagePart.toByteArray(),
fileTranslationData.binaryAst ?: ByteArray(0),
fileTranslationData.inlineData ?: ByteArray(0)
)
}
val settings = config.configuration.languageVersionSettings
@@ -48,62 +48,28 @@ import java.util.*
abstract class TranslationResult protected constructor(val diagnostics: Diagnostics) {
class Fail(diagnostics: Diagnostics) : TranslationResult(diagnostics)
class Success(
private val config: JsConfig,
private val files: List<KtFile>,
val program: JsProgram,
diagnostics: Diagnostics,
private val importedModules: List<String>,
val moduleDescriptor: ModuleDescriptor,
val bindingContext: BindingContext,
val packageMetadata: Map<FqName, ByteArray>
abstract class SuccessBase(
protected val config: JsConfig,
protected val files: List<KtFile>,
diagnostics: Diagnostics,
protected val importedModules: List<String>,
val moduleDescriptor: ModuleDescriptor,
val bindingContext: BindingContext,
val packageMetadata: Map<FqName, ByteArray>
) : TranslationResult(diagnostics) {
@Suppress("unused") // Used in kotlin-web-demo in WebDemoTranslatorFacade
fun getCode(): String {
val output = TextOutputImpl()
getCode(output, sourceLocationConsumer = null)
return output.toString()
abstract fun getOutputFiles(outputFile: File, outputPrefixFile: File?, outputPostfixFile: File?): OutputFileCollection
protected val sourceFiles = files.map {
val virtualFile = it.originalFile.virtualFile
when {
virtualFile == null -> File(it.name)
else -> VfsUtilCore.virtualToIoFile(virtualFile)
}
}
fun getOutputFiles(outputFile: File, outputPrefixFile: File?, outputPostfixFile: File?): OutputFileCollection {
val output = TextOutputImpl()
val sourceMapBuilder = SourceMap3Builder(outputFile, output, config.sourceMapPrefix)
val sourceMapBuilderConsumer =
if (config.configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)) {
val sourceMapContentEmbedding = config.sourceMapContentEmbedding
val pathResolver = SourceFilePathResolver.create(config)
SourceMapBuilderConsumer(
File("."),
sourceMapBuilder,
pathResolver,
sourceMapContentEmbedding == SourceMapSourceEmbedding.ALWAYS,
sourceMapContentEmbedding != SourceMapSourceEmbedding.NEVER)
}
else {
null
}
getCode(output, sourceMapBuilderConsumer)
if (sourceMapBuilderConsumer != null) {
sourceMapBuilder.addLink()
}
val code = output.toString()
val prefix = outputPrefixFile?.readText() ?: ""
val postfix = outputPostfixFile?.readText() ?: ""
val sourceFiles = files.map {
val virtualFile = it.originalFile.virtualFile
when {
virtualFile == null -> File(it.name)
else -> VfsUtilCore.virtualToIoFile(virtualFile)
}
}
val jsFile = SimpleOutputFile(sourceFiles, outputFile.name, prefix + code + postfix)
val outputFiles = arrayListOf<OutputFile>(jsFile)
protected fun metadataFiles(outputFile: File): List<OutputFile> {
if (config.configuration.getBoolean(JSConfigurationKeys.META_INFO)) {
val metaFileName = KotlinJavascriptMetadataUtils.replaceSuffix(outputFile.name)
val moduleDescription = JsModuleDescriptor(
@@ -121,12 +87,74 @@ abstract class TranslationResult protected constructor(val diagnostics: Diagnost
val metaFileContent = serializedMetadata.asString()
val sourceFilesForMetaFile = ArrayList(sourceFiles)
val jsMetaFile = SimpleOutputFile(sourceFilesForMetaFile, metaFileName, metaFileContent)
outputFiles.add(jsMetaFile)
for (serializedPackage in serializedMetadata.serializedPackages()) {
outputFiles.add(kjsmFileForPackage(serializedPackage.fqName, serializedPackage.bytes))
return listOf(jsMetaFile) + serializedMetadata.serializedPackages().map { serializedPackage ->
kjsmFileForPackage(serializedPackage.fqName, serializedPackage.bytes)
}
} else {
return emptyList()
}
}
private fun kjsmFileForPackage(packageFqName: FqName, bytes: ByteArray): SimpleOutputBinaryFile {
val ktFiles = (bindingContext.get(BindingContext.PACKAGE_TO_FILES, packageFqName) ?: emptyList())
val sourceFiles = ktFiles.map { VfsUtilCore.virtualToIoFile(it.virtualFile) }
val relativePath = config.moduleId +
VfsUtilCore.VFS_SEPARATOR_CHAR +
JsSerializerProtocol.getKjsmFilePath(packageFqName)
return SimpleOutputBinaryFile(sourceFiles, relativePath, bytes)
}
}
class Success(
config: JsConfig,
files: List<KtFile>,
val program: JsProgram,
diagnostics: Diagnostics,
importedModules: List<String>,
moduleDescriptor: ModuleDescriptor,
bindingContext: BindingContext,
packageMetadata: Map<FqName, ByteArray>
) : SuccessBase(config, files, diagnostics, importedModules, moduleDescriptor, bindingContext, packageMetadata) {
@Suppress("unused") // Used in kotlin-web-demo in WebDemoTranslatorFacade
fun getCode(): String {
val output = TextOutputImpl()
getCode(output, sourceLocationConsumer = null)
return output.toString()
}
override fun getOutputFiles(outputFile: File, outputPrefixFile: File?, outputPostfixFile: File?): OutputFileCollection {
val output = TextOutputImpl()
val sourceMapBuilder = SourceMap3Builder(outputFile, output, config.sourceMapPrefix)
val sourceMapBuilderConsumer =
if (config.configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)) {
val sourceMapContentEmbedding = config.sourceMapContentEmbedding
val pathResolver = SourceFilePathResolver.create(config)
SourceMapBuilderConsumer(
File("."),
sourceMapBuilder,
pathResolver,
sourceMapContentEmbedding == SourceMapSourceEmbedding.ALWAYS,
sourceMapContentEmbedding != SourceMapSourceEmbedding.NEVER
)
} else {
null
}
getCode(output, sourceMapBuilderConsumer)
if (sourceMapBuilderConsumer != null) {
sourceMapBuilder.addLink()
}
val code = output.toString()
val prefix = outputPrefixFile?.readText() ?: ""
val postfix = outputPostfixFile?.readText() ?: ""
val jsFile = SimpleOutputFile(sourceFiles, outputFile.name, prefix + code + postfix)
val outputFiles = arrayListOf<OutputFile>(jsFile)
outputFiles += metadataFiles(outputFile)
if (sourceMapBuilderConsumer != null) {
sourceMapBuilder.skipLinesAtBeginning(StringUtil.getLineBreakCount(prefix))
@@ -138,17 +166,23 @@ abstract class TranslationResult protected constructor(val diagnostics: Diagnost
return SimpleOutputFileCollection(outputFiles)
}
private fun kjsmFileForPackage(packageFqName: FqName, bytes: ByteArray): SimpleOutputBinaryFile {
val ktFiles = (bindingContext.get(BindingContext.PACKAGE_TO_FILES, packageFqName) ?: emptyList())
val sourceFiles = ktFiles.map { VfsUtilCore.virtualToIoFile(it.virtualFile) }
val relativePath = config.moduleId +
VfsUtilCore.VFS_SEPARATOR_CHAR +
JsSerializerProtocol.getKjsmFilePath(packageFqName)
return SimpleOutputBinaryFile(sourceFiles, relativePath, bytes)
}
private fun getCode(output: TextOutput, sourceLocationConsumer: SourceLocationConsumer?) {
program.accept(JsToStringGenerationVisitor(output, sourceLocationConsumer ?: NoOpSourceLocationConsumer))
}
}
}
class SuccessNoCode(
config: JsConfig,
files: List<KtFile>,
diagnostics: Diagnostics,
importedModules: List<String>,
moduleDescriptor: ModuleDescriptor,
bindingContext: BindingContext,
packageMetadata: Map<FqName, ByteArray>
) : SuccessBase(config, files, diagnostics, importedModules, moduleDescriptor, bindingContext, packageMetadata) {
override fun getOutputFiles(outputFile: File, outputPrefixFile: File?, outputPostfixFile: File?): OutputFileCollection {
return SimpleOutputFileCollection(metadataFiles(outputFile))
}
}
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.facade.TranslationUnit
import org.jetbrains.kotlin.protobuf.CodedInputStream
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.serialization.js.ast.JsAstDeserializer
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf
import java.io.ByteArrayInputStream
@@ -30,7 +31,7 @@ import java.util.HashSet
class AstGenerationResult(
val units: Collection<TranslationUnit>,
val translatedSourceFiles: Map<TranslationUnit.SourceFile, SourceFileTranslationResult>,
val translatedSourceFiles: Map<KtFile, SourceFileTranslationResult>,
val inlineFunctionTagMap: Map<String, TranslationUnit>,
moduleDescriptor: ModuleDescriptor,
config: JsConfig
@@ -47,7 +48,7 @@ class AstGenerationResult(
fun getTranslationResult(unit: TranslationUnit): FileTranslationResult =
when (unit) {
is TranslationUnit.SourceFile -> translatedSourceFiles[unit]!!
is TranslationUnit.SourceFile -> translatedSourceFiles[unit.file]!!
is TranslationUnit.BinaryAst -> cache.getOrPut(unit) {
// TODO Don't deserialize header twice
val inlineData = JsAstProtoBuf.InlineData.parseFrom(CodedInputStream.newInstance(unit.inlineData))
@@ -324,7 +324,7 @@ public final class Translation {
Map<String, TranslationUnit> inlineFunctionTagMap = new HashMap<>();
Map<TranslationUnit.SourceFile, SourceFileTranslationResult> translatedSourceFiles = new LinkedHashMap<>();
Map<KtFile, SourceFileTranslationResult> translatedSourceFiles = new LinkedHashMap<>();
for (TranslationUnit unit : units) {
if (unit instanceof TranslationUnit.SourceFile) {
@@ -344,7 +344,7 @@ public final class Translation {
fragment.setTests(mayBeGenerateTests(context, file, fileMemberScope));
fragment.setMainFunction(maybeGenerateCallToMain(context, config, moduleDescriptor, fileMemberScope, mainCallParameters));
translatedSourceFiles.put(sourceFileUnit, new SourceFileTranslationResult(fragment, staticContext.getInlineFunctionTags(), fileMemberScope));
translatedSourceFiles.put(file, new SourceFileTranslationResult(fragment, staticContext.getInlineFunctionTags(), fileMemberScope));
}
else if (unit instanceof TranslationUnit.BinaryAst) {
byte[] inlineDataArray = ((TranslationUnit.BinaryAst) unit).getInlineData();