KT-2752: fixes after code review
This commit is contained in:
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.calls.results.isSignatureNotLessSpecific
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.getTypeAliasConstructors
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasLowPriorityInOverloadResolution
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.varargParameterPosition
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -80,14 +81,10 @@ class OverloadChecker(val specificityComparator: TypeSpecificityComparator) {
|
||||
EXTENSION_PROPERTY
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.isExtensionProperty() =
|
||||
this is PropertyDescriptor &&
|
||||
extensionReceiverParameter != null
|
||||
|
||||
private fun getDeclarationCategory(a: DeclarationDescriptor): DeclarationCategory =
|
||||
when (a) {
|
||||
is PropertyDescriptor ->
|
||||
if (a.isExtensionProperty())
|
||||
if (a.isExtensionProperty)
|
||||
DeclarationCategory.EXTENSION_PROPERTY
|
||||
else
|
||||
DeclarationCategory.TYPE_OR_VALUE
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package foo
|
||||
|
||||
class <!JS_NAME_CLASH!>A(val x: Int)<!>
|
||||
|
||||
<!JS_NAME_CLASH!>fun A()<!> {}
|
||||
@@ -0,0 +1,13 @@
|
||||
package
|
||||
|
||||
package foo {
|
||||
public fun A(): kotlin.Unit
|
||||
|
||||
public final class A {
|
||||
public constructor A(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -12,7 +12,7 @@ class C : A, B {
|
||||
<!JS_NAME_CLASH!>override fun g()<!> {}
|
||||
}
|
||||
|
||||
abstract class <!JS_NAME_CLASH_SYNTHETIC!>D<!> : A, B
|
||||
abstract class <!JS_FAKE_NAME_CLASH!>D<!> : A, B
|
||||
|
||||
open class E {
|
||||
open fun f() {}
|
||||
@@ -20,4 +20,4 @@ open class E {
|
||||
open fun g() {}
|
||||
}
|
||||
|
||||
class <!JS_NAME_CLASH_SYNTHETIC!>F<!> : E(), A, B
|
||||
class <!JS_FAKE_NAME_CLASH!>F<!> : E(), A, B
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ class A {
|
||||
set(value) {}
|
||||
|
||||
<!JS_NAME_IS_NOT_ON_ALL_ACCESSORS!>var y: Int<!>
|
||||
@JsName("get_y") get() = 23
|
||||
set(value) {}
|
||||
get() = 23
|
||||
@JsName("set_y") set(value) {}
|
||||
|
||||
var z: Int
|
||||
@JsName("get_z") get() = 23
|
||||
|
||||
@@ -295,6 +295,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithJsStdLib/name"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("classAndFunction.kt")
|
||||
public void testClassAndFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/classAndFunction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("classLevelMethodAndProperty.kt")
|
||||
public void testClassLevelMethodAndProperty() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/classLevelMethodAndProperty.kt");
|
||||
|
||||
@@ -322,5 +322,16 @@ private fun ClassDescriptor.getAllSuperClassesTypesIncludeItself(): List<KotlinT
|
||||
return result
|
||||
}
|
||||
|
||||
fun FunctionDescriptor.isEnumValueOfMethod(): Boolean {
|
||||
val methodTypeParameters = valueParameters
|
||||
val nullableString = TypeUtils.makeNullable(builtIns.stringType)
|
||||
return DescriptorUtils.ENUM_VALUE_OF == name
|
||||
&& methodTypeParameters.size == 1
|
||||
&& KotlinTypeChecker.DEFAULT.isSubtypeOf(methodTypeParameters[0].type, nullableString)
|
||||
}
|
||||
|
||||
val DeclarationDescriptor.isExtensionProperty: Boolean
|
||||
get() = this is PropertyDescriptor && extensionReceiverParameter != null
|
||||
|
||||
fun ClassDescriptor.getAllSuperclassesWithoutAny() =
|
||||
generateSequence(getSuperClassNotAny(), ClassDescriptor::getSuperClassNotAny).toCollection(SmartList<ClassDescriptor>())
|
||||
|
||||
@@ -189,16 +189,18 @@ class NameSuggestion {
|
||||
overriddenDescriptor is CallableDescriptor && stable -> {
|
||||
getStableMangledName(baseName, getArgumentTypesAsString(overriddenDescriptor))
|
||||
}
|
||||
shouldMangleUnstable(overriddenDescriptor) -> {
|
||||
val ownerName = descriptor.containingDeclaration!!.fqNameUnsafe.asString()
|
||||
getStableMangledName(baseName, ownerName + ":" + getArgumentTypesAsString(overriddenDescriptor as CallableDescriptor))
|
||||
}
|
||||
shouldMangleUnstable(overriddenDescriptor) -> getPrivateMangledName(baseName, overriddenDescriptor as CallableDescriptor)
|
||||
else -> baseName
|
||||
}
|
||||
|
||||
return Pair(finalName, stable)
|
||||
}
|
||||
|
||||
@JvmStatic fun getPrivateMangledName(baseName: String, descriptor: CallableDescriptor): String {
|
||||
val ownerName = descriptor.containingDeclaration.fqNameUnsafe.asString()
|
||||
return getStableMangledName(baseName, ownerName + ":" + getArgumentTypesAsString(descriptor))
|
||||
}
|
||||
|
||||
private fun getArgumentTypesAsString(descriptor: CallableDescriptor): String {
|
||||
val argTypes = StringBuilder()
|
||||
|
||||
@@ -226,7 +228,7 @@ class NameSuggestion {
|
||||
return false
|
||||
}
|
||||
|
||||
fun getStableMangledName(suggestedName: String, forCalculateId: String): String {
|
||||
@JvmStatic fun getStableMangledName(suggestedName: String, forCalculateId: String): String {
|
||||
val suffix = if (forCalculateId.isEmpty()) "" else "_${mangledId(forCalculateId)}\$"
|
||||
return suggestedName + suffix
|
||||
}
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
|
||||
put(ErrorsJs.NATIVE_INNER_CLASS_PROHIBITED, "Native inner classes are prohibited")
|
||||
put(ErrorsJs.JS_NAME_CLASH, "JavaScript name ({0}) generated for this declaration clashes with another declaration: {1}",
|
||||
Renderers.STRING, Renderers.COMPACT)
|
||||
put(ErrorsJs.JS_NAME_CLASH_SYNTHETIC, "JavaScript name {0} is generated from two different inherited members: {1} and {2}",
|
||||
put(ErrorsJs.JS_FAKE_NAME_CLASH, "JavaScript name {0} is generated for different inherited members: {1} and {2}",
|
||||
Renderers.STRING, Renderers.COMPACT, Renderers.COMPACT)
|
||||
put(ErrorsJs.JS_NAME_ON_PRIMARY_CONSTRUCTOR_PROHIBITED, "@JsName annotation is prohibited for primary constructors")
|
||||
put(ErrorsJs.JS_NAME_ON_ACCESSOR_AND_PROPERTY, "@JsName can be either on a property or its accessors, not both of them")
|
||||
|
||||
@@ -44,7 +44,7 @@ public interface ErrorsJs {
|
||||
DiagnosticFactory0<KtExpression> NATIVE_INNER_CLASS_PROHIBITED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory2<KtElement, String, DeclarationDescriptor> JS_NAME_CLASH = DiagnosticFactory2.create(
|
||||
ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
DiagnosticFactory3<KtElement, String, DeclarationDescriptor, DeclarationDescriptor> JS_NAME_CLASH_SYNTHETIC =
|
||||
DiagnosticFactory3<KtElement, String, DeclarationDescriptor, DeclarationDescriptor> JS_FAKE_NAME_CLASH =
|
||||
DiagnosticFactory3.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
DiagnosticFactory0<PsiElement> JS_NAME_ON_PRIMARY_CONSTRUCTOR_PROHIBITED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> JS_NAME_ON_ACCESSOR_AND_PROPERTY = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.js.PredefinedAnnotation
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.SimpleDeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.SimpleDeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
|
||||
|
||||
+23
-23
@@ -21,11 +21,11 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.js.naming.NameSuggestion
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.SimpleDeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.SimpleDeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
|
||||
class JsNameClashChecker : SimpleDeclarationChecker {
|
||||
@@ -40,20 +40,24 @@ class JsNameClashChecker : SimpleDeclarationChecker {
|
||||
diagnosticHolder: DiagnosticSink,
|
||||
bindingContext: BindingContext
|
||||
) {
|
||||
if (declaration !is KtProperty || !descriptor.isExtension) {
|
||||
// We don't generate JS properties for extension properties, we generate methods instead, so in this case
|
||||
// check name clash only for accessors, not properties
|
||||
if (!descriptor.isExtensionProperty) {
|
||||
checkDescriptor(descriptor, declaration, diagnosticHolder)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkDescriptor(descriptor: DeclarationDescriptor, declaration: KtDeclaration, diagnosticHolder: DiagnosticSink) {
|
||||
if (descriptor is ConstructorDescriptor && descriptor.isPrimary) return
|
||||
|
||||
val suggested = nameSuggestion.suggest(descriptor)!!
|
||||
if (suggested.stable && suggested.scope is ClassOrPackageFragmentDescriptor && isOpaque(suggested.descriptor)) {
|
||||
if (suggested.stable && suggested.scope is ClassOrPackageFragmentDescriptor && presentsInGeneratedCode(suggested.descriptor)) {
|
||||
val scope = getScope(suggested.scope)
|
||||
val name = suggested.names.last()
|
||||
val existing = scope[name]
|
||||
if (existing != null && existing != suggested.descriptor) {
|
||||
diagnosticHolder.report(ErrorsJs.JS_NAME_CLASH.on(declaration, name, existing))
|
||||
val existingDeclaration = existing.findPsi() ?: declaration
|
||||
val existingDeclaration = existing.findPsi()
|
||||
if (clashedDescriptors.add(existing) && existingDeclaration is KtDeclaration && existingDeclaration != declaration) {
|
||||
diagnosticHolder.report(ErrorsJs.JS_NAME_CLASH.on(existingDeclaration, name, descriptor))
|
||||
}
|
||||
@@ -71,14 +75,14 @@ class JsNameClashChecker : SimpleDeclarationChecker {
|
||||
val name = overrideFqn.names.last()
|
||||
val existing = scope[name]
|
||||
if (existing != null && existing != overrideFqn.descriptor) {
|
||||
diagnosticHolder.report(ErrorsJs.JS_NAME_CLASH_SYNTHETIC.on(declaration, name, override, existing))
|
||||
diagnosticHolder.report(ErrorsJs.JS_FAKE_NAME_CLASH.on(declaration, name, override, existing))
|
||||
break
|
||||
}
|
||||
|
||||
val clashedOverrides = clashedFakeOverrides[override]
|
||||
if (clashedOverrides != null) {
|
||||
val (firstExample, secondExample) = clashedOverrides
|
||||
diagnosticHolder.report(ErrorsJs.JS_NAME_CLASH_SYNTHETIC.on(declaration, name, firstExample, secondExample))
|
||||
diagnosticHolder.report(ErrorsJs.JS_FAKE_NAME_CLASH.on(declaration, name, firstExample, secondExample))
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -115,33 +119,29 @@ class JsNameClashChecker : SimpleDeclarationChecker {
|
||||
}
|
||||
|
||||
val fqn = nameSuggestion.suggest(descriptor) ?: return
|
||||
if (fqn.stable && isOpaque(fqn.descriptor)) {
|
||||
if (fqn.stable && presentsInGeneratedCode(fqn.descriptor)) {
|
||||
target[fqn.names.last()] = fqn.descriptor
|
||||
(fqn.descriptor as? CallableMemberDescriptor)?.let { checkOverrideClashes(it, target) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOverrideClashes(descriptor: CallableMemberDescriptor, target: MutableMap<String, DeclarationDescriptor>) {
|
||||
var overridden = descriptor.overriddenDescriptors
|
||||
while (overridden.isNotEmpty()) {
|
||||
for (overridenDescriptor in overridden) {
|
||||
val overriddenFqn = nameSuggestion.suggest(overridenDescriptor)!!
|
||||
if (overriddenFqn.stable) {
|
||||
val existing = target[overriddenFqn.names.last()]
|
||||
if (existing != null) {
|
||||
if (existing != descriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
|
||||
clashedFakeOverrides[descriptor] = Pair(existing, overridenDescriptor)
|
||||
}
|
||||
}
|
||||
else {
|
||||
target[overriddenFqn.names.last()] = descriptor
|
||||
for (overriddenDescriptor in DescriptorUtils.getAllOverriddenDeclarations(descriptor)) {
|
||||
val overriddenFqn = nameSuggestion.suggest(overriddenDescriptor)!!
|
||||
if (overriddenFqn.stable) {
|
||||
val existing = target[overriddenFqn.names.last()]
|
||||
if (existing != null) {
|
||||
if (existing != descriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
|
||||
clashedFakeOverrides[descriptor] = Pair(existing, overriddenDescriptor)
|
||||
}
|
||||
}
|
||||
else {
|
||||
target[overriddenFqn.names.last()] = descriptor
|
||||
}
|
||||
}
|
||||
overridden = overridden.flatMap { it.overriddenDescriptors }
|
||||
}
|
||||
}
|
||||
|
||||
private fun isOpaque(descriptor: DeclarationDescriptor) =
|
||||
private fun presentsInGeneratedCode(descriptor: DeclarationDescriptor) =
|
||||
!AnnotationsUtils.isNativeObject(descriptor) && !AnnotationsUtils.isLibraryObject(descriptor)
|
||||
}
|
||||
@@ -102,7 +102,14 @@ public final class AnnotationsUtils {
|
||||
}
|
||||
|
||||
public static boolean isNativeObject(@NotNull DeclarationDescriptor descriptor) {
|
||||
return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.NATIVE);
|
||||
if (hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.NATIVE)) return true;
|
||||
|
||||
if (descriptor instanceof PropertyAccessorDescriptor) {
|
||||
PropertyAccessorDescriptor accessor = (PropertyAccessorDescriptor) descriptor;
|
||||
return hasAnnotationOrInsideAnnotatedClass(accessor.getCorrespondingProperty(), PredefinedAnnotation.NATIVE);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isLibraryObject(@NotNull DeclarationDescriptor descriptor) {
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.test
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.js.config.EcmaVersion
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.js.config.JsConfig
|
||||
import org.jetbrains.kotlin.js.facade.MainCallParameters
|
||||
import org.jetbrains.kotlin.js.test.rhino.RhinoFunctionResultChecker
|
||||
import org.jetbrains.kotlin.js.test.utils.JsTestUtils.getAllFilesInDir
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
abstract class MultipleModulesTranslationTest(main: String) : BasicTest(main) {
|
||||
private val MAIN_MODULE_NAME: String = "main"
|
||||
private val OLD_MODULE_SUFFIX = "-old"
|
||||
private var dependencies: Map<String, List<String>>? = null
|
||||
protected var moduleKind = ModuleKind.PLAIN
|
||||
|
||||
override fun checkFooBoxIsOkByPath(filePath: String) {
|
||||
val dirName = getTestName(true)
|
||||
dependencies = readModuleDependencies(filePath)
|
||||
|
||||
// KT-7428: !! is necessary here
|
||||
for ((moduleName, dependencies) in dependencies!!) {
|
||||
translateModule(dirName, filePath, moduleName, dependencies)
|
||||
}
|
||||
|
||||
val filename = getInputFilePath(getModuleDirectoryName(dirName, MAIN_MODULE_NAME) + File.separator + MAIN_MODULE_NAME + ".kt")
|
||||
val packageName = getPackageName(filename)
|
||||
runMultiModuleTest(dirName, packageName, BasicTest.TEST_FUNCTION, "OK")
|
||||
}
|
||||
|
||||
private fun runMultiModuleTest(dirName: String, packageName: String, functionName: String, expectedResult: Any) {
|
||||
val moduleDirectoryName = getModuleDirectoryName(dirName, MAIN_MODULE_NAME)
|
||||
val checker = RhinoFunctionResultChecker(MAIN_MODULE_NAME, packageName, functionName, expectedResult)
|
||||
runRhinoTests(moduleDirectoryName, BasicTest.DEFAULT_ECMA_VERSIONS, checker)
|
||||
}
|
||||
|
||||
private fun translateModule(dirName: String, pathToDir: String, moduleName: String, dependencies: List<String>) {
|
||||
val moduleDirectoryName = getModuleDirectoryName(dirName, moduleName)
|
||||
val fullFilePaths = getAllFilesInDir(pathToDir + File.separator + moduleName)
|
||||
|
||||
BasicTest.DEFAULT_ECMA_VERSIONS.forEach { version ->
|
||||
val libraries = arrayListOf<String>()
|
||||
for (dependencyName in dependencies) {
|
||||
val moduleDir = getModuleDirectoryName(dirName, dependencyName)
|
||||
libraries.add(getMetaFileOutputPath(moduleDir, version))
|
||||
}
|
||||
generateJavaScriptFiles(fullFilePaths, moduleDirectoryName, MainCallParameters.noCall(), version,
|
||||
moduleName.removeSuffix(OLD_MODULE_SUFFIX), libraries)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMetaFileOutputPath(moduleDirectoryName: String, version: EcmaVersion) =
|
||||
KotlinJavascriptMetadataUtils.replaceSuffix(getOutputFilePath(moduleDirectoryName, version))
|
||||
|
||||
override fun setupConfig(configuration: CompilerConfiguration) {
|
||||
val method = try {
|
||||
javaClass.getMethod(name)
|
||||
}
|
||||
catch (e: NoSuchMethodException) {
|
||||
return
|
||||
}
|
||||
|
||||
method.getAnnotation(WithModuleKind::class.java)?.let { moduleKind = it.value }
|
||||
configuration.put(JSConfigurationKeys.MODULE_KIND, moduleKind)
|
||||
}
|
||||
|
||||
override fun shouldGenerateMetaInfo() = true
|
||||
|
||||
override fun additionalJsFiles(ecmaVersion: EcmaVersion): List<String> {
|
||||
val result = mutableListOf(MODULE_EMULATION_FILE)
|
||||
result += super.additionalJsFiles(ecmaVersion)
|
||||
val dirName = getTestName(true)
|
||||
assert(dependencies != null) { "dependencies should not be null" }
|
||||
|
||||
for (moduleName in dependencies!!.keys.filter { !it.endsWith(OLD_MODULE_SUFFIX) }) {
|
||||
if (moduleName != MAIN_MODULE_NAME && !moduleName.endsWith(OLD_MODULE_SUFFIX)) {
|
||||
result.add(getOutputFilePath(getModuleDirectoryName(dirName, moduleName), ecmaVersion))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override fun translateFiles(jetFiles: MutableList<KtFile>, outputFile: File, mainCallParameters: MainCallParameters,
|
||||
config: JsConfig) {
|
||||
super.translateFiles(jetFiles, outputFile, mainCallParameters, config)
|
||||
|
||||
if (config.moduleKind == ModuleKind.COMMON_JS) {
|
||||
val content = FileUtil.loadFile(outputFile, true)
|
||||
val wrappedContent = "__beginModule__();\n" +
|
||||
"$content\n" +
|
||||
"__endModule__(\"${StringUtil.escapeStringCharacters(config.moduleId)}\");"
|
||||
FileUtil.writeToFile(outputFile, wrappedContent)
|
||||
// TODO: it would be better to wrap output before JS file is actually written
|
||||
}
|
||||
}
|
||||
|
||||
private fun readModuleDependencies(testDataDir: String): Map<String, List<String>> {
|
||||
val dependenciesTxt = upsearchFile(testDataDir, "dependencies.txt")
|
||||
assert(dependenciesTxt.isFile) { "moduleDependencies should not be null" }
|
||||
|
||||
val result = LinkedHashMap<String, List<String>>()
|
||||
for (line in dependenciesTxt.readLines()) {
|
||||
val split = line.split("->")
|
||||
val module = split[0]
|
||||
val dependencies = if (split.size > 1) split[1] else ""
|
||||
val dependencyList = dependencies.split(",").filterNot { it.isEmpty() }
|
||||
|
||||
result[module] = dependencyList
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun upsearchFile(startingDir: String, name: String): File {
|
||||
var dir: File? = File(startingDir)
|
||||
var file = File(dir, name)
|
||||
|
||||
while (dir != null && dir.isDirectory && !file.isFile) {
|
||||
dir = dir.parentFile
|
||||
file = File(dir, name)
|
||||
}
|
||||
|
||||
return file
|
||||
}
|
||||
}
|
||||
@@ -25,22 +25,26 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage;
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||
import org.jetbrains.kotlin.js.naming.NameSuggestion;
|
||||
import org.jetbrains.kotlin.js.naming.SuggestedName;
|
||||
import org.jetbrains.kotlin.js.resolve.JsPlatform;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.dart.compiler.backend.js.ast.JsScopesKt.JsObjectScope;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getModuleName;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getStableMangledNameForDescriptor;
|
||||
|
||||
/**
|
||||
* Encapsulates different types of constants and naming conventions.
|
||||
@@ -300,6 +304,17 @@ public final class Namer {
|
||||
modulesMap = kotlin("modules");
|
||||
}
|
||||
|
||||
// TODO: get rid of this function
|
||||
@NotNull
|
||||
public static String getStableMangledNameForDescriptor(@NotNull ClassDescriptor descriptor, @NotNull String functionName) {
|
||||
Collection<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 JsExpression classCreationMethodReference() {
|
||||
return kotlin(className);
|
||||
|
||||
@@ -113,8 +113,6 @@ public final class StaticContext {
|
||||
@NotNull
|
||||
private final Map<PropertyDescriptor, JsName> backingFieldNameCache = new HashMap<PropertyDescriptor, JsName>();
|
||||
|
||||
private final Map<JsScope, Map<String, JsName>> persistentNames = new HashMap<JsScope, Map<String, JsName>>();
|
||||
|
||||
@NotNull
|
||||
private final Map<DeclarationDescriptor, JsExpression> fqnCache = new HashMap<DeclarationDescriptor, JsExpression>();
|
||||
|
||||
@@ -224,13 +222,8 @@ public final class StaticContext {
|
||||
SuggestedName suggested = nameSuggestion.suggest(descriptor);
|
||||
if (suggested == null) {
|
||||
ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
|
||||
if (currentModule == module) {
|
||||
return pureFqn(Namer.getRootPackageName(), null);
|
||||
}
|
||||
else {
|
||||
JsExpression result = getModuleExpressionFor(module);
|
||||
return result != null ? result : JsAstUtils.pureFqn(rootScope.declareName(Namer.getRootPackageName()), null);
|
||||
}
|
||||
JsExpression result = getModuleExpressionFor(module);
|
||||
return result != null ? result : pureFqn(Namer.getRootPackageName(), null);
|
||||
}
|
||||
|
||||
JsExpression expression;
|
||||
@@ -239,22 +232,20 @@ public final class StaticContext {
|
||||
expression = Namer.kotlinObject();
|
||||
partNames = Collections.singletonList(standardClasses.getStandardObjectName(suggested.getDescriptor()));
|
||||
}
|
||||
else if (isLibraryObject(suggested.getDescriptor())) {
|
||||
expression = Namer.kotlinObject();
|
||||
partNames = getActualNameFromSuggested(suggested);
|
||||
}
|
||||
else if (isNative(suggested.getDescriptor()) && !isNativeObject(suggested.getScope())) {
|
||||
expression = null;
|
||||
partNames = getActualNameFromSuggested(suggested);
|
||||
}
|
||||
else {
|
||||
if (suggested.getDescriptor() instanceof CallableDescriptor && suggested.getScope() instanceof FunctionDescriptor) {
|
||||
partNames = getActualNameFromSuggested(suggested);
|
||||
if (isLibraryObject(suggested.getDescriptor())) {
|
||||
expression = Namer.kotlinObject();
|
||||
}
|
||||
// Don't generate qualifier for top-level native declarations
|
||||
// Don't generate qualifier for local declarations
|
||||
else if (isNativeObject(suggested.getDescriptor()) && !isNativeObject(suggested.getScope()) ||
|
||||
suggested.getDescriptor() instanceof CallableDescriptor && suggested.getScope() instanceof FunctionDescriptor) {
|
||||
expression = null;
|
||||
}
|
||||
else {
|
||||
expression = getQualifiedExpression(suggested.getScope());
|
||||
}
|
||||
partNames = getActualNameFromSuggested(suggested);
|
||||
}
|
||||
for (JsName partName : partNames) {
|
||||
expression = new JsNameRef(partName, expression);
|
||||
@@ -264,17 +255,6 @@ public final class StaticContext {
|
||||
return expression;
|
||||
}
|
||||
|
||||
private static boolean isNative(DeclarationDescriptor descriptor) {
|
||||
if (isNativeObject(descriptor)) return true;
|
||||
|
||||
if (descriptor instanceof PropertyAccessorDescriptor) {
|
||||
PropertyAccessorDescriptor accessor = (PropertyAccessorDescriptor) descriptor;
|
||||
return isNative(accessor.getCorrespondingProperty());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
|
||||
JsName packageName = getNameForPackage(packageFqName);
|
||||
@@ -300,11 +280,7 @@ public final class StaticContext {
|
||||
assert fqn.getNames().size() == 1 : "Private names must always consist of exactly one name";
|
||||
|
||||
JsScope scope = getScopeForDescriptor(fqn.getScope());
|
||||
|
||||
if (DynamicCallsKt.isDynamic(property)) {
|
||||
scope = JsDynamicScope.INSTANCE;
|
||||
}
|
||||
String baseName = fqn.getNames().get(0) + "_0";
|
||||
String baseName = NameSuggestion.getPrivateMangledName(fqn.getNames().get(0), property) + "_0";
|
||||
name = scope.declareFreshName(baseName);
|
||||
backingFieldNameCache.put(property, name);
|
||||
}
|
||||
@@ -322,18 +298,9 @@ public final class StaticContext {
|
||||
|
||||
List<JsName> names = new ArrayList<JsName>();
|
||||
if (suggested.getStable()) {
|
||||
Map<String, JsName> scopeNames = persistentNames.get(scope);
|
||||
if (scopeNames == null) {
|
||||
scopeNames = new HashMap<String, JsName>();
|
||||
persistentNames.put(scope, scopeNames);
|
||||
}
|
||||
|
||||
for (String namePart : suggested.getNames()) {
|
||||
JsName name = scopeNames.get(namePart);
|
||||
if (name == null) {
|
||||
name = scope.declareName(namePart);
|
||||
scopeNames.put(namePart, name);
|
||||
}
|
||||
names.add(name);
|
||||
names.add(scope.declareName(namePart));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ class DelegationTranslator(
|
||||
else {
|
||||
val classFqName = DescriptorUtils.getFqName(classDescriptor)
|
||||
val typeFqName = DescriptorUtils.getFqName(descriptor)
|
||||
val idForMangling = "${classFqName.asString()}:${typeFqName.asString()}"
|
||||
val idForMangling = classFqName.asString()
|
||||
val suggestedName = NameSuggestion.getStableMangledName(Namer.getDelegatePrefix(), idForMangling)
|
||||
val delegateName = context.getScopeForDescriptor(classDescriptor).declareFreshName("${suggestedName}_0")
|
||||
fields[specifier] = Field(delegateName, true)
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ import java.util.Map;
|
||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES;
|
||||
import static org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern;
|
||||
import static org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic.CallParametersAwareFunctionIntrinsic;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getStableMangledNameForDescriptor;
|
||||
import static org.jetbrains.kotlin.js.translate.context.Namer.getStableMangledNameForDescriptor;
|
||||
|
||||
public final class TopLevelFIF extends CompositeFIF {
|
||||
public static final DescriptorPredicate EQUALS_IN_ANY = pattern("kotlin", "Any", "equals");
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.utils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||
import org.jetbrains.kotlin.js.naming.NameSuggestion;
|
||||
import org.jetbrains.kotlin.js.naming.SuggestedName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class ManglingUtils {
|
||||
private ManglingUtils() {}
|
||||
|
||||
@NotNull
|
||||
public static String getStableMangledNameForDescriptor(@NotNull ClassDescriptor descriptor, @NotNull String functionName) {
|
||||
Collection<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);
|
||||
}
|
||||
}
|
||||
Vendored
+3
-5
@@ -1,15 +1,13 @@
|
||||
// KT-2995 creating factory methods to simulate overloaded constructors don't work in JavaScript
|
||||
// This test is incorrect, since both constructor and function must have the same name.
|
||||
package foo
|
||||
|
||||
class Foo(val name: String)
|
||||
|
||||
//fun Foo() = Foo("<default-name>")
|
||||
fun Foo(x: Int) = Foo("<$x>")
|
||||
|
||||
fun box(): String {
|
||||
// TODO: Don't know another way to suppress the test
|
||||
/*assertEquals("<default-name>", Foo().name)
|
||||
assertEquals("BarBaz", Foo("BarBaz").name)*/
|
||||
assertEquals("<123>", Foo(123).name)
|
||||
assertEquals("BarBaz", Foo("BarBaz").name)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
package foo
|
||||
|
||||
@JsName("AA") object A {
|
||||
@JsName("foo") fun foo() = "A.foo"
|
||||
@JsName("foo") fun bar() = "A.foo"
|
||||
}
|
||||
|
||||
@JsName("BB") class B {
|
||||
@JsName("foo") fun foo() = "B.foo"
|
||||
@JsName("foo") fun bar() = "B.foo"
|
||||
}
|
||||
|
||||
fun testA() = js("""
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package foo
|
||||
|
||||
class A {
|
||||
val x: String
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
init {
|
||||
val o = "O"
|
||||
fun baz() = o + "K"
|
||||
x = baz()
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = A().x
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
lib1->
|
||||
lib2->
|
||||
main->lib1,lib2
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package lib1
|
||||
|
||||
interface A {
|
||||
private fun foo() = "A.foo"
|
||||
|
||||
fun bar() = foo()
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package lib2
|
||||
|
||||
interface B {
|
||||
private fun foo() = "B.foo"
|
||||
|
||||
fun bar() = foo()
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import lib1.A
|
||||
import lib2.B
|
||||
|
||||
class Derived1 : A, B {
|
||||
override fun bar() = super<A>.bar()
|
||||
}
|
||||
|
||||
class Derived2 : A, B {
|
||||
override fun bar() = super<B>.bar()
|
||||
}
|
||||
|
||||
// NOTE. This test is fragile, it may fail due to unexpected (and correct) changes in algorithm that assigns
|
||||
// unique identifiers to non-public declarations. However, we don't see any way of doing such test so that
|
||||
// it won't report false positives eventually. So be patient and just update this test whenever you changed
|
||||
// algorithm of assigning unique identifiers.
|
||||
// Please, check that A.foo and B.foo have different JS names.
|
||||
private fun checkJsNames(o: dynamic): Boolean = "foo_2pru9n\$_0" in o && "foo_2psha1\$_0" in o
|
||||
|
||||
fun box(): String {
|
||||
val a = Derived1()
|
||||
if (a.bar() != "A.foo") return "fail1: ${a.bar()}"
|
||||
|
||||
val b = Derived2()
|
||||
if (b.bar() != "B.foo") return "fail2: ${b.bar()}"
|
||||
|
||||
if (!checkJsNames(a)) return "fail3"
|
||||
if (!checkJsNames(b)) return "fail4"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
lib->
|
||||
lib-old->
|
||||
main->lib-old
|
||||
@@ -0,0 +1,7 @@
|
||||
package lib
|
||||
|
||||
open class A {
|
||||
fun foo() = 12
|
||||
}
|
||||
|
||||
inline fun check() = true
|
||||
@@ -0,0 +1,7 @@
|
||||
package lib
|
||||
|
||||
open class A {
|
||||
private val x = 23
|
||||
|
||||
fun foo() = x
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import lib.A
|
||||
import lib.check
|
||||
|
||||
class B : A() {
|
||||
private var x = 42
|
||||
|
||||
fun bar() = x
|
||||
}
|
||||
|
||||
// NOTE. This test is fragile, it may fail due to unexpected (and correct) changes in algorithm that assigns
|
||||
// unique identifiers to non-public declarations. However, we don't see any way of doing such test so that
|
||||
// it won't report false positives eventually. So be patient and just update this test whenever you changed
|
||||
// algorithm of assigning unique identifiers.
|
||||
// Please, check that A.x and B.x have different JS names.
|
||||
private fun checkJsNames(o: dynamic): Boolean = "x_i8qwny\$_0" in o && "x_dqqnpp\$_0" in o
|
||||
|
||||
fun box(): String {
|
||||
if (!check()) return "check failed: did not compile agains old library"
|
||||
|
||||
val a = A()
|
||||
if (a.foo() != 23) return "fail1: ${a.foo()}"
|
||||
|
||||
val b = B()
|
||||
if (b.foo() != 23) return "fail2: ${b.foo()}"
|
||||
if (b.bar() != 42) return "fail3: ${b.bar()}"
|
||||
if (!checkJsNames(b)) return "fail4"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user