JS: prohibit stable (public) names with non-identifier chars. Rewrite unstable (private) names with non-identifier chars. See KT-4160
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
private fun ` .private `(): String = TODO("")
|
||||||
|
|
||||||
|
<!NAME_CONTAINS_ILLEGAL_CHARS!>fun ` .public `(): String<!> = TODO("")
|
||||||
|
|
||||||
|
<!NAME_CONTAINS_ILLEGAL_CHARS!>@JsName(" __ ")
|
||||||
|
fun foo(): String<!> = TODO("")
|
||||||
|
|
||||||
|
<!NAME_CONTAINS_ILLEGAL_CHARS!>@JsName(" ___ ")
|
||||||
|
private fun bar(): String<!> = TODO("")
|
||||||
|
|
||||||
|
@JsName("validName")
|
||||||
|
private fun ` .private with @JsName `(): String = TODO("")
|
||||||
|
|
||||||
|
private class <!NAME_CONTAINS_ILLEGAL_CHARS!>` .private class `<!> {
|
||||||
|
<!NAME_CONTAINS_ILLEGAL_CHARS!>val ` .field. `<!> = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val x: Int
|
||||||
|
<!NAME_CONTAINS_ILLEGAL_CHARS!>@JsName(".")
|
||||||
|
get()<!> = TODO("")
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public val x: kotlin.Int
|
||||||
|
private fun ` .private `(): kotlin.String
|
||||||
|
@kotlin.js.JsName(name = "validName") private fun ` .private with @JsName `(): kotlin.String
|
||||||
|
public fun ` .public `(): kotlin.String
|
||||||
|
@kotlin.js.JsName(name = " ___ ") private fun bar(): kotlin.String
|
||||||
|
@kotlin.js.JsName(name = " __ ") public fun foo(): kotlin.String
|
||||||
|
|
||||||
|
private final class ` .private class ` {
|
||||||
|
public constructor ` .private class `()
|
||||||
|
public final val ` .field. `: kotlin.String = ""
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -440,6 +440,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("illegalName.kt")
|
||||||
|
public void testIllegalName() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/illegalName.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("jsNameAndNamedNative.kt")
|
@TestMetadata("jsNameAndNamedNative.kt")
|
||||||
public void testJsNameAndNamedNative() throws Exception {
|
public void testJsNameAndNamedNative() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameAndNamedNative.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameAndNamedNative.kt");
|
||||||
|
|||||||
@@ -330,5 +330,12 @@ class NameSuggestion {
|
|||||||
|
|
||||||
private val DeclarationDescriptorWithVisibility.ownEffectiveVisibility
|
private val DeclarationDescriptorWithVisibility.ownEffectiveVisibility
|
||||||
get() = visibility.effectiveVisibility(this, checkPublishedApi = true).toVisibility()
|
get() = visibility.effectiveVisibility(this, checkPublishedApi = true).toVisibility()
|
||||||
|
|
||||||
|
@JvmStatic fun sanitizeName(name: String): String {
|
||||||
|
if (name.isEmpty()) return "_"
|
||||||
|
|
||||||
|
val first = name.first().let { if (java.lang.Character.isJavaIdentifierStart(it)) it else '_' }
|
||||||
|
return first.toString() + name.drop(1).map { if (java.lang.Character.isJavaIdentifierPart(it)) it else '_' }.joinToString("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ object JsPlatformConfigurator : PlatformConfigurator(
|
|||||||
container.useInstance(SyntheticConstructorsProvider.Empty)
|
container.useInstance(SyntheticConstructorsProvider.Empty)
|
||||||
container.useInstance(JsTypeSpecificityComparator)
|
container.useInstance(JsTypeSpecificityComparator)
|
||||||
container.useInstance(JsNameClashChecker())
|
container.useInstance(JsNameClashChecker())
|
||||||
|
container.useInstance(JsNameCharsChecker())
|
||||||
container.useImpl<JsReflectionAPICallChecker>()
|
container.useImpl<JsReflectionAPICallChecker>()
|
||||||
container.useImpl<JsNativeRttiChecker>()
|
container.useImpl<JsNativeRttiChecker>()
|
||||||
container.useImpl<JsReifiedNativeChecker>()
|
container.useImpl<JsReifiedNativeChecker>()
|
||||||
|
|||||||
+4
@@ -39,6 +39,7 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
|
|||||||
put(ErrorsJs.JSCODE_NO_JAVASCRIPT_PRODUCED, "Argument must be non-empty JavaScript code")
|
put(ErrorsJs.JSCODE_NO_JAVASCRIPT_PRODUCED, "Argument must be non-empty JavaScript code")
|
||||||
put(ErrorsJs.NESTED_EXTERNAL_DECLARATION, "Non top-level `external` declaration")
|
put(ErrorsJs.NESTED_EXTERNAL_DECLARATION, "Non top-level `external` declaration")
|
||||||
put(ErrorsJs.WRONG_EXTERNAL_DECLARATION, "Declaration of such kind ({0}) can't be external", Renderers.STRING)
|
put(ErrorsJs.WRONG_EXTERNAL_DECLARATION, "Declaration of such kind ({0}) can't be external", Renderers.STRING)
|
||||||
|
|
||||||
put(ErrorsJs.JS_NAME_CLASH, "JavaScript name ({0}) generated for this declaration clashes with another declaration: {1}",
|
put(ErrorsJs.JS_NAME_CLASH, "JavaScript name ({0}) generated for this declaration clashes with another declaration: {1}",
|
||||||
Renderers.STRING, Renderers.COMPACT)
|
Renderers.STRING, Renderers.COMPACT)
|
||||||
put(ErrorsJs.JS_FAKE_NAME_CLASH, "JavaScript name {0} is generated for different inherited members: {1} and {2}",
|
put(ErrorsJs.JS_FAKE_NAME_CLASH, "JavaScript name {0} is generated for different inherited members: {1} and {2}",
|
||||||
@@ -49,6 +50,9 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
|
|||||||
put(ErrorsJs.JS_NAME_PROHIBITED_FOR_OVERRIDE, "@JsName is prohibited for overridden members")
|
put(ErrorsJs.JS_NAME_PROHIBITED_FOR_OVERRIDE, "@JsName is prohibited for overridden members")
|
||||||
put(ErrorsJs.JS_NAME_PROHIBITED_FOR_EXTENSION_PROPERTY, "@JsName is prohibited for extension properties")
|
put(ErrorsJs.JS_NAME_PROHIBITED_FOR_EXTENSION_PROPERTY, "@JsName is prohibited for extension properties")
|
||||||
put(ErrorsJs.JS_NAME_PROHIBITED_FOR_NAMED_NATIVE, "@JsName is prohibited for external declaration with explicit name")
|
put(ErrorsJs.JS_NAME_PROHIBITED_FOR_NAMED_NATIVE, "@JsName is prohibited for external declaration with explicit name")
|
||||||
|
|
||||||
|
put(ErrorsJs.NAME_CONTAINS_ILLEGAL_CHARS, "Name contains illegal chars that can't appear in JavaScript identifier")
|
||||||
|
|
||||||
put(ErrorsJs.JS_MODULE_PROHIBITED_ON_VAR, "@JsModule and @JsNonModule annotations prohibited for 'var' declarations. " +
|
put(ErrorsJs.JS_MODULE_PROHIBITED_ON_VAR, "@JsModule and @JsNonModule annotations prohibited for 'var' declarations. " +
|
||||||
"Use 'val' instead.")
|
"Use 'val' instead.")
|
||||||
put(ErrorsJs.JS_MODULE_PROHIBITED_ON_NON_NATIVE, "@JsModule and @JsNonModule annotations prohibited for non-external declarations.")
|
put(ErrorsJs.JS_MODULE_PROHIBITED_ON_NON_NATIVE, "@JsModule and @JsNonModule annotations prohibited for non-external declarations.")
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ public interface ErrorsJs {
|
|||||||
DiagnosticFactory0<KtExpression> JSCODE_NO_JAVASCRIPT_PRODUCED = DiagnosticFactory0.create(ERROR, DEFAULT);
|
DiagnosticFactory0<KtExpression> JSCODE_NO_JAVASCRIPT_PRODUCED = DiagnosticFactory0.create(ERROR, DEFAULT);
|
||||||
DiagnosticFactory1<KtExpression, String> WRONG_EXTERNAL_DECLARATION = DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
DiagnosticFactory1<KtExpression, String> WRONG_EXTERNAL_DECLARATION = DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
DiagnosticFactory0<KtExpression> NESTED_EXTERNAL_DECLARATION = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
DiagnosticFactory0<KtExpression> NESTED_EXTERNAL_DECLARATION = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
|
|
||||||
DiagnosticFactory2<KtElement, String, DeclarationDescriptor> JS_NAME_CLASH = DiagnosticFactory2.create(
|
DiagnosticFactory2<KtElement, String, DeclarationDescriptor> JS_NAME_CLASH = DiagnosticFactory2.create(
|
||||||
ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
DiagnosticFactory3<KtElement, String, DeclarationDescriptor, DeclarationDescriptor> JS_FAKE_NAME_CLASH =
|
DiagnosticFactory3<KtElement, String, DeclarationDescriptor, DeclarationDescriptor> JS_FAKE_NAME_CLASH =
|
||||||
@@ -52,6 +53,10 @@ public interface ErrorsJs {
|
|||||||
DiagnosticFactory0<PsiElement> JS_NAME_PROHIBITED_FOR_OVERRIDE = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> JS_NAME_PROHIBITED_FOR_OVERRIDE = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<PsiElement> JS_NAME_PROHIBITED_FOR_EXTENSION_PROPERTY = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> JS_NAME_PROHIBITED_FOR_EXTENSION_PROPERTY = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<PsiElement> JS_NAME_PROHIBITED_FOR_NAMED_NATIVE = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> JS_NAME_PROHIBITED_FOR_NAMED_NATIVE = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
|
DiagnosticFactory0<PsiElement> NAME_CONTAINS_ILLEGAL_CHARS = DiagnosticFactory0.create(
|
||||||
|
ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
|
|
||||||
DiagnosticFactory0<KtElement> JS_MODULE_PROHIBITED_ON_VAR = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
DiagnosticFactory0<KtElement> JS_MODULE_PROHIBITED_ON_VAR = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
DiagnosticFactory0<KtElement> JS_MODULE_PROHIBITED_ON_NON_NATIVE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
DiagnosticFactory0<KtElement> JS_MODULE_PROHIBITED_ON_NON_NATIVE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
DiagnosticFactory0<KtElement> NESTED_JS_MODULE_PROHIBITED = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
DiagnosticFactory0<KtElement> NESTED_JS_MODULE_PROHIBITED = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.js.resolve.diagnostics
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
|
import org.jetbrains.kotlin.js.naming.NameSuggestion
|
||||||
|
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.SimpleDeclarationChecker
|
||||||
|
|
||||||
|
class JsNameCharsChecker() : SimpleDeclarationChecker {
|
||||||
|
val suggestion = NameSuggestion()
|
||||||
|
|
||||||
|
override fun check(
|
||||||
|
declaration: KtDeclaration, descriptor: DeclarationDescriptor,
|
||||||
|
diagnosticHolder: DiagnosticSink, bindingContext: BindingContext
|
||||||
|
) {
|
||||||
|
if (descriptor is PropertyAccessorDescriptor && AnnotationsUtils.getJsName(descriptor) == null) return
|
||||||
|
|
||||||
|
val suggestedName = suggestion.suggest(descriptor) ?: return
|
||||||
|
if (suggestedName.stable && suggestedName.names.any { NameSuggestion.sanitizeName(it) != it }) {
|
||||||
|
diagnosticHolder.report(ErrorsJs.NAME_CONTAINS_ILLEGAL_CHARS.on(declaration))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5165,6 +5165,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("peculiarIdentifiers.kt")
|
||||||
|
public void testPeculiarIdentifiers() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsName/peculiarIdentifiers.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("privateMethod.kt")
|
@TestMetadata("privateMethod.kt")
|
||||||
public void testPrivateMethod() throws Exception {
|
public void testPrivateMethod() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsName/privateMethod.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsName/privateMethod.kt");
|
||||||
|
|||||||
@@ -368,7 +368,7 @@ public final class StaticContext {
|
|||||||
assert suggested.getNames().size() == 1 : "Private names must always consist of exactly one name";
|
assert suggested.getNames().size() == 1 : "Private names must always consist of exactly one name";
|
||||||
JsName name = nameCache.get(suggested.getDescriptor());
|
JsName name = nameCache.get(suggested.getDescriptor());
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
String baseName = suggested.getNames().get(0);
|
String baseName = NameSuggestion.sanitizeName(suggested.getNames().get(0));
|
||||||
if (suggested.getDescriptor() instanceof LocalVariableDescriptor ||
|
if (suggested.getDescriptor() instanceof LocalVariableDescriptor ||
|
||||||
suggested.getDescriptor() instanceof ValueParameterDescriptor
|
suggested.getDescriptor() instanceof ValueParameterDescriptor
|
||||||
) {
|
) {
|
||||||
@@ -530,14 +530,14 @@ public final class StaticContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
suggestedName = descriptor.getName().asString();
|
suggestedName = NameSuggestion.sanitizeName(descriptor.getName().asString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(descriptor instanceof PackageFragmentDescriptor) && !DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
if (!(descriptor instanceof PackageFragmentDescriptor) && !DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
||||||
DeclarationDescriptor container = descriptor.getContainingDeclaration();
|
DeclarationDescriptor container = descriptor.getContainingDeclaration();
|
||||||
assert container != null : "We just figured out that descriptor is not for a top-level declaration: " + descriptor;
|
assert container != null : "We just figured out that descriptor is not for a top-level declaration: " + descriptor;
|
||||||
suggestedName = getSuggestedName(container) + "$" + suggestedName;
|
suggestedName = getSuggestedName(container) + "$" + NameSuggestion.sanitizeName(suggestedName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return suggestedName;
|
return suggestedName;
|
||||||
|
|||||||
@@ -166,12 +166,12 @@ class UsageTracker(
|
|||||||
|
|
||||||
// Append 'closure$' prefix to avoid name clash between closure and member fields in case of local classes
|
// Append 'closure$' prefix to avoid name clash between closure and member fields in case of local classes
|
||||||
else -> {
|
else -> {
|
||||||
val mangled = NameSuggestion().suggest(this)!!.names.last()
|
val mangled = NameSuggestion.sanitizeName(NameSuggestion().suggest(this)!!.names.last())
|
||||||
"closure\$$mangled"
|
"closure\$$mangled"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return scope.declareFreshName(suggestedName)
|
return scope.declareTemporaryName(suggestedName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.ConstructorDescriptor;
|
|||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
|
||||||
|
import org.jetbrains.kotlin.js.naming.NameSuggestion;
|
||||||
import org.jetbrains.kotlin.js.translate.context.Namer;
|
import org.jetbrains.kotlin.js.translate.context.Namer;
|
||||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||||
import org.jetbrains.kotlin.js.translate.expression.LocalFunctionCollector;
|
import org.jetbrains.kotlin.js.translate.expression.LocalFunctionCollector;
|
||||||
@@ -58,7 +59,7 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
|
|||||||
|
|
||||||
for (FunctionDescriptor localFunction : functionCollector.getFunctions()) {
|
for (FunctionDescriptor localFunction : functionCollector.getFunctions()) {
|
||||||
String localIdent = localFunction.getName().isSpecial() ? "lambda" : localFunction.getName().asString();
|
String localIdent = localFunction.getName().isSpecial() ? "lambda" : localFunction.getName().asString();
|
||||||
JsName localName = functionBodyContext.scope().getParent().declareTemporaryName(localIdent);
|
JsName localName = functionBodyContext.scope().getParent().declareTemporaryName(NameSuggestion.sanitizeName(localIdent));
|
||||||
JsExpression alias = JsAstUtils.pureFqn(localName, null);
|
JsExpression alias = JsAstUtils.pureFqn(localName, null);
|
||||||
aliases.put(localFunction, alias);
|
aliases.put(localFunction, alias);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
private fun `+`(a: Int, b: Int) = a + b
|
||||||
|
|
||||||
|
@JsName("minus")
|
||||||
|
fun `-`(a: Int, b: Int) = a - b
|
||||||
|
|
||||||
|
fun test1(): Int {
|
||||||
|
val ` x ` = 23
|
||||||
|
val `*` = 3
|
||||||
|
return `-`(` x `, `*`)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test2(`p 1`: Int, `.p 2`: Int) = `+`(`p 1`, `.p 2`)
|
||||||
|
|
||||||
|
fun test3(): String {
|
||||||
|
val `#` = "K"
|
||||||
|
class ` `(private val `::`: String) {
|
||||||
|
internal fun `@`() = `::` + `#`
|
||||||
|
}
|
||||||
|
return ` `("O").`@`()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test4(): String {
|
||||||
|
val `()` = "OK"
|
||||||
|
fun `[]`() = `()`
|
||||||
|
return `[]`()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
if (test1() != 20) return "fail1"
|
||||||
|
if (test2(10, 13) != 23) return "fail2"
|
||||||
|
if (test3() != "OK") return "fail3"
|
||||||
|
if (test4() != "OK") return "fail4"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+2
-4
@@ -8,8 +8,6 @@ public class JsTestsAsserter() : Asserter {
|
|||||||
public override fun fail(message: String?): Nothing = failWithMessage(message)
|
public override fun fail(message: String?): Nothing = failWithMessage(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsName("JsTests.assert")
|
public inline fun assert(value: Boolean, message: String?): Unit = js("JsTests").assert(value, message)
|
||||||
public external fun assert(value: Boolean, message: String?): Unit = noImpl
|
|
||||||
|
|
||||||
@JsName("JsTests.fail")
|
private inline fun failWithMessage(message: String?): Nothing = js("JsTests").fail(message)
|
||||||
private external fun failWithMessage(message: String?): Nothing = noImpl
|
|
||||||
|
|||||||
Reference in New Issue
Block a user