diff --git a/.idea/misc.xml b/.idea/misc.xml
index d1ca2e70450..6e5ccca3e24 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -70,7 +70,7 @@
http://www.w3.org/1999/xhtml
-
+
diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt
index ce3ed60bb95..05465d3fd8c 100644
--- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt
+++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt
@@ -240,6 +240,9 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
if (extensionFunctionsInExternals) {
this[LanguageFeature.JsEnableExtensionFunctionInExternals] = LanguageFeature.State.ENABLED
}
+ if (!isIrBackendEnabled()) {
+ this[LanguageFeature.JsAllowInvalidCharsIdentifiersEscaping] = LanguageFeature.State.DISABLED
+ }
}
}
}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt
index b250451f10f..e27f9d98cdf 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt
@@ -98,7 +98,7 @@ class ExportModelGenerator(
private fun exportParameter(parameter: IrValueParameter): ExportedParameter {
// Parameter names do not matter in d.ts files. They can be renamed as we like
- var parameterName = sanitizeName(parameter.name.asString())
+ var parameterName = sanitizeName(parameter.name.asString(), withHash = false)
if (parameterName in allReservedWords)
parameterName = "_$parameterName"
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt
index e4aea9ef9da..c742cefde71 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsAstUtils
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.defineProperty
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsAssignment
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.prototypeOf
+import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsElementAccess
import org.jetbrains.kotlin.ir.backend.js.utils.IrNamer
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
@@ -26,19 +27,19 @@ class ExportModelToJsStatements(
return module.declarations.flatMap { generateDeclarationExport(it, JsNameRef(internalModuleName)) }
}
- fun generateDeclarationExport(declaration: ExportedDeclaration, namespace: JsNameRef?): List {
+ fun generateDeclarationExport(declaration: ExportedDeclaration, namespace: JsExpression?): List {
return when (declaration) {
is ExportedNamespace -> {
require(namespace != null) { "Only namespaced namespaces are allowed" }
val statements = mutableListOf()
val elements = declaration.name.split(".")
var currentNamespace = ""
- var currentRef: JsNameRef = namespace
+ var currentRef: JsExpression = namespace
for (element in elements) {
val newNamespace = "$currentNamespace$$element"
val newNameSpaceRef = namespaceToRefMap.getOrPut(newNamespace) {
val varName = JsName(declareNewNamespace(newNamespace), false)
- val namespaceRef = JsNameRef(element, currentRef)
+ val namespaceRef = jsElementAccess(element, currentRef)
statements += JsVars(
JsVars.JsVar(
varName,
@@ -66,7 +67,7 @@ class ExportModelToJsStatements(
} else {
listOf(
jsAssignment(
- JsNameRef(declaration.name, namespace),
+ jsElementAccess(declaration.name, namespace),
JsNameRef(name)
).makeStmt()
)
@@ -87,7 +88,7 @@ class ExportModelToJsStatements(
is ExportedClass -> {
if (declaration.isInterface) return emptyList()
- val newNameSpace = JsNameRef(declaration.name, namespace)
+ val newNameSpace = jsElementAccess(declaration.name, namespace)
val name = namer.getNameForStaticDeclaration(declaration.ir)
val klassExport =
if (namespace == null) {
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt
index 8a8153bcd7d..18a21cf0cc8 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.util.parentAsClass
+import org.jetbrains.kotlin.js.common.isValidES5Identifier
import org.jetbrains.kotlin.serialization.js.ModuleKind
// TODO: Support module kinds other than plain
@@ -22,7 +23,7 @@ fun wrapTypeScript(name: String, moduleKind: ModuleKind, dts: String): String {
val declarationsDts = types + dts
- val namespaceName = sanitizeName(name)
+ val namespaceName = sanitizeName(name, withHash = false)
return when (moduleKind) {
ModuleKind.PLAIN -> "declare namespace $namespaceName {\n$declarationsDts\n}\n"
@@ -73,8 +74,14 @@ fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): Strin
""
val renderedReturnType = returnType.toTypeScript(indent)
+ val containsUnresolvedChar = !name.isValidES5Identifier()
- "${prefix}$visibility$keyword$name$renderedTypeParameters($renderedParameters): $renderedReturnType;"
+ val escapedName = when {
+ isMember && containsUnresolvedChar -> "\"$name\""
+ else -> name
+ }
+
+ if (!isMember && containsUnresolvedChar) "" else "${prefix}$visibility$keyword$escapedName$renderedTypeParameters($renderedParameters): $renderedReturnType;"
}
is ExportedConstructor -> {
@@ -95,7 +102,12 @@ fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): Strin
else -> if (mutable) "let " else "const "
}
val possibleStatic = if (isMember && isStatic) "static " else ""
- "$prefix$visibility$possibleStatic$keyword$name: ${type.toTypeScript(indent)};"
+ val containsUnresolvedChar = !name.isValidES5Identifier()
+ val memberName = when {
+ isMember && containsUnresolvedChar -> "\"$name\""
+ else -> name
+ }
+ if (!isMember && containsUnresolvedChar) "" else "$prefix$visibility$possibleStatic$keyword$memberName: ${type.toTypeScript(indent)};"
}
is ExportedClass -> {
@@ -140,7 +152,8 @@ fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): Strin
val nestedClasses = nonInnerClasses + innerClasses.map { it.withProtectedConstructors() }
val klassExport = "$prefix$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$bodyString}"
val staticsExport = if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(indent, prefix) else ""
- klassExport + staticsExport
+
+ if (name.isValidES5Identifier()) klassExport + staticsExport else ""
}
}
@@ -192,7 +205,7 @@ fun ExportedClass.toReadonlyProperty(): ExportedProperty {
}
fun ExportedParameter.toTypeScript(indent: String): String =
- "$name: ${type.toTypeScript(indent)}"
+ "${sanitizeName(name, withHash = false)}: ${type.toTypeScript(indent)}"
fun ExportedType.toTypeScript(indent: String): String = when (this) {
is ExportedType.Primitive -> typescript
@@ -208,8 +221,8 @@ fun ExportedType.toTypeScript(indent: String): String = when (this) {
is ExportedType.TypeOf ->
"typeof $name"
- is ExportedType.ErrorType -> "any /*$comment*/"
is ExportedType.TypeParameter -> name
+ is ExportedType.ErrorType -> "any /*$comment*/"
is ExportedType.Nullable -> "Nullable<" + baseType.toTypeScript(indent) + ">"
is ExportedType.InlineInterfaceType -> {
members.joinToString(prefix = "{\n", postfix = "$indent}", separator = "") { it.toTypeScript("$indent ") + "\n" }
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt
index 837945b5b10..6645e64a797 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt
@@ -102,7 +102,8 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer(namer.globalNames)
val exportStatements = ExportModelToJsStatements(nameGenerator) { globalNames.declareFreshName(it, it) }
.generateModuleExport(exportedModule, internalModuleName)
@@ -163,6 +164,7 @@ class IrModuleToJsTransformer(
val program = JsProgram()
if (generateScriptModule) {
with(program.globalBlock) {
+ statements += globalThisDeclaration
statements.addWithComment("block: imports", importStatements + crossModuleImports)
statements += moduleBody
statements.addWithComment("block: exports", exportStatements + crossModuleExports)
@@ -172,6 +174,7 @@ class IrModuleToJsTransformer(
parameters += JsParameter(internalModuleName)
parameters += (importedJsModules + importedKotlinModules).map { JsParameter(it.internalName) }
with(body) {
+ statements += globalThisDeclaration
statements.addWithComment("block: imports", importStatements + crossModuleImports)
statements += moduleBody
statements.addWithComment("block: exports", exportStatements + crossModuleExports)
@@ -411,7 +414,7 @@ class IrModuleToJsTransformer(
assert(jsModule != null || jsQualifier != null)
- val qualifiedReference: JsNameRef
+ val qualifiedReference: JsExpression
if (jsModule != null) {
val internalName = declareFreshGlobal("\$module\$$jsModule")
@@ -433,7 +436,7 @@ class IrModuleToJsTransformer(
.forEach { declaration ->
val declName = getNameForExternalDeclaration(declaration)
importStatements.add(
- JsVars(JsVars.JsVar(declName, JsNameRef(declaration.getJsNameOrKotlinName().identifier, qualifiedReference)))
+ JsVars(JsVars.JsVar(declName, jsElementAccess(declaration.getJsNameOrKotlinName().identifier, qualifiedReference)))
)
}
}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt
index 63027c054e4..75b5a264a33 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.backend.ast.*
+import org.jetbrains.kotlin.js.common.isValidES5Identifier
import org.jetbrains.kotlin.utils.addIfNotNull
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
@@ -77,11 +78,13 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
properties.addIfNotNull(declaration.correspondingPropertySymbol?.owner)
if (es6mode) {
- val (_, function) = generateMemberFunction(declaration)
+ val (memberRef, function) = generateMemberFunction(declaration)
function?.let { jsClass.members += it }
+ declaration.generateAssignmentIfMangled(memberRef)
} else {
val (memberRef, function) = generateMemberFunction(declaration)
function?.let { classBlock.statements += jsAssignment(memberRef, it.apply { name = null }).makeStmt() }
+ declaration.generateAssignmentIfMangled(memberRef)
}
}
is IrClass -> {
@@ -204,6 +207,24 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
return classBlock
}
+ private fun IrSimpleFunction.generateAssignmentIfMangled(memberRef: JsExpression) {
+ if (
+ irClass.isExported(context.staticContext.backendContext) &&
+ visibility.isPublicAPI && hasMangledName() &&
+ correspondingPropertySymbol == null
+ ) {
+ classBlock.statements += jsAssignment(prototypeAccessRef(), memberRef).makeStmt()
+ }
+ }
+
+ private fun IrSimpleFunction.hasMangledName(): Boolean {
+ return getJsName() == null && !name.asString().isValidES5Identifier()
+ }
+
+ private fun IrSimpleFunction.prototypeAccessRef(): JsExpression {
+ return jsElementAccess(name.asString(), classPrototypeRef)
+ }
+
private fun IrSimpleFunction.overridesExternal(): Boolean {
if (this.isEffectivelyExternal()) return true
@@ -214,9 +235,9 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
return isInterface && !isEffectivelyExternal()
}
- private fun generateMemberFunction(declaration: IrSimpleFunction): Pair {
+ private fun generateMemberFunction(declaration: IrSimpleFunction): Pair {
val memberName = context.getNameForMemberFunction(declaration.realOverrideTarget)
- val memberRef = JsNameRef(memberName, classPrototypeRef)
+ val memberRef = jsElementAccess(memberName.ident, classPrototypeRef)
if (declaration.isReal && declaration.body != null) {
val translatedFunction: JsFunction = declaration.accept(IrFunctionToJsTransformer(), context)
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleWrapperTranslation.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleWrapperTranslation.kt
index a135b324d36..25b43ebf311 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleWrapperTranslation.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleWrapperTranslation.kt
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.utils.RESERVED_IDENTIFIERS
import org.jetbrains.kotlin.js.backend.ast.*
-import org.jetbrains.kotlin.js.naming.isValidES5Identifier
+import org.jetbrains.kotlin.js.common.isValidES5Identifier
import org.jetbrains.kotlin.serialization.js.ModuleKind
object ModuleWrapperTranslation {
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/MultiModuleSupport.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/MultiModuleSupport.kt
index 9d239909e40..7a2702fc8fa 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/MultiModuleSupport.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/MultiModuleSupport.kt
@@ -123,5 +123,5 @@ val IrModuleFragment.safeName: String
if (result.startsWith('<')) result = result.substring(1)
if (result.endsWith('>')) result = result.substring(0, result.length - 1)
- return sanitizeName("kotlin-$result")
+ return sanitizeName("kotlin_$result")
}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt
index 7a1dca7d5d7..a172e6c7886 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.js.backend.ast.*
+import org.jetbrains.kotlin.js.common.isValidES5Identifier
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addIfNotNull
@@ -43,6 +44,39 @@ fun IrWhen.toJsNode(
}
}
+// https://mathiasbynens.be/notes/globalthis
+// TODO: add DCE for globalThis polyfill declaration
+fun jsGlobalThisPolyfill(): List =
+ parseJsCode(
+ """
+ (function() {
+ if (typeof globalThis === 'object') return;
+ Object.defineProperty(Object.prototype, '__magic__', {
+ get: function() {
+ return this;
+ },
+ configurable: true
+ });
+ __magic__.globalThis = __magic__;
+ delete Object.prototype.__magic__;
+ }());
+ """.trimIndent()
+ ) ?: emptyList()
+
+fun jsElementAccess(name: String, receiver: JsExpression?): JsExpression =
+ if (receiver == null || name.isValidES5Identifier()) {
+ JsNameRef(JsName(name, false), receiver)
+ } else {
+ JsArrayAccess(receiver, JsStringLiteral(name))
+ }
+
+fun jsGlobalVarRef(ref: JsNameRef): JsExpression =
+ if (ref.qualifier != null || ref.ident.isValidES5Identifier()) {
+ ref
+ } else {
+ jsElementAccess(ref.ident, JsNameRef("globalThis"))
+ }
+
fun jsAssignment(left: JsExpression, right: JsExpression) = JsBinaryOperation(JsBinaryOperator.ASG, left, right)
fun prototypeOf(classNameRef: JsExpression) = JsNameRef(Namer.PROTOTYPE_NAME, classNameRef)
@@ -114,7 +148,11 @@ fun translateCall(
if (function.getJsName() == null) {
val property = function.correspondingPropertySymbol?.owner
if (property != null && property.isEffectivelyExternal()) {
- val nameRef = JsNameRef(context.getNameForProperty(property), jsDispatchReceiver)
+ val propertyName = context.getNameForProperty(property)
+ val nameRef = when (jsDispatchReceiver) {
+ null -> jsGlobalVarRef(JsNameRef(propertyName))
+ else -> jsElementAccess(propertyName.ident, jsDispatchReceiver)
+ }
return when (function) {
property.getter -> nameRef
property.setter -> jsAssignment(nameRef, arguments.single())
@@ -156,8 +194,8 @@ fun translateCall(
}
val ref = when (jsDispatchReceiver) {
- null -> JsNameRef(symbolName)
- else -> JsNameRef(symbolName, jsDispatchReceiver)
+ null -> jsGlobalVarRef(JsNameRef(symbolName))
+ else -> jsElementAccess(symbolName.ident, jsDispatchReceiver)
}
return if (isExternalVararg) {
@@ -174,7 +212,7 @@ fun translateCall(
if (jsDispatchReceiver != null) {
if (argumentsAsSingleArray is JsArrayLiteral) {
JsInvocation(
- JsNameRef(symbolName, jsDispatchReceiver),
+ jsElementAccess(symbolName.ident, jsDispatchReceiver),
argumentsAsSingleArray.expressions
)
} else {
@@ -188,7 +226,7 @@ fun translateCall(
JsVars(JsVars.JsVar(receiverName, jsDispatchReceiver)),
JsReturn(
JsInvocation(
- JsNameRef("apply", JsNameRef(symbolName, receiverRef)),
+ JsNameRef("apply", jsElementAccess(symbolName.ident, receiverRef)),
listOf(
receiverRef,
argumentsAsSingleArray
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt
index 9c5690c7916..c2af3a05fa5 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt
@@ -20,9 +20,9 @@ import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
-import org.jetbrains.kotlin.js.naming.isES5IdentifierPart
-import org.jetbrains.kotlin.js.naming.isES5IdentifierStart
-import org.jetbrains.kotlin.js.naming.isValidES5Identifier
+import org.jetbrains.kotlin.js.common.isES5IdentifierPart
+import org.jetbrains.kotlin.js.common.isES5IdentifierStart
+import org.jetbrains.kotlin.js.common.isValidES5Identifier
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import java.util.*
@@ -386,23 +386,30 @@ class LocalNameGenerator(val variableNames: NameTable) : IrElemen
}
}
-
-fun sanitizeName(name: String): String {
+fun sanitizeName(name: String, withHash: Boolean = true): String {
if (name.isValidES5Identifier()) return name
if (name.isEmpty()) return "_"
val builder = StringBuilder()
- val first = name.first().let { if (it.isES5IdentifierStart()) it else '_' }
- builder.append(first)
+ val first = name.first()
+
+ builder.append(first.mangleIfNot(Char::isES5IdentifierStart))
for (idx in 1..name.lastIndex) {
val c = name[idx]
- builder.append(if (c.isES5IdentifierPart()) c else '_')
+ builder.append(c.mangleIfNot(Char::isES5IdentifierPart))
}
- return builder.toString()
+ return if (withHash) {
+ "${builder}_${name.hashCode().toUInt()}"
+ } else {
+ builder.toString()
+ }
}
+private inline fun Char.mangleIfNot(predicate: Char.() -> Boolean) =
+ if (predicate()) this else '_'
+
private const val SYNTHETIC_LOOP_LABEL = "\$l\$loop"
private const val SYNTHETIC_BLOCK_LABEL = "\$l\$block"
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt
index 1a721cabff3..4d8b0d73534 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt
@@ -114,5 +114,5 @@ val RESERVED_IDENTIFIERS = setOf(
"Math", "String", "Boolean", "Date", "Array", "RegExp", "JSON", "Map",
// global identifiers usually declared in know environments (node.js, browser, require.js, WebWorkers, etc)
- "require", "define", "module", "window", "self"
+ "require", "define", "module", "window", "self", "globalThis"
)
diff --git a/compiler/testData/codegen/box/classes/quotedClassName.kt b/compiler/testData/codegen/box/classes/quotedClassName.kt
index 222edec08c6..f42928afbb2 100644
--- a/compiler/testData/codegen/box/classes/quotedClassName.kt
+++ b/compiler/testData/codegen/box/classes/quotedClassName.kt
@@ -1,8 +1,5 @@
-// IGNORE_BACKEND: WASM
-// WASM_MUTE_REASON: IGNORED_IN_JS
-// IGNORE_BACKEND: JS_IR
-// IGNORE_BACKEND: JS_IR_ES6
// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
// Exclamation marks are not valid in names in the dex file format.
// Therefore, do not attempt to dex this file as it will fail.
diff --git a/compiler/testData/codegen/box/localClasses/nameWithWhitespace.kt b/compiler/testData/codegen/box/localClasses/nameWithWhitespace.kt
index 81961c89dce..3607849f8da 100644
--- a/compiler/testData/codegen/box/localClasses/nameWithWhitespace.kt
+++ b/compiler/testData/codegen/box/localClasses/nameWithWhitespace.kt
@@ -1,8 +1,5 @@
-// IGNORE_BACKEND: WASM
-// WASM_MUTE_REASON: IGNORED_IN_JS
-// IGNORE_BACKEND: JS_IR
-// IGNORE_BACKEND: JS_IR_ES6
// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
// Names with spaces are not valid according to the dex specification
// before DEX version 040. Therefore, do not attempt to dex the resulting
diff --git a/compiler/testData/codegen/box/mangling/parentheses.kt b/compiler/testData/codegen/box/mangling/parentheses.kt
index 6fed9586acd..6462a803dab 100644
--- a/compiler/testData/codegen/box/mangling/parentheses.kt
+++ b/compiler/testData/codegen/box/mangling/parentheses.kt
@@ -1,8 +1,6 @@
-// IGNORE_BACKEND: WASM
-// WASM_MUTE_REASON: IGNORED_IN_JS
// !SANITIZE_PARENTHESES
-// IGNORE_BACKEND: JS, JS_IR
-// IGNORE_BACKEND: JS_IR_ES6
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
// Sanitization is needed here because DxChecker reports ParseException on parentheses in names.
diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalNameIR.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalNameIR.kt
new file mode 100644
index 00000000000..22eca4762a8
--- /dev/null
+++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalNameIR.kt
@@ -0,0 +1,29 @@
+// SKIP_TXT
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+private fun ` .private `(): String = TODO("")
+
+fun ` .public `(): String = TODO("")
+
+@JsName(" __ ")
+fun foo(): String = TODO("")
+
+@JsName(" ___ ")
+private fun bar(): String = TODO("")
+
+@JsName("validName")
+private fun ` .private with @JsName `(): String = TODO("")
+
+private class ` .private class ` {
+ val ` .field. ` = ""
+}
+
+val x: Int
+ @JsName(".")
+ get() = TODO("")
+
+fun box(x: dynamic) {
+ x.`foo-bar`()
+ x.`ba-z`
+}
\ No newline at end of file
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithJsStdLibGenerated.java
index 823ea6751de..0870bc56b31 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithJsStdLibGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithJsStdLibGenerated.java
@@ -637,6 +637,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes
runTest("compiler/testData/diagnostics/testsWithJsStdLib/name/illegalName.kt");
}
+ @Test
+ @TestMetadata("illegalNameIR.kt")
+ public void testIllegalNameIR() throws Exception {
+ runTest("compiler/testData/diagnostics/testsWithJsStdLib/name/illegalNameIR.kt");
+ }
+
@Test
@TestMetadata("illegalPackageName.kt")
public void testIllegalPackageName() throws Exception {
diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt
index e4019e6c961..ace963cbdbd 100644
--- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt
+++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt
@@ -262,6 +262,7 @@ enum class LanguageFeature(
ProhibitComparisonOfIncompatibleClasses(sinceVersion = null, kind = BUG_FIX, defaultState = State.DISABLED),
ExplicitBackingFields(sinceVersion = null, defaultState = State.DISABLED, kind = UNSTABLE_FEATURE),
FunctionalTypeWithExtensionAsSupertype(sinceVersion = KOTLIN_1_6, defaultState = State.DISABLED),
+ JsAllowInvalidCharsIdentifiersEscaping(sinceVersion = null, defaultState = State.DISABLED, kind = UNSTABLE_FEATURE)
;
diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/JsToStringGenerationVisitor.java b/js/js.ast/src/org/jetbrains/kotlin/js/backend/JsToStringGenerationVisitor.java
index ac41ade5fe1..86fe735557b 100644
--- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/JsToStringGenerationVisitor.java
+++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/JsToStringGenerationVisitor.java
@@ -8,6 +8,7 @@ import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.JsDoubleLiteral;
import org.jetbrains.kotlin.js.backend.ast.JsIntLiteral;
import org.jetbrains.kotlin.js.backend.ast.JsVars.JsVar;
+import org.jetbrains.kotlin.js.common.IdentifierPolicyKt;
import org.jetbrains.kotlin.js.util.TextOutput;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
@@ -895,7 +896,19 @@ public class JsToStringGenerationVisitor extends JsVisitor {
p.print(((JsNameRef) labelExpr).getIdent());
}
else if (labelExpr instanceof JsStringLiteral) {
- p.print(((JsStringLiteral) labelExpr).getValue());
+ JsStringLiteral stringLiteral = (JsStringLiteral) labelExpr;
+ String value = stringLiteral.getValue();
+ boolean isValidIdentifier = IdentifierPolicyKt.isValidES5Identifier(value);
+
+ if (!isValidIdentifier) {
+ p.print('\'');
+ }
+
+ p.print(value);
+
+ if (!isValidIdentifier) {
+ p.print('\'');
+ }
}
else {
accept(labelExpr);
diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/common/IdentifierPolicy.kt b/js/js.ast/src/org/jetbrains/kotlin/js/common/IdentifierPolicy.kt
new file mode 100644
index 00000000000..d4f78f968de
--- /dev/null
+++ b/js/js.ast/src/org/jetbrains/kotlin/js/common/IdentifierPolicy.kt
@@ -0,0 +1,52 @@
+package org.jetbrains.kotlin.js.common
+
+private fun Char.isAllowedLatinLetterOrSpecial(): Boolean {
+ return this in 'a'..'z' || this in 'A'..'Z' || this == '_' || this == '$'
+}
+
+private fun Char.isAllowedSimpleDigit() =
+ this in '0'..'9'
+
+private fun Char.isNotAllowedSimpleCharacter() = when (this) {
+ ' ', '<', '>', '-', '?' -> true
+ else -> false
+}
+
+fun Char.isES5IdentifierStart(): Boolean {
+ if (isAllowedLatinLetterOrSpecial()) return true
+ if (isNotAllowedSimpleCharacter()) return false
+
+ return isES5IdentifierStartFull()
+}
+
+// See ES 5.1 spec: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
+private fun Char.isES5IdentifierStartFull() =
+ Character.isLetter(this) || // Lu | Ll | Lt | Lm | Lo
+ // Nl which is missing in Character.isLetter, but present in UnicodeLetter in spec
+ Character.getType(this).toByte() == Character.LETTER_NUMBER
+
+
+fun Char.isES5IdentifierPart(): Boolean {
+ if (isAllowedLatinLetterOrSpecial()) return true
+ if (isAllowedSimpleDigit()) return true
+ if (isNotAllowedSimpleCharacter()) return false
+
+ return isES5IdentifierStartFull() ||
+ when (Character.getType(this).toByte()) {
+ Character.NON_SPACING_MARK,
+ Character.COMBINING_SPACING_MARK,
+ Character.DECIMAL_DIGIT_NUMBER,
+ Character.CONNECTOR_PUNCTUATION -> true
+ else -> false
+ } ||
+ this == '\u200C' || // Zero-width non-joiner
+ this == '\u200D' // Zero-width joiner
+}
+
+fun String.isValidES5Identifier(): Boolean {
+ if (isEmpty() || !this[0].isES5IdentifierStart()) return false
+ for (idx in 1 until length) {
+ if (!get(idx).isES5IdentifierPart()) return false
+ }
+ return true
+}
\ No newline at end of file
diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt
index cb9abc35d09..0b9e97cae12 100644
--- a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt
+++ b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt
@@ -19,6 +19,8 @@ package org.jetbrains.kotlin.js.naming
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
+import org.jetbrains.kotlin.js.common.isES5IdentifierPart
+import org.jetbrains.kotlin.js.common.isES5IdentifierStart
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.getNameForAnnotatedObject
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject
import org.jetbrains.kotlin.name.Name
@@ -358,55 +360,4 @@ class NameSuggestion {
return first.toString() + name.drop(1).map { if (it.isES5IdentifierPart()) it else '_' }.joinToString("")
}
}
-}
-
-private fun Char.isAllowedLatinLetterOrSpecial(): Boolean {
- return this in 'a'..'z' || this in 'A'..'Z' || this == '_' || this == '$'
-}
-
-private fun Char.isAllowedSimpleDigit() =
- this in '0'..'9'
-
-private fun Char.isNotAllowedSimpleCharacter() = when (this) {
- ' ', '<', '>', '-', '?' -> true
- else -> false
-}
-
-fun Char.isES5IdentifierStart(): Boolean {
- if (isAllowedLatinLetterOrSpecial()) return true
- if (isNotAllowedSimpleCharacter()) return false
-
- return isES5IdentifierStartFull()
-}
-
-// See ES 5.1 spec: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
-private fun Char.isES5IdentifierStartFull() =
- Character.isLetter(this) || // Lu | Ll | Lt | Lm | Lo
- // Nl which is missing in Character.isLetter, but present in UnicodeLetter in spec
- Character.getType(this).toByte() == Character.LETTER_NUMBER
-
-
-fun Char.isES5IdentifierPart(): Boolean {
- if (isAllowedLatinLetterOrSpecial()) return true
- if (isAllowedSimpleDigit()) return true
- if (isNotAllowedSimpleCharacter()) return false
-
- return isES5IdentifierStartFull() ||
- when (Character.getType(this).toByte()) {
- Character.NON_SPACING_MARK,
- Character.COMBINING_SPACING_MARK,
- Character.DECIMAL_DIGIT_NUMBER,
- Character.CONNECTOR_PUNCTUATION -> true
- else -> false
- } ||
- this == '\u200C' || // Zero-width non-joiner
- this == '\u200D' // Zero-width joiner
-}
-
-fun String.isValidES5Identifier(): Boolean {
- if (isEmpty() || !this[0].isES5IdentifierStart()) return false
- for (idx in 1 ..length-1) {
- if (!get(idx).isES5IdentifierPart()) return false
- }
- return true
}
\ No newline at end of file
diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt
index a6eef1a0486..1457b9f85e1 100644
--- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt
+++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt
@@ -17,28 +17,28 @@ import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.types.DynamicTypesAllowed
object JsPlatformConfigurator : PlatformConfiguratorBase(
- DynamicTypesAllowed(),
- additionalDeclarationCheckers = listOf(
- NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(),
- JsNameChecker, JsModuleChecker, JsExternalFileChecker,
- JsExternalChecker, JsInheritanceChecker, JsMultipleInheritanceChecker,
- JsRuntimeAnnotationChecker,
- JsDynamicDeclarationChecker,
- JsExportAnnotationChecker,
- JsExportDeclarationChecker
- ),
- additionalCallCheckers = listOf(
- JsModuleCallChecker,
- JsDynamicCallChecker,
- JsDefinedExternallyCallChecker,
- ),
- identifierChecker = JsIdentifierChecker
+ DynamicTypesAllowed(),
+ additionalDeclarationCheckers = listOf(
+ NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(),
+ JsNameChecker, JsModuleChecker, JsExternalFileChecker,
+ JsExternalChecker, JsInheritanceChecker, JsMultipleInheritanceChecker,
+ JsRuntimeAnnotationChecker,
+ JsDynamicDeclarationChecker,
+ JsExportAnnotationChecker,
+ JsExportDeclarationChecker
+ ),
+ additionalCallCheckers = listOf(
+ JsModuleCallChecker,
+ JsDynamicCallChecker,
+ JsDefinedExternallyCallChecker,
+ ),
) {
override fun configureModuleComponents(container: StorageComponentContainer) {
container.useInstance(NameSuggestion())
container.useImpl()
container.useImpl()
container.useImpl()
+ container.useImpl()
container.useImpl()
container.useImpl()
container.useInstance(JsModuleClassLiteralChecker)
diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsDynamicCallChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsDynamicCallChecker.kt
index 23789634e0a..77a40b3141f 100644
--- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsDynamicCallChecker.kt
+++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsDynamicCallChecker.kt
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.js.resolve.diagnostics
import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.js.naming.NameSuggestion
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
@@ -82,7 +83,9 @@ object JsDynamicCallChecker : CallChecker {
}
private fun checkIdentifier(name: String?, reportOn: PsiElement, context: CallCheckerContext) {
- if (name == null) return
+ if (name == null || context.languageVersionSettings.supportsFeature(LanguageFeature.JsAllowInvalidCharsIdentifiersEscaping)) {
+ return
+ }
if (NameSuggestion.sanitizeName(name) != name) {
context.trace.report(ErrorsJs.NAME_CONTAINS_ILLEGAL_CHARS.on(reportOn))
}
diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsIdentifierChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsIdentifierChecker.kt
index ab57618a4af..147300c6abb 100644
--- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsIdentifierChecker.kt
+++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsIdentifierChecker.kt
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.js.resolve.diagnostics
+import org.jetbrains.kotlin.config.LanguageFeature
+import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.js.naming.NameSuggestion
@@ -23,8 +25,11 @@ import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.resolve.IdentifierChecker
-object JsIdentifierChecker : IdentifierChecker {
+class JsIdentifierChecker(private val languageVersionSettings: LanguageVersionSettings) : IdentifierChecker {
override fun checkIdentifier(simpleNameExpression: KtSimpleNameExpression, diagnosticHolder: DiagnosticSink) {
+ if (languageVersionSettings.supportsFeature(LanguageFeature.JsAllowInvalidCharsIdentifiersEscaping)) {
+ return
+ }
val simpleName = simpleNameExpression.getReferencedName()
val hasIllegalChars = simpleName.split('.').any { NameSuggestion.sanitizeName(it) != it }
diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameCharsChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameCharsChecker.kt
index 083e301215a..39bd3274737 100644
--- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameCharsChecker.kt
+++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameCharsChecker.kt
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.js.resolve.diagnostics
+import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
@@ -16,6 +17,9 @@ import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
class JsNameCharsChecker(private val suggestion: NameSuggestion) : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
+ if (context.languageVersionSettings.supportsFeature(LanguageFeature.JsAllowInvalidCharsIdentifiersEscaping)) {
+ return
+ }
val bindingContext = context.trace.bindingContext
if (descriptor is PropertyAccessorDescriptor && AnnotationsUtils.getJsName(descriptor) == null) return
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java
index 734d162c87c..5eb5a5a23ac 100644
--- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java
@@ -2075,6 +2075,99 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
}
}
+ @TestMetadata("js/js.translator/testData/box/escapedIdentifiers")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class EscapedIdentifiers extends AbstractIrBoxJsES6Test {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInEscapedIdentifiers() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/escapedIdentifiers"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
+ }
+
+ @TestMetadata("classLikeMemberClassMangling.kt")
+ public void testClassLikeMemberClassMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/classLikeMemberClassMangling.kt");
+ }
+
+ @TestMetadata("classLikeMemberFieldMangling.kt")
+ public void testClassLikeMemberFieldMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFieldMangling.kt");
+ }
+
+ @TestMetadata("classLikeMemberFunctionMangling.kt")
+ public void testClassLikeMemberFunctionMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFunctionMangling.kt");
+ }
+
+ @TestMetadata("dynamicEscapedField.kt")
+ public void testDynamicEscapedField() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/dynamicEscapedField.kt");
+ }
+
+ @TestMetadata("externalEscapedAMDTopLevel.kt")
+ public void testExternalEscapedAMDTopLevel() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedAMDTopLevel.kt");
+ }
+
+ @TestMetadata("externalEscapedClassFields.kt")
+ public void testExternalEscapedClassFields() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedClassFields.kt");
+ }
+
+ @TestMetadata("externalEscapedCommonJSTopLevel.kt")
+ public void testExternalEscapedCommonJSTopLevel() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedCommonJSTopLevel.kt");
+ }
+
+ @TestMetadata("externalEscapedTopLevel.kt")
+ public void testExternalEscapedTopLevel() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.kt");
+ }
+
+ @TestMetadata("topLevelExportedClass.kt")
+ public void testTopLevelExportedClass() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedClass.kt");
+ }
+
+ @TestMetadata("topLevelExportedCompanion.kt")
+ public void testTopLevelExportedCompanion() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedCompanion.kt");
+ }
+
+ @TestMetadata("topLevelExportedFunction.kt")
+ public void testTopLevelExportedFunction() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedFunction.kt");
+ }
+
+ @TestMetadata("topLevelExportedVariable.kt")
+ public void testTopLevelExportedVariable() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedVariable.kt");
+ }
+
+ @TestMetadata("topLevelLocalClassMangling.kt")
+ public void testTopLevelLocalClassMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalClassMangling.kt");
+ }
+
+ @TestMetadata("topLevelLocalCompanionMangling.kt")
+ public void testTopLevelLocalCompanionMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalCompanionMangling.kt");
+ }
+
+ @TestMetadata("topLevelLocalFunctionMangling.kt")
+ public void testTopLevelLocalFunctionMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalFunctionMangling.kt");
+ }
+
+ @TestMetadata("topLevelLocalVariableMangling.kt")
+ public void testTopLevelLocalVariableMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalVariableMangling.kt");
+ }
+ }
+
@TestMetadata("js/js.translator/testData/box/examples")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsTypeScriptExportES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsTypeScriptExportES6TestGenerated.java
index 57379b2599d..022d17388a2 100644
--- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsTypeScriptExportES6TestGenerated.java
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsTypeScriptExportES6TestGenerated.java
@@ -84,6 +84,24 @@ public class IrJsTypeScriptExportES6TestGenerated extends AbstractIrJsTypeScript
}
}
+ @TestMetadata("js/js.translator/testData/typescript-export/escapedDeclarations")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class EscapedDeclarations extends AbstractIrJsTypeScriptExportES6Test {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInEscapedDeclarations() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/escapedDeclarations"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
+ }
+
+ @TestMetadata("escapedDeclarations.kt")
+ public void testEscapedDeclarations() throws Exception {
+ runTest("js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations.kt");
+ }
+ }
+
@TestMetadata("js/js.translator/testData/typescript-export/inheritance")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java
index 330f3428b61..8f3744be978 100644
--- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java
@@ -2070,6 +2070,99 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
}
}
+ @TestMetadata("js/js.translator/testData/box/escapedIdentifiers")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class EscapedIdentifiers extends AbstractIrBoxJsTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInEscapedIdentifiers() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/escapedIdentifiers"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
+ }
+
+ @TestMetadata("classLikeMemberClassMangling.kt")
+ public void testClassLikeMemberClassMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/classLikeMemberClassMangling.kt");
+ }
+
+ @TestMetadata("classLikeMemberFieldMangling.kt")
+ public void testClassLikeMemberFieldMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFieldMangling.kt");
+ }
+
+ @TestMetadata("classLikeMemberFunctionMangling.kt")
+ public void testClassLikeMemberFunctionMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFunctionMangling.kt");
+ }
+
+ @TestMetadata("dynamicEscapedField.kt")
+ public void testDynamicEscapedField() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/dynamicEscapedField.kt");
+ }
+
+ @TestMetadata("externalEscapedAMDTopLevel.kt")
+ public void testExternalEscapedAMDTopLevel() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedAMDTopLevel.kt");
+ }
+
+ @TestMetadata("externalEscapedClassFields.kt")
+ public void testExternalEscapedClassFields() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedClassFields.kt");
+ }
+
+ @TestMetadata("externalEscapedCommonJSTopLevel.kt")
+ public void testExternalEscapedCommonJSTopLevel() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedCommonJSTopLevel.kt");
+ }
+
+ @TestMetadata("externalEscapedTopLevel.kt")
+ public void testExternalEscapedTopLevel() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.kt");
+ }
+
+ @TestMetadata("topLevelExportedClass.kt")
+ public void testTopLevelExportedClass() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedClass.kt");
+ }
+
+ @TestMetadata("topLevelExportedCompanion.kt")
+ public void testTopLevelExportedCompanion() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedCompanion.kt");
+ }
+
+ @TestMetadata("topLevelExportedFunction.kt")
+ public void testTopLevelExportedFunction() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedFunction.kt");
+ }
+
+ @TestMetadata("topLevelExportedVariable.kt")
+ public void testTopLevelExportedVariable() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedVariable.kt");
+ }
+
+ @TestMetadata("topLevelLocalClassMangling.kt")
+ public void testTopLevelLocalClassMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalClassMangling.kt");
+ }
+
+ @TestMetadata("topLevelLocalCompanionMangling.kt")
+ public void testTopLevelLocalCompanionMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalCompanionMangling.kt");
+ }
+
+ @TestMetadata("topLevelLocalFunctionMangling.kt")
+ public void testTopLevelLocalFunctionMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalFunctionMangling.kt");
+ }
+
+ @TestMetadata("topLevelLocalVariableMangling.kt")
+ public void testTopLevelLocalVariableMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalVariableMangling.kt");
+ }
+ }
+
@TestMetadata("js/js.translator/testData/box/examples")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsTypeScriptExportTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsTypeScriptExportTestGenerated.java
index 31d8629c971..207acde5625 100644
--- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsTypeScriptExportTestGenerated.java
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsTypeScriptExportTestGenerated.java
@@ -84,6 +84,24 @@ public class IrJsTypeScriptExportTestGenerated extends AbstractIrJsTypeScriptExp
}
}
+ @TestMetadata("js/js.translator/testData/typescript-export/escapedDeclarations")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class EscapedDeclarations extends AbstractIrJsTypeScriptExportTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInEscapedDeclarations() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/escapedDeclarations"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
+ }
+
+ @TestMetadata("escapedDeclarations.kt")
+ public void testEscapedDeclarations() throws Exception {
+ runTest("js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations.kt");
+ }
+ }
+
@TestMetadata("js/js.translator/testData/typescript-export/inheritance")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testNew/BoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testNew/BoxJsTestGenerated.java
index 809a598b062..1a56ebd8e5c 100644
--- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testNew/BoxJsTestGenerated.java
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testNew/BoxJsTestGenerated.java
@@ -1915,6 +1915,112 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
}
}
+ @Nested
+ @TestMetadata("js/js.translator/testData/box/escapedIdentifiers")
+ @TestDataPath("$PROJECT_ROOT")
+ public class EscapedIdentifiers {
+ @Test
+ public void testAllFilesPresentInEscapedIdentifiers() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/escapedIdentifiers"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true);
+ }
+
+ @Test
+ @TestMetadata("classLikeMemberClassMangling.kt")
+ public void testClassLikeMemberClassMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/classLikeMemberClassMangling.kt");
+ }
+
+ @Test
+ @TestMetadata("classLikeMemberFieldMangling.kt")
+ public void testClassLikeMemberFieldMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFieldMangling.kt");
+ }
+
+ @Test
+ @TestMetadata("classLikeMemberFunctionMangling.kt")
+ public void testClassLikeMemberFunctionMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFunctionMangling.kt");
+ }
+
+ @Test
+ @TestMetadata("dynamicEscapedField.kt")
+ public void testDynamicEscapedField() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/dynamicEscapedField.kt");
+ }
+
+ @Test
+ @TestMetadata("externalEscapedAMDTopLevel.kt")
+ public void testExternalEscapedAMDTopLevel() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedAMDTopLevel.kt");
+ }
+
+ @Test
+ @TestMetadata("externalEscapedClassFields.kt")
+ public void testExternalEscapedClassFields() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedClassFields.kt");
+ }
+
+ @Test
+ @TestMetadata("externalEscapedCommonJSTopLevel.kt")
+ public void testExternalEscapedCommonJSTopLevel() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedCommonJSTopLevel.kt");
+ }
+
+ @Test
+ @TestMetadata("externalEscapedTopLevel.kt")
+ public void testExternalEscapedTopLevel() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.kt");
+ }
+
+ @Test
+ @TestMetadata("topLevelExportedClass.kt")
+ public void testTopLevelExportedClass() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedClass.kt");
+ }
+
+ @Test
+ @TestMetadata("topLevelExportedCompanion.kt")
+ public void testTopLevelExportedCompanion() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedCompanion.kt");
+ }
+
+ @Test
+ @TestMetadata("topLevelExportedFunction.kt")
+ public void testTopLevelExportedFunction() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedFunction.kt");
+ }
+
+ @Test
+ @TestMetadata("topLevelExportedVariable.kt")
+ public void testTopLevelExportedVariable() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelExportedVariable.kt");
+ }
+
+ @Test
+ @TestMetadata("topLevelLocalClassMangling.kt")
+ public void testTopLevelLocalClassMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalClassMangling.kt");
+ }
+
+ @Test
+ @TestMetadata("topLevelLocalCompanionMangling.kt")
+ public void testTopLevelLocalCompanionMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalCompanionMangling.kt");
+ }
+
+ @Test
+ @TestMetadata("topLevelLocalFunctionMangling.kt")
+ public void testTopLevelLocalFunctionMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalFunctionMangling.kt");
+ }
+
+ @Test
+ @TestMetadata("topLevelLocalVariableMangling.kt")
+ public void testTopLevelLocalVariableMangling() throws Exception {
+ runTest("js/js.translator/testData/box/escapedIdentifiers/topLevelLocalVariableMangling.kt");
+ }
+ }
+
@Nested
@TestMetadata("js/js.translator/testData/box/examples")
@TestDataPath("$PROJECT_ROOT")
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java
index ec5eda34840..57d17f5a77e 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java
@@ -31,9 +31,9 @@ import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind;
import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction;
+import org.jetbrains.kotlin.js.common.IdentifierPolicyKt;
import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.naming.NameSuggestion;
-import org.jetbrains.kotlin.js.naming.NameSuggestionKt;
import org.jetbrains.kotlin.js.naming.SuggestedName;
import org.jetbrains.kotlin.js.resolve.diagnostics.JsBuiltinNameClashChecker;
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver;
@@ -843,7 +843,7 @@ public final class StaticContext {
JsExpression currentImports = pureFqn(getNameForImportsForInline(), null);
JsExpression lhsModuleRef;
- if (NameSuggestionKt.isValidES5Identifier(moduleId)) {
+ if (IdentifierPolicyKt.isValidES5Identifier(moduleId)) {
moduleRef = pureFqn(moduleId, importsRef);
lhsModuleRef = pureFqn(moduleId, currentImports);
}
diff --git a/js/js.translator/testData/box/escapedIdentifiers/classLikeMemberClassMangling.kt b/js/js.translator/testData/box/escapedIdentifiers/classLikeMemberClassMangling.kt
new file mode 100644
index 00000000000..7a5211b1826
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/classLikeMemberClassMangling.kt
@@ -0,0 +1,23 @@
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+package foo
+
+@JsExport()
+class A {
+ class `$invalid inner` {}
+}
+
+class B {
+ class `$invalid inner` {}
+}
+
+fun box(): String {
+ // DCE preventing
+ val b = B()
+
+ assertEquals("function", js("typeof A['\$invalid inner']"))
+ assertEquals(js("undefined"), js("B['\$invalid inner']"))
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFieldMangling.kt b/js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFieldMangling.kt
new file mode 100644
index 00000000000..b216972e0c4
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFieldMangling.kt
@@ -0,0 +1,47 @@
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+package foo
+
+@JsExport()
+class A {
+ val `#invalid@char value`: Int = 41
+ val __invalid_char_value: Int = 23
+
+ var `--invalud char@var`: String = "A: before"
+}
+
+class B {
+ val `#invalid@char value`: Int = 42
+ val __invalid_char_value: Int = 24
+
+ var `--invalud char@var`: String = "B: before"
+}
+
+fun box(): String {
+ val a = A()
+ val b = B()
+
+ assertEquals(23, a.__invalid_char_value)
+ assertEquals(24, b.__invalid_char_value)
+
+ assertEquals(41, a.`#invalid@char value`)
+ assertEquals(42, b.`#invalid@char value`)
+
+ assertEquals("A: before", a.`--invalud char@var`)
+ assertEquals("B: before", b.`--invalud char@var`)
+
+ a.`--invalud char@var` = "A: after"
+ b.`--invalud char@var` = "B: after"
+
+ assertEquals("A: after", a.`--invalud char@var`)
+ assertEquals("B: after", b.`--invalud char@var`)
+
+ assertEquals(41, js("a['#invalid@char value']"))
+ assertEquals(js("undefined"), js("b['#invalid@char value']"))
+
+ assertEquals("A: after", js("a['--invalud char@var']"))
+ assertEquals(js("undefined"), js("b['--invalud char@var']"))
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFunctionMangling.kt b/js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFunctionMangling.kt
new file mode 100644
index 00000000000..83d95e1fbc3
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/classLikeMemberFunctionMangling.kt
@@ -0,0 +1,47 @@
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+package foo
+
+interface IA {
+ fun `run•invalid@test`(): Int
+ fun `run@invalid@test`(): Int
+ fun run_invalid_test(): Int
+}
+
+@JsExport()
+class A : IA {
+ override fun `run•invalid@test`(): Int = 41
+ override fun `run@invalid@test`(): Int = 34
+ override fun run_invalid_test(): Int = 23
+}
+
+class B : IA {
+ override fun `run•invalid@test`(): Int = 42
+ override fun `run@invalid@test`(): Int = 35
+ override fun run_invalid_test(): Int = 24
+}
+
+fun box(): String {
+ val a: IA = A()
+ val b: IA = B()
+
+ assertEquals(23, a.run_invalid_test())
+ assertEquals(24, b.run_invalid_test())
+
+ assertEquals(34, a.`run@invalid@test`())
+ assertEquals(35, b.`run@invalid@test`())
+
+ assertEquals(41, a.`run•invalid@test`())
+ assertEquals(42, b.`run•invalid@test`())
+
+ assertEquals("function", js("typeof a['run•invalid@test']"))
+ assertEquals(41, js("a['run•invalid@test']()"))
+ assertEquals(js("undefined"), js("b['run•invalid@test']"))
+
+ assertEquals("function", js("typeof a['run@invalid@test']"))
+ assertEquals(34, js("a['run@invalid@test']()"))
+ assertEquals(js("undefined"), js("b['run@invalid@test']"))
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/dynamicEscapedField.kt b/js/js.translator/testData/box/escapedIdentifiers/dynamicEscapedField.kt
new file mode 100644
index 00000000000..1e579b23e6a
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/dynamicEscapedField.kt
@@ -0,0 +1,11 @@
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+package foo
+
+fun box(): String {
+ val a: dynamic = js("{ \"--invalid--property@\": 42 }")
+ assertEquals(42, a.`--invalid--property@`)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/externalEscapedAMDTopLevel.js b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedAMDTopLevel.js
new file mode 100644
index 00000000000..5cffd74218d
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedAMDTopLevel.js
@@ -0,0 +1,11 @@
+define("lib", [], function() {
+ return {
+ "@get something-invalid"() {
+ return "something invalid"
+ },
+ "some+value": 42,
+ "+some+object%:": {
+ foo: "%%++%%"
+ }
+ };
+});
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/externalEscapedAMDTopLevel.kt b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedAMDTopLevel.kt
new file mode 100644
index 00000000000..1e3d79c2b9c
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedAMDTopLevel.kt
@@ -0,0 +1,28 @@
+// IGNORE_BACKEND: JS
+// MODULE_KIND: COMMON_JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+// FILE: lib.kt
+@file:JsModule("lib")
+package lib
+
+external fun `@get something-invalid`(): String = definedExternally
+
+external val `some+value`: Int = definedExternally
+
+external object `+some+object%:` {
+ val foo: String = definedExternally
+}
+
+// FILE: main.kt
+import lib.`some+value`
+import lib.`@get something-invalid`
+import lib.`+some+object%:`
+
+fun box(): String {
+ assertEquals(42, `some+value`)
+ assertEquals("%%++%%", `+some+object%:`.foo)
+ assertEquals("something invalid", `@get something-invalid`())
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/externalEscapedClassFields.js b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedClassFields.js
new file mode 100644
index 00000000000..2de1cd1fb6d
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedClassFields.js
@@ -0,0 +1,15 @@
+function A() {
+ this["@invalid @ val@"] = 23
+ this["--invalid-var"] = "A: before"
+}
+
+A.prototype["get something$weird"] = function() {
+ return "something weird"
+}
+
+A["static val"] = 42
+A["static var"] = "Companion: before"
+
+A["get 🦄"] = function() {
+ return "🦄"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/externalEscapedClassFields.kt b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedClassFields.kt
new file mode 100644
index 00000000000..bbc0f52c7de
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedClassFields.kt
@@ -0,0 +1,39 @@
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+package foo
+
+external class A {
+ val `@invalid @ val@`: Int = definedExternally
+ var `--invalid-var`: String = definedExternally
+
+ fun `get something$weird`(): String = definedExternally
+
+ companion object {
+ val `static val`: Int = definedExternally
+ var `static var`: String = definedExternally
+
+ fun `get 🦄`(): String = definedExternally
+ }
+}
+
+fun box(): String {
+ val a = A()
+
+ assertEquals(23, a.`@invalid @ val@`)
+ assertEquals("A: before", a.`--invalid-var`)
+ assertEquals("something weird", a.`get something$weird`())
+
+ a.`--invalid-var` = "A: after"
+ assertEquals("A: after", a.`--invalid-var`)
+
+ assertEquals(42, A.Companion.`static val`)
+ assertEquals("Companion: before", A.Companion.`static var`)
+ assertEquals("\uD83E\uDD84", A.Companion.`get 🦄`())
+
+ A.`static var` = "Companion: after"
+
+ assertEquals("Companion: after", A.Companion.`static var`)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/externalEscapedCommonJSTopLevel.js b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedCommonJSTopLevel.js
new file mode 100644
index 00000000000..2ad791941ea
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedCommonJSTopLevel.js
@@ -0,0 +1,11 @@
+$kotlin_test_internal$.beginModule("lib");
+module.exports = {
+ "@get something-invalid"() {
+ return "something invalid"
+ },
+ "some+value": 42,
+ "+some+object%:": {
+ foo: "%%++%%"
+ }
+}
+$kotlin_test_internal$.endModule("lib");
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/externalEscapedCommonJSTopLevel.kt b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedCommonJSTopLevel.kt
new file mode 100644
index 00000000000..1e3d79c2b9c
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedCommonJSTopLevel.kt
@@ -0,0 +1,28 @@
+// IGNORE_BACKEND: JS
+// MODULE_KIND: COMMON_JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+// FILE: lib.kt
+@file:JsModule("lib")
+package lib
+
+external fun `@get something-invalid`(): String = definedExternally
+
+external val `some+value`: Int = definedExternally
+
+external object `+some+object%:` {
+ val foo: String = definedExternally
+}
+
+// FILE: main.kt
+import lib.`some+value`
+import lib.`@get something-invalid`
+import lib.`+some+object%:`
+
+fun box(): String {
+ assertEquals(42, `some+value`)
+ assertEquals("%%++%%", `+some+object%:`.foo)
+ assertEquals("something invalid", `@get something-invalid`())
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.js b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.js
new file mode 100644
index 00000000000..b61776df451
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.js
@@ -0,0 +1,9 @@
+this["@get something-invalid"] = function() {
+ return "something invalid"
+}
+
+this["some+value"] = 42
+
+this["+some+object%:"] = {
+ foo: "%%++%%"
+}
diff --git a/js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.kt b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.kt
new file mode 100644
index 00000000000..bb1c6422896
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/externalEscapedTopLevel.kt
@@ -0,0 +1,23 @@
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+package foo
+
+external fun `@get something-invalid`(): String = definedExternally
+
+external var `some+value`: Int
+ get() = definedExternally
+ set(a: Int) = definedExternally
+
+external object `+some+object%:` {
+ val foo: String = definedExternally
+}
+
+fun box(): String {
+ val a = js("2")
+ assertEquals(42, `some+value`)
+ assertEquals("%%++%%", `+some+object%:`.foo)
+ assertEquals("something invalid", `@get something-invalid`())
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedClass.kt b/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedClass.kt
new file mode 100644
index 00000000000..1cb65937bda
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedClass.kt
@@ -0,0 +1,24 @@
+// IGNORE_BACKEND: JS
+// RUN_PLAIN_BOX_FUNCTION
+// INFER_MAIN_MODULE
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+// MODULE: export_invalid_name_class
+// FILE: lib.kt
+
+@JsExport
+class `invalid@class-name `() {
+ fun foo(): Int = 42
+}
+
+// FILE: test.js
+function box() {
+ var InvalidClass = this["export_invalid_name_class"]["invalid@class-name "]
+ var instance = new InvalidClass()
+ var value = instance.foo()
+
+ if (value !== 42)
+ return "false: expect exproted class function to return 42 but it equals " + value
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedCompanion.kt b/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedCompanion.kt
new file mode 100644
index 00000000000..bcc539bd96d
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedCompanion.kt
@@ -0,0 +1,30 @@
+// IGNORE_BACKEND: JS
+// RUN_PLAIN_BOX_FUNCTION
+// INFER_MAIN_MODULE
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+// MODULE: export_invalid_name_class
+// FILE: lib.kt
+
+@JsExport
+class `invalid@class-name `() {
+ companion object {
+ val `@invalid val@`: Int = 23
+ fun `invalid fun`(): Int = 42
+ }
+}
+
+// FILE: test.js
+function box() {
+ var InvalidClass = this["export_invalid_name_class"]["invalid@class-name "]
+
+ if (InvalidClass.Companion["@invalid val@"] !== 23)
+ return "false: expect exproted class static variable '@invalid val@' to be 23 but it equals " + InvalidClass.Companion["@invalud val@"]
+
+ var result = InvalidClass.Companion["invalid fun"]()
+
+ if (result !== 42)
+ return "false: expect exproted class static function 'invalid fun' to return 23 but it equals " + result
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedFunction.kt b/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedFunction.kt
new file mode 100644
index 00000000000..267673b4cf0
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedFunction.kt
@@ -0,0 +1,20 @@
+// IGNORE_BACKEND: JS
+// RUN_PLAIN_BOX_FUNCTION
+// INFER_MAIN_MODULE
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+// MODULE: export_invalid_name_function
+// FILE: lib.kt
+
+@JsExport
+fun `@do something like-this`(): Int = 42
+
+// FILE: test.js
+function box() {
+ var value = this["export_invalid_name_function"]["@do something like-this"]()
+
+ if (value !== 42)
+ return "false: expect exproted function '@do something like-this' to return 42 but it equals " + value
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedVariable.kt b/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedVariable.kt
new file mode 100644
index 00000000000..b1d4e4b0bd3
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/topLevelExportedVariable.kt
@@ -0,0 +1,20 @@
+// IGNORE_BACKEND: JS
+// RUN_PLAIN_BOX_FUNCTION
+// INFER_MAIN_MODULE
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+// MODULE: export_invalid_name_variable
+// FILE: lib.kt
+
+@JsExport
+val `my@invalid variable` = 42
+
+// FILE: test.js
+function box() {
+ var variableValue = this["export_invalid_name_variable"]["my@invalid variable"]
+
+ if (variableValue !== 42)
+ return "false: expect exproted 'my@invalid variable' to be 42 but it equals " + variableValue
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalClassMangling.kt b/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalClassMangling.kt
new file mode 100644
index 00000000000..1fc8454a317
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalClassMangling.kt
@@ -0,0 +1,25 @@
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+package foo
+
+class class_with_invalid_chars {
+ fun foo(): Int = 23
+}
+
+class `class@with$invalid chars` {
+ fun foo(): Int = 42
+}
+
+fun box(): String {
+ val a = class_with_invalid_chars()
+ val b = `class@with$invalid chars`()
+
+ assertEquals(true, a is class_with_invalid_chars)
+ assertEquals(true, b is `class@with$invalid chars`)
+
+ assertEquals(23, a.foo())
+ assertEquals(42, b.foo())
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalCompanionMangling.kt b/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalCompanionMangling.kt
new file mode 100644
index 00000000000..4a1709f675c
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalCompanionMangling.kt
@@ -0,0 +1,23 @@
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+package foo
+
+class class_with_invalid_chars {
+ companion object {
+ fun foo(): Int = 23
+ }
+}
+
+class `class@with$invalid chars` {
+ companion object {
+ fun foo(): Int = 42
+ }
+}
+
+fun box(): String {
+ assertEquals(23, class_with_invalid_chars.foo())
+ assertEquals(42, `class@with$invalid chars`.foo())
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalFunctionMangling.kt b/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalFunctionMangling.kt
new file mode 100644
index 00000000000..4c1055a8055
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalFunctionMangling.kt
@@ -0,0 +1,20 @@
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+package foo
+
+fun _my_fn(a: Int): Int { return a }
+fun `my fn`(): Int { return 42 }
+
+fun box(): String {
+ val fn1 = ::_my_fn
+ val fn2 = ::`my fn`
+
+ assertEquals("_my_fn", fn1.name)
+ assertEquals("my fn", fn2.name)
+
+ assertEquals(23, _my_fn(23))
+ assertEquals(42, `my fn`())
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalVariableMangling.kt b/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalVariableMangling.kt
new file mode 100644
index 00000000000..42f7a2ec2c5
--- /dev/null
+++ b/js/js.translator/testData/box/escapedIdentifiers/topLevelLocalVariableMangling.kt
@@ -0,0 +1,13 @@
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+
+package foo
+
+val _my_invalid_variable = 23
+val `my@invalid variable` = 42
+
+fun box(): String {
+ assertEquals(23, _my_invalid_variable)
+ assertEquals(42, `my@invalid variable`)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/box/inline/privateProperty.kt b/js/js.translator/testData/box/inline/privateProperty.kt
index 0cdf20fd2c2..80b3ecbfa4f 100644
--- a/js/js.translator/testData/box/inline/privateProperty.kt
+++ b/js/js.translator/testData/box/inline/privateProperty.kt
@@ -7,10 +7,10 @@ var a = 0
// CHECK_NOT_CALLED_IN_SCOPE: function=get_p1 scope=box TARGET_BACKENDS=JS
// CHECK_FUNCTION_EXISTS: set_p1 TARGET_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=set_p1 scope=box TARGET_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _get_p1_ IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _get_p1__59117 IGNORED_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=_get_p1_ scope=box IGNORED_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _set_p1_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p1_ scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _set_p1__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p1__59117 scope=box IGNORED_BACKENDS=JS
private inline var p1: Int
get() = a + 10000
set(v) {
@@ -20,9 +20,9 @@ private inline var p1: Int
// CHECK_FUNCTION_EXISTS: get_p2 TARGET_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=get_p2 scope=box TARGET_BACKENDS=JS
// CHECK_CALLED_IN_SCOPE: function=set_p2 scope=box TARGET_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _get_p2_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_get_p2_ scope=box IGNORED_BACKENDS=JS
-// CHECK_CALLED_IN_SCOPE: function=_set_p2_ scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _get_p2__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_get_p2__59117 scope=box IGNORED_BACKENDS=JS
+// CHECK_CALLED_IN_SCOPE: function=_set_p2__59117 scope=box IGNORED_BACKENDS=JS
private var p2: Int
inline get() = a + 20000
set(v) {
@@ -32,9 +32,9 @@ private var p2: Int
// CHECK_CALLED_IN_SCOPE: function=get_p3 scope=box TARGET_BACKENDS=JS
// CHECK_FUNCTION_EXISTS: set_p3 TARGET_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=set_p3 scope=box TARGET_BACKENDS=JS
-// CHECK_CALLED_IN_SCOPE: function=_get_p3_ scope=box IGNORED_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _set_p3_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p3_ scope=box IGNORED_BACKENDS=JS
+// CHECK_CALLED_IN_SCOPE: function=_get_p3__59117 scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _set_p3__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p3__59117 scope=box IGNORED_BACKENDS=JS
var p3: Int
get() = a + 30000
private inline set(v) {
@@ -44,9 +44,9 @@ var p3: Int
// CHECK_CALLED_IN_SCOPE: function=get_p4 scope=box TARGET_BACKENDS=JS
// CHECK_FUNCTION_EXISTS: set_p4 TARGET_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=set_p4 scope=box TARGET_BACKENDS=JS
-// CHECK_CALLED_IN_SCOPE: function=_get_p4_ scope=box IGNORED_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _set_p4_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p4_ scope=box IGNORED_BACKENDS=JS
+// CHECK_CALLED_IN_SCOPE: function=_get_p4__59117 scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _set_p4__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p4__59117 scope=box IGNORED_BACKENDS=JS
private var p4: Int
get() = a + 40000
inline set(v) {
@@ -57,10 +57,10 @@ private var p4: Int
// CHECK_NOT_CALLED_IN_SCOPE: function=get_p5 scope=box TARGET_BACKENDS=JS
// CHECK_FUNCTION_EXISTS: set_p5 TARGET_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=set_p5 scope=box TARGET_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _get_p5_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_get_p5_ scope=box IGNORED_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _set_p5_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p5_ scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _get_p5__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_get_p5__59117 scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _set_p5__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p5__59117 scope=box IGNORED_BACKENDS=JS
private inline var Int.p5: Int
get() = this * 100 + a + 50000
set(v) {
@@ -70,9 +70,9 @@ private inline var Int.p5: Int
// CHECK_FUNCTION_EXISTS: get_p6 TARGET_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=get_p6 scope=box TARGET_BACKENDS=JS
// CHECK_CALLED_IN_SCOPE: function=set_p6 scope=box TARGET_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _get_p6_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_get_p6_ scope=box IGNORED_BACKENDS=JS
-// CHECK_CALLED_IN_SCOPE: function=_set_p6_ scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _get_p6__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_get_p6__59117 scope=box IGNORED_BACKENDS=JS
+// CHECK_CALLED_IN_SCOPE: function=_set_p6__59117 scope=box IGNORED_BACKENDS=JS
private var Int.p6: Int
inline get() = this * 100 + a + 60000
set(v) {
@@ -82,9 +82,9 @@ private var Int.p6: Int
// CHECK_CALLED_IN_SCOPE: function=get_p7 scope=box TARGET_BACKENDS=JS
// CHECK_FUNCTION_EXISTS: set_p7 TARGET_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=set_p7 scope=box TARGET_BACKENDS=JS
-// CHECK_CALLED_IN_SCOPE: function=_get_p7_ scope=box IGNORED_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _set_p7_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p7_ scope=box IGNORED_BACKENDS=JS
+// CHECK_CALLED_IN_SCOPE: function=_get_p7__59117 scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _set_p7__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p7__59117 scope=box IGNORED_BACKENDS=JS
var Int.p7: Int
get() = this * 100 + a + 70000
private inline set(v) {
@@ -94,9 +94,9 @@ var Int.p7: Int
// CHECK_CALLED_IN_SCOPE: function=get_p8 scope=box TARGET_BACKENDS=JS
// CHECK_FUNCTION_EXISTS: set_p8 TARGET_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=set_p8 scope=box TARGET_BACKENDS=JS
-// CHECK_CALLED_IN_SCOPE: function=_get_p8_ scope=box IGNORED_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _set_p8_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p8_ scope=box IGNORED_BACKENDS=JS
+// CHECK_CALLED_IN_SCOPE: function=_get_p8__59117 scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _set_p8__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p8__59117 scope=box IGNORED_BACKENDS=JS
private var Int.p8: Int
get() = this * 100 + a + 80000
inline set(v) {
@@ -160,10 +160,10 @@ private class A {
// CHECK_NOT_CALLED_IN_SCOPE: function=get_p15 scope=box TARGET_BACKENDS=JS
// CHECK_FUNCTION_EXISTS: set_p15 TARGET_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=set_p15 scope=box TARGET_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _get_p15_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_get_p15_ scope=box IGNORED_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _set_p15_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p15_ scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _get_p15__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_get_p15__59117 scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _set_p15__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p15__59117 scope=box IGNORED_BACKENDS=JS
private inline var A.p15: Int
get() = a + 150000
set(v) {
@@ -173,9 +173,9 @@ private inline var A.p15: Int
// CHECK_FUNCTION_EXISTS: get_p16 TARGET_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=get_p16 scope=box TARGET_BACKENDS=JS
// CHECK_CALLED_IN_SCOPE: function=set_p16 scope=box TARGET_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _get_p16_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_get_p16_ scope=box IGNORED_BACKENDS=JS
-// CHECK_CALLED_IN_SCOPE: function=_set_p16_ scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _get_p16__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_get_p16__59117 scope=box IGNORED_BACKENDS=JS
+// CHECK_CALLED_IN_SCOPE: function=_set_p16__59117 scope=box IGNORED_BACKENDS=JS
private var A.p16: Int
inline get() = a + 160000
set(v) {
@@ -185,9 +185,9 @@ private var A.p16: Int
// CHECK_CALLED_IN_SCOPE: function=get_p17 scope=box TARGET_BACKENDS=JS
// CHECK_FUNCTION_EXISTS: set_p17 TARGET_BACKENDS=JS
// CHECK_NOT_CALLED_IN_SCOPE: function=set_p17 scope=box TARGET_BACKENDS=JS
-// CHECK_CALLED_IN_SCOPE: function=_get_p17_ scope=box IGNORED_BACKENDS=JS
-// CHECK_FUNCTION_EXISTS: _set_p17_ IGNORED_BACKENDS=JS
-// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p17_ scope=box IGNORED_BACKENDS=JS
+// CHECK_CALLED_IN_SCOPE: function=_get_p17__59117 scope=box IGNORED_BACKENDS=JS
+// CHECK_FUNCTION_EXISTS: _set_p17__59117 IGNORED_BACKENDS=JS
+// CHECK_NOT_CALLED_IN_SCOPE: function=_set_p17__59117 scope=box IGNORED_BACKENDS=JS
private var A.p17: Int
get() = a + 170000
inline set(v) {
diff --git a/js/js.translator/testData/typescript-export/declarations/declarations__main.js b/js/js.translator/testData/typescript-export/declarations/declarations__main.js
index 584970664d2..ddd572d2ee9 100644
--- a/js/js.translator/testData/typescript-export/declarations/declarations__main.js
+++ b/js/js.translator/testData/typescript-export/declarations/declarations__main.js
@@ -22,6 +22,10 @@ var _valCustomWithField = JS_TESTS.foo._valCustomWithField;
var A4 = JS_TESTS.foo.A4;
var O = JS_TESTS.foo.O;
var takesO = JS_TESTS.foo.takesO;
+var KT_37829 = JS_TESTS.foo.KT_37829;
+var TestSealed = JS_TESTS.foo.TestSealed;
+var TestAbstract = JS_TESTS.foo.TestAbstract;
+var TestDataClass = JS_TESTS.foo.TestDataClass;
function assert(condition) {
if (!condition) {
throw "Assertion failed";
@@ -80,5 +84,15 @@ function box() {
assert(O.x === 10);
assert(O.foo() === 20);
assert(takesO(O) === 30);
+ assert(KT_37829.Companion.x == 10);
+ assert(new TestSealed.AA().name == "AA");
+ assert(new TestSealed.AA().bar() == "bar");
+ assert(new TestSealed.BB().name == "BB");
+ assert(new TestSealed.BB().baz() == "baz");
+ assert(new TestAbstract.AA().name == "AA");
+ assert(new TestAbstract.AA().bar() == "bar");
+ assert(new TestAbstract.BB().name == "BB");
+ assert(new TestAbstract.BB().baz() == "baz");
+ assert(new TestDataClass.Nested().prop == "hello");
return "OK";
}
diff --git a/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations.d.ts b/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations.d.ts
new file mode 100644
index 00000000000..690ddbf28e8
--- /dev/null
+++ b/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations.d.ts
@@ -0,0 +1,31 @@
+declare namespace JS_TESTS {
+ type Nullable = T | null | undefined
+ namespace foo {
+
+
+
+ function invalid_args_name_sum(first_value: number, second_value: number): number;
+
+ class A1 {
+ constructor(first_value: number, second_value: number);
+ readonly "first value": number;
+ "second.value": number;
+ }
+ class A2 {
+ constructor();
+ "invalid:name": number;
+ }
+ class A3 {
+ constructor();
+ "invalid@name sum"(x: number, y: number): number;
+ invalid_args_name_sum(first_value: number, second_value: number): number;
+ }
+ class A4 {
+ constructor();
+ static readonly Companion: {
+ "@invalid+name@": number;
+ "^)run.something.weird^("(): string;
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations.kt b/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations.kt
new file mode 100644
index 00000000000..c18754dcc92
--- /dev/null
+++ b/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations.kt
@@ -0,0 +1,46 @@
+// CHECK_TYPESCRIPT_DECLARATIONS
+// RUN_PLAIN_BOX_FUNCTION
+// SKIP_MINIFICATION
+// SKIP_NODE_JS
+// IGNORE_BACKEND: JS
+// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping
+// INFER_MAIN_MODULE
+// MODULE: JS_TESTS
+// FILE: escapedDeclarations.kt
+
+@file:JsExport
+
+package foo
+
+fun `invalid@name sum`(x: Int, y: Int): Int =
+ x + y
+
+fun invalid_args_name_sum(`first value`: Int, `second value`: Int): Int =
+ `first value` + `second value`
+
+// Properties
+
+val `invalid name val`: Int = 1
+var `invalid@name var`: Int = 1
+
+// Classes
+
+class `Invalid A`
+class A1(val `first value`: Int, var `second.value`: Int)
+class A2 {
+ var `invalid:name`: Int = 42
+}
+class A3 {
+ fun `invalid@name sum`(x: Int, y: Int): Int =
+ x + y
+
+ fun invalid_args_name_sum(`first value`: Int, `second value`: Int): Int =
+ `first value` + `second value`
+}
+
+class A4 {
+ companion object {
+ var `@invalid+name@` = 23
+ fun `^)run.something.weird^(`(): String = ")_("
+ }
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations__main.js b/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations__main.js
new file mode 100644
index 00000000000..a9f68a6ae6c
--- /dev/null
+++ b/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations__main.js
@@ -0,0 +1,29 @@
+"use strict";
+var foo = JS_TESTS.foo;
+var A1 = JS_TESTS.foo.A1;
+var A2 = JS_TESTS.foo.A2;
+var A3 = JS_TESTS.foo.A3;
+var A4 = JS_TESTS.foo.A4;
+function assert(condition) {
+ if (!condition) {
+ throw "Assertion failed";
+ }
+}
+function box() {
+ assert(foo.invalid_args_name_sum(10, 20) === 30);
+ assert(foo["invalid@name sum"](10, 20) === 30);
+ assert(foo["invalid name val"] === 1);
+ assert(foo["invalid@name var"] === 1);
+ foo["invalid@name var"] = 4;
+ assert(foo["invalid@name var"] === 4);
+ new foo["Invalid A"]();
+ assert(new A1(10, 20)["first value"] === 10);
+ assert(new A1(10, 20)["second.value"] === 20);
+ assert(new A2()["invalid:name"] === 42);
+ var a3 = new A3();
+ assert(a3.invalid_args_name_sum(10, 20) === 30);
+ assert(a3["invalid@name sum"](10, 20) === 30);
+ assert(A4.Companion["@invalid+name@"] == 23);
+ assert(A4.Companion["^)run.something.weird^("]() === ")_(");
+ return "OK";
+}
diff --git a/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations__main.ts b/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations__main.ts
new file mode 100644
index 00000000000..3cf8eedc13d
--- /dev/null
+++ b/js/js.translator/testData/typescript-export/escapedDeclarations/escapedDeclarations__main.ts
@@ -0,0 +1,37 @@
+import foo = JS_TESTS.foo;
+import A1 = JS_TESTS.foo.A1;
+import A2 = JS_TESTS.foo.A2;
+import A3 = JS_TESTS.foo.A3;
+import A4 = JS_TESTS.foo.A4;
+
+function assert(condition: boolean) {
+ if (!condition) {
+ throw "Assertion failed";
+ }
+}
+
+function box(): string {
+ assert(foo.invalid_args_name_sum(10, 20) === 30);
+ assert((foo as any)["invalid@name sum"](10, 20) === 30);
+
+ assert((foo as any)["invalid name val"] === 1);
+ assert((foo as any)["invalid@name var"] === 1);
+ (foo as any)["invalid@name var"] = 4
+ assert((foo as any)["invalid@name var"] === 4);
+
+ new (foo as any)["Invalid A"]();
+
+ assert(new A1(10, 20)["first value"] === 10);
+ assert(new A1(10, 20)["second.value"] === 20);
+
+ assert(new A2()["invalid:name"] === 42);
+
+ const a3 = new A3()
+ assert(a3.invalid_args_name_sum(10, 20) === 30);
+ assert(a3["invalid@name sum"](10, 20) === 30);
+
+ assert(A4.Companion["@invalid+name@"] == 23);
+ assert(A4.Companion["^)run.something.weird^("]() === ")_(");
+
+ return "OK";
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/typescript-export/escapedDeclarations/tsconfig.json b/js/js.translator/testData/typescript-export/escapedDeclarations/tsconfig.json
new file mode 100644
index 00000000000..17612c56c9d
--- /dev/null
+++ b/js/js.translator/testData/typescript-export/escapedDeclarations/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "include": [ "./*" ],
+ "extends": "../common.tsconfig.json"
+}
\ No newline at end of file