Fix translation of labels with non-identifier names in JS BE
See KT-18027
This commit is contained in:
@@ -37,8 +37,8 @@ open class JsDeclarationScope(parent: JsScope, description: String, useParentSco
|
|||||||
private val topLabelScope
|
private val topLabelScope
|
||||||
get() = if (labelScopes.isNotEmpty()) labelScopes.peek() else null
|
get() = if (labelScopes.isNotEmpty()) labelScopes.peek() else null
|
||||||
|
|
||||||
open fun enterLabel(label: String): JsName {
|
open fun enterLabel(label: String, outputName: String): JsName {
|
||||||
val scope = LabelScope(topLabelScope, label)
|
val scope = LabelScope(topLabelScope, label, outputName)
|
||||||
labelScopes.push(scope)
|
labelScopes.push(scope)
|
||||||
return scope.labelName
|
return scope.labelName
|
||||||
}
|
}
|
||||||
@@ -51,18 +51,8 @@ open class JsDeclarationScope(parent: JsScope, description: String, useParentSco
|
|||||||
open fun findLabel(label: String): JsName? =
|
open fun findLabel(label: String): JsName? =
|
||||||
topLabelScope?.findName(label)
|
topLabelScope?.findName(label)
|
||||||
|
|
||||||
private inner class LabelScope(parent: LabelScope?, val ident: String) : JsScope(parent, "Label scope for $ident") {
|
private inner class LabelScope(parent: LabelScope?, val ident: String, outputIdent: String) : JsScope(parent, "Label scope for $ident") {
|
||||||
val labelName: JsName
|
val labelName: JsName = JsName(outputIdent, true)
|
||||||
|
|
||||||
init {
|
|
||||||
val freshIdent = when {
|
|
||||||
ident in RESERVED_WORDS -> getFreshIdent(ident)
|
|
||||||
parent != null -> parent.getFreshIdent(ident)
|
|
||||||
else -> ident
|
|
||||||
}
|
|
||||||
|
|
||||||
labelName = JsName(freshIdent, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findOwnName(name: String): JsName? =
|
override fun findOwnName(name: String): JsName? =
|
||||||
if (name == ident) labelName else null
|
if (name == ident) labelName else null
|
||||||
@@ -130,8 +120,8 @@ class DelegatingJsFunctionScopeWithTemporaryParent(
|
|||||||
override fun declareFreshName(suggestedName: String): JsName =
|
override fun declareFreshName(suggestedName: String): JsName =
|
||||||
delegatingScope.declareFreshName(suggestedName)
|
delegatingScope.declareFreshName(suggestedName)
|
||||||
|
|
||||||
override fun enterLabel(label: String): JsName =
|
override fun enterLabel(label: String, outputName: String): JsName =
|
||||||
delegatingScope.enterLabel(label)
|
delegatingScope.enterLabel(label, outputName)
|
||||||
|
|
||||||
override fun exitLabel() =
|
override fun exitLabel() =
|
||||||
delegatingScope.exitLabel()
|
delegatingScope.exitLabel()
|
||||||
|
|||||||
+2
-2
@@ -42,7 +42,7 @@ class LabelNameRefreshingVisitor(val functionScope: JsFunctionScope) : JsVisitor
|
|||||||
|
|
||||||
override fun visit(x: JsLabel, ctx: JsContext<JsNode>): Boolean {
|
override fun visit(x: JsLabel, ctx: JsContext<JsNode>): Boolean {
|
||||||
val labelName = x.name
|
val labelName = x.name
|
||||||
val freshName = functionScope.enterLabel(labelName.ident)
|
val freshName = functionScope.enterLabel(labelName.ident, labelName.ident)
|
||||||
substitutions.getOrPut(labelName) { ArrayDeque() }.push(freshName)
|
substitutions.getOrPut(labelName) { ArrayDeque() }.push(freshName)
|
||||||
|
|
||||||
return super.visit(x, ctx)
|
return super.visit(x, ctx)
|
||||||
@@ -57,5 +57,5 @@ class LabelNameRefreshingVisitor(val functionScope: JsFunctionScope) : JsVisitor
|
|||||||
super.endVisit(x, ctx)
|
super.endVisit(x, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSubstitution(name: JsName) = substitutions[name]?.let { it.peek() } ?: name
|
private fun getSubstitution(name: JsName) = substitutions[name]?.peek() ?: name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -665,7 +665,7 @@ public class JsAstMapper {
|
|||||||
private JsLabel mapLabel(Node labelNode) throws JsParserException {
|
private JsLabel mapLabel(Node labelNode) throws JsParserException {
|
||||||
String fromName = labelNode.getFirstChild().getString();
|
String fromName = labelNode.getFirstChild().getString();
|
||||||
|
|
||||||
JsName toName = scopeContext.enterLabel(fromName);
|
JsName toName = scopeContext.enterLabel(fromName, fromName);
|
||||||
|
|
||||||
Node fromStmt = labelNode.getFirstChild().getNext();
|
Node fromStmt = labelNode.getFirstChild().getNext();
|
||||||
JsLabel toLabel = new JsLabel(toName);
|
JsLabel toLabel = new JsLabel(toName);
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ class ScopeContext(scope: JsScope) {
|
|||||||
exitScope()
|
exitScope()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun enterLabel(ident: String): JsName =
|
fun enterLabel(ident: String, outputName: String): JsName =
|
||||||
(currentScope as JsDeclarationScope).enterLabel(ident)
|
(currentScope as JsDeclarationScope).enterLabel(ident, outputName)
|
||||||
|
|
||||||
fun exitLabel() =
|
fun exitLabel() =
|
||||||
(currentScope as JsDeclarationScope).exitLabel()
|
(currentScope as JsDeclarationScope).exitLabel()
|
||||||
|
|||||||
@@ -5450,6 +5450,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("peculiarNames.kt")
|
||||||
|
public void testPeculiarNames() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/labels/peculiarNames.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("siblingLabels.kt")
|
@TestMetadata("siblingLabels.kt")
|
||||||
public void testSiblingLabels() throws Exception {
|
public void testSiblingLabels() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/labels/siblingLabels.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/labels/siblingLabels.kt");
|
||||||
|
|||||||
+2
-1
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
|||||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention;
|
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention;
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*;
|
import org.jetbrains.kotlin.js.backend.ast.*;
|
||||||
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
|
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
|
||||||
|
import org.jetbrains.kotlin.js.naming.NameSuggestion;
|
||||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||||
import org.jetbrains.kotlin.js.translate.declaration.ClassTranslator;
|
import org.jetbrains.kotlin.js.translate.declaration.ClassTranslator;
|
||||||
import org.jetbrains.kotlin.js.translate.declaration.PropertyTranslatorKt;
|
import org.jetbrains.kotlin.js.translate.declaration.PropertyTranslatorKt;
|
||||||
@@ -363,7 +364,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
|
|
||||||
String labelIdent = getReferencedName(expression.getTargetLabel());
|
String labelIdent = getReferencedName(expression.getTargetLabel());
|
||||||
|
|
||||||
JsName labelName = functionScope.enterLabel(labelIdent);
|
JsName labelName = functionScope.enterLabel(labelIdent, NameSuggestion.sanitizeName(labelIdent));
|
||||||
JsStatement baseStatement = Translation.translateAsStatement(baseExpression, context);
|
JsStatement baseStatement = Translation.translateAsStatement(baseExpression, context);
|
||||||
functionScope.exitLabel();
|
functionScope.exitLabel();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// EXPECTED_REACHABLE_NODES: 487
|
||||||
|
fun box(): String {
|
||||||
|
var log = ""
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
`!`@while (true) {
|
||||||
|
log += "i=$i;"
|
||||||
|
for (j in 1..3) {
|
||||||
|
log += "j=$j;"
|
||||||
|
if (i++ + j > 5) break@`!`
|
||||||
|
log += "!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log != "i=0;j=1;!j=2;!j=3;!i=3;j=1;!j=2;") return "fail: $log"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user