KT-2752: refactor NameSuggestion, change rules for determining name stability and applying mangling

This commit is contained in:
Alexey Andreev
2016-09-21 16:17:49 +03:00
parent 6f7e7d8504
commit 9c7c82b151
21 changed files with 190 additions and 273 deletions
@@ -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
@@ -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
@@ -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
View File
@@ -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"
}
@@ -1,3 +0,0 @@
lib1->
lib2->
main->lib1,lib2
@@ -1,7 +0,0 @@
package lib1
interface A {
private fun foo() = "A.foo"
fun bar() = foo()
}
@@ -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
}