From 1df6f2f9a0d3a3f44de1c6f034cd1b342c03a578 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 31 May 2017 17:49:25 +0300 Subject: [PATCH] JS BE: expose imported modules via `$$importsForInline$$` property ... and use it as prefix to FQN in inline functions. This allows to properly inline function declared in module A to module B, when this function calls another function in module C. See KT-18201 --- .../kotlin/js/naming/NameSuggestion.kt | 45 ++++--- .../js/test/semantics/BoxJsTestGenerated.java | 30 +++++ .../translate/context/DeclarationExporter.kt | 16 +-- .../kotlin/js/translate/context/Namer.java | 2 + .../js/translate/context/StaticContext.java | 50 +++++-- .../translate/context/TranslationContext.java | 124 +++++++++++++++++- .../kotlin/js/translate/general/Merger.kt | 14 +- .../kotlin/js/translate/utils/utils.kt | 8 ++ .../box/crossModuleRef/inlineJsModule.kt | 25 ++++ .../crossModuleRef/inlineJsModulePackage.kt | 28 ++++ .../box/crossModuleRef/inlineModule.kt | 15 +++ .../inlineModuleNonIndentifier.kt | 15 +++ .../box/incremental/multipleReimport.kt | 24 ++++ 13 files changed, 346 insertions(+), 50 deletions(-) create mode 100644 js/js.translator/testData/box/crossModuleRef/inlineJsModule.kt create mode 100644 js/js.translator/testData/box/crossModuleRef/inlineJsModulePackage.kt create mode 100644 js/js.translator/testData/box/crossModuleRef/inlineModule.kt create mode 100644 js/js.translator/testData/box/crossModuleRef/inlineModuleNonIndentifier.kt create mode 100644 js/js.translator/testData/box/incremental/multipleReimport.kt diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt index 179e0dfda96..870fa579f81 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt @@ -349,25 +349,30 @@ class NameSuggestion { val first = name.first().let { if (it.isES5IdentifierStart()) it else '_' } return first.toString() + name.drop(1).map { if (it.isES5IdentifierPart()) it else '_' }.joinToString("") } - - // See ES 5.1 spec: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6 - private fun Char.isES5IdentifierStart() = - Character.isLetter(this) || // Lu | Ll | Lt | Lm | Lo - Character.getType(this).toByte() == Character.LETTER_NUMBER || - // Nl which is missing in Character.isLetter, but present in UnicodeLetter in spec - this == '_' || - this == '$' - - private fun Char.isES5IdentifierPart() = - isES5IdentifierStart() || - when (Character.getType(this).toByte()) { - Character.NON_SPACING_MARK, - Character.COMBINING_SPACING_MARK, - Character.DECIMAL_DIGIT_NUMBER, - Character.CONNECTOR_PUNCTUATION -> true - else -> false - } || - this == '\u200C' || // Zero-width non-joiner - this == '\u200D' // Zero-width joiner } } + +// See ES 5.1 spec: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6 +fun Char.isES5IdentifierStart() = + Character.isLetter(this) || // Lu | Ll | Lt | Lm | Lo + Character.getType(this).toByte() == Character.LETTER_NUMBER || + // Nl which is missing in Character.isLetter, but present in UnicodeLetter in spec + this == '_' || + this == '$' + +fun Char.isES5IdentifierPart() = + isES5IdentifierStart() || + when (Character.getType(this).toByte()) { + Character.NON_SPACING_MARK, + Character.COMBINING_SPACING_MARK, + Character.DECIMAL_DIGIT_NUMBER, + Character.CONNECTOR_PUNCTUATION -> true + else -> false + } || + this == '\u200C' || // Zero-width non-joiner + this == '\u200D' // Zero-width joiner + +fun String.isValidES5Identifier() = + isNotEmpty() && + first().isES5IdentifierStart() && + drop(1).all { it.isES5IdentifierPart() } \ No newline at end of file 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 b4365f2d5ec..8a8265015e5 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 @@ -821,6 +821,30 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("inlineJsModule.kt") + public void testInlineJsModule() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/crossModuleRef/inlineJsModule.kt"); + doTest(fileName); + } + + @TestMetadata("inlineJsModulePackage.kt") + public void testInlineJsModulePackage() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/crossModuleRef/inlineJsModulePackage.kt"); + doTest(fileName); + } + + @TestMetadata("inlineModule.kt") + public void testInlineModule() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/crossModuleRef/inlineModule.kt"); + doTest(fileName); + } + + @TestMetadata("inlineModuleNonIndentifier.kt") + public void testInlineModuleNonIndentifier() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/crossModuleRef/inlineModuleNonIndentifier.kt"); + doTest(fileName); + } + @TestMetadata("lambda.kt") public void testLambda() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/crossModuleRef/lambda.kt"); @@ -3497,6 +3521,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("multipleReimport.kt") + public void testMultipleReimport() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/incremental/multipleReimport.kt"); + doTest(fileName); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/incremental/simple.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt index 29506b3f129..5ccec026ff7 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt @@ -19,17 +19,13 @@ package org.jetbrains.kotlin.js.translate.context import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.isEffectivelyInlineOnly import org.jetbrains.kotlin.js.backend.ast.* -import org.jetbrains.kotlin.js.backend.ast.metadata.exportedPackage import org.jetbrains.kotlin.js.backend.ast.metadata.exportedTag import org.jetbrains.kotlin.js.backend.ast.metadata.staticRef import org.jetbrains.kotlin.js.config.JSConfigurationKeys -import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils +import org.jetbrains.kotlin.js.translate.utils.* import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isLibraryObject import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject -import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.assignment -import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils -import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -138,13 +134,9 @@ internal class DeclarationExporter(val context: StaticContext) { var name = localPackageNames[packageName] if (name == null) { name = JsScope.declareTemporaryName("package$" + packageName.shortName().asString()) - localPackageNames.put(packageName, name) - - val parentRef = getLocalPackageReference(packageName.parent()) - val selfRef = JsNameRef(packageName.shortName().asString(), parentRef) - val rhs = JsAstUtils.or(selfRef, assignment(selfRef.deepCopy(), JsObjectLiteral(false))) - - statements.add(JsAstUtils.newVar(name, rhs).apply { exportedPackage = packageName.asString() }) + localPackageNames[packageName] = name + statements += definePackageAlias(packageName.shortName().asString(), name, packageName.asString(), + getLocalPackageReference(packageName.parent())) } return name.makeRef() } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java index 1fd8c699dd4..4bee4d7851b 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java @@ -115,6 +115,8 @@ public final class Namer { public static final String ENUM_NAME_FIELD = "name$"; public static final String ENUM_ORDINAL_FIELD = "ordinal$"; + public static final String IMPORTS_FOR_INLINE_PROPERTY = "$$importsForInline$$"; + @NotNull public static String getFunctionTag(@NotNull CallableDescriptor functionDescriptor, @NotNull JsConfig config) { String intrinsicTag = ArrayFIF.INSTANCE.getTag(functionDescriptor, config); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index fa871aac83c..f75c5c2d241 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -53,7 +53,10 @@ import org.jetbrains.kotlin.serialization.js.ModuleKind; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import static org.jetbrains.kotlin.js.config.JsConfig.UNKNOWN_EXTERNAL_MODULE_NAME; import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isLibraryObject; @@ -135,6 +138,9 @@ public final class StaticContext { @NotNull private final ClassModelGenerator classModelGenerator; + @Nullable + private JsName nameForImportsForInline; + public StaticContext( @NotNull BindingTrace bindingTrace, @NotNull JsConfig config, @@ -230,6 +236,11 @@ public final class StaticContext { return fqn.deepCopy(); } + @Nullable + public SuggestedName suggestName(@NotNull DeclarationDescriptor descriptor) { + return nameSuggestion.suggest(descriptor); + } + @NotNull private JsExpression buildQualifiedExpression(@NotNull DeclarationDescriptor descriptor) { if (descriptor instanceof ClassDescriptor) { @@ -265,7 +276,7 @@ public final class StaticContext { } } - SuggestedName suggested = nameSuggestion.suggest(descriptor); + SuggestedName suggested = suggestName(descriptor); if (suggested == null) { ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor); JsExpression result = getModuleExpressionFor(module); @@ -686,14 +697,7 @@ public final class StaticContext { if (currentModule == module) { return rootScope.declareName(Namer.getRootPackageName()); } - String moduleName; - if (module == module.getBuiltIns().getBuiltInsModule()) { - moduleName = Namer.KOTLIN_LOWER_NAME; - } - else { - moduleName = module.getName().asString(); - moduleName = moduleName.substring(1, moduleName.length() - 1); - } + String moduleName = suggestModuleName(module); if (UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) return null; @@ -701,7 +705,18 @@ public final class StaticContext { } @NotNull - private JsImportedModule getImportedModule(@NotNull String baseName, @Nullable DeclarationDescriptor descriptor) { + public static String suggestModuleName(@NotNull ModuleDescriptor module) { + if (module == module.getBuiltIns().getBuiltInsModule()) { + return Namer.KOTLIN_LOWER_NAME; + } + else { + String moduleName = module.getName().asString(); + return moduleName.substring(1, moduleName.length() - 1); + } + } + + @NotNull + public JsImportedModule getImportedModule(@NotNull String baseName, @Nullable DeclarationDescriptor descriptor) { String plainName = descriptor != null && config.getModuleKind() == ModuleKind.UMD ? getPlainId(descriptor) : null; JsImportedModuleKey key = new JsImportedModuleKey(baseName, plainName); @@ -785,4 +800,17 @@ public final class StaticContext { String tag = Namer.getFunctionTag(descriptor, config); fragment.getInlineModuleMap().put(tag, getModuleExpressionFor(descriptor)); } + + @NotNull + public JsName getNameForImportsForInline() { + if (nameForImportsForInline == null) { + JsName name = JsScope.declareTemporaryName(Namer.IMPORTS_FOR_INLINE_PROPERTY); + fragment.getNameBindings().add(new JsNameBinding(Namer.IMPORTS_FOR_INLINE_PROPERTY, name)); + nameForImportsForInline = name; + return name; + } + else { + return nameForImportsForInline; + } + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java index b6df667e0e5..e2c95f76296 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java @@ -26,9 +26,14 @@ import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor; import org.jetbrains.kotlin.incremental.components.NoLookupLocation; import org.jetbrains.kotlin.js.backend.ast.*; import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; +import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind; import org.jetbrains.kotlin.js.config.JsConfig; +import org.jetbrains.kotlin.js.naming.NameSuggestionKt; +import org.jetbrains.kotlin.js.naming.SuggestedName; import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics; +import org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator; import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator; +import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.name.Name; @@ -38,12 +43,15 @@ import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver; +import org.jetbrains.kotlin.serialization.js.ModuleKind; import java.util.*; import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt.isCoroutineLambda; import static org.jetbrains.kotlin.js.translate.context.UsageTrackerKt.getNameForCapturedDescriptor; +import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject; import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForElement; +import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn; /** * All the info about the state of the translation process. @@ -66,6 +74,8 @@ public class TranslationContext { @Nullable private final VariableDescriptor continuationParameterDescriptor; + private final Set modulesImportedForInline = new HashSet<>(); + @NotNull public static TranslationContext rootContext(@NotNull StaticContext staticContext) { DynamicContext rootDynamicContext = DynamicContext.rootContext( @@ -265,15 +275,121 @@ public class TranslationContext { @NotNull public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) { - if (descriptor instanceof MemberDescriptor && isFromCurrentModule(descriptor) && isPublicInlineFunction()) { - staticContext.export((MemberDescriptor) descriptor, true); + JsNameRef result = staticContext.getQualifiedReference(descriptor); + if (isPublicInlineFunction()) { + if (isFromCurrentModule(descriptor)) { + if (descriptor instanceof MemberDescriptor) { + staticContext.export((MemberDescriptor) descriptor, true); + } + } + else { + ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor); + ModuleDescriptor currentModule = staticContext.getCurrentModule(); + if (module != currentModule && !isInlineFunction(descriptor)) { + result = exportModuleForInline(currentModule, module, result); + } + } + } + return result; + } + + private boolean isInlineFunction(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof CallableDescriptor)) return false; + return CallExpressionTranslator.shouldBeInlined((CallableDescriptor) descriptor, this); + } + + @NotNull + private JsNameRef exportModuleForInline( + @NotNull ModuleDescriptor currentModule, @NotNull ModuleDescriptor module, + @NotNull JsNameRef fqn + ) { + if (currentModule.getBuiltIns().getBuiltInsModule() == module) return fqn; + + String moduleName = StaticContext.suggestModuleName(module); + if (moduleName.equals(Namer.KOTLIN_LOWER_NAME)) return fqn; + + return exportModuleForInline(currentModule, moduleName, staticContext.getInnerNameForDescriptor(module), fqn); + } + + private JsNameRef exportModuleForInline( + @NotNull ModuleDescriptor currentModule, + @NotNull String moduleId, @NotNull JsName moduleName, + @NotNull JsNameRef fqn) { + JsExpression currentModuleRef = pureFqn(staticContext.getInnerNameForDescriptor(currentModule), null); + JsExpression importsRef = pureFqn(Namer.IMPORTS_FOR_INLINE_PROPERTY, currentModuleRef); + JsExpression currentImports = pureFqn(staticContext.getNameForImportsForInline(), null); + + JsExpression moduleRef; + JsExpression lhsModuleRef; + if (NameSuggestionKt.isValidES5Identifier(moduleId)) { + moduleRef = pureFqn(moduleId, importsRef); + lhsModuleRef = pureFqn(moduleId, currentImports); + } + else { + moduleRef = new JsArrayAccess(importsRef, new JsStringLiteral(moduleId)); + MetadataProperties.setSideEffects(moduleRef, SideEffectKind.PURE); + lhsModuleRef = new JsArrayAccess(currentImports, new JsStringLiteral(moduleId)); + } + + fqn = (JsNameRef) replaceModuleReference(fqn, moduleName, moduleRef); + + if (modulesImportedForInline.add(moduleId)) { + JsExpressionStatement importStmt = new JsExpressionStatement(JsAstUtils.assignment(lhsModuleRef, moduleName.makeRef())); + MetadataProperties.setExportedTag(importStmt, "imports:" + moduleId); + staticContext.getFragment().getExportBlock().getStatements().add(importStmt); + } + + return fqn; + } + + private static JsExpression replaceModuleReference( + @NotNull JsExpression expression, + @NotNull JsName expectedModuleName, + @NotNull JsExpression reexportExpr + ) { + if (expression instanceof JsNameRef) { + JsNameRef nameRef = (JsNameRef) expression; + if (nameRef.getQualifier() == null) { + return expectedModuleName == nameRef.getName() ? reexportExpr : expression; + } + else { + JsExpression newQualifier = replaceModuleReference(nameRef.getQualifier(), expectedModuleName, reexportExpr); + if (newQualifier == nameRef.getQualifier()) { + return expression; + } + JsExpression result = nameRef.getName() != null ? + new JsNameRef(nameRef.getName(), newQualifier) : + new JsNameRef(nameRef.getIdent(), newQualifier); + result.copyMetadataFrom(nameRef); + return result; + } + } + else { + return expression; } - return staticContext.getQualifiedReference(descriptor); } @NotNull public JsNameRef getInnerReference(@NotNull DeclarationDescriptor descriptor) { - return JsAstUtils.pureFqn(getInnerNameForDescriptor(descriptor), null); + JsNameRef result = pureFqn(getInnerNameForDescriptor(descriptor), null); + + SuggestedName suggested = staticContext.suggestName(descriptor); + if (suggested != null && getConfig().getModuleKind() != ModuleKind.PLAIN && isPublicInlineFunction()) { + String moduleId = AnnotationsUtils.getModuleName(suggested.getDescriptor()); + if (moduleId != null && result.getName() != null && result.getQualifier() == null) { + result = exportModuleForInline(getCurrentModule(), moduleId, result.getName(), result); + } + else if (isNativeObject(suggested.getDescriptor()) && DescriptorUtils.isTopLevelDeclaration(suggested.getDescriptor())) { + String fileModuleId = AnnotationsUtils.getFileModuleName(bindingContext(), suggested.getDescriptor()); + if (fileModuleId != null) { + JsName fileModuleName = staticContext.getImportedModule(fileModuleId, null).getInternalName(); + result = exportModuleForInline(getCurrentModule(), fileModuleId, fileModuleName, + staticContext.getQualifiedReference(descriptor)); + } + } + } + + return result; } @NotNull diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt index 289222488d8..8770fa27106 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.exportedPackage import org.jetbrains.kotlin.js.backend.ast.metadata.exportedTag import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.utils.JsAstUtils +import org.jetbrains.kotlin.js.translate.utils.definePackageAlias class Merger(private val rootFunction: JsFunction, val internalModuleName: JsName, val module: ModuleDescriptor) { // Maps unique signature (see generateSignature) to names @@ -134,8 +135,8 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam fragment.inlineModuleMap.forEach { (_, value) -> rename(value) } } - private fun Map.rename(node: T): T { - node.accept(object : RecursiveJsVisitor() { + private fun Map.rename(rootNode: T): T { + rootNode.accept(object : RecursiveJsVisitor() { override fun visitElement(node: JsNode) { super.visitElement(node) if (node is HasName) { @@ -152,12 +153,13 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam } } }) - return node + return rootNode } // Adds different boilerplate code (like imports, class prototypes, etc) to resulting program. fun merge() { rootFunction.body.statements.apply { + addImportForInlineDeclarationIfNecessary() this += importBlock.statements addClassPrototypes(this) this += declarationBlock.statements @@ -167,6 +169,12 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam } } + private fun MutableList.addImportForInlineDeclarationIfNecessary() { + val importsForInlineName = nameTable[Namer.IMPORTS_FOR_INLINE_PROPERTY] ?: return + this += definePackageAlias(Namer.IMPORTS_FOR_INLINE_PROPERTY, importsForInlineName, Namer.IMPORTS_FOR_INLINE_PROPERTY, + JsNameRef(Namer.getRootPackageName())) + } + private fun addClassPrototypes(statements: MutableList) { val visited = mutableSetOf() for (cls in classes.keys) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt index 256291a9a5c..9c9c20d4456 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.metadata.CoroutineMetadata import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata +import org.jetbrains.kotlin.js.backend.ast.metadata.exportedPackage import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed @@ -193,3 +194,10 @@ fun JsFunction.fillCoroutineMetadata( psiElement = descriptor.source.getPsi() ) } + +fun definePackageAlias(name: String, varName: JsName, tag: String, parentRef: JsExpression): JsStatement { + val selfRef = JsNameRef(name, parentRef) + val rhs = JsAstUtils.or(selfRef, JsAstUtils.assignment(selfRef.deepCopy(), JsObjectLiteral(false))) + + return JsAstUtils.newVar(varName, rhs).apply { exportedPackage = tag } +} diff --git a/js/js.translator/testData/box/crossModuleRef/inlineJsModule.kt b/js/js.translator/testData/box/crossModuleRef/inlineJsModule.kt new file mode 100644 index 00000000000..a75e2df52dd --- /dev/null +++ b/js/js.translator/testData/box/crossModuleRef/inlineJsModule.kt @@ -0,0 +1,25 @@ +// EXPECTED_REACHABLE_NODES: 547 +// MODULE: lib1 +// FILE: lib1.js +define("lib1", [], function() { + return function() { + return "OK"; + } +}) + +// MODULE: lib2(lib1) +// FILE: lib2.kt +// MODULE_KIND: AMD +@JsModule("lib1") +external fun foo(): String + +// MODULE: lib3(lib2) +// FILE: lib3.kt +// MODULE_KIND: AMD +inline fun bar() = foo() + +// MODULE: main(lib3) +// FILE: main.kt +// MODULE_KIND: AMD + +fun box() = bar() \ No newline at end of file diff --git a/js/js.translator/testData/box/crossModuleRef/inlineJsModulePackage.kt b/js/js.translator/testData/box/crossModuleRef/inlineJsModulePackage.kt new file mode 100644 index 00000000000..39e871d4751 --- /dev/null +++ b/js/js.translator/testData/box/crossModuleRef/inlineJsModulePackage.kt @@ -0,0 +1,28 @@ +// EXPECTED_REACHABLE_NODES: 490 +// MODULE: lib1 +// FILE: lib1.js +define("lib1", [], function() { + return { + foo: function() { + return "OK"; + } + }; +}) + +// MODULE: lib2(lib1) +// FILE: lib2.kt +// MODULE_KIND: AMD +@file:JsModule("lib1") + +external fun foo(): String + +// MODULE: lib3(lib2) +// FILE: lib3.kt +// MODULE_KIND: AMD +inline fun bar() = foo() + +// MODULE: main(lib3) +// FILE: main.kt +// MODULE_KIND: AMD + +fun box() = bar() \ No newline at end of file diff --git a/js/js.translator/testData/box/crossModuleRef/inlineModule.kt b/js/js.translator/testData/box/crossModuleRef/inlineModule.kt new file mode 100644 index 00000000000..613fb52f40f --- /dev/null +++ b/js/js.translator/testData/box/crossModuleRef/inlineModule.kt @@ -0,0 +1,15 @@ +// EXPECTED_REACHABLE_NODES: 489 +// MODULE: lib1 +// FILE: lib1.kt + +fun foo() = "OK" + +// MODULE: lib2(lib1) +// FILE: lib2.kt + +inline fun bar() = foo() + +// MODULE: main(lib1, lib2) +// FILE: main.kt + +fun box() = bar() \ No newline at end of file diff --git a/js/js.translator/testData/box/crossModuleRef/inlineModuleNonIndentifier.kt b/js/js.translator/testData/box/crossModuleRef/inlineModuleNonIndentifier.kt new file mode 100644 index 00000000000..33239bd716e --- /dev/null +++ b/js/js.translator/testData/box/crossModuleRef/inlineModuleNonIndentifier.kt @@ -0,0 +1,15 @@ +// EXPECTED_REACHABLE_NODES: 489 +// MODULE: 1 +// FILE: lib1.kt + +fun foo() = "OK" + +// MODULE: 2(1) +// FILE: lib2.kt + +inline fun bar() = foo() + +// MODULE: main(2) +// FILE: main.kt + +fun box() = bar() \ No newline at end of file diff --git a/js/js.translator/testData/box/incremental/multipleReimport.kt b/js/js.translator/testData/box/incremental/multipleReimport.kt new file mode 100644 index 00000000000..ebf223b0fa8 --- /dev/null +++ b/js/js.translator/testData/box/incremental/multipleReimport.kt @@ -0,0 +1,24 @@ +// EXPECTED_REACHABLE_NODES: 491 +// MODULE: lib1 +// FILE: lib1.kt + +fun foo() = "O" + +fun bar() = "K" + +// MODULE: lib2(lib1) +// FILE: lib2a.kt +// PROPERTY_WRITE_COUNT: name=lib1 count=1 +// PROPERTY_WRITE_COUNT: name=$$imports$$ count=1 + +inline fun o() = foo() + +// FILE: lib2b.kt + +inline fun k() = bar() + +// MODULE: main(lib2) +// FILE: main.kt +// RECOMPILE + +fun box() = o() + k() \ No newline at end of file