diff --git a/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt b/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt index df899dab8a0..2ca23922e2e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt @@ -17,15 +17,14 @@ package org.jetbrains.kotlin.idea import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation +import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.Variance class MainFunctionDetector { @@ -68,8 +67,22 @@ class MainFunctionDetector { return isMain(getFunctionDescriptor(function)) } - fun getMainFunction(files: Collection): KtNamedFunction? = - files.asSequence().map { findMainFunction(it.declarations) }.firstOrNull { it != null } + fun getMainFunction(module: ModuleDescriptor): FunctionDescriptor? = getMainFunction(module, module.getPackage(FqName.ROOT)) + + private fun getMainFunction(module: ModuleDescriptor, packageView: PackageViewDescriptor): FunctionDescriptor? { + for (packageFragment in packageView.fragments) { + DescriptorUtils.getAllDescriptors(packageFragment.getMemberScope()) + .filterIsInstance() + .firstOrNull { isMain(it) } + ?.let { return it } + } + + for (subpackageName in module.getSubPackagesOf(packageView.fqName, MemberScope.ALL_NAME_FILTER)) { + getMainFunction(module, module.getPackage(subpackageName))?.let { return it } + } + + return null + } private fun findMainFunction(declarations: List) = declarations.filterIsInstance().find { isMain(it) } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/frontend/js/di/injection.kt b/js/js.frontend/src/org/jetbrains/kotlin/frontend/js/di/injection.kt index 9f258d9569e..3d76d9fc004 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/frontend/js/di/injection.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/frontend/js/di/injection.kt @@ -22,6 +22,8 @@ import org.jetbrains.kotlin.container.get import org.jetbrains.kotlin.container.useImpl import org.jetbrains.kotlin.container.useInstance import org.jetbrains.kotlin.context.ModuleContext +import org.jetbrains.kotlin.descriptors.PackageFragmentProvider +import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.frontend.di.configureModule import org.jetbrains.kotlin.incremental.components.LookupTracker @@ -30,12 +32,15 @@ import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer import org.jetbrains.kotlin.resolve.lazy.ResolveSession import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory +import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration +import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil fun createTopDownAnalyzerForJs( moduleContext: ModuleContext, bindingTrace: BindingTrace, declarationProviderFactory: DeclarationProviderFactory, - languageVersionSettings: LanguageVersionSettings + languageVersionSettings: LanguageVersionSettings, + fallbackMetadata: List = emptyList() ): LazyTopDownAnalyzer { val storageComponentContainer = createContainer("TopDownAnalyzerForJs", JsPlatform) { configureModule(moduleContext, JsPlatform, TargetPlatformVersion.NoVersion, bindingTrace) @@ -50,7 +55,13 @@ fun createTopDownAnalyzerForJs( useImpl() useImpl() }.apply { - get().initialize(get().packageFragmentProvider) + val packagePartProviders = mutableListOf(get().packageFragmentProvider) + val moduleDescriptor = get() + if (fallbackMetadata.isNotEmpty()) { + packagePartProviders += KotlinJavascriptSerializationUtil.readScope( + fallbackMetadata, moduleContext.storageManager, moduleDescriptor, DeserializationConfiguration.Default) + } + moduleDescriptor.initialize(CompositePackageFragmentProvider(packagePartProviders)) } return storageComponentContainer.get() } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/analyze/TopDownAnalyzerFacadeForJS.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/analyze/TopDownAnalyzerFacadeForJS.kt index 198ff01bf3b..0e0c9bcf727 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/analyze/TopDownAnalyzerFacadeForJS.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/analyze/TopDownAnalyzerFacadeForJS.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.context.ModuleContext import org.jetbrains.kotlin.context.ProjectContext import org.jetbrains.kotlin.frontend.js.di.createTopDownAnalyzerForJs import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult +import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JsConfig import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.js.resolve.MODULE_KIND @@ -56,7 +57,8 @@ object TopDownAnalyzerFacadeForJS { val analyzerForJs = createTopDownAnalyzerForJs( moduleContext, trace, FileBasedDeclarationProviderFactory(moduleContext.storageManager, files), - config.configuration.languageVersionSettings + config.configuration.languageVersionSettings, + config.configuration[JSConfigurationKeys.FALLBACK_METADATA].orEmpty() ) analyzerForJs.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files) return JsAnalysisResult.success(trace, moduleContext.module) diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java index 11172ada46b..4cca3f16938 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java @@ -39,4 +39,9 @@ public class JSConfigurationKeys { public static final CompilerConfigurationKey TYPED_ARRAYS_ENABLED = CompilerConfigurationKey.create("TypedArrays enabled"); + + public static final CompilerConfigurationKey> FALLBACK_METADATA = + CompilerConfigurationKey.create("fallback metadata"); + + public static final CompilerConfigurationKey SERIALIZE_FRAGMENTS = CompilerConfigurationKey.create("serialize fragments"); } diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt index 5e96fcb641e..1a64f817113 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt @@ -51,7 +51,17 @@ object KotlinJavascriptSerializationUtil { )) } - private fun serializeMetadata( + fun readScope( + metadata: Collection, + storageManager: StorageManager, + module: ModuleDescriptor, + configuration: DeserializationConfiguration + ): PackageFragmentProvider { + val scopeProto = metadata.map { JsProtoBuf.Library.Part.parseFrom(it) } + return createKotlinJavascriptPackageFragmentProvider(storageManager, module, scopeProto, configuration) + } + + fun serializeMetadata( bindingContext: BindingContext, module: ModuleDescriptor, moduleKind: ModuleKind, @@ -84,10 +94,18 @@ object KotlinJavascriptSerializationUtil { fun metadataAsString(bindingContext: BindingContext, jsDescriptor: JsModuleDescriptor): String = KotlinJavascriptMetadataUtils.formatMetadataAsString(jsDescriptor.name, jsDescriptor.serializeToBinaryMetadata(bindingContext)) - private fun serializePackageFragment(bindingContext: BindingContext, module: ModuleDescriptor, fqName: FqName): ProtoBuf.PackageFragment { - val builder = ProtoBuf.PackageFragment.newBuilder() - + fun serializePackage(bindingContext: BindingContext, module: ModuleDescriptor, fqName: FqName): JsProtoBuf.Library.Part { val packageView = module.getPackage(fqName) + return serializeScope(bindingContext, module, packageView.memberScope.getContributedDescriptors(), fqName) + } + + fun serializeScope( + bindingContext: BindingContext, + module: ModuleDescriptor, + scope: Collection, + fqName: FqName + ): JsProtoBuf.Library.Part { + val builder = JsProtoBuf.Library.Part.newBuilder() // TODO: ModuleDescriptor should be able to return the package only with the contents of that module, without dependencies val skip: (DeclarationDescriptor) -> Boolean = { DescriptorUtils.getContainingModule(it) != module || (it is MemberDescriptor && it.isHeader) } @@ -96,9 +114,7 @@ object KotlinJavascriptSerializationUtil { val serializerExtension = KotlinJavascriptSerializerExtension(fileRegistry) val serializer = DescriptorSerializer.createTopLevel(serializerExtension) - val classDescriptors = DescriptorSerializer.sort( - packageView.memberScope.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) - ).filterIsInstance() + val classDescriptors = DescriptorSerializer.sort(scope).filterIsInstance() fun serializeClasses(descriptors: Collection) { fun serializeClass(classDescriptor: ClassDescriptor) { @@ -119,10 +135,7 @@ object KotlinJavascriptSerializationUtil { val stringTable = serializerExtension.stringTable - val fragments = packageView.fragments - val members = fragments - .flatMap { fragment -> DescriptorUtils.getAllDescriptors(fragment.getMemberScope()) } - .filterNot(skip) + val members = scope.filterNot(skip) builder.`package` = serializer.packagePartProto(fqName, members).build() builder.setExtension( diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt index d8ddcd67988..f7c90c1bd3e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt @@ -21,6 +21,7 @@ import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.StandardFileSystems import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.PsiManager +import junit.framework.TestCase import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector @@ -37,6 +38,7 @@ import org.jetbrains.kotlin.js.config.JsConfig import org.jetbrains.kotlin.js.facade.K2JSTranslator import org.jetbrains.kotlin.js.facade.MainCallParameters import org.jetbrains.kotlin.js.facade.TranslationResult +import org.jetbrains.kotlin.js.facade.TranslationUnit import org.jetbrains.kotlin.js.test.rhino.RhinoFunctionResultChecker import org.jetbrains.kotlin.js.test.rhino.RhinoUtils import org.jetbrains.kotlin.js.test.utils.DirectiveTestUtils @@ -227,7 +229,8 @@ abstract class BasicBoxTest( outputPostfixFile: File?, mainCallParameters: MainCallParameters ) { - val testFiles = module.files.map { it.fileName }.filter { it.endsWith(".kt") } + val kotlinFiles = module.files.filter { it.fileName.endsWith(".kt") } + val testFiles = kotlinFiles.map { it.fileName } val globalCommonFiles = JsTestUtils.getFilesInDirectoryByExtension( TEST_DATA_DIR_PATH + COMMON_FILES_DIR, KotlinFileType.EXTENSION) val localCommonFile = directory + "/" + COMMON_FILES_NAME + "." + KotlinFileType.EXTENSION @@ -235,24 +238,47 @@ abstract class BasicBoxTest( val additionalCommonFiles = additionalCommonFileDirectories.flatMap { baseDir -> JsTestUtils.getFilesInDirectoryByExtension(baseDir + "/", KotlinFileType.EXTENSION) } - val psiFiles = createPsiFiles(testFiles + globalCommonFiles + localCommonFiles + additionalCommonFiles) + val additionalFiles = globalCommonFiles + localCommonFiles + additionalCommonFiles + val psiFiles = createPsiFiles(testFiles + additionalFiles) - val config = createConfig(module, dependencies, multiModule) + val config = createConfig(module, dependencies, multiModule, additionalMetadata = null) val outputFile = File(outputFileName) - translateFiles(psiFiles, outputFile, config, outputPrefixFile, outputPostfixFile, mainCallParameters) - } + translateFiles(psiFiles.map(TranslationUnit::SourceFile), outputFile, config, outputPrefixFile, outputPostfixFile, mainCallParameters) - protected fun translateFiles( - psiFiles: List, - outputFile: File, - config: JsConfig, + if (module.hasFilesToRecompile) { + val incrementalDir = File(outputFile.parentFile, "incremental/" + outputFile.nameWithoutExtension) + val serializedMetadata = mutableListOf() + val translationUnits = kotlinFiles.withIndex().map { (index, file) -> + if (file.recompile) { + TranslationUnit.SourceFile(createPsiFile(file.fileName)) + } + else { + serializedMetadata += File(incrementalDir, "$index.$METADATA_EXTENSION") + val astFile = File(incrementalDir, "$index.$AST_EXTENSION") + TranslationUnit.BinaryAst(FileUtil.loadFileBytes(astFile)) + } + } + val allTranslationUnits = translationUnits + additionalFiles.withIndex().map { (index, _) -> + val astFile = File(incrementalDir, "${index + translationUnits.size}.$AST_EXTENSION") + TranslationUnit.BinaryAst(FileUtil.loadFileBytes(astFile)) + } + + val recompiledConfig = createConfig(module, dependencies, multiModule, serializedMetadata) + val recompiledOutputFile = File(outputFile.parentFile, outputFile.nameWithoutExtension + "-recompiled.js") + + translateFiles(allTranslationUnits, recompiledOutputFile, recompiledConfig) + + val originalOutput = FileUtil.loadFile(outputFile) + val recompiledOutput = FileUtil.loadFile(recompiledOutputFile) + TestCase.assertEquals("Output file changed after recompilation", originalOutput, recompiledOutput) + } + }protected fun translateFiles(units: List, outputFile: File, config: JsConfig, outputPrefixFile: File?, outputPostfixFile: File?, - mainCallParameters: MainCallParameters - ) { + mainCallParameters: MainCallParameters) { val translator = K2JSTranslator(config) - val translationResult = translator.translate(psiFiles, mainCallParameters) + val translationResult = translator.translateUnits(units, mainCallParameters) if (translationResult !is TranslationResult.Success) { val outputStream = ByteArrayOutputStream() @@ -281,7 +307,24 @@ abstract class BasicBoxTest( FileUtil.writeToFile(outputFile, wrappedContent) } - processJsProgram(translationResult.program, psiFiles) + val incrementalDir = File(outputDir, "incremental/${outputFile.nameWithoutExtension}") + + for ((index, unit) in units.withIndex()) { + if (unit !is TranslationUnit.SourceFile) continue + val fileTranslationResult = translationResult.fileTranslationResults[unit.file]!! + val basePath = "$index." + val binaryAst = fileTranslationResult.binaryAst + val binaryMetadata = fileTranslationResult.metadata + + if (binaryAst != null) { + FileUtil.writeToFile(File(incrementalDir, basePath + AST_EXTENSION), binaryAst) + } + if (binaryMetadata != null) { + FileUtil.writeToFile(File(incrementalDir, basePath + METADATA_EXTENSION), binaryMetadata) + } + } + + processJsProgram(translationResult.program, units.filterIsInstance().map { it.file }) } protected fun processJsProgram(program: JsProgram, psiFiles: List) { @@ -291,14 +334,19 @@ abstract class BasicBoxTest( program.verifyAst() } - private fun createPsiFiles(fileNames: List): List { + private fun createPsiFile(fileName: String): KtFile { val psiManager = PsiManager.getInstance(project) val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) - return fileNames.map { fileName -> psiManager.findFile(fileSystem.findFileByPath(fileName)!!) as KtFile } + return psiManager.findFile(fileSystem.findFileByPath(fileName)!!) as KtFile } - private fun createConfig(module: TestModule, dependencies: List, multiModule: Boolean): JsConfig { + private fun createPsiFiles(fileNames: List): List = fileNames.map(this::createPsiFile) + + private fun createConfig( + module: TestModule, dependencies: List, multiModule: Boolean, + additionalMetadata: List? + ): JsConfig { val configuration = environment.configuration.copy() configuration.put(CommonConfigurationKeys.DISABLE_INLINE, module.inliningDisabled) @@ -314,7 +362,13 @@ abstract class BasicBoxTest( configuration.put(JSConfigurationKeys.TARGET, EcmaVersion.v5) configuration.put(JSConfigurationKeys.SOURCE_MAP, generateSourceMap) + val hasFilesToRecompile = module.hasFilesToRecompile configuration.put(JSConfigurationKeys.META_INFO, multiModule) + configuration.put(JSConfigurationKeys.SERIALIZE_FRAGMENTS, hasFilesToRecompile) + + if (additionalMetadata != null) { + configuration.put(JSConfigurationKeys.FALLBACK_METADATA, additionalMetadata.map { FileUtil.loadFileBytes(it) }) + } if (typedArraysEnabled) { configuration.put(JSConfigurationKeys.TYPED_ARRAYS_ENABLED, true) @@ -359,7 +413,9 @@ abstract class BasicBoxTest( currentModule.languageVersion = LanguageVersion.fromVersionString(version) } - return TestFile(temporaryFile.absolutePath, currentModule) + return TestFile(temporaryFile.absolutePath, currentModule).apply { + recompile = RECOMPILE_PATTERN.matcher(text).find() + } } override fun createModule(name: String, dependencies: List): TestModule? { @@ -375,6 +431,8 @@ abstract class BasicBoxTest( init { module.files += this } + + var recompile = false } private class TestModule( @@ -386,6 +444,8 @@ abstract class BasicBoxTest( var inliningDisabled = false val files = mutableListOf() var languageVersion: LanguageVersion? = null + + val hasFilesToRecompile get() = files.any { it.recompile } } override fun createEnvironment(): KotlinCoreEnvironment { @@ -404,6 +464,9 @@ abstract class BasicBoxTest( private val NO_MODULE_SYSTEM_PATTERN = Pattern.compile("^// *NO_JS_MODULE_SYSTEM", Pattern.MULTILINE) private val NO_INLINE_PATTERN = Pattern.compile("^// *NO_INLINE *$", Pattern.MULTILINE) private val SKIP_NODE_JS = Pattern.compile("^// *SKIP_NODE_JS *$", Pattern.MULTILINE) + private val RECOMPILE_PATTERN = Pattern.compile("^// *RECOMPILE *$", Pattern.MULTILINE) + private val AST_EXTENSION = "jsast" + private val METADATA_EXTENSION = "jsmeta" private val TEST_MODULE = "JS_TESTS" private val DEFAULT_MODULE = "main" diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 4559c07d073..71b152004bf 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -3435,6 +3435,21 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { } } + @TestMetadata("js/js.translator/testData/box/incremental") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Incremental extends AbstractBoxJsTest { + public void testAllFilesPresentInIncremental() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/incremental"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/incremental/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("js/js.translator/testData/box/inheritance") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java index b1bdbb0d683..7b7571f23e4 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java @@ -18,12 +18,13 @@ package org.jetbrains.kotlin.js.facade; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; 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.backend.ast.JsImportedModule; -import org.jetbrains.kotlin.js.backend.ast.JsProgram; import org.jetbrains.kotlin.js.backend.ast.JsProgramFragment; +import org.jetbrains.kotlin.js.config.JSConfigurationKeys; import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.coroutine.CoroutineTransformer; import org.jetbrains.kotlin.js.facade.exceptions.TranslationException; @@ -31,19 +32,22 @@ import org.jetbrains.kotlin.js.inline.JsInliner; import org.jetbrains.kotlin.js.inline.clean.RemoveUnusedImportsKt; import org.jetbrains.kotlin.js.inline.clean.ResolveTemporaryNamesKt; import org.jetbrains.kotlin.js.translate.general.AstGenerationResult; +import org.jetbrains.kotlin.js.translate.general.FileTranslationResult; import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.js.translate.utils.ExpandIsCallsKt; import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics; -import org.jetbrains.kotlin.serialization.js.ast.JsAstDeserializer; +import org.jetbrains.kotlin.serialization.js.JsProtoBuf; +import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil; import org.jetbrains.kotlin.serialization.js.ast.JsAstSerializer; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static org.jetbrains.kotlin.diagnostics.DiagnosticUtils.hasError; @@ -73,6 +77,34 @@ public final class K2JSTranslator { @NotNull MainCallParameters mainCallParameters, @Nullable JsAnalysisResult analysisResult ) throws TranslationException { + List units = new ArrayList(); + for (KtFile file : files) { + units.add(new TranslationUnit.SourceFile(file)); + } + return translateUnits(units, mainCallParameters, analysisResult); + } + + @NotNull + public TranslationResult translateUnits( + @NotNull List units, + @NotNull MainCallParameters mainCallParameters + ) throws TranslationException { + return translateUnits(units, mainCallParameters, null); + } + + @NotNull + public TranslationResult translateUnits( + @NotNull List units, + @NotNull MainCallParameters mainCallParameters, + @Nullable JsAnalysisResult analysisResult + ) throws TranslationException { + List files = new ArrayList(); + for (TranslationUnit unit : units) { + if (unit instanceof TranslationUnit.SourceFile) { + files.add(((TranslationUnit.SourceFile) unit).getFile()); + } + } + if (analysisResult == null) { analysisResult = TopDownAnalyzerFacadeForJS.analyzeFiles(files, config); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); @@ -83,33 +115,39 @@ public final class K2JSTranslator { ModuleDescriptor moduleDescriptor = analysisResult.getModuleDescriptor(); Diagnostics diagnostics = bindingTrace.getBindingContext().getDiagnostics(); - AstGenerationResult translationResult = Translation.generateAst(bindingTrace, files, mainCallParameters, moduleDescriptor, config); + AstGenerationResult translationResult = Translation.generateAst(bindingTrace, units, mainCallParameters, moduleDescriptor, config); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); - JsInliner.process(config, analysisResult.getBindingTrace(), translationResult.getInnerModuleName(), - translationResult.getFragments(), translationResult.getFragments()); + List newFragments = translationResult.getFragments(); + List allFragments = new ArrayList(newFragments); - // TODO: temporary code for testing purposes, remove later + JsInliner.process(config, analysisResult.getBindingTrace(), translationResult.getInnerModuleName(), allFragments, newFragments); + + Map fileMap = new HashMap(); JsAstSerializer serializer = new JsAstSerializer(); - JsAstDeserializer deserializer = new JsAstDeserializer(new JsProgram()); - JsProgram program = translationResult.getProgram(); - int bytesTotal = 0; - for (JsProgramFragment fragment : translationResult.getFragments()) { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - serializer.serialize(fragment, output); - bytesTotal += output.size(); + boolean serializeFragments = config.getConfiguration().get(JSConfigurationKeys.SERIALIZE_FRAGMENTS, false); + for (KtFile file : files) { + List scope = translationResult.getFileMemberScopes().get(file); + byte[] binaryAst = null; + byte[] binaryMetadata = null; + if (serializeFragments) { + JsProgramFragment fragment = translationResult.getFragmentMap().get(file); + if (fragment != null) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + serializer.serialize(fragment, output); + binaryAst = output.toByteArray(); + } + } - ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); - JsProgramFragment deserializedFragment = deserializer.deserialize(input); - - String expected = fragmentToString(fragment); - String actual = fragmentToString(deserializedFragment); - assert expected.equals(actual) : "Deserialization: " + expected + "\n\nvs.\n\n" + actual; + if (scope != null) { + JsProtoBuf.Library.Part part = KotlinJavascriptSerializationUtil.INSTANCE.serializeScope( + bindingTrace.getBindingContext(), moduleDescriptor, scope, file.getPackageFqName()); + binaryMetadata = part.toByteArray(); + } + fileMap.put(file, new FileTranslationResult(file, binaryMetadata, binaryAst)); } - program.getGlobalBlock().getStatements().add(program.getNumberLiteral(bytesTotal).makeStmt()); - ResolveTemporaryNamesKt.resolveTemporaryNames(translationResult.getProgram()); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); @@ -120,7 +158,7 @@ public final class K2JSTranslator { ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); - ExpandIsCallsKt.expandIsCalls(translationResult.getFragments()); + ExpandIsCallsKt.expandIsCalls(newFragments); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); List importedModules = new ArrayList<>(); @@ -128,11 +166,6 @@ public final class K2JSTranslator { importedModules.add(module.getExternalName()); } return new TranslationResult.Success(config, files, translationResult.getProgram(), diagnostics, importedModules, - moduleDescriptor, bindingTrace.getBindingContext()); - } - - private static String fragmentToString(JsProgramFragment fragment) { - return fragment.getDeclarationBlock().toString() + fragment.getInitializerBlock().toString() + - fragment.getExportBlock().toString(); + moduleDescriptor, bindingTrace.getBindingContext(), fileMap); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/facade/TranslationResult.kt b/js/js.translator/src/org/jetbrains/kotlin/js/facade/TranslationResult.kt index ab2268d14c9..f59b47442d4 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/facade/TranslationResult.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/facade/TranslationResult.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.js.config.JsConfig import org.jetbrains.kotlin.js.sourceMap.JsSourceGenerationVisitor import org.jetbrains.kotlin.js.sourceMap.SourceMap3Builder import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilder +import org.jetbrains.kotlin.js.translate.general.FileTranslationResult import org.jetbrains.kotlin.js.util.TextOutput import org.jetbrains.kotlin.js.util.TextOutputImpl import org.jetbrains.kotlin.psi.KtFile @@ -47,7 +48,8 @@ abstract class TranslationResult protected constructor(val diagnostics: Diagnost diagnostics: Diagnostics, private val importedModules: List, private val moduleDescriptor: ModuleDescriptor, - private val bindingContext: BindingContext + private val bindingContext: BindingContext, + val fileTranslationResults: Map ) : TranslationResult(diagnostics) { @Suppress("unused") // Used in kotlin-web-demo in WebDemoTranslatorFacade fun getCode(): String = getCode(TextOutputImpl(), sourceMapBuilder = null) diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/facade/TranslationUnit.kt b/js/js.translator/src/org/jetbrains/kotlin/js/facade/TranslationUnit.kt new file mode 100644 index 00000000000..1a111441125 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/facade/TranslationUnit.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.facade + +import org.jetbrains.kotlin.psi.KtFile + +sealed class TranslationUnit { + class SourceFile(val file: KtFile) : TranslationUnit() + + class BinaryAst(val data: ByteArray) : TranslationUnit() +} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/AstGenerationResult.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/AstGenerationResult.kt index ae8843d134e..6a8ecd84d4a 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/AstGenerationResult.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/AstGenerationResult.kt @@ -16,14 +16,18 @@ package org.jetbrains.kotlin.js.translate.general +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.js.backend.ast.JsImportedModule import org.jetbrains.kotlin.js.backend.ast.JsName import org.jetbrains.kotlin.js.backend.ast.JsProgram import org.jetbrains.kotlin.js.backend.ast.JsProgramFragment +import org.jetbrains.kotlin.psi.KtFile class AstGenerationResult( val program: JsProgram, val innerModuleName: JsName, val fragments: List, + val fragmentMap: Map, + val fileMemberScopes: Map>, val importedModuleList: List ) \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/FileTranslationResult.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/FileTranslationResult.kt new file mode 100644 index 00000000000..2730fe33a8f --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/FileTranslationResult.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.translate.general + +import org.jetbrains.kotlin.psi.KtFile + +class FileTranslationResult(val file: KtFile, val metadata: ByteArray?, val binaryAst: ByteArray?) \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/JetTestFunctionDetector.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/JetTestFunctionDetector.java index 87ef1562b9e..85cf2e62876 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/JetTestFunctionDetector.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/JetTestFunctionDetector.java @@ -19,17 +19,10 @@ package org.jetbrains.kotlin.js.translate.general; import com.google.common.collect.Lists; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.descriptors.ClassDescriptor; -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; -import org.jetbrains.kotlin.descriptors.FunctionDescriptor; -import org.jetbrains.kotlin.descriptors.Modality; +import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.kotlin.descriptors.annotations.Annotations; -import org.jetbrains.kotlin.js.translate.utils.BindingUtils; -import org.jetbrains.kotlin.psi.KtClass; -import org.jetbrains.kotlin.psi.KtDeclaration; -import org.jetbrains.kotlin.psi.KtFile; -import org.jetbrains.kotlin.resolve.BindingContext; +import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.scopes.MemberScope; import org.jetbrains.kotlin.types.KotlinType; @@ -66,50 +59,54 @@ public class JetTestFunctionDetector { @NotNull public static List getTestFunctionDescriptors( - @NotNull BindingContext bindingContext, - @NotNull Collection files + @NotNull ModuleDescriptor moduleDescriptor ) { List answer = Lists.newArrayList(); - for (KtFile file : files) { - answer.addAll(getTestFunctions(bindingContext, file.getDeclarations())); - } + getTestFunctions(FqName.ROOT, moduleDescriptor, answer); return answer; } + private static void getTestFunctions( + @NotNull FqName packageName, + @NotNull ModuleDescriptor moduleDescriptor, + @NotNull List foundFunctions + ) { + for (PackageFragmentDescriptor packageDescriptor : moduleDescriptor.getPackage(packageName).getFragments()) { + Collection descriptors = DescriptorUtils.getAllDescriptors(packageDescriptor.getMemberScope()); + for (DeclarationDescriptor descriptor : descriptors) { + if (descriptor instanceof ClassDescriptor) { + getTestFunctions((ClassDescriptor) descriptor, foundFunctions); + } + } + } + + for (FqName subpackageName : moduleDescriptor.getSubPackagesOf(packageName, MemberScope.Companion.getALL_NAME_FILTER())) { + getTestFunctions(subpackageName, moduleDescriptor, foundFunctions); + } + } + @NotNull - private static List getTestFunctions( - @NotNull BindingContext bindingContext, - @NotNull List declarations + private static void getTestFunctions( + @NotNull ClassDescriptor classDescriptor, + @NotNull List foundFunctions ) { - List answer = Lists.newArrayList(); - for (KtDeclaration declaration : declarations) { - MemberScope scope = null; + if (classDescriptor.getModality() == Modality.ABSTRACT) return; - if (declaration instanceof KtClass) { - KtClass klass = (KtClass) declaration; - ClassDescriptor classDescriptor = BindingUtils.getClassDescriptor(bindingContext, klass); - if (classDescriptor.getModality() != Modality.ABSTRACT) { - scope = classDescriptor.getDefaultType().getMemberScope(); - } - } - - if (scope != null) { - Collection allDescriptors = DescriptorUtils.getAllDescriptors(scope); + Collection allDescriptors = DescriptorUtils.getAllDescriptors(classDescriptor.getUnsubstitutedMemberScope()); List testFunctions = ContainerUtil.mapNotNull( allDescriptors, - descriptor -> { - if (descriptor instanceof FunctionDescriptor) { - FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; - if (isTest(functionDescriptor)) return functionDescriptor; - } + descriptor-> { + if (descriptor instanceof FunctionDescriptor) { + FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; + if (isTest(functionDescriptor)) return functionDescriptor; + } - return null; - }); + return null; + }); + + + foundFunctions.addAll(testFunctions); - answer.addAll(testFunctions); - } - } - return answer; } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java index c7ad036ddbf..887c193acc6 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java @@ -19,12 +19,14 @@ package org.jetbrains.kotlin.js.translate.general; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.descriptors.ModuleDescriptor; import org.jetbrains.kotlin.idea.MainFunctionDetector; import org.jetbrains.kotlin.js.backend.ast.*; import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.facade.MainCallParameters; +import org.jetbrains.kotlin.js.facade.TranslationUnit; import org.jetbrains.kotlin.js.facade.exceptions.TranslationException; import org.jetbrains.kotlin.js.facade.exceptions.TranslationRuntimeException; import org.jetbrains.kotlin.js.facade.exceptions.UnsupportedFeatureException; @@ -43,24 +45,25 @@ import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; import org.jetbrains.kotlin.js.translate.utils.BindingUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.mutator.AssignToExpressionMutator; -import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.psi.KtDeclaration; +import org.jetbrains.kotlin.psi.KtExpression; +import org.jetbrains.kotlin.psi.KtFile; +import org.jetbrains.kotlin.psi.KtUnaryExpression; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; import org.jetbrains.kotlin.resolve.constants.ConstantValue; import org.jetbrains.kotlin.resolve.constants.NullValue; import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; +import org.jetbrains.kotlin.serialization.js.ast.JsAstDeserializer; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.utils.ExceptionUtilsKt; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.io.ByteArrayInputStream; +import java.util.*; import static org.jetbrains.kotlin.js.translate.general.ModuleWrapperTranslation.wrapIfNecessary; -import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor; import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatement; import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.toStringLiteralList; import static org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression; @@ -236,13 +239,13 @@ public final class Translation { @NotNull public static AstGenerationResult generateAst( @NotNull BindingTrace bindingTrace, - @NotNull Collection files, + @NotNull Collection units, @NotNull MainCallParameters mainCallParameters, @NotNull ModuleDescriptor moduleDescriptor, @NotNull JsConfig config ) throws TranslationException { try { - return doGenerateAst(bindingTrace, files, mainCallParameters, moduleDescriptor, config); + return doGenerateAst(bindingTrace, units, mainCallParameters, moduleDescriptor, config); } catch (UnsupportedOperationException e) { throw new UnsupportedFeatureException("Unsupported feature used.", e); @@ -255,7 +258,7 @@ public final class Translation { @NotNull private static AstGenerationResult doGenerateAst( @NotNull BindingTrace bindingTrace, - @NotNull Collection files, + @NotNull Collection units, @NotNull MainCallParameters mainCallParameters, @NotNull ModuleDescriptor moduleDescriptor, @NotNull JsConfig config @@ -265,23 +268,40 @@ public final class Translation { JsName internalModuleName = program.getScope().declareName("_"); Merger merger = new Merger(rootFunction, internalModuleName, moduleDescriptor); + Map fragmentMap = new HashMap(); List fragments = new ArrayList(); - for (KtFile file : files) { - StaticContext staticContext = new StaticContext(bindingTrace, config, moduleDescriptor); - TranslationContext context = TranslationContext.rootContext(staticContext); - translateFile(context, file); - fragments.add(staticContext.getFragment()); - merger.addFragment(staticContext.getFragment()); + + Map> fileMemberScopes = new HashMap>(); + + JsAstDeserializer deserializer = new JsAstDeserializer(program); + for (TranslationUnit unit : units) { + if (unit instanceof TranslationUnit.SourceFile) { + KtFile file = ((TranslationUnit.SourceFile) unit).getFile(); + StaticContext staticContext = new StaticContext(bindingTrace, config, moduleDescriptor); + TranslationContext context = TranslationContext.rootContext(staticContext); + List fileMemberScope = new ArrayList(); + translateFile(context, file, fileMemberScope); + fragments.add(staticContext.getFragment()); + fragmentMap.put(file, staticContext.getFragment()); + fileMemberScopes.put(file, fileMemberScope); + merger.addFragment(staticContext.getFragment()); + } + 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); + } } - JsProgramFragment testFragment = mayBeGenerateTests(files, config, bindingTrace, moduleDescriptor); + JsProgramFragment testFragment = mayBeGenerateTests(config, bindingTrace, moduleDescriptor); fragments.add(testFragment); merger.addFragment(testFragment); rootFunction.getParameters().add(new JsParameter(internalModuleName)); if (mainCallParameters.shouldBeGenerated()) { JsProgramFragment mainCallFragment = generateCallToMain( - bindingTrace, config, moduleDescriptor, files, mainCallParameters.arguments()); + bindingTrace, config, moduleDescriptor, mainCallParameters.arguments()); if (mainCallFragment != null) { fragments.add(mainCallFragment); merger.addFragment(mainCallFragment); @@ -313,7 +333,7 @@ public final class Translation { block.getStatements().addAll(wrapIfNecessary(config.getModuleId(), rootFunction, importedModuleList, program, config.getModuleKind())); - return new AstGenerationResult(program, internalModuleName, fragments, importedModuleList); + return new AstGenerationResult(program, internalModuleName, fragments, fragmentMap, fileMemberScopes, importedModuleList); } private static boolean isBuiltinModule(@NotNull List fragments) { @@ -327,12 +347,18 @@ public final class Translation { return false; } - private static void translateFile(@NotNull TranslationContext context, @NotNull KtFile file) { + private static void translateFile( + @NotNull TranslationContext context, + @NotNull KtFile file, + @NotNull List fileMemberScope + ) { FileDeclarationVisitor fileVisitor = new FileDeclarationVisitor(context); try { for (KtDeclaration declaration : file.getDeclarations()) { - if (!AnnotationsUtils.isPredefinedObject(BindingUtils.getDescriptorForElement(context.bindingContext(), declaration))) { + DeclarationDescriptor descriptor = BindingUtils.getDescriptorForElement(context.bindingContext(), declaration); + fileMemberScope.add(descriptor); + if (!AnnotationsUtils.isPredefinedObject(descriptor)) { declaration.accept(fileVisitor, context); } } @@ -359,13 +385,13 @@ public final class Translation { @NotNull private static JsProgramFragment mayBeGenerateTests( - @NotNull Collection files, @NotNull JsConfig config, @NotNull BindingTrace trace, + @NotNull JsConfig config, @NotNull BindingTrace trace, @NotNull ModuleDescriptor moduleDescriptor ) { StaticContext staticContext = new StaticContext(trace, config, moduleDescriptor); TranslationContext context = TranslationContext.rootContext(staticContext); JSTester tester = new QUnitTester(context); - JSTestGenerator.generateTestCalls(context, files, tester); + JSTestGenerator.generateTestCalls(context, moduleDescriptor, tester); return staticContext.getFragment(); } @@ -374,16 +400,15 @@ public final class Translation { @Nullable private static JsProgramFragment generateCallToMain( @NotNull BindingTrace trace, @NotNull JsConfig config, @NotNull ModuleDescriptor moduleDescriptor, - @NotNull Collection files, @NotNull List arguments + @NotNull List arguments ) { StaticContext staticContext = new StaticContext(trace, config, moduleDescriptor); TranslationContext context = TranslationContext.rootContext(staticContext); MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(context.bindingContext()); - KtNamedFunction mainFunction = mainFunctionDetector.getMainFunction(files); - if (mainFunction == null) { + FunctionDescriptor functionDescriptor = mainFunctionDetector.getMainFunction(moduleDescriptor); + if (functionDescriptor == null) { return null; } - FunctionDescriptor functionDescriptor = getFunctionDescriptor(context.bindingContext(), mainFunction); JsArrayLiteral argument = new JsArrayLiteral(toStringLiteralList(arguments, context.program())); JsExpression call = CallTranslator.INSTANCE.buildCall(context, functionDescriptor, Collections.singletonList(argument), null); context.addTopLevelStatement(call.makeStmt()); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/test/JSTestGenerator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/test/JSTestGenerator.java index 4018086185a..7d973b8e350 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/test/JSTestGenerator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/test/JSTestGenerator.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.js.translate.test; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; +import org.jetbrains.kotlin.descriptors.ModuleDescriptor; import org.jetbrains.kotlin.js.backend.ast.JsExpression; import org.jetbrains.kotlin.js.backend.ast.JsNew; import org.jetbrains.kotlin.js.backend.ast.JsStringLiteral; @@ -26,10 +27,8 @@ import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.general.JetTestFunctionDetector; import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator; -import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.resolve.DescriptorUtils; -import java.util.Collection; import java.util.Collections; import java.util.List; @@ -39,8 +38,9 @@ public final class JSTestGenerator { } public static void generateTestCalls(@NotNull TranslationContext context, - @NotNull Collection files, @NotNull JSTester tester) { - List functionDescriptors = JetTestFunctionDetector.getTestFunctionDescriptors(context.bindingContext(), files); + @NotNull ModuleDescriptor moduleDescriptor, @NotNull JSTester tester) { + List functionDescriptors = + JetTestFunctionDetector.getTestFunctionDescriptors(moduleDescriptor); doGenerateTestCalls(functionDescriptors, context, tester); } diff --git a/js/js.translator/testData/box/incremental/simple.kt b/js/js.translator/testData/box/incremental/simple.kt new file mode 100644 index 00000000000..37994b12504 --- /dev/null +++ b/js/js.translator/testData/box/incremental/simple.kt @@ -0,0 +1,12 @@ +// FILE: a.kt + +private fun bar(): String = "O" + +internal fun foo(): String = bar() + +fun baz(): String = "K" + +// FILE: b.kt +// RECOMPILE + +fun box(): String = foo() + baz() \ No newline at end of file