[JS IR] Add test with boolean in external interface

[JS IR] Add possibility to safely access Boolean in external declaration

[JS IR] Add diagnostic for booleans in externals
This commit is contained in:
Ilya Goncharov
2021-06-15 20:18:30 +03:00
committed by Space
parent 2e049c1208
commit 21a3494bca
20 changed files with 293 additions and 69 deletions
@@ -127,7 +127,7 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
@Argument(
value = "-Xir-dce-runtime-diagnostic",
valueDescription = "{$DCE_RUNTIME_DIAGNOSTIC_LOG|$DCE_RUNTIME_DIAGNOSTIC_EXCEPTION}",
valueDescription = "{$RUNTIME_DIAGNOSTIC_LOG|$RUNTIME_DIAGNOSTIC_EXCEPTION}",
description = "Enable runtime diagnostics when performing DCE instead of removing declarations"
)
var irDceRuntimeDiagnostic: String? by NullableStringFreezableVar(null)
@@ -157,6 +157,19 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
@Argument(value = "-Xir-base-class-in-metadata", description = "Write base class into metadata")
var irBaseClassInMetadata: Boolean by FreezableVar(false)
@Argument(
value = "-Xir-safe-external-boolean",
description = "Safe access via Boolean() to Boolean properties in externals to safely cast falsy values."
)
var irSafeExternalBoolean: Boolean by FreezableVar(false)
@Argument(
value = "-Xir-safe-external-boolean-diagnostic",
valueDescription = "{$RUNTIME_DIAGNOSTIC_LOG|$RUNTIME_DIAGNOSTIC_EXCEPTION}",
description = "Enable runtime diagnostics when access safely to boolean in external declarations"
)
var irSafeExternalBooleanDiagnostic: String? by NullableStringFreezableVar(null)
@Argument(value = "-Xir-per-module", description = "Splits generated .js per-module")
var irPerModule: Boolean by FreezableVar(false)
@@ -29,6 +29,6 @@ public interface K2JsArgumentConstants {
String SOURCE_MAP_SOURCE_CONTENT_NEVER = "never";
String SOURCE_MAP_SOURCE_CONTENT_INLINING = "inlining";
String DCE_RUNTIME_DIAGNOSTIC_LOG = "log";
String DCE_RUNTIME_DIAGNOSTIC_EXCEPTION = "exception";
String RUNTIME_DIAGNOSTIC_LOG = "log";
String RUNTIME_DIAGNOSTIC_EXCEPTION = "exception";
}
@@ -17,8 +17,8 @@ import org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR
import org.jetbrains.kotlin.cli.common.ExitCode.OK
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants
import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.DCE_RUNTIME_DIAGNOSTIC_EXCEPTION
import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.DCE_RUNTIME_DIAGNOSTIC_LOG
import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.RUNTIME_DIAGNOSTIC_EXCEPTION
import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.RUNTIME_DIAGNOSTIC_LOG
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
import org.jetbrains.kotlin.cli.common.extensions.ScriptEvaluationExtension
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
@@ -267,7 +267,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
mainArguments = mainCallArguments,
generateFullJs = !arguments.irDce,
generateDceJs = arguments.irDce,
dceRuntimeDiagnostic = DceRuntimeDiagnostic.resolve(
dceRuntimeDiagnostic = RuntimeDiagnostic.resolve(
arguments.irDceRuntimeDiagnostic,
messageCollector
),
@@ -277,6 +277,11 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
propertyLazyInitialization = arguments.irPropertyLazyInitialization,
legacyPropertyAccess = arguments.irLegacyPropertyAccess,
baseClassIntoMetadata = arguments.irBaseClassInMetadata,
safeExternalBoolean = arguments.irSafeExternalBoolean,
safeExternalBooleanDiagnostic = RuntimeDiagnostic.resolve(
arguments.irSafeExternalBooleanDiagnostic,
messageCollector
),
)
val jsCode = if (arguments.irDce && !arguments.irDceDriven) compiledModule.dceJsCode!! else compiledModule.jsCode!!
@@ -436,15 +441,15 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
}
}
fun DceRuntimeDiagnostic.Companion.resolve(
fun RuntimeDiagnostic.Companion.resolve(
value: String?,
messageCollector: MessageCollector
): DceRuntimeDiagnostic? = when (value?.lowercase()) {
DCE_RUNTIME_DIAGNOSTIC_LOG -> DceRuntimeDiagnostic.LOG
DCE_RUNTIME_DIAGNOSTIC_EXCEPTION -> DceRuntimeDiagnostic.EXCEPTION
): RuntimeDiagnostic? = when (value?.lowercase()) {
RUNTIME_DIAGNOSTIC_LOG -> RuntimeDiagnostic.LOG
RUNTIME_DIAGNOSTIC_EXCEPTION -> RuntimeDiagnostic.EXCEPTION
null -> null
else -> {
messageCollector.report(STRONG_WARNING, "Unknown DCE runtime diagnostic '$value'")
messageCollector.report(STRONG_WARNING, "Unknown runtime diagnostic '$value'")
null
}
}
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.lower.ClassReferenceLowering
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
@@ -23,9 +22,8 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.js.config.DceRuntimeDiagnostic
import org.jetbrains.kotlin.js.config.RuntimeDiagnostic
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.removingBody
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
@@ -109,11 +107,6 @@ private fun buildRoots(modules: Iterable<IrModuleFragment>, context: JsIrBackend
return rootDeclarations
}
private fun DceRuntimeDiagnostic.unreachableDeclarationMethod(context: JsIrBackendContext) =
when (this) {
DceRuntimeDiagnostic.LOG -> context.intrinsics.jsUnreachableDeclarationLog
DceRuntimeDiagnostic.EXCEPTION -> context.intrinsics.jsUnreachableDeclarationException
}
private fun processUselessDeclarations(
modules: Iterable<IrModuleFragment>,
@@ -173,6 +166,16 @@ private fun IrDeclaration.processUselessDeclaration(context: JsIrBackendContext)
}
}
private fun RuntimeDiagnostic.unreachableDeclarationMethod(context: JsIrBackendContext) =
when (this) {
RuntimeDiagnostic.LOG -> context.intrinsics.jsUnreachableDeclarationLog
RuntimeDiagnostic.EXCEPTION -> context.intrinsics.jsUnreachableDeclarationException
}
private fun RuntimeDiagnostic.removingBody(): Boolean {
return this != RuntimeDiagnostic.LOG
}
private fun IrDeclaration.processWithDiagnostic(context: JsIrBackendContext) {
when (this) {
is IrFunction -> processFunctionWithDiagnostic(context)
@@ -164,6 +164,10 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val jsUnreachableDeclarationLog = getInternalFunction("unreachableDeclarationLog")
val jsUnreachableDeclarationException = getInternalFunction("unreachableDeclarationException")
val jsNativeBoolean = getInternalFunction("nativeBoolean")
val jsBooleanInExternalLog = getInternalFunction("booleanInExternalLog")
val jsBooleanInExternalException = getInternalFunction("booleanInExternalException")
// Coroutines
val jsCoroutineContext
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmen
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrDynamicTypeImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.config.DceRuntimeDiagnostic
import org.jetbrains.kotlin.js.config.RuntimeDiagnostic
import org.jetbrains.kotlin.js.config.ErrorTolerancePolicy
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.name.FqName
@@ -45,10 +45,12 @@ class JsIrBackendContext(
override val configuration: CompilerConfiguration, // TODO: remove configuration from backend context
override val scriptMode: Boolean = false,
override val es6mode: Boolean = false,
val dceRuntimeDiagnostic: DceRuntimeDiagnostic? = null,
val dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
val propertyLazyInitialization: Boolean = false,
val legacyPropertyAccess: Boolean = false,
val baseClassIntoMetadata: Boolean = false,
val safeExternalBoolean: Boolean = false,
val safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null,
) : JsCommonBackendContext {
val fileToInitializationFuns: MutableMap<IrFile, IrSimpleFunction?> = mutableMapOf()
val fileToInitializerPureness: MutableMap<IrFile, Boolean> = mutableMapOf()
@@ -391,6 +391,12 @@ private val copyPropertyAccessorBodiesLoweringPass = makeDeclarationTransformerP
prerequisite = setOf(propertyAccessorInlinerLoweringPhase)
)
private val booleanPropertyInExternalLowering = makeBodyLoweringPhase(
::BooleanPropertyInExternalLowering,
name = "BooleanPropertyInExternalLowering",
description = "Lowering which wrap boolean in external declarations with Boolean() call and add diagnostic for such cases"
)
private val foldConstantLoweringPhase = makeBodyLoweringPhase(
{ FoldConstantLowering(it, true) },
name = "FoldConstantLowering",
@@ -779,6 +785,7 @@ private val loweringList = listOf<Lowering>(
removeInitializersForLazyProperties,
propertyAccessorInlinerLoweringPhase,
copyPropertyAccessorBodiesLoweringPass,
booleanPropertyInExternalLowering,
foldConstantLoweringPhase,
privateMembersLoweringPhase,
privateMemberUsagesLoweringPhase,
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.ir.declarations.StageController
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.noUnboundLeft
import org.jetbrains.kotlin.js.config.DceRuntimeDiagnostic
import org.jetbrains.kotlin.js.config.RuntimeDiagnostic
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult
import org.jetbrains.kotlin.name.FqName
@@ -48,7 +48,7 @@ fun compile(
generateFullJs: Boolean = true,
generateDceJs: Boolean = false,
dceDriven: Boolean = false,
dceRuntimeDiagnostic: DceRuntimeDiagnostic? = null,
dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
es6mode: Boolean = false,
multiModule: Boolean = false,
relativeRequirePath: Boolean = false,
@@ -56,6 +56,8 @@ fun compile(
legacyPropertyAccess: Boolean = false,
baseClassIntoMetadata: Boolean = false,
lowerPerModule: Boolean = false,
safeExternalBoolean: Boolean = false,
safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null,
): CompilerResult {
val (moduleFragment: IrModuleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer, moduleToName) =
loadIr(project, mainModule, analyzer, configuration, allDependencies, friendDependencies, irFactory)
@@ -78,7 +80,9 @@ fun compile(
dceRuntimeDiagnostic = dceRuntimeDiagnostic,
propertyLazyInitialization = propertyLazyInitialization,
legacyPropertyAccess = legacyPropertyAccess,
baseClassIntoMetadata = baseClassIntoMetadata
baseClassIntoMetadata = baseClassIntoMetadata,
safeExternalBoolean = safeExternalBoolean,
safeExternalBooleanDiagnostic = safeExternalBooleanDiagnostic
)
// Load declarations referenced during `context` initialization
@@ -0,0 +1,101 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.builders.createTmpVariable
import org.jetbrains.kotlin.ir.builders.irBlock
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.js.config.RuntimeDiagnostic
class BooleanPropertyInExternalLowering(
private val context: JsIrBackendContext
) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(
ExternalBooleanPropertyProcessor(
context
)
)
}
private class ExternalBooleanPropertyProcessor(
private val context: JsIrBackendContext
) : IrElementTransformerVoid() {
private val safeExternalBoolean
get() = context.safeExternalBoolean
private val safeExternalBooleanDiagnostic
get() = context.safeExternalBooleanDiagnostic
private val booleanType
get() = context.irBuiltIns.booleanType
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
val symbol = expression.symbol
val callee = symbol.owner
val property = callee.correspondingPropertySymbol?.owner ?: return expression
if (!property.isEffectivelyExternal()) return expression
if (callee != property.getter) return expression
if (callee.returnType != booleanType) return expression
val function = safeExternalBooleanDiagnostic?.diagnosticMethod()
if (!safeExternalBoolean && function == null) return expression
if (safeExternalBoolean && function == null) {
return JsIrBuilder.buildCall(
target = context.intrinsics.jsNativeBoolean
).apply {
putValueArgument(0, expression)
}
}
return context.createIrBuilder(symbol).irBlock {
val tmp = createTmpVariable(expression)
val call = JsIrBuilder.buildCall(
target = function!!
).apply {
putValueArgument(0, irGet(tmp))
}
+call
val newBooleanGet = if (safeExternalBoolean) {
JsIrBuilder.buildCall(
target = this@ExternalBooleanPropertyProcessor.context.intrinsics.jsNativeBoolean
).apply {
putValueArgument(0, irGet(tmp))
}
} else irGet(tmp)
+newBooleanGet
}
}
private fun RuntimeDiagnostic.diagnosticMethod() = when (this) {
RuntimeDiagnostic.LOG -> context.intrinsics.jsBooleanInExternalLog
RuntimeDiagnostic.EXCEPTION -> context.intrinsics.jsBooleanInExternalException
}
}
}
@@ -110,7 +110,7 @@ fun translateCall(
// @JsName-annotated external property accessors are translated as function calls
if (function.getJsName() == null) {
val property = function.correspondingPropertySymbol?.owner
if (property != null && (property.isEffectivelyExternal())) {
if (property != null && property.isEffectivelyExternal()) {
val nameRef = JsNameRef(context.getNameForProperty(property), jsDispatchReceiver)
return when (function) {
property.getter -> nameRef
@@ -222,22 +222,23 @@ fun translateCall(
} else {
val defaultResult = JsInvocation(ref, listOfNotNull(jsExtensionReceiver) + arguments)
val alternativeResult = if (jsDispatchReceiver != null && jsExtensionReceiver == null && context.staticContext.backendContext.legacyPropertyAccess) {
val property = function.correspondingPropertySymbol?.owner
if (property != null) {
val propertyName = context.getNameForProperty(property)
val args = mutableListOf(jsDispatchReceiver, JsStringLiteral(symbolName.ident), JsStringLiteral(propertyName.ident))
val fnName = when (function) {
property.getter -> context.getNameForStaticFunction(context.staticContext.backendContext.intrinsics.safePropertyGet.owner)
property.setter -> {
args += arguments
context.getNameForStaticFunction(context.staticContext.backendContext.intrinsics.safePropertySet.owner)
val alternativeResult =
if (jsDispatchReceiver != null && jsExtensionReceiver == null && context.staticContext.backendContext.legacyPropertyAccess) {
val property = function.correspondingPropertySymbol?.owner
if (property != null) {
val propertyName = context.getNameForProperty(property)
val args = mutableListOf(jsDispatchReceiver, JsStringLiteral(symbolName.ident), JsStringLiteral(propertyName.ident))
val fnName = when (function) {
property.getter -> context.getNameForStaticFunction(context.staticContext.backendContext.intrinsics.safePropertyGet.owner)
property.setter -> {
args += arguments
context.getNameForStaticFunction(context.staticContext.backendContext.intrinsics.safePropertySet.owner)
}
else -> error("Function must be an accessor of corresponding property")
}
else -> error("Function must be an accessor of corresponding property")
}
JsInvocation(fnName.makeRef(), args)
JsInvocation(fnName.makeRef(), args)
} else null
} else null
} else null
alternativeResult ?: defaultResult
}
+3
View File
@@ -27,6 +27,9 @@ where advanced options include:
-Xir-produce-klib-file Generate packed klib into file specified by -output. Disables pre-IR backend
-Xir-property-lazy-initialization
Perform lazy initialization for properties
-Xir-safe-external-boolean Safe access via Boolean() to Boolean properties in externals to safely cast falsy values.
-Xir-safe-external-boolean-diagnostic={log|exception}
Enable runtime diagnostics when access safely to boolean in external declarations
-Xmetadata-only Generate *.meta.js and *.kjsm files only
-Xrepositories=<path> Paths to additional places where libraries could be found
-Xtyped-arrays Translate primitive arrays to JS typed arrays
@@ -1,24 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.js.config
enum class DceRuntimeDiagnostic {
LOG,
EXCEPTION;
companion object
}
fun DceRuntimeDiagnostic.removingBody(): Boolean {
return this != DceRuntimeDiagnostic.LOG
}
fun DceRuntimeDiagnostic.dceRuntimeDiagnosticToArgumentOfUnreachableMethod(): Int {
return when (this) {
DceRuntimeDiagnostic.LOG -> 0
DceRuntimeDiagnostic.EXCEPTION -> 1
}
}
@@ -0,0 +1,13 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.js.config
enum class RuntimeDiagnostic {
LOG,
EXCEPTION;
companion object
}
@@ -150,6 +150,7 @@ abstract class BasicBoxTest(
val splitPerModule = SPLIT_PER_MODULE.matcher(fileContent).find()
val propertyLazyInitialization = PROPERTY_LAZY_INITIALIZATION.matcher(fileContent).find()
val safeExternalBoolean = SAFE_EXTERNAL_BOOLEAN.matcher(fileContent).find()
TestFileFactoryImpl().use { testFactory ->
val inputFiles = TestFiles.createTestFiles(
@@ -214,7 +215,8 @@ abstract class BasicBoxTest(
skipDceDriven,
splitPerModule,
errorPolicy,
propertyLazyInitialization
propertyLazyInitialization,
safeExternalBoolean
)
when {
@@ -468,6 +470,7 @@ abstract class BasicBoxTest(
splitPerModule: Boolean,
errorIgnorancePolicy: ErrorTolerancePolicy,
propertyLazyInitialization: Boolean,
safeExternalBoolean: Boolean,
) {
val kotlinFiles = module.files.filter { it.fileName.endsWith(".kt") }
val testFiles = kotlinFiles.map { it.fileName }
@@ -518,6 +521,7 @@ abstract class BasicBoxTest(
skipDceDriven,
splitPerModule,
propertyLazyInitialization,
safeExternalBoolean,
)
if (incrementalCompilationChecksEnabled && module.hasFilesToRecompile) {
@@ -608,10 +612,11 @@ abstract class BasicBoxTest(
testPackage,
testFunction,
needsFullIrRuntime,
isMainModule = false,
skipDceDriven = true,
splitPerModule = false,
isMainModule = false,
skipDceDriven = true,
splitPerModule = false,
propertyLazyInitialization = false,
safeExternalBoolean = false,
)
val originalOutput = FileUtil.loadFile(outputFile)
@@ -691,6 +696,7 @@ abstract class BasicBoxTest(
skipDceDriven: Boolean,
splitPerModule: Boolean,
propertyLazyInitialization: Boolean,
safeExternalBoolean: Boolean,
) {
val translator = K2JSTranslator(config, false)
val translationResult = translator.translateUnits(ExceptionThrowingReporter, units, mainCallParameters)
@@ -1094,6 +1100,8 @@ abstract class BasicBoxTest(
private val PROPERTY_LAZY_INITIALIZATION = Pattern.compile("^// *PROPERTY_LAZY_INITIALIZATION *$", Pattern.MULTILINE)
private val SAFE_EXTERNAL_BOOLEAN = Pattern.compile("^// *SAFE_EXTERNAL_BOOLEAN *$", Pattern.MULTILINE)
@JvmStatic
protected val runTestInNashorn = getBoolean("kotlin.js.useNashorn")
@@ -95,6 +95,7 @@ abstract class BasicIrBoxTest(
skipDceDriven: Boolean,
splitPerModule: Boolean,
propertyLazyInitialization: Boolean,
safeExternalBoolean: Boolean,
) {
val filesToCompile = units.map { (it as TranslationUnit.SourceFile).file }
@@ -150,7 +151,8 @@ abstract class BasicIrBoxTest(
es6mode = runEs6Mode,
multiModule = splitPerModule || perModule,
propertyLazyInitialization = propertyLazyInitialization,
lowerPerModule = lowerPerModule
lowerPerModule = lowerPerModule,
safeExternalBoolean = safeExternalBoolean,
)
compiledModule.jsCode!!.writeTo(outputFile, config)
@@ -179,7 +181,8 @@ abstract class BasicIrBoxTest(
dceDriven = true,
es6mode = runEs6Mode,
multiModule = splitPerModule || perModule,
propertyLazyInitialization = propertyLazyInitialization
propertyLazyInitialization = propertyLazyInitialization,
safeExternalBoolean = safeExternalBoolean,
).jsCode!!.writeTo(pirOutputFile, config)
}
} else {
@@ -6822,6 +6822,11 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/propertyAccess"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
}
@TestMetadata("booleanInExternals.kt")
public void testBooleanInExternals() throws Exception {
runTest("js/js.translator/testData/box/propertyAccess/booleanInExternals.kt");
}
@TestMetadata("classUsesPackageProperties.kt")
public void testClassUsesPackageProperties() throws Exception {
runTest("js/js.translator/testData/box/propertyAccess/classUsesPackageProperties.kt");
@@ -6822,6 +6822,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/propertyAccess"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
}
@TestMetadata("booleanInExternals.kt")
public void testBooleanInExternals() throws Exception {
runTest("js/js.translator/testData/box/propertyAccess/booleanInExternals.kt");
}
@TestMetadata("classUsesPackageProperties.kt")
public void testClassUsesPackageProperties() throws Exception {
runTest("js/js.translator/testData/box/propertyAccess/classUsesPackageProperties.kt");
@@ -6842,6 +6842,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/propertyAccess"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true);
}
@TestMetadata("booleanInExternals.kt")
public void testBooleanInExternals() throws Exception {
runTest("js/js.translator/testData/box/propertyAccess/booleanInExternals.kt");
}
@TestMetadata("classUsesPackageProperties.kt")
public void testClassUsesPackageProperties() throws Exception {
runTest("js/js.translator/testData/box/propertyAccess/classUsesPackageProperties.kt");
@@ -0,0 +1,43 @@
// SAFE_EXTERNAL_BOOLEAN
fun box(): String {
val interfaceWithBoolean: InterfaceWithBoolean = js("{}")
C().c = interfaceWithBoolean.foo
C().c = interfaceWithBoolean.bar
return "OK"
}
abstract class A<T> {
open fun get(): T {
return this.asDynamic()["attr"].unsafeCast<T>()
}
open fun set(value: T) {
this.asDynamic()["attr"] = value
}
}
class B : A<Boolean>() {
override fun set(value: Boolean) {
if (value) {
this.asDynamic()["attr"] = value
}
}
}
val b: A<Boolean> = B()
class C {
var c: Boolean
get() = b.get()
set(newValue) {
b.set(newValue)
}
}
external interface InterfaceWithBoolean {
var foo: Boolean
@JsName("goo")
var bar: Boolean
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.js
import JsError
@JsName("Boolean")
internal external fun nativeBoolean(obj: Any?): Boolean
internal fun booleanInExternalLog(obj: dynamic) {
if (jsTypeOf(obj) != "boolean") {
console.asDynamic().error("Boolean expected, but actual:", obj)
}
}
internal fun booleanInExternalException(obj: dynamic) {
if (jsTypeOf(obj) != "boolean") {
throw JsError("Boolean expected, but actual: $obj")
}
}