JS: postpone JS AST merging
This commit is contained in:
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.js.facade
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
|
||||
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
@@ -43,6 +43,11 @@ import java.io.IOException
|
||||
import java.util.ArrayList
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils.hasError
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsProgramFragment
|
||||
import org.jetbrains.kotlin.js.coroutine.transformCoroutines
|
||||
import org.jetbrains.kotlin.js.translate.general.AstGenerationResult
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
|
||||
/**
|
||||
* An entry point of translator.
|
||||
@@ -74,7 +79,6 @@ class K2JSTranslator(private val config: JsConfig) {
|
||||
mainCallParameters: MainCallParameters,
|
||||
analysisResult: JsAnalysisResult? = null
|
||||
): TranslationResult {
|
||||
var analysisResult = analysisResult
|
||||
val files = ArrayList<KtFile>()
|
||||
for (unit in units) {
|
||||
if (unit is TranslationUnit.SourceFile) {
|
||||
@@ -82,46 +86,93 @@ class K2JSTranslator(private val config: JsConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
if (analysisResult == null) {
|
||||
analysisResult = TopDownAnalyzerFacadeForJS.analyzeFiles(files, config)
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
}
|
||||
val actualAnalysisResult = analysisResult ?: TopDownAnalyzerFacadeForJS.analyzeFiles(files, config)
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
return translate(reporter, files, units, mainCallParameters, actualAnalysisResult)
|
||||
}
|
||||
|
||||
@Throws(TranslationException::class)
|
||||
private fun translate(
|
||||
reporter: JsConfig.Reporter,
|
||||
files: List<KtFile>,
|
||||
allUnits: List<TranslationUnit>,
|
||||
mainCallParameters: MainCallParameters,
|
||||
analysisResult: JsAnalysisResult
|
||||
): 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 translationResult = Translation.generateAst(
|
||||
bindingTrace, units, mainCallParameters, moduleDescriptor, config, pathResolver
|
||||
)
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
val translationResult = Translation.generateAst(bindingTrace, allUnits, mainCallParameters, moduleDescriptor, config, pathResolver)
|
||||
if (hasError(diagnostics)) return TranslationResult.Fail(diagnostics)
|
||||
|
||||
val newFragments = ArrayList(translationResult.newFragments)
|
||||
val allFragments = ArrayList(translationResult.fragments)
|
||||
checkCanceled()
|
||||
|
||||
JsInliner.process(
|
||||
reporter, config, analysisResult.bindingTrace, translationResult.innerModuleName,
|
||||
allFragments, newFragments, translationResult.importStatements
|
||||
reporter,
|
||||
config,
|
||||
analysisResult.bindingTrace,
|
||||
translationResult
|
||||
)
|
||||
|
||||
LabeledBlockToDoWhileTransformation.apply(newFragments)
|
||||
|
||||
val coroutineTransformer = CoroutineTransformer()
|
||||
for (fragment in newFragments) {
|
||||
coroutineTransformer.accept(fragment.declarationBlock)
|
||||
coroutineTransformer.accept(fragment.initializerBlock)
|
||||
}
|
||||
removeUnusedImports(translationResult.program)
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
if (hasError(diagnostics)) return TranslationResult.Fail(diagnostics)
|
||||
checkCanceled()
|
||||
|
||||
expandIsCalls(newFragments)
|
||||
transformLabeledBlockToDoWhile(translationResult.newFragments)
|
||||
checkCanceled()
|
||||
|
||||
transformCoroutines(translationResult.newFragments)
|
||||
checkCanceled()
|
||||
|
||||
expandIsCalls(translationResult.newFragments)
|
||||
checkCanceled()
|
||||
|
||||
trySaveIncrementalData(files, translationResult, pathResolver, bindingTrace, moduleDescriptor)
|
||||
checkCanceled()
|
||||
|
||||
// Global phases
|
||||
|
||||
val program = translationResult.buildProgram()
|
||||
|
||||
removeUnusedImports(program)
|
||||
checkCanceled()
|
||||
|
||||
removeDuplicateImports(program)
|
||||
checkCanceled()
|
||||
|
||||
program.resolveTemporaryNames()
|
||||
checkCanceled()
|
||||
|
||||
return if (hasError(diagnostics)) {
|
||||
TranslationResult.Fail(diagnostics)
|
||||
} else {
|
||||
TranslationResult.Success(
|
||||
config,
|
||||
files,
|
||||
program,
|
||||
diagnostics,
|
||||
translationResult.importedModuleList.map { it.externalName },
|
||||
moduleDescriptor,
|
||||
bindingTrace.bindingContext
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkCanceled() {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
}
|
||||
|
||||
private fun trySaveIncrementalData(
|
||||
files: List<KtFile>,
|
||||
translationResult: AstGenerationResult,
|
||||
pathResolver: SourceFilePathResolver,
|
||||
bindingTrace: BindingTrace,
|
||||
moduleDescriptor: ModuleDescriptor
|
||||
) {
|
||||
if (incrementalResults == null) return
|
||||
|
||||
val serializer = JsAstSerializer { file ->
|
||||
try {
|
||||
pathResolver.getPathRelativeToSourceRoots(file)
|
||||
@@ -130,45 +181,25 @@ class K2JSTranslator(private val config: JsConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
if (incrementalResults != null) {
|
||||
val serializationUtil = KotlinJavascriptSerializationUtil
|
||||
for (file in files) {
|
||||
val fragment = translationResult.fragmentMap[file] ?: error("Could not find AST for file: $file")
|
||||
val output = ByteArrayOutputStream()
|
||||
serializer.serialize(fragment, output)
|
||||
val binaryAst = output.toByteArray()
|
||||
|
||||
for (file in files) {
|
||||
val fragment = translationResult.fragmentMap[file] ?: error("Could not find AST for file: $file")
|
||||
val output = ByteArrayOutputStream()
|
||||
serializer.serialize(fragment, output)
|
||||
val binaryAst = output.toByteArray()
|
||||
val scope = translationResult.fileMemberScopes[file] ?: error("Could not find descriptors for file: $file")
|
||||
val metadataVersion = config.configuration.get(CommonConfigurationKeys.METADATA_VERSION)
|
||||
val packagePart = KotlinJavascriptSerializationUtil.serializeDescriptors(
|
||||
bindingTrace.bindingContext, moduleDescriptor, scope, file.packageFqName,
|
||||
config.configuration.languageVersionSettings,
|
||||
metadataVersion ?: JsMetadataVersion.INSTANCE
|
||||
)
|
||||
|
||||
val scope = translationResult.fileMemberScopes[file] ?: error("Could not find descriptors for file: $file")
|
||||
val metadataVersion = config.configuration.get(CommonConfigurationKeys.METADATA_VERSION)
|
||||
val packagePart = serializationUtil.serializeDescriptors(
|
||||
bindingTrace.bindingContext, moduleDescriptor, scope, file.packageFqName,
|
||||
config.configuration.languageVersionSettings,
|
||||
metadataVersion ?: JsMetadataVersion.INSTANCE
|
||||
)
|
||||
|
||||
val ioFile = VfsUtilCore.virtualToIoFile(file.virtualFile)
|
||||
incrementalResults.processPackagePart(ioFile, packagePart.toByteArray(), binaryAst)
|
||||
}
|
||||
|
||||
val settings = config.configuration.languageVersionSettings
|
||||
incrementalResults.processHeader(serializationUtil.serializeHeader(moduleDescriptor, null, settings).toByteArray())
|
||||
val ioFile = VfsUtilCore.virtualToIoFile(file.virtualFile)
|
||||
incrementalResults.processPackagePart(ioFile, packagePart.toByteArray(), binaryAst)
|
||||
}
|
||||
|
||||
removeDuplicateImports(translationResult.program)
|
||||
translationResult.program.resolveTemporaryNames()
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
if (hasError(diagnostics)) return TranslationResult.Fail(diagnostics)
|
||||
|
||||
val importedModules = ArrayList<String>()
|
||||
for (module in translationResult.importedModuleList) {
|
||||
importedModules.add(module.externalName)
|
||||
}
|
||||
|
||||
return TranslationResult.Success(
|
||||
config, files, translationResult.program, diagnostics, importedModules,
|
||||
moduleDescriptor, bindingTrace.bindingContext
|
||||
)
|
||||
val settings = config.configuration.languageVersionSettings
|
||||
incrementalResults.processHeader(KotlinJavascriptSerializationUtil.serializeHeader(moduleDescriptor, null, settings).toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
+23
-9
@@ -17,16 +17,30 @@
|
||||
package org.jetbrains.kotlin.js.translate.general
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
|
||||
class AstGenerationResult(
|
||||
val program: JsProgram,
|
||||
val innerModuleName: JsName,
|
||||
val fragments: List<JsProgramFragment>,
|
||||
val fragmentMap: Map<KtFile, JsProgramFragment>,
|
||||
val newFragments: List<JsProgramFragment>,
|
||||
val importStatements: List<JsStatement>,
|
||||
val fileMemberScopes: Map<KtFile, List<DeclarationDescriptor>>,
|
||||
val importedModuleList: List<JsImportedModule>
|
||||
)
|
||||
val fragments: List<JsProgramFragment>,
|
||||
val fragmentMap: Map<KtFile, JsProgramFragment>,
|
||||
val newFragments: List<JsProgramFragment>,
|
||||
val fileMemberScopes: Map<KtFile, List<DeclarationDescriptor>>,
|
||||
private val program: JsProgram,
|
||||
private val moduleDescriptor: ModuleDescriptor,
|
||||
private val moduleId: String,
|
||||
private val moduleKind: ModuleKind
|
||||
) {
|
||||
|
||||
val innerModuleName = program.getScope().declareName("_");
|
||||
|
||||
val importedModuleList: List<JsImportedModule>
|
||||
get() = fragments.flatMap { it.importedModules }
|
||||
|
||||
fun buildProgram(): JsProgram {
|
||||
val merger = Merger(program, innerModuleName, moduleDescriptor, moduleId, moduleKind)
|
||||
fragments.forEach { merger.addFragment(it) }
|
||||
return merger.buildProgram()
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,24 @@ import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.createPrototypeStatements
|
||||
import org.jetbrains.kotlin.js.translate.utils.definePackageAlias
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
|
||||
class Merger(
|
||||
val program: JsProgram,
|
||||
val internalModuleName: JsName,
|
||||
val module: ModuleDescriptor,
|
||||
val moduleId: String,
|
||||
val moduleKind: ModuleKind
|
||||
) {
|
||||
private val rootFunction = JsFunction(program.rootScope, JsBlock(), "root function").also {
|
||||
it.parameters.add(JsParameter(internalModuleName))
|
||||
}
|
||||
|
||||
class Merger(private val rootFunction: JsFunction, val internalModuleName: JsName, val module: ModuleDescriptor) {
|
||||
// Maps unique signature (see generateSignature) to names
|
||||
private val nameTable = mutableMapOf<String, JsName>()
|
||||
|
||||
private val importedModuleTable = mutableMapOf<JsImportedModuleKey, JsName>()
|
||||
val importBlock = JsGlobalBlock()
|
||||
private val importBlock = JsGlobalBlock()
|
||||
private val declarationBlock = JsGlobalBlock()
|
||||
private val initializerBlock = JsGlobalBlock()
|
||||
private val testsMap = mutableMapOf<String, JsStatement>()
|
||||
@@ -43,25 +55,11 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
|
||||
private val exportedPackages = mutableMapOf<String, JsName>()
|
||||
private val exportedTags = mutableSetOf<String>()
|
||||
|
||||
private val fragments = mutableListOf<JsProgramFragment>()
|
||||
|
||||
// Add declaration and initialization statements from program fragment to resulting single program
|
||||
fun addFragment(fragment: JsProgramFragment) {
|
||||
val nameMap = buildNameMap(fragment)
|
||||
nameMap.rename(fragment)
|
||||
|
||||
for ((key, importExpr) in fragment.imports) {
|
||||
if (declaredImports.add(key)) {
|
||||
val name = nameTable[key]!!
|
||||
importBlock.statements += JsAstUtils.newVar(name, importExpr)
|
||||
}
|
||||
}
|
||||
|
||||
declarationBlock.statements += fragment.declarationBlock
|
||||
initializerBlock.statements += fragment.initializerBlock
|
||||
fragment.tryUpdateTests()
|
||||
fragment.tryUpdateMain()
|
||||
addExportStatements(fragment)
|
||||
|
||||
classes += fragment.classes
|
||||
fragments.add(fragment)
|
||||
}
|
||||
|
||||
private fun JsProgramFragment.tryUpdateTests() {
|
||||
@@ -205,8 +203,32 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
|
||||
return rootNode
|
||||
}
|
||||
|
||||
private fun mergeNames() {
|
||||
for (fragment in fragments) {
|
||||
val nameMap = buildNameMap(fragment)
|
||||
nameMap.rename(fragment)
|
||||
|
||||
for ((key, importExpr) in fragment.imports) {
|
||||
if (declaredImports.add(key)) {
|
||||
val name = nameTable[key]!!
|
||||
importBlock.statements += JsAstUtils.newVar(name, importExpr)
|
||||
}
|
||||
}
|
||||
|
||||
declarationBlock.statements += fragment.declarationBlock
|
||||
initializerBlock.statements += fragment.initializerBlock
|
||||
fragment.tryUpdateTests()
|
||||
fragment.tryUpdateMain()
|
||||
addExportStatements(fragment)
|
||||
|
||||
classes += fragment.classes
|
||||
}
|
||||
}
|
||||
|
||||
// Adds different boilerplate code (like imports, class prototypes, etc) to resulting program.
|
||||
fun merge() {
|
||||
mergeNames()
|
||||
|
||||
rootFunction.body.statements.apply {
|
||||
addImportForInlineDeclarationIfNecessary()
|
||||
this += importBlock.statements
|
||||
@@ -220,6 +242,37 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
|
||||
}
|
||||
}
|
||||
|
||||
fun buildProgram(): JsProgram {
|
||||
merge()
|
||||
|
||||
val rootBlock = rootFunction.getBody()
|
||||
|
||||
val statements = rootBlock.getStatements()
|
||||
|
||||
statements.add(0, JsStringLiteral("use strict").makeStmt())
|
||||
if (!isBuiltinModule(fragments)) {
|
||||
defineModule(program, statements, moduleId)
|
||||
}
|
||||
|
||||
// Invoke function passing modules as arguments
|
||||
// This should help minifier tool to recognize references to these modules as local variables and make them shorter.
|
||||
for (importedModule in importedModules) {
|
||||
rootFunction.parameters.add(JsParameter(importedModule.internalName))
|
||||
}
|
||||
|
||||
statements.add(JsReturn(internalModuleName.makeRef()))
|
||||
|
||||
val block = program.globalBlock
|
||||
block.statements.addAll(
|
||||
ModuleWrapperTranslation.wrapIfNecessary(
|
||||
moduleId, rootFunction, importedModules, program,
|
||||
moduleKind
|
||||
)
|
||||
)
|
||||
|
||||
return program
|
||||
}
|
||||
|
||||
private fun MutableList<JsStatement>.addImportForInlineDeclarationIfNecessary() {
|
||||
val importsForInlineName = nameTable[Namer.IMPORTS_FOR_INLINE_PROPERTY] ?: return
|
||||
this += definePackageAlias(
|
||||
@@ -267,4 +320,34 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
|
||||
cls.interfaces.forEach { addClassPostDeclarations(it, visited, statements) }
|
||||
statements += cls.postDeclarationBlock.statements
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val ENUM_SIGNATURE = "kotlin\$Enum"
|
||||
|
||||
// TODO is there no better way?
|
||||
private fun isBuiltinModule(fragments: List<JsProgramFragment>): Boolean {
|
||||
for (fragment in fragments) {
|
||||
for (nameBinding in fragment.nameBindings) {
|
||||
if (nameBinding.key == ENUM_SIGNATURE && !fragment.imports.containsKey(ENUM_SIGNATURE)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun defineModule(program: JsProgram, statements: MutableList<JsStatement>, moduleId: String) {
|
||||
val rootPackageName = program.scope.findName(Namer.getRootPackageName())
|
||||
if (rootPackageName != null) {
|
||||
val namer = Namer.newInstance(program.scope)
|
||||
statements.add(
|
||||
JsInvocation(
|
||||
namer.kotlin("defineModule"),
|
||||
JsStringLiteral(moduleId),
|
||||
rootPackageName.makeRef()
|
||||
).makeStmt()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -324,10 +324,8 @@ public final class Translation {
|
||||
@NotNull JsConfig config,
|
||||
@NotNull SourceFilePathResolver sourceFilePathResolver
|
||||
) {
|
||||
|
||||
JsProgram program = new JsProgram();
|
||||
JsFunction rootFunction = new JsFunction(program.getRootScope(), new JsBlock(), "root function");
|
||||
JsName internalModuleName = program.getScope().declareName("_");
|
||||
Merger merger = new Merger(rootFunction, internalModuleName, moduleDescriptor);
|
||||
|
||||
Map<KtFile, JsProgramFragment> fragmentMap = new HashMap<>();
|
||||
List<JsProgramFragment> fragments = new ArrayList<>();
|
||||
@@ -354,56 +352,22 @@ public final class Translation {
|
||||
newFragments.add(fragment);
|
||||
fragmentMap.put(file, fragment);
|
||||
fileMemberScopes.put(file, fileMemberScope);
|
||||
merger.addFragment(fragment);
|
||||
}
|
||||
else if (unit instanceof TranslationUnit.BinaryAst) {
|
||||
byte[] astData = ((TranslationUnit.BinaryAst) unit).getData();
|
||||
JsProgramFragment fragment = deserializer.deserialize(new ByteArrayInputStream(astData));
|
||||
merger.addFragment(fragment);
|
||||
fragments.add(fragment);
|
||||
}
|
||||
}
|
||||
|
||||
rootFunction.getParameters().add(new JsParameter(internalModuleName));
|
||||
|
||||
merger.merge();
|
||||
|
||||
JsBlock rootBlock = rootFunction.getBody();
|
||||
|
||||
List<JsStatement> statements = rootBlock.getStatements();
|
||||
|
||||
statements.add(0, new JsStringLiteral("use strict").makeStmt());
|
||||
if (!isBuiltinModule(fragments)) {
|
||||
defineModule(program, statements, config.getModuleId());
|
||||
}
|
||||
|
||||
// Invoke function passing modules as arguments
|
||||
// This should help minifier tool to recognize references to these modules as local variables and make them shorter.
|
||||
List<JsImportedModule> importedModuleList = merger.getImportedModules();
|
||||
|
||||
for (JsImportedModule importedModule : importedModuleList) {
|
||||
rootFunction.getParameters().add(new JsParameter(importedModule.getInternalName()));
|
||||
}
|
||||
|
||||
statements.add(new JsReturn(internalModuleName.makeRef()));
|
||||
|
||||
JsBlock block = program.getGlobalBlock();
|
||||
block.getStatements().addAll(wrapIfNecessary(config.getModuleId(), rootFunction, importedModuleList, program,
|
||||
config.getModuleKind()));
|
||||
|
||||
return new AstGenerationResult(program, internalModuleName, fragments, fragmentMap, newFragments,
|
||||
merger.getImportBlock().getStatements(), fileMemberScopes, importedModuleList);
|
||||
}
|
||||
|
||||
private static boolean isBuiltinModule(@NotNull List<JsProgramFragment> fragments) {
|
||||
for (JsProgramFragment fragment : fragments) {
|
||||
for (JsNameBinding nameBinding : fragment.getNameBindings()) {
|
||||
if (nameBinding.getKey().equals(ENUM_SIGNATURE) && !fragment.getImports().containsKey(ENUM_SIGNATURE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return new AstGenerationResult(fragments,
|
||||
fragmentMap,
|
||||
newFragments,
|
||||
fileMemberScopes,
|
||||
program,
|
||||
moduleDescriptor,
|
||||
config.getModuleId(),
|
||||
config.getModuleKind());
|
||||
}
|
||||
|
||||
private static void translateFile(
|
||||
@@ -430,15 +394,6 @@ public final class Translation {
|
||||
}
|
||||
}
|
||||
|
||||
private static void defineModule(@NotNull JsProgram program, @NotNull List<JsStatement> statements, @NotNull String moduleId) {
|
||||
JsName rootPackageName = program.getScope().findName(Namer.getRootPackageName());
|
||||
if (rootPackageName != null) {
|
||||
Namer namer = Namer.newInstance(program.getScope());
|
||||
statements.add(new JsInvocation(namer.kotlin("defineModule"), new JsStringLiteral(moduleId),
|
||||
rootPackageName.makeRef()).makeStmt());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JsStatement mayBeGenerateTests(
|
||||
@NotNull TranslationContext context,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// KJS_WITH_FULL_RUNTIME
|
||||
// EXPECTED_REACHABLE_NODES: 1625
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
|
||||
@@ -8,8 +7,8 @@ inline fun tenUInt() = 10U
|
||||
|
||||
inline fun tenULong() = 10UL
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
// RECOMPILE
|
||||
|
||||
fun box(): String {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user