[JS] JsExport diagnostics and legacy support
Account for JsExport in legacy backend namer. It means we catch overloaded exported function conflicts for free! Add error diagnostics: * NESTED_JS_EXPORT (Fixes KT-36798) * WRONG_EXPORTED_DECLARATION (Part of the fix for KT-37752) * NON_EXPORTABLE_TYPE (Fixes KT-37771)
This commit is contained in:
@@ -167,6 +167,7 @@ class K2JSTranslator @JvmOverloads constructor(
|
||||
reporter,
|
||||
config,
|
||||
analysisResult.bindingTrace,
|
||||
bindingTrace.bindingContext,
|
||||
translationResult
|
||||
).process()
|
||||
if (hasError(diagnostics)) return TranslationResult.Fail(diagnostics)
|
||||
|
||||
@@ -34,12 +34,12 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.TypeCheck;
|
||||
import org.jetbrains.kotlin.js.config.JsConfig;
|
||||
import org.jetbrains.kotlin.js.naming.NameSuggestion;
|
||||
import org.jetbrains.kotlin.js.naming.SuggestedName;
|
||||
import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices;
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.ArrayFIF;
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils;
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -57,8 +57,8 @@ public final class Namer {
|
||||
public static final String KOTLIN_NAME = KotlinLanguage.NAME;
|
||||
public static final String KOTLIN_LOWER_NAME = KOTLIN_NAME.toLowerCase();
|
||||
|
||||
public static final String EQUALS_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatformAnalyzerServices.INSTANCE.getBuiltIns().getAny(), "equals");
|
||||
public static final String COMPARE_TO_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatformAnalyzerServices.INSTANCE.getBuiltIns().getComparable(), "compareTo");
|
||||
public static final String EQUALS_METHOD_NAME = "equals";
|
||||
public static final String COMPARE_TO_METHOD_NAME = "compareTo_11rb$";
|
||||
public static final String LONG_FROM_NUMBER = "fromNumber";
|
||||
public static final String LONG_TO_NUMBER = "toNumber";
|
||||
public static final String LONG_FROM_INT = "fromInt";
|
||||
@@ -134,7 +134,11 @@ public final class Namer {
|
||||
public static final String SAM_FIELD_NAME = "function$";
|
||||
|
||||
@NotNull
|
||||
public static String getFunctionTag(@NotNull CallableDescriptor functionDescriptor, @NotNull JsConfig config) {
|
||||
public static String getFunctionTag(
|
||||
@NotNull CallableDescriptor functionDescriptor,
|
||||
@NotNull JsConfig config,
|
||||
@NotNull BindingContext bindingContext
|
||||
) {
|
||||
String intrinsicTag = ArrayFIF.INSTANCE.getTag(functionDescriptor, config);
|
||||
if (intrinsicTag != null) return intrinsicTag;
|
||||
|
||||
@@ -147,7 +151,7 @@ public final class Namer {
|
||||
qualifier = fqNameParent.asString();
|
||||
}
|
||||
|
||||
SuggestedName suggestedName = new NameSuggestion().suggest(functionDescriptor);
|
||||
SuggestedName suggestedName = new NameSuggestion(bindingContext).suggest(functionDescriptor);
|
||||
assert suggestedName != null : "Suggested name can be null only for module descriptors: " + functionDescriptor;
|
||||
String mangledName = suggestedName.getNames().get(0);
|
||||
return StringUtil.join(Arrays.asList(moduleName, qualifier, mangledName), ".");
|
||||
@@ -225,18 +229,6 @@ public final class Namer {
|
||||
callSetProperty = kotlin("callSetter");
|
||||
}
|
||||
|
||||
// TODO: get rid of this function
|
||||
@NotNull
|
||||
private static String getStableMangledNameForDescriptor(@NotNull ClassDescriptor descriptor, @NotNull String functionName) {
|
||||
Collection<? extends SimpleFunctionDescriptor> functions = descriptor.getDefaultType().getMemberScope().getContributedFunctions(
|
||||
Name.identifier(functionName), NoLookupLocation.FROM_BACKEND
|
||||
);
|
||||
assert functions.size() == 1 : "Can't select a single function: " + functionName + " in " + descriptor;
|
||||
SuggestedName suggested = new NameSuggestion().suggest(functions.iterator().next());
|
||||
assert suggested != null : "Suggested name for class members is always non-null: " + functions.iterator().next();
|
||||
return suggested.getNames().get(0);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsNameRef kotlin(@NotNull JsName name) {
|
||||
return pureFqn(name, kotlinObject());
|
||||
|
||||
@@ -111,7 +111,7 @@ public final class StaticContext {
|
||||
private final JsImportedModule currentModuleAsImported;
|
||||
|
||||
@NotNull
|
||||
private final NameSuggestion nameSuggestion = new NameSuggestion();
|
||||
private final NameSuggestion nameSuggestion;
|
||||
|
||||
@NotNull
|
||||
private final Map<DeclarationDescriptor, JsName> nameCache = new HashMap<>();
|
||||
@@ -169,6 +169,7 @@ public final class StaticContext {
|
||||
fragment = new JsProgramFragment(rootFunction.getScope(), packageFqn);
|
||||
|
||||
this.bindingTrace = bindingTrace;
|
||||
this.nameSuggestion = new NameSuggestion(bindingTrace.getBindingContext());
|
||||
this.namer = Namer.newInstance(program.getRootScope());
|
||||
this.intrinsics = new Intrinsics();
|
||||
this.rootScope = fragment.getScope();
|
||||
@@ -801,7 +802,7 @@ public final class StaticContext {
|
||||
|
||||
public void addInlineCall(@NotNull CallableDescriptor descriptor) {
|
||||
descriptor = (CallableDescriptor) JsDescriptorUtils.findRealInlineDeclaration(descriptor);
|
||||
String tag = Namer.getFunctionTag(descriptor, config);
|
||||
String tag = Namer.getFunctionTag(descriptor, config, getBindingContext());
|
||||
JsExpression moduleExpression = exportModuleForInline(DescriptorUtils.getContainingModule(descriptor));
|
||||
if (moduleExpression == null) {
|
||||
moduleExpression = getModuleExpressionFor(descriptor);
|
||||
|
||||
+2
-2
@@ -179,13 +179,13 @@ public class TranslationContext {
|
||||
@NotNull
|
||||
public TranslationContext newFunctionBodyWithUsageTracker(@NotNull JsFunction fun, @NotNull MemberDescriptor descriptor) {
|
||||
DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
|
||||
UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor);
|
||||
UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, bindingContext());
|
||||
return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker, descriptor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TranslationContext innerWithUsageTracker(@NotNull MemberDescriptor descriptor) {
|
||||
UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor);
|
||||
UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, bindingContext());
|
||||
return new TranslationContext(this, staticContext, dynamicContext, aliasingContext.inner(), usageTracker, descriptor);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.js.backend.ast.JsScope
|
||||
import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor
|
||||
import org.jetbrains.kotlin.js.descriptorUtils.isCoroutineLambda
|
||||
import org.jetbrains.kotlin.js.naming.NameSuggestion
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils.*
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||
@@ -30,7 +31,8 @@ private val CAPTURED_RECEIVER_NAME_PREFIX : String = "this$"
|
||||
|
||||
class UsageTracker(
|
||||
private val parent: UsageTracker?,
|
||||
val containingDescriptor: MemberDescriptor
|
||||
val containingDescriptor: MemberDescriptor,
|
||||
val bindingContext: BindingContext
|
||||
) {
|
||||
|
||||
private val captured = linkedMapOf<DeclarationDescriptor, JsName>()
|
||||
@@ -175,7 +177,7 @@ class UsageTracker(
|
||||
|
||||
// Append 'closure$' prefix to avoid name clash between closure and member fields in case of local classes
|
||||
else -> {
|
||||
val mangled = NameSuggestion.sanitizeName(NameSuggestion().suggest(this)!!.names.last())
|
||||
val mangled = NameSuggestion.sanitizeName(NameSuggestion(bindingContext).suggest(this)!!.names.last())
|
||||
"closure\$$mangled"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class InlineMetadata(val tag: JsStringLiteral, val function: FunctionWithWrapper
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun compose(function: JsFunction, descriptor: CallableDescriptor, context: TranslationContext): InlineMetadata {
|
||||
val tag = JsStringLiteral(Namer.getFunctionTag(descriptor, context.config))
|
||||
val tag = JsStringLiteral(Namer.getFunctionTag(descriptor, context.config, context.bindingContext()))
|
||||
val inliningContext = context.inlineFunctionContext!!
|
||||
val block = JsBlock(inliningContext.importBlock.statements + inliningContext.prototypeBlock.statements +
|
||||
inliningContext.declarationsBlock.statements + JsReturn(function))
|
||||
|
||||
+3
-4
@@ -1,16 +1,15 @@
|
||||
// SKIP_MINIFICATION
|
||||
|
||||
// Exported declaration uses non-exportable return type: Char
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
@JsName("foo")
|
||||
@JsExport
|
||||
fun foo(): Char = '1'
|
||||
|
||||
@JsExport
|
||||
val p1: Char = '2'
|
||||
|
||||
@JsExport
|
||||
var p2: Char = '3'
|
||||
|
||||
@JsExport
|
||||
var p3: Char = '4'
|
||||
get() = field + 1
|
||||
set(value) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1276
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
@JsExport
|
||||
class A(val x: Char)
|
||||
|
||||
fun typeOf(x: dynamic): String = js("typeof x")
|
||||
|
||||
@@ -4,7 +4,6 @@ interface I {
|
||||
val a: Char
|
||||
}
|
||||
|
||||
@JsExport
|
||||
object X : I {
|
||||
override var a = '#'
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1289
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
@JsExport
|
||||
open class A {
|
||||
val foo: Char
|
||||
get() = 'X'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1282
|
||||
|
||||
@JsExport
|
||||
interface I {
|
||||
fun ok(): String
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ class A {
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
val A.z: Int
|
||||
@JsName("getZ_") get() = 42
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
|
||||
@file:JsExport
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
|
||||
@file:JsExport
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
|
||||
// FILE: file1.kt
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
namespace foo {
|
||||
const _any: any;
|
||||
const _unit: void;
|
||||
function _nothing(): never
|
||||
const _throwable: Error;
|
||||
const _string: string;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
|
||||
@file:JsExport
|
||||
|
||||
@@ -8,8 +9,6 @@ package foo
|
||||
|
||||
val _any: Any = Any()
|
||||
|
||||
val _unit: Unit = Unit
|
||||
|
||||
fun _nothing(): Nothing { throw Throwable() }
|
||||
|
||||
val _throwable: Throwable = Throwable()
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
|
||||
// FILE: file1.kt
|
||||
package foo
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
|
||||
// TODO fix statics export in DCE-driven mode
|
||||
// SKIP_DCE_DRIVEN
|
||||
|
||||
Reference in New Issue
Block a user