KT-2752: refactor NameSuggestion, change rules for determining name stability and applying mangling
This commit is contained in:
@@ -110,7 +110,7 @@ public class KotlinTestUtils {
|
||||
* Several files may follow one module
|
||||
*/
|
||||
public static final Pattern FILE_OR_MODULE_PATTERN = Pattern.compile(
|
||||
"(?://\\s*MODULE:\\s*([\\w\\d_\\-]+)(\\([\\w\\d_\\-]+(?:, [\\w\\d_\\-]+)*\\))?\\s*)?" +
|
||||
"(?://\\s*MODULE:\\s*([\\w\\d_\\-]+)(\\([\\w\\d_\\-]+(?:,\\s*[\\w\\d_\\-]+)*\\))?\\s*)?" +
|
||||
"//\\s*FILE:\\s*(.*)$", Pattern.MULTILINE);
|
||||
public static final Pattern DIRECTIVE_PATTERN = Pattern.compile("^//\\s*!([\\w_]+)(:\\s*(.*)$)?", Pattern.MULTILINE);
|
||||
|
||||
|
||||
@@ -66,6 +66,11 @@ class NameSuggestion {
|
||||
return suggest(descriptor.containingDeclaration!!)
|
||||
}
|
||||
|
||||
// Dynamic declarations always require stable names as defined in Kotlin source code
|
||||
if (descriptor.isDynamic()) {
|
||||
return SuggestedName(listOf(descriptor.name.asString()), true, descriptor, descriptor.containingDeclaration!!)
|
||||
}
|
||||
|
||||
when (descriptor) {
|
||||
// Modules are root declarations, we don't produce declarations for them, therefore they can't clash
|
||||
is ModuleDescriptor -> return null
|
||||
@@ -94,8 +99,8 @@ class NameSuggestion {
|
||||
// Local functions and variables are always private with their own names as suggested names
|
||||
is CallableDescriptor ->
|
||||
if (DescriptorUtils.isDescriptorWithLocalVisibility(descriptor)) {
|
||||
val name = getMangledName(getSuggestedName(descriptor), descriptor)
|
||||
return SuggestedName(listOf(name.first), false, descriptor, descriptor.containingDeclaration)
|
||||
val name = getNameForAnnotatedObject(descriptor) ?: getSuggestedName(descriptor)
|
||||
return SuggestedName(listOf(name), false, descriptor, descriptor.containingDeclaration)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,11 +108,6 @@ class NameSuggestion {
|
||||
}
|
||||
|
||||
private fun generateDefault(descriptor: DeclarationDescriptor): SuggestedName {
|
||||
// Dynamic declarations always require stable names as defined in Kotlin source code
|
||||
if (descriptor.isDynamic()) {
|
||||
return SuggestedName(listOf(descriptor.name.asString()), true, descriptor, descriptor.containingDeclaration!!)
|
||||
}
|
||||
|
||||
// For any non-local declaration suggest its own suggested name and put it in scope of its containing declaration.
|
||||
// For local declaration get a sequence for names of all containing functions and join their names with '$' symbol,
|
||||
// and use container of topmost function, i.e.
|
||||
@@ -146,7 +146,7 @@ class NameSuggestion {
|
||||
|
||||
parts.reverse()
|
||||
val unmangledName = parts.joinToString("$")
|
||||
val (id, stable) = getMangledName(unmangledName, descriptor)
|
||||
val (id, stable) = mangleNameIfNecessary(unmangledName, descriptor)
|
||||
return SuggestedName(listOf(id), stable, descriptor, current)
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ class NameSuggestion {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun getMangledName(baseName: String, descriptor: DeclarationDescriptor): Pair<String, Boolean> {
|
||||
private fun mangleNameIfNecessary(baseName: String, descriptor: DeclarationDescriptor): NameAndStability {
|
||||
// If we have a callable descriptor (property or method) it can override method in a parent class.
|
||||
// Traverse to the topmost overridden method.
|
||||
// It does not matter which path to choose during traversal, since front-end must ensure
|
||||
@@ -182,20 +182,64 @@ class NameSuggestion {
|
||||
|
||||
// If declaration is marked with either @native, @library or @JsName, return its stable name as is.
|
||||
val nativeName = getNameForAnnotatedObject(overriddenDescriptor)
|
||||
if (nativeName != null) return Pair(nativeName, true)
|
||||
if (nativeName != null) return NameAndStability(nativeName, true)
|
||||
|
||||
val stable = shouldBeStable(descriptor)
|
||||
val finalName = when {
|
||||
overriddenDescriptor is CallableDescriptor && stable -> {
|
||||
getStableMangledName(baseName, getArgumentTypesAsString(overriddenDescriptor))
|
||||
}
|
||||
shouldMangleUnstable(overriddenDescriptor) -> getPrivateMangledName(baseName, overriddenDescriptor as CallableDescriptor)
|
||||
else -> baseName
|
||||
return mangleRegularNameIfNecessary(baseName, overriddenDescriptor)
|
||||
}
|
||||
|
||||
private fun mangleRegularNameIfNecessary(baseName: String, descriptor: DeclarationDescriptor): NameAndStability {
|
||||
if (descriptor is ClassOrPackageFragmentDescriptor) {
|
||||
return NameAndStability(baseName, !DescriptorUtils.isDescriptorWithLocalVisibility(descriptor))
|
||||
}
|
||||
|
||||
return Pair(finalName, stable)
|
||||
fun regularAndUnstable() = NameAndStability(baseName, false)
|
||||
|
||||
if (descriptor !is CallableMemberDescriptor) {
|
||||
// Actually, only reified types get here, and it would be properly to put assertion here
|
||||
// However, it's better to generate wrong code than crash
|
||||
return regularAndUnstable()
|
||||
}
|
||||
|
||||
fun mangledAndStable() = NameAndStability(getStableMangledName(baseName, getArgumentTypesAsString(descriptor)), true)
|
||||
fun mangledPrivate() = NameAndStability(getPrivateMangledName(baseName, descriptor), false)
|
||||
|
||||
val containingDeclaration = descriptor.containingDeclaration
|
||||
return when (containingDeclaration) {
|
||||
is PackageFragmentDescriptor -> if (descriptor.visibility.isPublicAPI) mangledAndStable() else regularAndUnstable()
|
||||
is ClassDescriptor -> {
|
||||
// valueOf() is created in the library with a mangled name for every enum class
|
||||
if (descriptor is FunctionDescriptor && descriptor.isEnumValueOfMethod()) return mangledAndStable()
|
||||
|
||||
// Make all public declarations stable
|
||||
if (descriptor.visibility == Visibilities.PUBLIC) return mangledAndStable()
|
||||
|
||||
// Make all protected declarations of non-final public classes stable
|
||||
if (descriptor.visibility == Visibilities.PROTECTED &&
|
||||
!containingDeclaration.isFinalClass &&
|
||||
containingDeclaration.visibility.isPublicAPI
|
||||
) {
|
||||
return mangledAndStable()
|
||||
}
|
||||
|
||||
// Mangle (but make unstable) all non-public API of public classes
|
||||
if (containingDeclaration.visibility.isPublicAPI && !containingDeclaration.isFinalClass) {
|
||||
return mangledPrivate()
|
||||
}
|
||||
|
||||
regularAndUnstable()
|
||||
}
|
||||
else -> {
|
||||
assert(containingDeclaration is CallableMemberDescriptor) {
|
||||
"containingDeclaration for descriptor have unsupported type for mangling, " +
|
||||
"descriptor: " + descriptor + ", containingDeclaration: " + containingDeclaration
|
||||
}
|
||||
regularAndUnstable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class NameAndStability(val name: String, val stable: Boolean)
|
||||
|
||||
@JvmStatic fun getPrivateMangledName(baseName: String, descriptor: CallableDescriptor): String {
|
||||
val ownerName = descriptor.containingDeclaration.fqNameUnsafe.asString()
|
||||
return getStableMangledName(baseName, ownerName + ":" + getArgumentTypesAsString(descriptor))
|
||||
@@ -214,67 +258,11 @@ class NameSuggestion {
|
||||
return argTypes.toString()
|
||||
}
|
||||
|
||||
// Sometimes private members of a class can clash with public members of subclasses, therefore we must
|
||||
// mangle them
|
||||
private fun shouldMangleUnstable(descriptor: DeclarationDescriptor): Boolean {
|
||||
if (descriptor is ClassDescriptor) return false
|
||||
if (DescriptorUtils.isDescriptorWithLocalVisibility(descriptor)) return false
|
||||
|
||||
val containingClass = DescriptorUtils.getContainingClass(descriptor)
|
||||
if (containingClass != null && descriptor is CallableMemberDescriptor && !descriptor.isOverridable) {
|
||||
return containingClass.visibility.isPublicAPI
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@JvmStatic fun getStableMangledName(suggestedName: String, forCalculateId: String): String {
|
||||
val suffix = if (forCalculateId.isEmpty()) "" else "_${mangledId(forCalculateId)}\$"
|
||||
return suggestedName + suffix
|
||||
}
|
||||
|
||||
private fun shouldBeStable(descriptor: DeclarationDescriptor): Boolean {
|
||||
if (DescriptorUtils.isDescriptorWithLocalVisibility(descriptor)) return false
|
||||
if (descriptor is ClassOrPackageFragmentDescriptor) return true
|
||||
if (descriptor !is CallableMemberDescriptor) return false
|
||||
|
||||
// Use stable mangling for overrides because we use stable mangling when any function inside a overridable declaration
|
||||
// for avoid clashing names when inheritance.
|
||||
if (DescriptorUtils.isOverride(descriptor)) return true
|
||||
|
||||
val containingDeclaration = descriptor.containingDeclaration
|
||||
if (isNativeObject(containingDeclaration) || isLibraryObject(containingDeclaration)) return true
|
||||
|
||||
return when (containingDeclaration) {
|
||||
is PackageFragmentDescriptor -> descriptor.visibility.isPublicAPI
|
||||
is ClassDescriptor -> {
|
||||
// Open (abstract) public methods of classes or final public methods of open (abstract) classes should be stable
|
||||
if (containingDeclaration.modality == Modality.OPEN || containingDeclaration.modality == Modality.ABSTRACT) {
|
||||
return descriptor.visibility.isPublicAPI
|
||||
}
|
||||
|
||||
// valueOf() is created in the library with a mangled name for every enum class
|
||||
if (descriptor is FunctionDescriptor && descriptor.isEnumValueOfMethod()) return true
|
||||
|
||||
// Don't use stable mangling when it inside a non-public class.
|
||||
if (!containingDeclaration.visibility.isPublicAPI) return false
|
||||
|
||||
// Ignore the `protected` visibility because it can be use outside a containing declaration
|
||||
// only when the containing declaration is overridable.
|
||||
if (descriptor.visibility === Visibilities.PUBLIC) return true
|
||||
|
||||
return false
|
||||
}
|
||||
else -> {
|
||||
assert(containingDeclaration is CallableMemberDescriptor) {
|
||||
"containingDeclaration for descriptor have unsupported type for mangling, " +
|
||||
"descriptor: " + descriptor + ", containingDeclaration: " + containingDeclaration
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun mangledId(forCalculateId: String): String {
|
||||
val absHashCode = Math.abs(forCalculateId.hashCode())
|
||||
return if (absHashCode != 0) Integer.toString(absHashCode, Character.MAX_RADIX) else ""
|
||||
|
||||
@@ -71,6 +71,7 @@ abstract class BasicBoxTest(
|
||||
val TEST_MODULE = "JS_TESTS"
|
||||
val DEFAULT_MODULE = "main"
|
||||
val TEST_FUNCTION = "box"
|
||||
private val OLD_MODULE_SUFFIX = "-old"
|
||||
|
||||
fun doTest(filePath: String) {
|
||||
val file = File(filePath)
|
||||
@@ -85,12 +86,13 @@ abstract class BasicBoxTest(
|
||||
|
||||
val orderedModules = DFS.topologicalOrder(modules.values) { module -> module.dependencies.mapNotNull { modules[it] } }
|
||||
|
||||
val generatedJsFiles = orderedModules.asReversed().map { module ->
|
||||
val generatedJsFiles = orderedModules.asReversed().mapNotNull { module ->
|
||||
val dependencies = module.dependencies.mapNotNull { modules[it]?.outputFileName(outputDir) + ".meta.js" }
|
||||
|
||||
val outputFileName = module.outputFileName(outputDir) + ".js"
|
||||
generateJavaScriptFile(file.parent, module, outputFileName, dependencies, modules.size > 1)
|
||||
outputFileName
|
||||
|
||||
if (!module.name.endsWith(OLD_MODULE_SUFFIX)) outputFileName else null
|
||||
}
|
||||
val mainModuleName = if (TEST_MODULE in modules) TEST_MODULE else DEFAULT_MODULE
|
||||
val mainModule = modules[mainModuleName]!!
|
||||
@@ -247,7 +249,7 @@ abstract class BasicBoxTest(
|
||||
|
||||
configuration.put(JSConfigurationKeys.LIBRARY_FILES, LibrarySourcesConfig.JS_STDLIB + dependencies)
|
||||
|
||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, module.name)
|
||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, module.name.removeSuffix(OLD_MODULE_SUFFIX))
|
||||
configuration.put(JSConfigurationKeys.MODULE_KIND, module.moduleKind)
|
||||
configuration.put(JSConfigurationKeys.TARGET, EcmaVersion.v5)
|
||||
|
||||
@@ -283,7 +285,7 @@ abstract class BasicBoxTest(
|
||||
(module ?: defaultModule).inliningDisabled = true
|
||||
}
|
||||
|
||||
val temporaryFile = File(tmpDir, fileName)
|
||||
val temporaryFile = File(tmpDir, "${(module ?: defaultModule).name}/$fileName")
|
||||
KotlinTestUtils.mkdirs(temporaryFile.parentFile)
|
||||
temporaryFile.writeText(text, Charsets.UTF_8)
|
||||
|
||||
|
||||
@@ -1,149 +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.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
|
||||
}
|
||||
}
|
||||
@@ -2259,6 +2259,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localInInitBlock.kt")
|
||||
public void testLocalInInitBlock() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/expression/function/localInInitBlock.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("loopClosure.kt")
|
||||
public void testLoopClosure() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/expression/function/loopClosure.kt");
|
||||
@@ -4978,6 +4984,18 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("privateInterfaceNameClash.kt")
|
||||
public void testPrivateInterfaceNameClash() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/multiModule/privateInterfaceNameClash.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("privateNameClash.kt")
|
||||
public void testPrivateNameClash() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/multiModule/privateNameClash.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("useElementsFromDefaultPackageInAnotherModule.kt")
|
||||
public void testUseElementsFromDefaultPackageInAnotherModule() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/multiModule/useElementsFromDefaultPackageInAnotherModule.kt");
|
||||
@@ -5305,6 +5323,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inheritanceInNativeClass.kt")
|
||||
public void testInheritanceInNativeClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/native/inheritanceInNativeClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt1519.kt")
|
||||
public void testKt1519() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/native/kt1519.kt");
|
||||
|
||||
@@ -8,7 +8,7 @@ package foo
|
||||
// CHECK_HAS_INLINE_METADATA: apply_hiyix$
|
||||
// CHECK_HAS_INLINE_METADATA: applyL_hiyix$
|
||||
// CHECK_HAS_INLINE_METADATA: applyM_hiyix$
|
||||
// CHECK_HAS_NO_INLINE_METADATA: applyN_0
|
||||
// CHECK_HAS_NO_INLINE_METADATA: applyN_hiyix$
|
||||
// CHECK_HAS_NO_INLINE_METADATA: applyO_hiyix$
|
||||
|
||||
inline
|
||||
|
||||
+22
@@ -1,3 +1,25 @@
|
||||
// MODULE: lib1
|
||||
// FILE: lib1.kt
|
||||
package lib1
|
||||
|
||||
interface A {
|
||||
private fun foo() = "A.foo"
|
||||
|
||||
fun bar() = foo()
|
||||
}
|
||||
|
||||
// MODULE: lib2
|
||||
// FILE: lib2.kt
|
||||
package lib2
|
||||
|
||||
interface B {
|
||||
private fun foo() = "B.foo"
|
||||
|
||||
fun bar() = foo()
|
||||
}
|
||||
|
||||
// MODULE: main(lib1,lib2)
|
||||
// FILE: main.kt
|
||||
package main
|
||||
|
||||
import lib1.A
|
||||
+24
-2
@@ -1,3 +1,25 @@
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
package lib
|
||||
|
||||
open class A {
|
||||
private val x = 23
|
||||
|
||||
fun foo() = x
|
||||
}
|
||||
|
||||
// MODULE: lib-old
|
||||
// FILE: lib.kt
|
||||
package lib
|
||||
|
||||
open class A {
|
||||
fun foo() = 12
|
||||
}
|
||||
|
||||
inline fun check() = true
|
||||
|
||||
// MODULE: main(lib-old)
|
||||
// FILE: main.kt
|
||||
package main
|
||||
|
||||
import lib.A
|
||||
@@ -14,10 +36,10 @@ class B : A() {
|
||||
// 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
|
||||
private fun checkJsNames(o: dynamic): Boolean = "x_i8qwny\$_0" in o && "x_0" in o
|
||||
|
||||
fun box(): String {
|
||||
if (!check()) return "check failed: did not compile agains old library"
|
||||
if (!check()) return "check failed: did not compile against old library"
|
||||
|
||||
val a = A()
|
||||
if (a.foo() != 23) return "fail1: ${a.foo()}"
|
||||
@@ -14,11 +14,9 @@ internal open class A(val a: Int) {
|
||||
internal class B(val b: Int) : A(b / 2) {
|
||||
override fun foo(i: Int): String = "B.foo($i: Int)"
|
||||
|
||||
fun boo(): String = "B.boo()"
|
||||
fun boo(i: String): String = "B.boo($i: String)"
|
||||
|
||||
fun bar(i: String): String = "B.bar($i: String)"
|
||||
fun bar(): String = "B.bar()"
|
||||
override fun baz(i: Int): String = "B.baz($i: Int)"
|
||||
fun bar(d: Double): String = "B.bar($d: Double)"
|
||||
}
|
||||
@@ -33,12 +31,10 @@ fun box(): String {
|
||||
if (b.foo(4) != "B.foo(4: Int)") return "b.foo(4) != \"B.foo(4: Int)\", it: ${b.foo(4)}"
|
||||
|
||||
if (b.boo(434) != "A.boo(434)") return "b.boo(434) != \"A.boo(434)\", it: ${b.boo(434)}"
|
||||
if (b.boo() != "B.boo()") return "b.boo() != \"B.boo()\", it: ${b.boo()}"
|
||||
if (b.boo("qlfj") != "B.boo(qlfj: String)") return "b.boo(\"qlfj\") != \"B.boo(qlfj: String)\", it: ${b.boo("qlfj")}"
|
||||
|
||||
if (b.bar("apl") != "B.bar(apl: String)") return "b.bar(\"apl\") != \"B.bar(apl: String)\", it: ${b.bar("apl")}"
|
||||
if (b.baz(34) != "B.baz(34: Int)") return "b.baz(34) != \"B.baz(34: Int)\", it: ${b.baz(34)}"
|
||||
if (b.bar() != "B.bar()") return "b.bar() != \"B.bar()\", it: ${b.bar()}"
|
||||
if (b.bar(2.213) != "B.bar(2.213: Double)") return "b.bar(2.213) != \"B.bar(2.213: Double)\", it: ${b.bar(2.213)}"
|
||||
|
||||
val a: A = b
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
function createA() {
|
||||
function ADerived() {
|
||||
}
|
||||
ADerived.prototype = Object.create(JS_TESTS.foo.A.prototype);
|
||||
ADerived.prototype.foo_za3lpa$ = function(n) {
|
||||
return 24;
|
||||
};
|
||||
return new ADerived();
|
||||
}
|
||||
|
||||
function createB() {
|
||||
function BDerived() {
|
||||
}
|
||||
BDerived.prototype = Object.create(JS_TESTS.foo.B.prototype);
|
||||
BDerived.prototype.bar_za3lpa$ = function(n) {
|
||||
return this.foo_za3lpa$(n);
|
||||
};
|
||||
return new BDerived();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package foo
|
||||
|
||||
open class A {
|
||||
open protected fun foo(n: Int) = 23
|
||||
|
||||
fun bar(n: Int) = foo(n) + 100
|
||||
}
|
||||
|
||||
open class B {
|
||||
protected fun foo(n: Int) = 42
|
||||
|
||||
open fun bar(n: Int) = 142
|
||||
}
|
||||
|
||||
@native fun createA(): A
|
||||
|
||||
@native fun createB(): B
|
||||
|
||||
fun box(): String {
|
||||
val a = createA()
|
||||
if (a.bar(0) != 124) return "fail1: ${a.bar(0)}"
|
||||
|
||||
val b = createB()
|
||||
if (b.bar(0) != 42) return "fail2: ${b.bar(0)}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -3,5 +3,5 @@ function A(v) {
|
||||
}
|
||||
|
||||
function nativeBox(b) {
|
||||
return b.bar_0(new A("foo"), function(i, s) { return "" + this.v + s + i })
|
||||
return b.bar_asnz92$(new A("foo"), function(i, s) { return "" + this.v + s + i })
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ fun box(): String {
|
||||
|
||||
val b: dynamic = object {val bar = ""}
|
||||
assertEquals(b.foo, undefined)
|
||||
assertNotEquals(b.bar_0, undefined)
|
||||
assertNotEquals(b.bar, undefined)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -38,8 +38,8 @@ fun box(): String {
|
||||
assertEquals(listOf(), getOwnPropertyNames(emptyObjectExpr))
|
||||
assertEquals(listOf(), keys(emptyObjectExpr))
|
||||
|
||||
assertEquals(listOf("foo_0", "bar_0"), getOwnPropertyNames(someObjectExpr))
|
||||
assertEquals(listOf("foo_0", "bar_0"), keys(someObjectExpr))
|
||||
assertEquals(listOf("foo", "bar"), getOwnPropertyNames(someObjectExpr))
|
||||
assertEquals(listOf("foo", "bar"), keys(someObjectExpr))
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
lib1->
|
||||
lib2->
|
||||
main->lib1,lib2
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package lib1
|
||||
|
||||
interface A {
|
||||
private fun foo() = "A.foo"
|
||||
|
||||
fun bar() = foo()
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package lib2
|
||||
|
||||
interface B {
|
||||
private fun foo() = "B.foo"
|
||||
|
||||
fun bar() = foo()
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
lib->
|
||||
lib-old->
|
||||
main->lib-old
|
||||
@@ -1,7 +0,0 @@
|
||||
package lib
|
||||
|
||||
open class A {
|
||||
fun foo() = 12
|
||||
}
|
||||
|
||||
inline fun check() = true
|
||||
@@ -1,7 +0,0 @@
|
||||
package lib
|
||||
|
||||
open class A {
|
||||
private val x = 23
|
||||
|
||||
fun foo() = x
|
||||
}
|
||||
Reference in New Issue
Block a user