KT-2752: fixes after code review

This commit is contained in:
Alexey Andreev
2016-09-20 16:03:33 +03:00
parent 00867cb269
commit 6f7e7d8504
30 changed files with 380 additions and 138 deletions
@@ -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
}
}
@@ -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
@@ -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
}
@@ -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
@@ -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 {
@@ -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)
@@ -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);
}
}
@@ -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
View File
@@ -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
@@ -0,0 +1,3 @@
lib1->
lib2->
main->lib1,lib2
@@ -0,0 +1,7 @@
package lib1
interface A {
private fun foo() = "A.foo"
fun bar() = foo()
}
@@ -0,0 +1,7 @@
package lib2
interface B {
private fun foo() = "B.foo"
fun bar() = foo()
}
@@ -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"
}