Support inlining internal functions to friend modules in JS BE

There's one exception: if an inline internal function calls
private function, this will fail at run time.
This commit is contained in:
Alexey Andreev
2017-07-25 13:59:20 +03:00
parent 41140d00b1
commit 918ce78c5a
8 changed files with 42 additions and 19 deletions
@@ -18,10 +18,11 @@ package org.jetbrains.kotlin.js.descriptorUtils
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.KotlinType
@@ -52,3 +53,14 @@ fun ClassDescriptor.hasPrimaryConstructor(): Boolean = unsubstitutedPrimaryConst
val DeclarationDescriptor.isCoroutineLambda: Boolean
get() = this is AnonymousFunctionDescriptor && isSuspend
fun DeclarationDescriptor.shouldBeExported(config: JsConfig): Boolean =
this !is DeclarationDescriptorWithVisibility || effectiveVisibility(visibility, true).shouldBeExported(config) ||
AnnotationsUtils.getJsNameAnnotation(this) != null
private fun EffectiveVisibility.shouldBeExported(config: JsConfig): Boolean {
if (publicApi) return true
if (config.configuration.getBoolean(JSConfigurationKeys.FRIEND_PATHS_DISABLED)) return false
return toVisibility() == Visibilities.INTERNAL
}
@@ -4754,6 +4754,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
doTest(fileName);
}
@TestMetadata("internalFriend.kt")
public void testInternalFriend() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineMultiModule/internalFriend.kt");
doTest(fileName);
}
@TestMetadata("internalNameClash.kt")
public void testInternalNameClash() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineMultiModule/internalNameClash.kt");
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.descriptors.annotations.isEffectivelyInlineOnly
import org.jetbrains.kotlin.js.backend.ast.*
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.descriptorUtils.shouldBeExported
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
@@ -147,13 +147,6 @@ internal class DeclarationExporter(val context: StaticContext) {
it.exportedTag = context.getTag(declaration)
}
private fun EffectiveVisibility.publicOrInternal(): Boolean {
if (publicApi) return true
if (context.config.configuration.getBoolean(JSConfigurationKeys.FRIEND_PATHS_DISABLED)) return false
return toVisibility() == Visibilities.INTERNAL
}
private fun MemberDescriptor.shouldBeExported(force: Boolean) =
force || effectiveVisibility(checkPublishedApi = true).publicOrInternal() || AnnotationsUtils.getJsNameAnnotation(this) != null
private fun MemberDescriptor.shouldBeExported(force: Boolean) = force || shouldBeExported(context.config)
}
@@ -50,6 +50,7 @@ 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.descriptorUtils.DescriptorUtilsKt.shouldBeExported;
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;
@@ -285,7 +286,7 @@ public class TranslationContext {
@NotNull
public JsName getInlineableInnerNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
JsName name;
if (inlineFunctionContext == null || !isPublicInlineFunction() ||
if (inlineFunctionContext == null || !isPublicInlineFunction() || !shouldBeExported(descriptor, getConfig()) ||
DescriptorUtils.isAncestor(inlineFunctionContext.getDescriptor(), descriptor, false)) {
name = getInnerNameForDescriptor(descriptor);
}
@@ -802,8 +803,7 @@ public class TranslationContext {
public boolean isPublicInlineFunction() {
if (inlineFunctionContext == null) return false;
CallableDescriptor function = inlineFunctionContext.getDescriptor();
return function.getVisibility().effectiveVisibility(function, true).getPublicApi();
return shouldBeExported(inlineFunctionContext.getDescriptor(), getConfig());
}
@Nullable
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor
import org.jetbrains.kotlin.js.backend.ast.metadata.functionDescriptor
import org.jetbrains.kotlin.js.backend.ast.metadata.hasDefaultValue
import org.jetbrains.kotlin.js.descriptorUtils.shouldBeExported
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.inline.util.FunctionWithWrapper
import org.jetbrains.kotlin.js.translate.context.Namer
@@ -35,7 +36,6 @@ import org.jetbrains.kotlin.psi.KtDeclarationWithBody
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
@@ -106,7 +106,7 @@ fun TranslationContext.wrapWithInlineMetadata(
function: JsFunction, descriptor: FunctionDescriptor
): JsExpression {
val sourceInfo = descriptor.source.getPsi()
return if (descriptor.isInline && descriptor.isEffectivelyPublicApi) {
return if (descriptor.isInline && descriptor.shouldBeExported(config)) {
val metadata = InlineMetadata.compose(function, descriptor, this)
val functionWithMetadata = metadata.functionWithMetadata(outerContext, sourceInfo)
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.js.backend.ast.JsName;
import org.jetbrains.kotlin.js.backend.ast.JsNameRef;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind;
import org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
@@ -109,7 +110,8 @@ public final class ReferenceTranslator {
}
private static boolean isLocallyAvailableDeclaration(@NotNull TranslationContext context, @NotNull DeclarationDescriptor descriptor) {
return context.isFromCurrentModule(descriptor) && !context.isPublicInlineFunction();
return context.isFromCurrentModule(descriptor) && !(context.isPublicInlineFunction() &&
DescriptorUtilsKt.shouldBeExported(descriptor, context.getConfig()));
}
@NotNull
@@ -9,7 +9,7 @@ package foo
// CHECK_HAS_INLINE_METADATA: apply
// CHECK_HAS_INLINE_METADATA: applyL_h43q6c$
// CHECK_HAS_INLINE_METADATA: applyM_h43q6c$
// CHECK_HAS_NO_INLINE_METADATA: applyN_h43q6c$
// CHECK_HAS_INLINE_METADATA: applyN_h43q6c$
// CHECK_HAS_NO_INLINE_METADATA: applyO_h43q6c$
inline
@@ -0,0 +1,10 @@
// EXPECTED_REACHABLE_NODES: 992
// MODULE: lib
// FILE: lib.kt
internal fun bar() = "OK"
internal inline fun foo() = bar()
// MODULE: main(lib)(lib)
// FILE: main.kt
fun box(): String = foo()