[IR] Replaced compilation errors with just exceptions

This commit is contained in:
Igor Chevdar
2020-10-02 21:40:28 +05:00
parent 5b457eadb1
commit 91e457f07a
5 changed files with 178 additions and 510 deletions
@@ -32,22 +32,25 @@ internal fun CommonBackendContext.reportCompilationWarning(message: String) {
}
internal fun error(irFile: IrFile?, element: IrElement?, message: String): Nothing {
error(buildString {
append("Internal compiler error: $message\n")
if (element == null) {
append("(IR element is null)")
} else {
if (irFile != null) {
val location = element.getCompilerMessageLocation(irFile)
append("at $location\n")
}
val renderedElement = try {
element.render()
} catch (e: Throwable) {
"(unable to render IR element)"
}
append(renderedElement)
}
})
error(renderCompilerError(irFile, element, message))
}
internal fun renderCompilerError(irFile: IrFile?, element: IrElement?, message: String) =
buildString {
append("Internal compiler error: $message\n")
if (element == null) {
append("(IR element is null)")
} else {
if (irFile != null) {
val location = element.getCompilerMessageLocation(irFile)
append("at $location\n")
}
val renderedElement = try {
element.render()
} catch (e: Throwable) {
"(unable to render IR element)"
}
append(renderedElement)
}
}
@@ -55,8 +55,8 @@ internal interface KotlinStubs {
fun getUniqueCName(prefix: String): String
fun getUniqueKotlinFunctionReferenceClassName(prefix: String): String
fun reportError(location: IrElement, message: String): Nothing
fun throwCompilerError(element: IrElement?, message: String): Nothing
fun renderCompilerError(element: IrElement?, message: String = "Failed requirement."): String
}
private class KotlinToCCallBuilder(
@@ -113,7 +113,7 @@ private fun KotlinToCCallBuilder.buildKotlinBridgeCall(transformCall: (IrMemberA
internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWithScope, isInvoke: Boolean,
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default): IrExpression {
require(expression.dispatchReceiver == null)
require(expression.dispatchReceiver == null) { renderCompilerError(expression) }
val callBuilder = KotlinToCCallBuilder(builder, this, isObjCMethod = false, foreignExceptionMode)
@@ -141,7 +141,7 @@ internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWit
)
}
} else {
require(expression.extensionReceiver == null)
require(expression.extensionReceiver == null) { renderCompilerError(expression) }
targetPtrParameter = null
targetFunctionName = this.getUniqueCName("target")
@@ -153,9 +153,9 @@ internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWit
val returnValuePassing = if (isInvoke) {
val returnType = expression.getTypeArgument(expression.typeArgumentsCount - 1)!!
mapReturnType(returnType, TypeLocation.FunctionCallResult(expression), signature = null)
mapReturnType(returnType, expression, signature = null)
} else {
mapReturnType(callee.returnType, TypeLocation.FunctionCallResult(expression), signature = callee)
mapReturnType(callee.returnType, expression, signature = callee)
}
val result = callBuilder.buildCall(targetFunctionName, returnValuePassing)
@@ -178,7 +178,7 @@ private fun KotlinToCCallBuilder.addArguments(arguments: List<IrExpression?>, ca
arguments.forEachIndexed { index, argument ->
val parameter = callee.valueParameters[index]
if (parameter.isVararg) {
require(index == arguments.lastIndex)
require(index == arguments.lastIndex) { stubs.renderCompilerError(argument) }
addVariadicArguments(argument)
cFunctionBuilder.variadic = true
} else {
@@ -220,14 +220,9 @@ private fun KotlinToCCallBuilder.unwrapVariadicArguments(
is IrExpression -> listOf(it)
is IrSpreadElement -> {
val expression = it.expression
if (expression is IrCall && expression.symbol == symbols.arrayOf) {
handleArgumentForVarargParameter(expression.getValueArgument(0)) { _, elements ->
unwrapVariadicArguments(elements)
}
} else {
stubs.reportError(it, "When calling variadic " +
(if (isObjCMethod) "Objective-C methods " else "C functions ") +
"spread operator is supported only for *arrayOf(...)")
require(expression is IrCall && expression.symbol == symbols.arrayOf) { stubs.renderCompilerError(it) }
handleArgumentForVarargParameter(expression.getValueArgument(0)) { _, elements ->
unwrapVariadicArguments(elements)
}
}
else -> stubs.throwCompilerError(it, "unexpected IrVarargElement")
@@ -260,15 +255,12 @@ private fun <R> KotlinToCCallBuilder.handleArgumentForVarargParameter(
val variable = argument.symbol.owner
if (variable is IrVariable && variable.origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE && !variable.isVar) {
val initializer = variable.initializer
if (initializer is IrVararg) {
block(variable, initializer.elements)
} else {
stubs.throwCompilerError(initializer, "unexpected initializer")
}
require(initializer is IrVararg) { stubs.renderCompilerError(initializer) }
block(variable, initializer.elements)
} else if (variable is IrValueParameter && FunctionReferenceLowering.isLoweredFunctionReference(variable)) {
val location = variable.parent // Parameter itself has incorrect location.
val kind = if (this.isObjCMethod) "Objective-C methods" else "C functions"
stubs.reportError(location, "callable references to variadic $kind are not supported")
stubs.throwCompilerError(location, "callable references to variadic $kind are not supported")
} else {
stubs.throwCompilerError(variable, "unexpected value declaration")
}
@@ -383,8 +375,7 @@ internal fun KotlinStubs.generateObjCCall(
callBuilder.addArguments(arguments, method)
val returnValuePassing =
mapReturnType(method.returnType, TypeLocation.FunctionCallResult(call), signature = method)
val returnValuePassing = mapReturnType(method.returnType, call, signature = method)
val result = callBuilder.buildCall(targetFunctionName, returnValuePassing)
@@ -398,7 +389,7 @@ internal fun KotlinStubs.generateObjCCall(
internal fun IrBuilderWithScope.getObjCClass(symbols: KonanSymbols, symbol: IrClassSymbol): IrExpression {
val classDescriptor = symbol.descriptor
assert(!classDescriptor.isObjCMetaClass())
require(!classDescriptor.isObjCMetaClass())
return irCall(symbols.interopGetObjCClass, symbols.nativePtrType, listOf(symbol.typeWithStarProjections))
}
@@ -437,25 +428,14 @@ private fun CCallbackBuilder.passThroughBridge(
}
private fun CCallbackBuilder.addParameter(it: IrValueParameter, functionParameter: IrValueParameter) {
val typeLocation = if (isObjCMethod) {
TypeLocation.ObjCMethodParameter(it.index, functionParameter)
} else {
TypeLocation.FunctionPointerParameter(cFunctionBuilder.numberOfParameters, location)
}
if (functionParameter.isVararg) {
stubs.reportError(typeLocation.element, if (isObjCMethod) {
"overriding variadic Objective-C methods is not supported"
} else {
"variadic function pointers are not supported"
})
}
val location = if (isObjCMethod) functionParameter else location
require(!functionParameter.isVararg) { stubs.renderCompilerError(location) }
val valuePassing = stubs.mapFunctionParameterType(
it.type,
retained = it.isObjCConsumed(),
variadic = false,
location = typeLocation
location = location
)
val kotlinArgument = with(valuePassing) { receiveValue() }
@@ -463,12 +443,11 @@ private fun CCallbackBuilder.addParameter(it: IrValueParameter, functionParamete
}
private fun CCallbackBuilder.build(function: IrSimpleFunction, signature: IrSimpleFunction): String {
val typeLocation = if (isObjCMethod) {
TypeLocation.ObjCMethodReturnValue(function)
} else {
TypeLocation.FunctionPointerReturnValue(location)
}
val valueReturning = stubs.mapReturnType(signature.returnType, typeLocation, signature)
val valueReturning = stubs.mapReturnType(
signature.returnType,
location = if (isObjCMethod) function else location,
signature = signature
)
buildValueReturn(function, valueReturning)
return buildCFunction()
}
@@ -512,7 +491,7 @@ private fun KotlinStubs.generateCFunction(
if (isObjCMethod) {
val receiver = signature.dispatchReceiverParameter!!
require(receiver.type.isObjCReferenceType(target, irBuiltIns))
require(receiver.type.isObjCReferenceType(target, irBuiltIns)) { renderCompilerError(signature) }
val valuePassing = ObjCReferenceValuePassing(symbols, receiver.type, retained = signature.objCConsumesReceiver())
val kotlinArgument = with(valuePassing) { callbackBuilder.receiveValue() }
callbackBuilder.kotlinCallBuilder.arguments += kotlinArgument
@@ -520,7 +499,7 @@ private fun KotlinStubs.generateCFunction(
// Selector is ignored:
with(TrivialValuePassing(symbols.nativePtrType, CTypes.voidPtr)) { callbackBuilder.receiveValue() }
} else {
require(signature.dispatchReceiverParameter == null)
require(signature.dispatchReceiverParameter == null) { renderCompilerError(signature) }
}
signature.extensionReceiverParameter?.let { callbackBuilder.addParameter(it, function.extensionReceiverParameter!!) }
@@ -633,11 +612,7 @@ private fun KotlinToCCallBuilder.mapCalleeFunctionParameter(
classifier == symbols.interopCValuesRef -> CValuesRefArgumentPassing
classifier == symbols.string && (variadic || parameter?.isCStringParameter() == true) -> {
if (variadic && isObjCMethod) {
stubs.reportError(argument, "Passing String as variadic Objective-C argument is ambiguous; " +
"cast it to NSString or pass with '.cstr' as C string")
// TODO: consider reporting a warning for C functions.
}
require(!variadic || !isObjCMethod) { stubs.renderCompilerError(argument) }
CStringArgumentPassing()
}
@@ -648,7 +623,7 @@ private fun KotlinToCCallBuilder.mapCalleeFunctionParameter(
type,
retained = parameter?.isObjCConsumed() ?: false,
variadic = variadic,
location = TypeLocation.FunctionArgument(argument)
location = argument
)
}
}
@@ -657,29 +632,15 @@ private fun KotlinStubs.mapFunctionParameterType(
type: IrType,
retained: Boolean,
variadic: Boolean,
location: TypeLocation
location: IrElement
): ArgumentPassing = when {
type.isUnit() && !variadic -> IgnoredUnitArgumentPassing
else -> mapType(type, retained = retained, variadic = variadic, location = location)
}
private sealed class TypeLocation(val element: IrElement) {
class FunctionArgument(val argument: IrExpression) : TypeLocation(argument)
class FunctionCallResult(val call: IrFunctionAccessExpression) : TypeLocation(call)
class FunctionPointerParameter(val index: Int, element: IrElement) : TypeLocation(element)
class FunctionPointerReturnValue(element: IrElement) : TypeLocation(element)
class ObjCMethodParameter(val index: Int, element: IrElement) : TypeLocation(element)
class ObjCMethodReturnValue(element: IrElement) : TypeLocation(element)
class BlockParameter(val index: Int, val blockLocation: TypeLocation) : TypeLocation(blockLocation.element)
class BlockReturnValue(val blockLocation: TypeLocation) : TypeLocation(blockLocation.element)
}
private fun KotlinStubs.mapReturnType(
type: IrType,
location: TypeLocation,
location: IrElement,
signature: IrSimpleFunction?
): ValueReturning = when {
type.isUnit() -> VoidReturning
@@ -689,39 +650,29 @@ private fun KotlinStubs.mapReturnType(
private fun KotlinStubs.mapBlockType(
type: IrType,
retained: Boolean,
location: TypeLocation
location: IrElement
): ObjCBlockPointerValuePassing {
type as IrSimpleType
require(type.classifier == symbols.functionN(type.arguments.size - 1))
require(type is IrSimpleType) { renderCompilerError(location) }
require(type.classifier == symbols.functionN(type.arguments.size - 1)) { renderCompilerError(location) }
val returnTypeArgument = type.arguments.last()
val valueReturning = when (returnTypeArgument) {
is IrTypeProjection -> if (returnTypeArgument.variance == Variance.INVARIANT) {
mapReturnType(returnTypeArgument.type, TypeLocation.BlockReturnValue(location), null)
} else {
reportUnsupportedType("${returnTypeArgument.variance.label}-variance of return type", type, location)
}
is IrStarProjection -> reportUnsupportedType("* as return type", type, location)
else -> error(returnTypeArgument)
}
val parameterValuePassings = type.arguments.dropLast(1).mapIndexed { index, argument ->
when (argument) {
is IrTypeProjection -> if (argument.variance == Variance.INVARIANT) {
mapType(
argument.type,
retained = false,
variadic = false,
location = TypeLocation.BlockParameter(index, location)
)
} else {
reportUnsupportedType("${argument.variance.label}-variance of ${index + 1} parameter type", type, location)
}
is IrStarProjection -> reportUnsupportedType("* as ${index + 1} parameter type", type, location)
else -> error(argument)
}
require(returnTypeArgument is IrTypeProjection) { renderCompilerError(location) }
require(returnTypeArgument.variance == Variance.INVARIANT) { renderCompilerError(location) }
val valueReturning = mapReturnType(returnTypeArgument.type, location, null)
val parameterValuePassings = type.arguments.dropLast(1).map { argument ->
require(argument is IrTypeProjection) { renderCompilerError(location) }
require(argument.variance == Variance.INVARIANT) { renderCompilerError(location) }
mapType(
argument.type,
retained = false,
variadic = false,
location = location
)
}
return ObjCBlockPointerValuePassing(
this,
location.element,
location,
type,
valueReturning,
parameterValuePassings,
@@ -729,20 +680,17 @@ private fun KotlinStubs.mapBlockType(
)
}
private fun KotlinStubs.mapType(type: IrType, retained: Boolean, variadic: Boolean, location: TypeLocation): ValuePassing =
mapType(type, retained, variadic, location, { reportUnsupportedType(it, type, location) })
private fun KotlinStubs.mapType(
type: IrType,
retained: Boolean,
variadic: Boolean,
typeLocation: TypeLocation,
reportUnsupportedType: (String) -> Nothing
location: IrElement
): ValuePassing = when {
type.isBoolean() -> BooleanValuePassing(
cBoolType(target) ?: reportUnsupportedType("unavailable on target platform"),
irBuiltIns
)
type.isBoolean() -> {
val cBoolType = cBoolType(target)
require(cBoolType != null) { renderCompilerError(location) }
BooleanValuePassing(cBoolType, irBuiltIns)
}
type.isByte() -> TrivialValuePassing(irBuiltIns.byteType, CTypes.signedChar)
type.isShort() -> TrivialValuePassing(irBuiltIns.shortType, CTypes.short)
@@ -750,7 +698,7 @@ private fun KotlinStubs.mapType(
type.isLong() -> TrivialValuePassing(irBuiltIns.longType, CTypes.longLong)
type.isFloat() -> TrivialValuePassing(irBuiltIns.floatType, CTypes.float)
type.isDouble() -> TrivialValuePassing(irBuiltIns.doubleType, CTypes.double)
type.classifierOrNull == symbols.interopCPointer -> TrivialValuePassing(type, CTypes.voidPtr)
type.isCPointer(symbols) -> TrivialValuePassing(type, CTypes.voidPtr)
type.isTypeOfNullLiteral() && variadic -> TrivialValuePassing(symbols.interopCPointer.typeWithStarProjections.makeNullable(), CTypes.voidPtr)
type.isUByte() -> UnsignedValuePassing(type, CTypes.signedChar, CTypes.unsignedChar)
type.isUShort() -> UnsignedValuePassing(type, CTypes.short, CTypes.unsignedShort)
@@ -768,33 +716,32 @@ private fun KotlinStubs.mapType(
CEnumValuePassing(
enumClass,
value,
mapType(value.getter!!.returnType, retained, variadic, typeLocation) as SimpleValuePassing
mapType(value.getter!!.returnType, retained, variadic, location) as SimpleValuePassing
)
}
type.classifierOrNull == symbols.interopCValue -> if (type.isNullable()) {
reportUnsupportedType("must not be nullable")
} else {
type.isCValue(symbols) -> {
require(!type.isNullable()) { renderCompilerError(location) }
val kotlinClass = (type as IrSimpleType).arguments.singleOrNull()?.typeOrNull?.getClass()
?: reportUnsupportedType("must be parameterized with concrete class")
require(kotlinClass != null) { renderCompilerError(location) }
val cStructType = getNamedCStructType(kotlinClass)
require(cStructType != null) { renderCompilerError(location) }
StructValuePassing(kotlinClass, getNamedCStructType(kotlinClass)
?: reportUnsupportedType("not a structure or too complex"))
StructValuePassing(kotlinClass, cStructType)
}
type.classOrNull?.isSubtypeOfClass(symbols.nativePointed) == true -> {
TrivialValuePassing(type, CTypes.voidPtr)
}
type.isFunction() -> if (variadic){
reportUnsupportedType("not supported as variadic argument")
} else {
mapBlockType(type, retained = retained, location = typeLocation)
type.isFunction() -> {
require(!variadic) { renderCompilerError(location) }
mapBlockType(type, retained = retained, location = location)
}
type.isObjCReferenceType(target, irBuiltIns) -> ObjCReferenceValuePassing(symbols, type, retained = retained)
else -> reportUnsupportedType("doesn't correspond to any C type")
else -> throwCompilerError(location, "doesn't correspond to any C type")
}
private class CExpression(val expression: String, val type: CType)
@@ -1211,7 +1158,7 @@ private class ObjCBlockPointerValuePassing(
}
val parameterCount = parameterValuePassings.size
assert(functionType.arguments.size == parameterCount + 1)
require(functionType.arguments.size == parameterCount + 1) { stubs.renderCompilerError(location) }
val overriddenInvokeMethod = (functionType.classifier.owner as IrClass).simpleFunctions()
.single { it.name == OperatorNameConventions.INVOKE }
@@ -1308,7 +1255,7 @@ private class ObjCBlockPointerValuePassing(
}
}
assert(functionType.isFunction())
require(functionType.isFunction()) { stubs.renderCompilerError(location) }
val invokeFunction = (functionType.classifier.owner as IrClass)
.simpleFunctions().single { it.name == OperatorNameConventions.INVOKE }
@@ -1447,22 +1394,3 @@ private object IgnoredUnitArgumentPassing : ArgumentPassing {
}
internal fun CType.cast(expression: String): String = "((${this.render("")})$expression)"
private fun KotlinStubs.reportUnsupportedType(reason: String, type: IrType, location: TypeLocation): Nothing {
// TODO: report errors in frontend instead.
fun TypeLocation.render(): String = when (this) {
is TypeLocation.FunctionArgument -> ""
is TypeLocation.FunctionCallResult -> " of return value"
is TypeLocation.FunctionPointerParameter -> " of callback parameter ${index + 1}"
is TypeLocation.FunctionPointerReturnValue -> " of callback return value"
is TypeLocation.ObjCMethodParameter -> " of overridden Objective-C method parameter"
is TypeLocation.ObjCMethodReturnValue -> " of overridden Objective-C method return value"
is TypeLocation.BlockParameter -> " of ${index + 1} parameter in Objective-C block type${blockLocation.render()}"
is TypeLocation.BlockReturnValue -> " of return value of Objective-C block type${blockLocation.render()}"
}
val typeLocation: String = location.render()
reportError(location.element, "type ${type.render()} $typeLocation is not supported here" +
if (reason.isNotEmpty()) ": $reason" else "")
}
@@ -355,7 +355,7 @@ private fun InteropCallContext.generateMemberAtAccess(callSite: IrCall): IrExpre
else -> failCompilation("Unsupported struct field type: ${type.getClass()?.name}")
}
}
else -> error("Unexpected accessor function: ${accessor.name}")
else -> failCompilation("Unexpected accessor function: ${accessor.name}")
}
}
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.backend.konan.ir.companionObject
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract
import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
@@ -62,33 +61,6 @@ internal class InteropLowering(context: Context) : FileLoweringPass {
}
}
private fun IrExpression.isNonCapturingFunction(): Boolean {
if (!type.isFunctionTypeOrSubtype())
return false
val fromContainerExpression = fun(expr: IrExpression): IrConstructorCall? {
if (expr !is IrContainerExpression)
return null
if (expr.statements.size != 2)
return null
val firstStatement = expr.statements[0]
if (firstStatement !is IrContainerExpression || firstStatement.statements.size != 0) {
return null
}
val secondStatement = expr.statements[1]
return secondStatement as? IrConstructorCall
}
val constructorCall = this as? IrConstructorCall
?: fromContainerExpression(this)
?: return false
return constructorCall.valueArgumentsCount == 0
}
private abstract class BaseInteropIrTransformer(private val context: Context) : IrBuildingTransformer(context) {
protected inline fun <T> generateWithStubs(element: IrElement? = null, block: KotlinStubs.() -> T): T =
@@ -131,15 +103,18 @@ private abstract class BaseInteropIrTransformer(private val context: Context) :
override val target get() = context.config.target
override fun reportError(location: IrElement, message: String): Nothing =
context.reportCompilationError(message, irFile, location)
override fun throwCompilerError(element: IrElement?, message: String): Nothing {
error(irFile, element, message)
}
override fun renderCompilerError(element: IrElement?, message: String) =
renderCompilerError(irFile, element, message)
}
}
protected fun renderCompilerError(element: IrElement?, message: String = "Failed requirement") =
renderCompilerError(irFile, element, message)
protected abstract val irFile: IrFile
protected abstract fun addTopLevel(declaration: IrDeclaration)
}
@@ -177,12 +152,6 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
putValueArgument(0, classPtr)
}
private fun IrBuilderWithScope.getObjCClass(classSymbol: IrClassSymbol): IrExpression {
val classDescriptor = classSymbol.descriptor
assert(!classDescriptor.isObjCMetaClass())
return irCall(symbols.interopGetObjCClass, symbols.nativePtrType, listOf(classSymbol.typeWithStarProjections))
}
private val outerClasses = mutableListOf<IrClass>()
override fun visitClass(declaration: IrClass): IrStatement {
@@ -220,7 +189,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
if (irClass.annotations.hasAnnotation(interop.exportObjCClass.fqNameSafe)) {
val irBuilder = context.createIrBuilder(currentFile.symbol).at(irClass)
topLevelInitializers.add(irBuilder.getObjCClass(irClass.symbol))
topLevelInitializers.add(irBuilder.getObjCClass(symbols, irClass.symbol))
}
}
@@ -241,38 +210,15 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
constructor.overridesConstructor(it)
}.toList()
val superConstructor = superConstructors.singleOrNull() ?: run {
val annotation = context.interopBuiltIns.objCOverrideInit.name
if (superConstructors.isEmpty()) {
context.reportCompilationError(
"""
constructor with @$annotation doesn't override any super class constructor.
It must completely match by parameter names and types.""".trimIndent(),
currentFile,
constructor
)
} else {
context.reportCompilationError(
"constructor with @$annotation matches more than one of super constructors",
currentFile,
constructor
)
}
}
val superConstructor = superConstructors.singleOrNull()
require(superConstructor != null) { renderCompilerError(constructor) }
val initMethod = superConstructor.getObjCInitMethod()!!
// Remove fake overrides of this init method, also check for explicit overriding:
irClass.declarations.removeAll {
if (it is IrSimpleFunction && initMethod.symbol in it.overriddenSymbols) {
if (it.isReal) {
val annotation = context.interopBuiltIns.objCOverrideInit.name
context.reportCompilationError(
"constructor with @$annotation overrides initializer that is already overridden explicitly",
currentFile,
constructor
)
}
require(it.isFakeOverride) { renderCompilerError(constructor) }
true
} else {
false
@@ -319,7 +265,8 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
)
}
assert(result.getObjCMethodInfo() != null) // Ensure it gets correctly recognized by the compiler.
// Ensure it gets correctly recognized by the compiler.
require(result.getObjCMethodInfo() != null) { renderCompilerError(constructor) }
}
}
@@ -335,55 +282,17 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
}
private fun generateActionImp(function: IrSimpleFunction): IrSimpleFunction {
val action = "@${context.interopBuiltIns.objCAction.name}"
function.extensionReceiverParameter?.let {
context.reportCompilationError("$action method must not have extension receiver",
currentFile, it)
}
function.valueParameters.forEach {
val kotlinType = it.descriptor.type
if (!kotlinType.isObjCObjectType()) {
context.reportCompilationError("Unexpected $action method parameter type: $kotlinType\n" +
"Only Objective-C object types are supported here",
currentFile, it)
}
}
val returnType = function.returnType
if (!returnType.isUnit()) {
context.reportCompilationError("Unexpected $action method return type: ${returnType.toKotlinType()}\n" +
"Only 'Unit' is supported here",
currentFile, function
)
}
require(function.extensionReceiverParameter == null) { renderCompilerError(function) }
require(function.valueParameters.all { it.type.isObjCObjectType() }) { renderCompilerError(function) }
require(function.returnType.isUnit()) { renderCompilerError(function) }
return generateFunctionImp(inferObjCSelector(function.descriptor), function)
}
private fun generateOutletSetterImp(property: IrProperty): IrSimpleFunction {
val descriptor = property.descriptor
val outlet = "@${context.interopBuiltIns.objCOutlet.name}"
if (!descriptor.isVar) {
context.reportCompilationError("$outlet property must be var",
currentFile, property)
}
property.getter?.extensionReceiverParameter?.let {
context.reportCompilationError("$outlet must not have extension receiver",
currentFile, it)
}
val type = descriptor.type
if (!type.isObjCObjectType()) {
context.reportCompilationError("Unexpected $outlet type: $type\n" +
"Only Objective-C object types are supported here",
currentFile, property)
}
require(property.isVar) { renderCompilerError(property) }
require(property.getter?.extensionReceiverParameter == null) { renderCompilerError(property) }
require(property.descriptor.type.isObjCObjectType()) { renderCompilerError(property) }
val name = property.name.asString()
val selector = "set${name.capitalize()}:"
@@ -392,39 +301,29 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
}
private fun getMethodSignatureEncoding(function: IrFunction): String {
assert(function.extensionReceiverParameter == null)
assert(function.valueParameters.all { it.type.isObjCObjectType() })
assert(function.returnType.isUnit())
require(function.extensionReceiverParameter == null) { renderCompilerError(function) }
require(function.valueParameters.all { it.type.isObjCObjectType() }) { renderCompilerError(function) }
require(function.returnType.isUnit()) { renderCompilerError(function) }
// Note: these values are valid for x86_64 and arm64.
return when (function.valueParameters.size) {
0 -> "v16@0:8"
1 -> "v24@0:8@16"
2 -> "v32@0:8@16@24"
else -> context.reportCompilationError("Only 0, 1 or 2 parameters are supported here",
currentFile, function
)
else -> error(irFile, function, "Only 0, 1 or 2 parameters are supported here")
}
}
private fun generateFunctionImp(selector: String, function: IrFunction): IrSimpleFunction {
val signatureEncoding = getMethodSignatureEncoding(function)
val returnType = function.returnType
assert(returnType.isUnit())
val nativePtrType = context.ir.symbols.nativePtrType
val parameterTypes = mutableListOf(nativePtrType) // id self
parameterTypes.add(nativePtrType) // SEL _cmd
function.valueParameters.mapTo(parameterTypes) {
when {
it.descriptor.type.isObjCObjectType() -> nativePtrType
else -> TODO()
}
}
function.valueParameters.mapTo(parameterTypes) { nativePtrType }
val newFunction = WrappedSimpleFunctionDescriptor().let {
IrFunctionImpl(
@@ -434,7 +333,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
("imp:$selector").synthesizedName,
DescriptorVisibilities.PRIVATE,
Modality.FINAL,
returnType,
function.returnType,
isInline = false,
isExternal = false,
isTailrec = false,
@@ -516,52 +415,23 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
}
private fun checkKotlinObjCClass(irClass: IrClass) {
val kind = irClass.descriptor.kind
if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) {
context.reportCompilationError(
"Only classes are supported as subtypes of Objective-C types",
currentFile, irClass
)
}
if (!irClass.descriptor.isFinalClass) {
context.reportCompilationError(
"Non-final Kotlin subclasses of Objective-C classes are not yet supported",
currentFile, irClass
)
}
irClass.companionObject()?.let {
if (it.hasFields() ||
it.getSuperClassNotAny()?.hasFields() ?: false) {
context.reportCompilationError(
"Fields are not supported for Companion of subclass of ObjC type",
currentFile, irClass
)
}
}
val kind = irClass.kind
require(kind == ClassKind.CLASS || kind == ClassKind.OBJECT) { renderCompilerError(irClass) }
require(irClass.isFinalClass) { renderCompilerError(irClass) }
require(irClass.companionObject()?.hasFields() != true) { renderCompilerError(irClass) }
require(irClass.companionObject()?.getSuperClassNotAny()?.hasFields() != true) { renderCompilerError(irClass) }
var hasObjCClassSupertype = false
irClass.descriptor.defaultType.constructor.supertypes.forEach {
val descriptor = it.constructor.declarationDescriptor as ClassDescriptor
if (!descriptor.isObjCClass()) {
context.reportCompilationError(
"Mixing Kotlin and Objective-C supertypes is not supported",
currentFile, irClass
)
}
require(descriptor.isObjCClass()) { renderCompilerError(irClass) }
if (descriptor.kind == ClassKind.CLASS) {
hasObjCClassSupertype = true
}
}
if (!hasObjCClassSupertype) {
context.reportCompilationError(
"Kotlin implementation of Objective-C protocol must have Objective-C superclass (e.g. NSObject)",
currentFile, irClass
)
}
require(hasObjCClassSupertype) { renderCompilerError(irClass) }
val methodsOfAny =
context.ir.symbols.any.owner.declarations.filterIsInstance<IrSimpleFunction>().toSet()
@@ -571,20 +441,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
it in methodsOfAny
}
if (overriddenMethodOfAny != null) {
val correspondingObjCMethod = when (method.name.asString()) {
"toString" -> "'description'"
"hashCode" -> "'hash'"
"equals" -> "'isEqual:'"
else -> "corresponding Objective-C method"
}
context.report(
method,
"can't override '${method.name}', override $correspondingObjCMethod instead",
isError = true
)
}
require(overriddenMethodOfAny == null) { renderCompilerError(method) }
}
}
@@ -616,23 +473,15 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
// Calling super constructor from Kotlin Objective-C class.
assert(constructedClass.getSuperClassNotAny() == delegatingCallConstructingClass)
require(constructedClass.getSuperClassNotAny() == delegatingCallConstructingClass) { renderCompilerError(expression) }
require(expression.symbol.owner.objCConstructorIsDesignated()) { renderCompilerError(expression) }
require(expression.dispatchReceiver == null) { renderCompilerError(expression) }
require(expression.extensionReceiver == null) { renderCompilerError(expression) }
val initMethod = expression.symbol.owner.getObjCInitMethod()!!
if (!expression.symbol.owner.objCConstructorIsDesignated()) {
context.reportCompilationError(
"Unable to call non-designated initializer as super constructor",
currentFile,
expression
)
}
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
assert(expression.dispatchReceiver == null)
assert(expression.extensionReceiver == null)
val initCall = builder.genLoweredObjCMethodCall(
initMethodInfo,
superQualifier = delegatingCallConstructingClass.symbol,
@@ -714,13 +563,13 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
val initMethod = callee.getObjCInitMethod()
if (initMethod != null) {
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
assert(expression.extensionReceiver == null)
assert(expression.dispatchReceiver == null)
require(expression.extensionReceiver == null) { renderCompilerError(expression) }
require(expression.dispatchReceiver == null) { renderCompilerError(expression) }
val constructedClass = callee.constructedClass
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
return builder.at(expression).run {
val classPtr = getObjCClass(constructedClass.symbol)
val classPtr = getObjCClass(symbols, constructedClass.symbol)
ensureObjCReferenceNotNull(callAllocAndInit(classPtr, initMethodInfo, arguments, expression, initMethod))
}
}
@@ -770,21 +619,9 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
if (!useKotlinDispatch) {
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
assert(expression.dispatchReceiver == null || expression.extensionReceiver == null)
if (expression.superQualifierSymbol?.owner?.isObjCMetaClass() == true) {
context.reportCompilationError(
"Super calls to Objective-C meta classes are not supported yet",
currentFile, expression
)
}
if (expression.superQualifierSymbol?.owner?.isInterface == true) {
context.reportCompilationError(
"Super calls to Objective-C protocols are not allowed",
currentFile, expression
)
}
require(expression.dispatchReceiver == null || expression.extensionReceiver == null) { renderCompilerError(expression) }
require(expression.superQualifierSymbol?.owner?.isObjCMetaClass() != true) { renderCompilerError(expression) }
require(expression.superQualifierSymbol?.owner?.isInterface != true) { renderCompilerError(expression) }
builder.at(expression)
return builder.genLoweredObjCMethodCall(
@@ -809,7 +646,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
val irClass = classSymbol.owner
val companionObject = irClass.companionObject() ?:
error("native variable class ${irClass.descriptor} must have the companion object")
error(irFile, expression, "native variable class ${irClass.descriptor} must have the companion object")
builder.at(expression).irGetObject(companionObject.symbol)
}
@@ -841,8 +678,8 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
// Note: in interop stubs const val initializer is either `IrConst` or quite simple expression,
// so it is ok to compute it every time.
assert(declaration.setter == null)
assert(!declaration.isVar)
require(declaration.setter == null) { renderCompilerError(declaration) }
require(!declaration.isVar) { renderCompilerError(declaration) }
declaration.transformChildrenVoid()
declaration
@@ -941,16 +778,15 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
val callee = expression.symbol.owner
val inlinedClass = callee.returnType.getInlinedClassNative()
if (inlinedClass?.descriptor == interop.cPointer || inlinedClass?.descriptor == interop.nativePointed) {
context.reportCompilationError("Native interop types constructors must not be called directly",
irFile, expression)
}
require(inlinedClass?.descriptor != interop.cPointer) { renderCompilerError(expression) }
require(inlinedClass?.descriptor != interop.nativePointed) { renderCompilerError(expression) }
val constructedClass = callee.constructedClass
if (!constructedClass.isObjCClass())
return expression
assert(constructedClass.isKotlinObjCClass()) // Calls to other ObjC class constructors must be lowered.
// Calls to other ObjC class constructors must be lowered.
require(constructedClass.isKotlinObjCClass()) { renderCompilerError(expression) }
return builder.at(expression).irBlock {
val rawPtr = irTemporary(irCall(symbols.interopAllocObjCObject.owner).apply {
putValueArgument(0, getObjCClass(symbols, constructedClass.symbol))
@@ -987,14 +823,11 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
?.takeIf { it.isConst }
?: return null
val irConstant = (constantProperty.backingField
?.initializer
?.expression
?: error("Constant property ${constantProperty.name} has no initializer!"))
as IrConst<*>
val initializer = constantProperty.backingField?.initializer?.expression
require(initializer is IrConst<*>) { renderCompilerError(expression) }
// Avoid node duplication
return irConstant.copy()
return initializer.copy()
}
override fun visitCall(expression: IrCall): IrExpression {
@@ -1002,30 +835,23 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
if (intrinsicType == IntrinsicType.OBJC_INIT_BY) {
// Need to do this separately as otherwise [expression.transformChildrenVoid(this)] would be called
// and the [IrConstructorCall] would be transformed which is not what we want.
val intrinsic = interop.objCObjectInitBy.name
val argument = expression.getValueArgument(0)!!
val constructorCall = argument as? IrConstructorCall
?: context.reportCompilationError("Argument of '$intrinsic' must be a constructor call",
irFile, argument)
require(argument is IrConstructorCall) { renderCompilerError(argument) }
val constructedClass = constructorCall.symbol.owner.constructedClass
val constructedClass = argument.symbol.owner.constructedClass
val extensionReceiver = expression.extensionReceiver!!
if (extensionReceiver !is IrGetValue ||
!extensionReceiver.symbol.owner.isDispatchReceiverFor(constructedClass)) {
require(extensionReceiver is IrGetValue &&
extensionReceiver.symbol.owner.isDispatchReceiverFor(constructedClass)) { renderCompilerError(extensionReceiver) }
context.reportCompilationError("Receiver of '$intrinsic' must be a 'this' of the constructed class",
irFile, extensionReceiver)
}
constructorCall.transformChildrenVoid(this)
argument.transformChildrenVoid(this)
return builder.at(expression).irBlock {
val instance = extensionReceiver.symbol.owner
+irCall(symbols.initInstance).apply {
putValueArgument(0, irGet(instance))
putValueArgument(1, constructorCall)
putValueArgument(1, argument)
}
+irGet(instance)
}
@@ -1052,7 +878,7 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
return generateWithStubs { generateCCall(expression, builder, isInvoke = false, exceptionMode) }
}
val failCompilation = { msg: String -> context.reportCompilationError(msg, irFile, expression) }
val failCompilation = { msg: String -> error(irFile, expression, msg) }
tryGenerateInteropMemberAccess(expression, symbols, builder, failCompilation)?.let { return it }
tryGenerateInteropConstantRead(expression)?.let { return it }
@@ -1080,14 +906,8 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
IntrinsicType.INTEROP_STATIC_C_FUNCTION -> {
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(0)!!)
if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()
|| irCallableReference.symbol !is IrSimpleFunctionSymbol) {
context.reportCompilationError(
"${function.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda",
irFile, expression
)
// TODO: should probably be reported during analysis.
}
require(irCallableReference != null && irCallableReference.getArguments().isEmpty()
&& irCallableReference.symbol is IrSimpleFunctionSymbol) { renderCompilerError(expression) }
val targetSymbol = irCallableReference.symbol
val target = targetSymbol.owner
@@ -1096,13 +916,9 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
function.typeParameters.indices.forEach { index ->
val typeArgument = expression.getTypeArgument(index)!!.toKotlinType()
val signatureType = signatureTypes[index].toKotlinType()
if (typeArgument.constructor != signatureType.constructor ||
typeArgument.isMarkedNullable != signatureType.isMarkedNullable) {
context.reportCompilationError(
"C function signature element mismatch: expected '$signatureType', got '$typeArgument'",
irFile, expression
)
}
require(typeArgument.constructor == signatureType.constructor &&
typeArgument.isMarkedNullable == signatureType.isMarkedNullable) { renderCompilerError(expression) }
}
generateCFunctionPointer(target as IrSimpleFunction, expression)
@@ -1118,36 +934,19 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
val receiver = expression.extensionReceiver!!
val typeOperand = expression.getSingleTypeArgument()
val kotlinTypeOperand = typeOperand.toKotlinType()
val receiverTypeIndex = integerTypePredicates.indexOfFirst { it(receiver.type) }
val typeOperandIndex = integerTypePredicates.indexOfFirst { it(typeOperand) }
val receiverKotlinType = receiver.type.toKotlinType()
if (receiverTypeIndex == -1) {
context.reportCompilationError("Receiver's type $receiverKotlinType is not an integer type",
irFile, receiver)
}
if (typeOperandIndex == -1) {
context.reportCompilationError("Type argument $kotlinTypeOperand is not an integer type",
irFile, expression)
}
require(receiverTypeIndex >= 0) { renderCompilerError(receiver) }
require(typeOperandIndex >= 0) { renderCompilerError(expression) }
when (intrinsicType) {
IntrinsicType.INTEROP_SIGN_EXTEND -> if (receiverTypeIndex > typeOperandIndex) {
context.reportCompilationError("unable to sign extend $receiverKotlinType to $kotlinTypeOperand",
irFile, expression)
}
IntrinsicType.INTEROP_NARROW -> if (receiverTypeIndex < typeOperandIndex) {
context.reportCompilationError("unable to narrow $receiverKotlinType to $kotlinTypeOperand",
irFile, expression)
}
else -> context.reportCompilationError("unexpected intrinsic $intrinsicType",
irFile, expression)
IntrinsicType.INTEROP_SIGN_EXTEND ->
require(receiverTypeIndex <= typeOperandIndex) { renderCompilerError(expression) }
IntrinsicType.INTEROP_NARROW ->
require(receiverTypeIndex >= typeOperandIndex) { renderCompilerError(expression) }
else -> error(intrinsicType)
}
val receiverClass = symbols.integerClasses.single {
@@ -1170,26 +969,20 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
val typeOperand = expression.getTypeArgument(0)!!
val receiverType = expression.symbol.owner.extensionReceiverParameter!!.type
val source = receiverType.classifierOrFail as IrClassSymbol
assert(source in integerClasses)
require(source in integerClasses) { renderCompilerError(expression) }
require(typeOperand is IrSimpleType && typeOperand.classifier in integerClasses
&& !typeOperand.hasQuestionMark) { renderCompilerError(expression) }
if (typeOperand is IrSimpleType && typeOperand.classifier in integerClasses && !typeOperand.hasQuestionMark) {
val target = typeOperand.classifier as IrClassSymbol
val valueToConvert = expression.extensionReceiver!!
val target = typeOperand.classifier as IrClassSymbol
val valueToConvert = expression.extensionReceiver!!
if (source in symbols.signedIntegerClasses && target in symbols.unsignedIntegerClasses) {
// Default Kotlin signed-to-unsigned widening integer conversions don't follow C rules.
val signedTarget = symbols.unsignedToSignedOfSameBitWidth[target]!!
val widened = builder.irConvertInteger(source, signedTarget, valueToConvert)
builder.irConvertInteger(signedTarget, target, widened)
} else {
builder.irConvertInteger(source, target, valueToConvert)
}
if (source in symbols.signedIntegerClasses && target in symbols.unsignedIntegerClasses) {
// Default Kotlin signed-to-unsigned widening integer conversions don't follow C rules.
val signedTarget = symbols.unsignedToSignedOfSameBitWidth[target]!!
val widened = builder.irConvertInteger(source, signedTarget, valueToConvert)
builder.irConvertInteger(signedTarget, target, widened)
} else {
context.reportCompilationError(
"unable to convert ${receiverType.toKotlinType()} to ${typeOperand.toKotlinType()}",
irFile,
expression
)
builder.irConvertInteger(source, target, valueToConvert)
}
}
IntrinsicType.INTEROP_MEMORY_COPY -> {
@@ -1198,12 +991,8 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
IntrinsicType.WORKER_EXECUTE -> {
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(2)!!)
if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()) {
context.reportCompilationError(
"${function.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda",
irFile, expression
)
}
require(irCallableReference != null
&& irCallableReference.getArguments().isEmpty()) { renderCompilerError(expression) }
val targetSymbol = irCallableReference.symbol
val jobPointer = IrFunctionReferenceImpl(
@@ -1230,17 +1019,6 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
builder.irCall(symbols.interopCPointerGetRawValue).apply {
extensionReceiver = expression.dispatchReceiver
}
// TODO: Move this check out of InteropLowering.
symbols.createCleaner.owner -> {
val irCallableReference = expression.getValueArgument(1)
if (irCallableReference == null || !irCallableReference.isNonCapturingFunction()) {
context.reportCompilationError(
"${function.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda",
irFile, expression
)
}
expression
}
else -> expression
}
}
@@ -1260,38 +1038,6 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
}
}
private fun IrType.ensureSupportedInCallbacks(isReturnType: Boolean, reportError: (String) -> Nothing) {
this.checkCTypeNullability(reportError)
if (isReturnType && this.isUnit()) {
return
}
if (this.isPrimitiveType()) {
return
}
if (UnsignedTypes.isUnsignedType(this.toKotlinType()) && !this.containsNull()) {
return
}
if (this.getClass()?.descriptor == interop.cPointer) {
return
}
reportError("Type ${this.toKotlinType()} is not supported in callback signature")
}
private fun IrType.checkCTypeNullability(reportError: (String) -> Nothing) {
if (this.isNullablePrimitiveType() || UnsignedTypes.isUnsignedType(this.toKotlinType()) && this.containsNull()) {
reportError("Type ${this.toKotlinType()} must not be nullable when used in C function signature")
}
if (this.getClass() == interop.cPointer && !this.isSimpleTypeWithQuestionMark) {
reportError("Type ${this.toKotlinType()} must be nullable when used in C function signature")
}
}
private fun unwrapStaticFunctionArgument(argument: IrExpression): IrFunctionReference? {
if (argument is IrFunctionReference) {
return argument
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.error
import org.jetbrains.kotlin.backend.konan.reportCompilationError
import org.jetbrains.kotlin.backend.konan.renderCompilerError
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.*
@@ -77,23 +77,14 @@ internal class PostInlineLowering(val context: Context) : FileLoweringPass {
// Convert arguments of the binary blob to special IrConst<String> structure, so that
// vararg lowering will not affect it.
val args = expression.getValueArgument(0) as? IrVararg
?: throw Error("varargs shall not be lowered yet")
if (args.elements.any { it is IrSpreadElement }) {
context.reportCompilationError("no spread elements allowed here", irFile, args)
}
?: error("varargs shall not be lowered yet")
val builder = StringBuilder()
args.elements.forEach {
if (it !is IrConst<*>) {
context.reportCompilationError(
"all elements of binary blob must be constants", irFile, it)
require(it is IrConst<*>) { renderCompilerError(irFile, it, "expected const") }
val value = (it as? IrConst<*>)?.value
require(value is Short && value >= 0 && value <= 0xff) {
renderCompilerError(irFile, it, "incorrect value for binary data: $value")
}
val value = when (it.kind) {
IrConstKind.Short -> (it.value as Short).toInt()
else ->
context.reportCompilationError("incorrect value for binary data: $it.value", irFile, it)
}
if (value < 0 || value > 0xff)
context.reportCompilationError("incorrect value for binary data: $value", irFile, it)
// Luckily, all values in range 0x00 .. 0xff represent valid UTF-16 symbols,
// block 0 (Basic Latin) and block 1 (Latin-1 Supplement) in
// Basic Multilingual Plane, so we could just append data "as is".