[Wasm] Lower complex external declarations
- Properties - Member functions - Object declarations - Constructors - Classes with instance checks
This commit is contained in:
@@ -122,6 +122,18 @@ private val tailrecLoweringPhase = makeWasmModulePhase(
|
||||
description = "Replace `tailrec` call sites with equivalent loop"
|
||||
)
|
||||
|
||||
private val complexExternalDeclarationsToTopLevelFunctionsLowering = makeWasmModulePhase(
|
||||
::ComplexExternalDeclarationsToTopLevelFunctionsLowering,
|
||||
name = "ComplexExternalDeclarationsToTopLevelFunctionsLowering",
|
||||
description = "Lower complex external declarations to top-level functions",
|
||||
)
|
||||
|
||||
private val complexExternalDeclarationsUsagesLowering = makeWasmModulePhase(
|
||||
::ComplexExternalDeclarationsUsageLowering,
|
||||
name = "ComplexExternalDeclarationsUsageLowering",
|
||||
description = "Lower usages of complex external declarations",
|
||||
)
|
||||
|
||||
private val jsInteropFunctionsLowering = makeWasmModulePhase(
|
||||
::JsInteropFunctionsLowering,
|
||||
name = "JsInteropFunctionsLowering",
|
||||
@@ -532,6 +544,9 @@ val wasmPhases = NamedCompilerPhase(
|
||||
delegateToPrimaryConstructorLoweringPhase then
|
||||
// Common prefix ends
|
||||
|
||||
complexExternalDeclarationsToTopLevelFunctionsLowering then
|
||||
complexExternalDeclarationsUsagesLowering then
|
||||
|
||||
jsInteropFunctionsLowering then
|
||||
jsInteropFunctionCallsLowering then
|
||||
|
||||
|
||||
+4
@@ -36,6 +36,10 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext) : IrElementVis
|
||||
error("Unexpected element of type ${element::class}")
|
||||
}
|
||||
|
||||
override fun visitProperty(declaration: IrProperty) {
|
||||
require(declaration.isExternal)
|
||||
}
|
||||
|
||||
override fun visitTypeAlias(declaration: IrTypeAlias) {
|
||||
// Type aliases are not material
|
||||
}
|
||||
|
||||
+301
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.backend.wasm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.builders.irCallConstructor
|
||||
import org.jetbrains.kotlin.ir.builders.irString
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import java.lang.StringBuilder
|
||||
|
||||
/**
|
||||
* Lower complex external declarations to top-level functions:
|
||||
* - Property accessors
|
||||
* - Member functions
|
||||
* - Class constructors
|
||||
* - Object declarations
|
||||
* - Class instance checks
|
||||
*/
|
||||
class ComplexExternalDeclarationsToTopLevelFunctionsLowering(val context: WasmBackendContext) : FileLoweringPass {
|
||||
lateinit var currentFile: IrFile
|
||||
val addedDeclarations = mutableListOf<IrDeclaration>()
|
||||
|
||||
val externalFunToTopLevelMapping =
|
||||
context.mapping.wasmNestedExternalToNewTopLevelFunction
|
||||
|
||||
val externalObjectToGetInstanceFunction =
|
||||
context.mapping.wasmExternalObjectToGetInstanceFunction
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
currentFile = irFile
|
||||
for (declaration in irFile.declarations) {
|
||||
if (declaration.isEffectivelyExternal()) {
|
||||
processExternalDeclaration(declaration)
|
||||
}
|
||||
}
|
||||
irFile.declarations += addedDeclarations
|
||||
addedDeclarations.clear()
|
||||
}
|
||||
|
||||
fun processExternalDeclaration(declaration: IrDeclaration) {
|
||||
declaration.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
error("Unknown external element ${element::class}")
|
||||
}
|
||||
|
||||
override fun visitValueParameter(declaration: IrValueParameter) {
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
declaration.acceptChildrenVoid(this)
|
||||
lowerExternalTopLevelClass(declaration)
|
||||
}
|
||||
|
||||
override fun visitProperty(declaration: IrProperty) {
|
||||
processExternalProperty(declaration)
|
||||
}
|
||||
|
||||
override fun visitConstructor(declaration: IrConstructor) {
|
||||
processExternalConstructor(declaration)
|
||||
}
|
||||
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
|
||||
processExternalSimpleFunction(declaration)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun lowerExternalTopLevelClass(klass: IrClass) {
|
||||
if (klass.kind == ClassKind.OBJECT)
|
||||
generateExternalObjectInstanceGetter(klass)
|
||||
|
||||
if (klass.kind != ClassKind.INTERFACE)
|
||||
generateInstanceCheckForExternalClass(klass)
|
||||
}
|
||||
|
||||
fun processExternalProperty(property: IrProperty) {
|
||||
if (property.isFakeOverride)
|
||||
return
|
||||
|
||||
val propName: String =
|
||||
property.getJsNameOrKotlinName().identifier
|
||||
|
||||
property.getter?.let { getter ->
|
||||
val dispatchReceiver = getter.dispatchReceiverParameter
|
||||
val jsCode =
|
||||
if (dispatchReceiver == null)
|
||||
"() => $propName"
|
||||
else
|
||||
"(_this) => _this.$propName"
|
||||
|
||||
val res = createExternalJsFunction(
|
||||
property.name,
|
||||
"_\$external_prop_getter",
|
||||
resultType = getter.returnType,
|
||||
jsCode = jsCode
|
||||
)
|
||||
|
||||
if (dispatchReceiver != null) {
|
||||
res.addValueParameter("_this", dispatchReceiver.type)
|
||||
}
|
||||
|
||||
externalFunToTopLevelMapping[getter] = res
|
||||
}
|
||||
|
||||
property.setter?.let { setter ->
|
||||
val dispatchReceiver = setter.dispatchReceiverParameter
|
||||
val jsCode =
|
||||
if (dispatchReceiver == null)
|
||||
"(v) => $propName = v"
|
||||
else
|
||||
"(_this, v) => _this.$propName = v"
|
||||
|
||||
val res = createExternalJsFunction(
|
||||
property.name,
|
||||
"_\$external_prop_setter",
|
||||
resultType = setter.returnType,
|
||||
jsCode = jsCode
|
||||
)
|
||||
|
||||
if (dispatchReceiver != null) {
|
||||
res.addValueParameter("_this", dispatchReceiver.type)
|
||||
}
|
||||
res.addValueParameter("v", setter.valueParameters[0].type)
|
||||
|
||||
externalFunToTopLevelMapping[setter] = res
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendExternalClassReference(klass: IrClass) {
|
||||
val parent = klass.parent
|
||||
if (parent is IrClass) {
|
||||
appendExternalClassReference(parent)
|
||||
append('.')
|
||||
}
|
||||
append(klass.getJsNameOrKotlinName())
|
||||
}
|
||||
|
||||
fun processExternalConstructor(constructor: IrConstructor) {
|
||||
val klass = constructor.constructedClass
|
||||
val jsCode = buildString {
|
||||
append("(")
|
||||
appendParameterList(constructor.valueParameters.size)
|
||||
append(") => new ")
|
||||
appendExternalClassReference(klass)
|
||||
append("(")
|
||||
appendParameterList(constructor.valueParameters.size)
|
||||
append(")")
|
||||
}
|
||||
|
||||
val res = createExternalJsFunction(
|
||||
klass.name,
|
||||
"_\$external_constructor",
|
||||
resultType = klass.defaultType,
|
||||
jsCode = jsCode
|
||||
)
|
||||
|
||||
constructor.valueParameters.forEach { res.addValueParameter(it.name, it.type) }
|
||||
externalFunToTopLevelMapping[constructor] = res
|
||||
}
|
||||
|
||||
fun processExternalSimpleFunction(function: IrSimpleFunction) {
|
||||
if (function.parent is IrPackageFragment) return
|
||||
val jsName = function.getJsNameOrKotlinName()
|
||||
val dispatchReceiver = function.dispatchReceiverParameter
|
||||
require(dispatchReceiver != null) {
|
||||
"Non top-level external function w/o dispatchReceiverParameter: ${function.fqNameWhenAvailable}"
|
||||
}
|
||||
|
||||
val jsCode = buildString {
|
||||
append("(_this, ")
|
||||
appendParameterList(function.valueParameters.size)
|
||||
append(") => _this.")
|
||||
append(jsName)
|
||||
append("(")
|
||||
appendParameterList(function.valueParameters.size)
|
||||
append(")")
|
||||
}
|
||||
val res = createExternalJsFunction(
|
||||
function.name,
|
||||
"_\$external_member_fun",
|
||||
resultType = function.returnType,
|
||||
jsCode = jsCode
|
||||
)
|
||||
res.addValueParameter("_this", dispatchReceiver.type)
|
||||
function.valueParameters.forEach { res.addValueParameter(it.name, it.type) }
|
||||
|
||||
externalFunToTopLevelMapping[function] = res
|
||||
}
|
||||
|
||||
fun generateExternalObjectInstanceGetter(obj: IrClass) {
|
||||
context.mapping.wasmExternalObjectToGetInstanceFunction[obj] = createExternalJsFunction(
|
||||
obj.name,
|
||||
"_\$external_object_getInstance",
|
||||
resultType = obj.defaultType,
|
||||
jsCode = buildString {
|
||||
append("() => ")
|
||||
appendExternalClassReference(obj)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun generateInstanceCheckForExternalClass(klass: IrClass) {
|
||||
context.mapping.wasmExternalClassToInstanceCheck[klass] = createExternalJsFunction(
|
||||
klass.name,
|
||||
"_\$external_class_instanceof",
|
||||
resultType = context.irBuiltIns.booleanType,
|
||||
jsCode = buildString {
|
||||
append("(x) => x instanceof ")
|
||||
appendExternalClassReference(klass)
|
||||
}
|
||||
).also {
|
||||
it.addValueParameter("x", context.irBuiltIns.anyType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createExternalJsFunction(
|
||||
originalName: Name,
|
||||
suffix: String,
|
||||
resultType: IrType,
|
||||
jsCode: String,
|
||||
): IrSimpleFunction {
|
||||
val res = context.irFactory.buildFun {
|
||||
name = Name.identifier(originalName.asStringStripSpecialMarkers() + suffix)
|
||||
returnType = resultType
|
||||
isExternal = true
|
||||
}
|
||||
val builder = context.createIrBuilder(res.symbol)
|
||||
res.annotations += builder.irCallConstructor(context.wasmSymbols.jsFunConstructor, typeArguments = emptyList()).also {
|
||||
it.putValueArgument(0, builder.irString(jsCode))
|
||||
}
|
||||
res.parent = currentFile
|
||||
addedDeclarations += res
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect usages of complex declarations to top-level functions
|
||||
*/
|
||||
class ComplexExternalDeclarationsUsageLowering(val context: WasmBackendContext) : BodyLoweringPass {
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
return transformCall(expression)
|
||||
}
|
||||
|
||||
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
return transformCall(expression)
|
||||
}
|
||||
|
||||
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
|
||||
val externalGetInstance = context.mapping.wasmExternalObjectToGetInstanceFunction[expression.symbol.owner]
|
||||
return if (externalGetInstance != null) {
|
||||
IrCallImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
externalGetInstance.symbol,
|
||||
valueArgumentsCount = 0,
|
||||
typeArgumentsCount = 0
|
||||
)
|
||||
} else {
|
||||
expression
|
||||
}
|
||||
}
|
||||
|
||||
fun transformCall(call: IrFunctionAccessExpression): IrExpression {
|
||||
val newFun: IrSimpleFunction? =
|
||||
context.mapping.wasmNestedExternalToNewTopLevelFunction[call.symbol.owner.realOverrideTarget]
|
||||
|
||||
return if (newFun != null) {
|
||||
irCall(call, newFun, receiversAsArguments = true)
|
||||
} else {
|
||||
call
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -20,7 +20,10 @@ import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
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.expressions.IrStatementOriginImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
@@ -53,6 +56,8 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
|
||||
if (declaration !is IrSimpleFunction) return null
|
||||
val isExported = declaration.isExported()
|
||||
val isExternal = declaration.isExternal
|
||||
if (declaration.isPropertyAccessor) return null
|
||||
if (declaration.parent !is IrPackageFragment) return null
|
||||
if (!isExported && !isExternal) return null
|
||||
check(!(isExported && isExternal)) { "Exported external declarations are not supported: ${declaration.fqNameWhenAvailable}" }
|
||||
check(declaration.parent !is IrClass) { "Interop members are not supported: ${declaration.fqNameWhenAvailable}" }
|
||||
@@ -595,7 +600,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendParameterList(size: Int) =
|
||||
internal fun StringBuilder.appendParameterList(size: Int) =
|
||||
repeat(size) {
|
||||
append("p")
|
||||
append(it)
|
||||
|
||||
+15
-2
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.backend.common.lower.irNot
|
||||
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||
import org.jetbrains.kotlin.backend.wasm.ir2wasm.erasedUpperBound
|
||||
import org.jetbrains.kotlin.backend.wasm.ir2wasm.getRuntimeClass
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
@@ -151,9 +152,14 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
|
||||
|
||||
private fun generateTypeCheckNonNull(argument: IrExpression, toType: IrType): IrExpression {
|
||||
assert(!toType.isMarkedNullable())
|
||||
val classOrInterface = toType.eraseToClassOrInterface
|
||||
return when {
|
||||
// TODO: Use instanceof for classes later
|
||||
toType.eraseToClassOrInterface.isExternal -> builder.irTrue()
|
||||
classOrInterface.isExternal -> {
|
||||
if (classOrInterface.kind == ClassKind.INTERFACE)
|
||||
builder.irTrue()
|
||||
else
|
||||
generateIsExternalClass(argument, classOrInterface)
|
||||
}
|
||||
toType.isNothing() -> builder.irFalse()
|
||||
toType.isTypeParameter() -> generateTypeCheckWithTypeParameter(argument, toType)
|
||||
toType.isInterface() -> generateIsInterface(argument, toType)
|
||||
@@ -315,4 +321,11 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
|
||||
putTypeArgument(0, toType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateIsExternalClass(argument: IrExpression, klass: IrClass): IrExpression {
|
||||
val function = context.mapping.wasmJsInteropFunctionToWrapper[context.mapping.wasmExternalClassToInstanceCheck[klass]!!]!!
|
||||
return builder.irCall(function).also {
|
||||
it.putValueArgument(0, argument)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,15 @@ class JsMapping(val state: JsMappingState) : DefaultMapping(state) {
|
||||
// Wasm mappings
|
||||
val wasmJsInteropFunctionToWrapper =
|
||||
state.newDeclarationToDeclarationMapping<IrSimpleFunction, IrSimpleFunction>()
|
||||
|
||||
val wasmNestedExternalToNewTopLevelFunction =
|
||||
state.newDeclarationToDeclarationMapping<IrFunction, IrSimpleFunction>()
|
||||
|
||||
val wasmExternalObjectToGetInstanceFunction =
|
||||
state.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
|
||||
val wasmExternalClassToInstanceCheck =
|
||||
state.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
}
|
||||
|
||||
|
||||
|
||||
+146
-2
@@ -1,6 +1,9 @@
|
||||
// FILE: externals.js
|
||||
function createObject() {
|
||||
return {};
|
||||
return {
|
||||
getXmethod() { return this.x; },
|
||||
setXmethod(v) { this.x = v; }
|
||||
};
|
||||
}
|
||||
|
||||
function setX(obj, x) {
|
||||
@@ -11,15 +14,156 @@ function getX(obj) {
|
||||
return obj.x;
|
||||
}
|
||||
|
||||
const readOnlyProp = 123;
|
||||
var mutableProp = "20";
|
||||
|
||||
class C1 {
|
||||
constructor(a, b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
getA() { return this.a; }
|
||||
setA(v) { this.a = v; }
|
||||
|
||||
getB() { return this.b; }
|
||||
}
|
||||
|
||||
C1.Nested1 = class {}
|
||||
C1.Nested1.Nested2 = class {}
|
||||
C1.Nested1.Nested2.Nested3 = class {
|
||||
constructor(x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
foo() { return this.x + " from Nested 3"; }
|
||||
}
|
||||
|
||||
class C2 extends C1 {
|
||||
constructor(a, b) {
|
||||
super(a, b);
|
||||
this.c = "C";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
C2.Object1 = { Object2: { Object3: { x: "C2.Object1.Object2.Object3.x" } } }
|
||||
|
||||
const externalObj = {
|
||||
x: "externalObj.x",
|
||||
y: { x: "externalObj.y.x" },
|
||||
c: class { x = "(new externalObj.c()).x" }
|
||||
}
|
||||
|
||||
|
||||
// FILE: externals.kt
|
||||
external interface Obj
|
||||
external interface Obj {
|
||||
var x: Int
|
||||
fun getXmethod(): Int
|
||||
fun setXmethod(v: Int)
|
||||
}
|
||||
external fun createObject(): Obj
|
||||
external fun setX(obj: Obj, x: Int)
|
||||
external fun getX(obj: Obj): Int
|
||||
|
||||
external val readOnlyProp: Int
|
||||
external var mutableProp: String
|
||||
|
||||
open external class C1 {
|
||||
constructor(a: String, b: String)
|
||||
var a: String
|
||||
val b: String
|
||||
|
||||
fun getA(): String
|
||||
fun setA(x: String)
|
||||
fun getB(): String
|
||||
|
||||
class Nested1 {
|
||||
class Nested2 {
|
||||
class Nested3 {
|
||||
constructor(x: String)
|
||||
fun foo(): String
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
external class C2 : C1 {
|
||||
constructor(a: String, b: String)
|
||||
|
||||
val c: String
|
||||
|
||||
object Object1 {
|
||||
object Object2 {
|
||||
object Object3 {
|
||||
val x: String
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
external object externalObj {
|
||||
val x: String
|
||||
object y {
|
||||
val x: String
|
||||
}
|
||||
class c {
|
||||
val x: String
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val obj = createObject()
|
||||
setX(obj, 100)
|
||||
if (getX(obj) != 100) return "Fail 2"
|
||||
|
||||
if (obj.x != 100) return "Fail 2.1"
|
||||
obj.x = 200
|
||||
if (getX(obj) != 200) return "Fail 2.2"
|
||||
val objXRef = obj::x
|
||||
objXRef.set(300)
|
||||
if (getX(obj) != 300 || obj.x != 300 || objXRef.get() != 300) return "Fail 2.3"
|
||||
|
||||
if (obj.getXmethod() != 300) return "Fail 2.4"
|
||||
obj.setXmethod(400)
|
||||
if (obj.getXmethod() != 400 || getX(obj) != 400) return "Fail 2.5"
|
||||
|
||||
if (readOnlyProp != 123) return "Fail 3"
|
||||
if (::readOnlyProp.get() != 123) return "Fail 4"
|
||||
if (mutableProp != "20") return "Fail 5"
|
||||
mutableProp = "30"
|
||||
if (mutableProp != "30") return "Fail 6"
|
||||
(::mutableProp).set("40")
|
||||
if (mutableProp != "40") return "Fail 7"
|
||||
|
||||
val c1 = C1("A", "B")
|
||||
if (c1.a != "A" || c1.b != "B") return "Fail 8"
|
||||
if (c1.getA() != "A" || c1.getB() != "B") return "Fail 9"
|
||||
c1.setA("A2")
|
||||
if (c1.a != "A2") return "Fail 10"
|
||||
c1.a = "A3"
|
||||
if (c1.getA() != "A3") return "Fail 11"
|
||||
val c2 = C2("A", "B")
|
||||
if (c2.a != "A" || c2.b != "B" || c2.c != "C") return "Fail 12"
|
||||
val c2_as_c1: C1 = c2
|
||||
if (c2_as_c1.a != "A" || c2_as_c1.b != "B") return "Fail 13"
|
||||
|
||||
val nested3 = C1.Nested1.Nested2.Nested3("example")
|
||||
if (nested3.foo() != "example from Nested 3") return "Fail 14"
|
||||
|
||||
if (C2.Object1.Object2.Object3.x != "C2.Object1.Object2.Object3.x") return "Fail 15"
|
||||
if (externalObj.x != "externalObj.x") return "Fail 16"
|
||||
if (externalObj.y.x != "externalObj.y.x") return "Fail 17"
|
||||
if (externalObj.c().x != "(new externalObj.c()).x") return "Fail 18"
|
||||
|
||||
|
||||
if (c1 as Any !is C1) return "Fail 19"
|
||||
if (c2 as Any !is C1) return "Fail 20"
|
||||
if (c2 as Any !is C2) return "Fail 21"
|
||||
if (externalObj.c() as Any !is externalObj.c) return "Fail 22"
|
||||
if (10 as Any is C1) return "Fail 23"
|
||||
if (c1 as Any is C2) return "Fail 24"
|
||||
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user