Parcelize: Add support for unsigned types on the IR backend
This commit is contained in:
committed by
Alexander Udalov
parent
34a200bf09
commit
9fd777cb7d
+11
@@ -4,9 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.jetbrains.kotlin.parcelize.ir
|
package org.jetbrains.kotlin.parcelize.ir
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
|
|
||||||
// An IR builder with access to AndroidSymbols and convenience methods to build calls to some of these methods.
|
// An IR builder with access to AndroidSymbols and convenience methods to build calls to some of these methods.
|
||||||
class AndroidIrBuilder internal constructor(
|
class AndroidIrBuilder internal constructor(
|
||||||
@@ -87,4 +90,12 @@ class AndroidIrBuilder internal constructor(
|
|||||||
fun getTextUtilsCharSequenceCreator(): IrExpression {
|
fun getTextUtilsCharSequenceCreator(): IrExpression {
|
||||||
return irGetField(null, androidSymbols.textUtilsCharSequenceCreator.owner)
|
return irGetField(null, androidSymbols.textUtilsCharSequenceCreator.owner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun unsafeCoerce(value: IrExpression, fromType: IrType, toType: IrType): IrExpression {
|
||||||
|
return IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, toType, androidSymbols.unsafeCoerceIntrinsic).apply {
|
||||||
|
putTypeArgument(0, fromType)
|
||||||
|
putTypeArgument(1, toType)
|
||||||
|
putValueArgument(0, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-5
@@ -6,20 +6,19 @@ package org.jetbrains.kotlin.parcelize.ir
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
|
import org.jetbrains.kotlin.descriptors.InlineClassRepresentation
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
|
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.*
|
import org.jetbrains.kotlin.ir.symbols.*
|
||||||
import org.jetbrains.kotlin.ir.types.defaultType
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
|
||||||
import org.jetbrains.kotlin.ir.types.starProjectedType
|
|
||||||
import org.jetbrains.kotlin.ir.types.typeWith
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
@@ -36,7 +35,9 @@ class AndroidSymbols(
|
|||||||
private val javaLang: IrPackageFragment = createPackage("java.lang")
|
private val javaLang: IrPackageFragment = createPackage("java.lang")
|
||||||
private val javaUtil: IrPackageFragment = createPackage("java.util")
|
private val javaUtil: IrPackageFragment = createPackage("java.util")
|
||||||
|
|
||||||
|
private val kotlin: IrPackageFragment = createPackage("kotlin")
|
||||||
private val kotlinJvm: IrPackageFragment = createPackage("kotlin.jvm")
|
private val kotlinJvm: IrPackageFragment = createPackage("kotlin.jvm")
|
||||||
|
private val kotlinJvmInternalPackage: IrPackageFragment = createPackage("kotlin.jvm.internal")
|
||||||
|
|
||||||
private val androidOs: IrPackageFragment = createPackage("android.os")
|
private val androidOs: IrPackageFragment = createPackage("android.os")
|
||||||
private val androidUtil: IrPackageFragment = createPackage("android.util")
|
private val androidUtil: IrPackageFragment = createPackage("android.util")
|
||||||
@@ -102,6 +103,26 @@ class AndroidSymbols(
|
|||||||
private val javaUtilTreeSet: IrClassSymbol =
|
private val javaUtilTreeSet: IrClassSymbol =
|
||||||
createClass(javaUtil, "TreeSet", ClassKind.CLASS, Modality.OPEN)
|
createClass(javaUtil, "TreeSet", ClassKind.CLASS, Modality.OPEN)
|
||||||
|
|
||||||
|
val kotlinUByte: IrClassSymbol =
|
||||||
|
createClass(kotlin, "UByte", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||||
|
owner.inlineClassRepresentation = InlineClassRepresentation(Name.identifier("data"), irBuiltIns.byteType as IrSimpleType)
|
||||||
|
}
|
||||||
|
|
||||||
|
val kotlinUShort: IrClassSymbol =
|
||||||
|
createClass(kotlin, "UShort", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||||
|
owner.inlineClassRepresentation = InlineClassRepresentation(Name.identifier("data"), irBuiltIns.shortType as IrSimpleType)
|
||||||
|
}
|
||||||
|
|
||||||
|
val kotlinUInt: IrClassSymbol =
|
||||||
|
createClass(kotlin, "UInt", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||||
|
owner.inlineClassRepresentation = InlineClassRepresentation(Name.identifier("data"), irBuiltIns.intType as IrSimpleType)
|
||||||
|
}
|
||||||
|
|
||||||
|
val kotlinULong: IrClassSymbol =
|
||||||
|
createClass(kotlin, "ULong", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||||
|
owner.inlineClassRepresentation = InlineClassRepresentation(Name.identifier("data"), irBuiltIns.longType as IrSimpleType)
|
||||||
|
}
|
||||||
|
|
||||||
val androidOsParcelableCreator: IrClassSymbol = irFactory.buildClass {
|
val androidOsParcelableCreator: IrClassSymbol = irFactory.buildClass {
|
||||||
name = Name.identifier("Creator")
|
name = Name.identifier("Creator")
|
||||||
kind = ClassKind.INTERFACE
|
kind = ClassKind.INTERFACE
|
||||||
@@ -436,6 +457,18 @@ class AndroidSymbols(
|
|||||||
isStatic = true
|
isStatic = true
|
||||||
}.symbol
|
}.symbol
|
||||||
|
|
||||||
|
val unsafeCoerceIntrinsic: IrSimpleFunctionSymbol =
|
||||||
|
irFactory.buildFun {
|
||||||
|
name = Name.special("<unsafe-coerce>")
|
||||||
|
origin = IrDeclarationOrigin.IR_BUILTINS_STUB
|
||||||
|
}.apply {
|
||||||
|
parent = kotlinJvmInternalPackage
|
||||||
|
val src = addTypeParameter("T", irBuiltIns.anyNType)
|
||||||
|
val dst = addTypeParameter("R", irBuiltIns.anyNType)
|
||||||
|
addValueParameter("v", src.defaultType)
|
||||||
|
returnType = dst.defaultType
|
||||||
|
}.symbol
|
||||||
|
|
||||||
private fun createPackage(packageName: String): IrPackageFragment =
|
private fun createPackage(packageName: String): IrPackageFragment =
|
||||||
IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(
|
IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(
|
||||||
moduleFragment.descriptor,
|
moduleFragment.descriptor,
|
||||||
@@ -446,11 +479,13 @@ class AndroidSymbols(
|
|||||||
irPackage: IrPackageFragment,
|
irPackage: IrPackageFragment,
|
||||||
shortName: String,
|
shortName: String,
|
||||||
classKind: ClassKind,
|
classKind: ClassKind,
|
||||||
classModality: Modality
|
classModality: Modality,
|
||||||
|
isInlineClass: Boolean = false
|
||||||
): IrClassSymbol = irFactory.buildClass {
|
): IrClassSymbol = irFactory.buildClass {
|
||||||
name = Name.identifier(shortName)
|
name = Name.identifier(shortName)
|
||||||
kind = classKind
|
kind = classKind
|
||||||
modality = classModality
|
modality = classModality
|
||||||
|
isInline = isInlineClass
|
||||||
}.apply {
|
}.apply {
|
||||||
parent = irPackage
|
parent = irPackage
|
||||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||||
|
|||||||
+16
@@ -71,6 +71,16 @@ class IrParcelSerializerFactory(symbols: AndroidSymbols) {
|
|||||||
"android.util.SizeF" ->
|
"android.util.SizeF" ->
|
||||||
return wrapNullableSerializerIfNeeded(irType, sizeFSerializer)
|
return wrapNullableSerializerIfNeeded(irType, sizeFSerializer)
|
||||||
|
|
||||||
|
// Unsigned primitive types
|
||||||
|
"kotlin.UByte" ->
|
||||||
|
return wrapNullableSerializerIfNeeded(irType, ubyteSerializer)
|
||||||
|
"kotlin.UShort" ->
|
||||||
|
return wrapNullableSerializerIfNeeded(irType, ushortSerializer)
|
||||||
|
"kotlin.UInt" ->
|
||||||
|
return wrapNullableSerializerIfNeeded(irType, uintSerializer)
|
||||||
|
"kotlin.ULong" ->
|
||||||
|
return wrapNullableSerializerIfNeeded(irType, ulongSerializer)
|
||||||
|
|
||||||
// Built-in non-parameterized container types.
|
// Built-in non-parameterized container types.
|
||||||
"kotlin.IntArray" ->
|
"kotlin.IntArray" ->
|
||||||
if (!scope.hasCustomSerializer(irBuiltIns.intType))
|
if (!scope.hasCustomSerializer(irBuiltIns.intType))
|
||||||
@@ -275,6 +285,12 @@ class IrParcelSerializerFactory(symbols: AndroidSymbols) {
|
|||||||
private val shortSerializer = IrWrappedIntParcelSerializer(irBuiltIns.shortType)
|
private val shortSerializer = IrWrappedIntParcelSerializer(irBuiltIns.shortType)
|
||||||
private val charSerializer = IrWrappedIntParcelSerializer(irBuiltIns.charType)
|
private val charSerializer = IrWrappedIntParcelSerializer(irBuiltIns.charType)
|
||||||
|
|
||||||
|
// Unsigned primitive types
|
||||||
|
private val ubyteSerializer = IrUnsafeCoerceWrappedSerializer(byteSerializer, symbols.kotlinUByte.defaultType, irBuiltIns.byteType)
|
||||||
|
private val ushortSerializer = IrUnsafeCoerceWrappedSerializer(shortSerializer, symbols.kotlinUShort.defaultType, irBuiltIns.shortType)
|
||||||
|
private val uintSerializer = IrUnsafeCoerceWrappedSerializer(intSerializer, symbols.kotlinUInt.defaultType, irBuiltIns.intType)
|
||||||
|
private val ulongSerializer = IrUnsafeCoerceWrappedSerializer(longSerializer, symbols.kotlinULong.defaultType, irBuiltIns.longType)
|
||||||
|
|
||||||
private val charSequenceSerializer = IrCharSequenceParcelSerializer()
|
private val charSequenceSerializer = IrCharSequenceParcelSerializer()
|
||||||
|
|
||||||
// TODO The old backend uses the hidden "read/writeRawFileDescriptor" methods.
|
// TODO The old backend uses the hidden "read/writeRawFileDescriptor" methods.
|
||||||
|
|||||||
+14
@@ -79,6 +79,20 @@ class IrWrappedIntParcelSerializer(private val parcelType: IrType) : IrParcelSer
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class IrUnsafeCoerceWrappedSerializer(
|
||||||
|
private val serializer: IrParcelSerializer,
|
||||||
|
private val wrappedType: IrType,
|
||||||
|
private val underlyingType: IrType,
|
||||||
|
) : IrParcelSerializer {
|
||||||
|
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||||
|
return unsafeCoerce(readParcelWith(serializer, parcel), underlyingType, wrappedType)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||||
|
return writeParcelWith(serializer, parcel, flags, unsafeCoerce(value, wrappedType, underlyingType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Wraps a non-null aware parceler to handle nullable types.
|
// Wraps a non-null aware parceler to handle nullable types.
|
||||||
class IrNullAwareParcelSerializer(private val serializer: IrParcelSerializer) : IrParcelSerializer {
|
class IrNullAwareParcelSerializer(private val serializer: IrParcelSerializer) : IrParcelSerializer {
|
||||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||||
|
|||||||
+9
@@ -148,6 +148,10 @@ interface ParcelSerializer {
|
|||||||
PrimitiveTypeParcelSerializer.getInstance(asmType)
|
PrimitiveTypeParcelSerializer.getInstance(asmType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmType.isUnsigned() -> {
|
||||||
|
ParcelSerializerStub(asmType, type)
|
||||||
|
}
|
||||||
|
|
||||||
asmType.isString() -> {
|
asmType.isString() -> {
|
||||||
NullCompliantObjectParcelSerializer(
|
NullCompliantObjectParcelSerializer(
|
||||||
asmType,
|
asmType,
|
||||||
@@ -422,6 +426,11 @@ interface ParcelSerializer {
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Type.isUnsigned(): Boolean = when (descriptor) {
|
||||||
|
"Lkotlin/UByte;", "Lkotlin/UShort;", "Lkotlin/UInt;", "Lkotlin/ULong;" -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
private fun Type.isBoxedPrimitive(): Boolean = when (this.descriptor) {
|
private fun Type.isBoxedPrimitive(): Boolean = when (this.descriptor) {
|
||||||
"Ljava/lang/Boolean;",
|
"Ljava/lang/Boolean;",
|
||||||
"Ljava/lang/Character;",
|
"Ljava/lang/Character;",
|
||||||
|
|||||||
+15
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.codegen.useTmpVar
|
|||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.parcelize.serializers.BoxedPrimitiveTypeParcelSerializer.Companion.BOXED_VALUE_METHOD_NAMES
|
import org.jetbrains.kotlin.parcelize.serializers.BoxedPrimitiveTypeParcelSerializer.Companion.BOXED_VALUE_METHOD_NAMES
|
||||||
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.org.objectweb.asm.Label
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
@@ -785,6 +786,20 @@ internal open class PrimitiveTypeParcelSerializer private constructor(final over
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class ParcelSerializerStub(override val asmType: Type, private val kotlinType: KotlinType) : ParcelSerializer {
|
||||||
|
private fun throwError() {
|
||||||
|
TODO("Type is only supported in the IR backend: ${DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(kotlinType)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeValue(v: InstructionAdapter) {
|
||||||
|
throwError()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readValue(v: InstructionAdapter) {
|
||||||
|
throwError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun readValueNullAware(v: InstructionAdapter, block: () -> Unit) {
|
private fun readValueNullAware(v: InstructionAdapter, block: () -> Unit) {
|
||||||
val labelNull = Label()
|
val labelNull = Label()
|
||||||
val labelReturn = Label()
|
val labelReturn = Label()
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IGNORE_BACKEND: JVM
|
||||||
|
|
||||||
|
@file:JvmName("TestKt")
|
||||||
|
package test
|
||||||
|
|
||||||
|
import kotlinx.parcelize.*
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class UnsignedTypes(
|
||||||
|
val ub: UByte, val us: UShort, val ui: UInt, val ul: ULong,
|
||||||
|
val nub: UByte?, val nus: UShort?, val nui: UInt?, val nul: ULong?
|
||||||
|
) : Parcelable
|
||||||
|
|
||||||
|
fun box() = parcelTest { parcel ->
|
||||||
|
val first = UnsignedTypes(
|
||||||
|
3.toUByte(), 10.toUShort(), 300.toUInt(), 3000UL,
|
||||||
|
3.toUByte(), 10.toUShort(), 300.toUInt(), 3000UL,
|
||||||
|
)
|
||||||
|
val second = UnsignedTypes(
|
||||||
|
UByte.MAX_VALUE, UShort.MAX_VALUE, UInt.MAX_VALUE, ULong.MAX_VALUE,
|
||||||
|
UByte.MAX_VALUE, UShort.MAX_VALUE, UInt.MAX_VALUE, ULong.MAX_VALUE,
|
||||||
|
)
|
||||||
|
val third = UnsignedTypes(
|
||||||
|
UByte.MIN_VALUE, UShort.MIN_VALUE, UInt.MIN_VALUE, ULong.MIN_VALUE,
|
||||||
|
null, null, null, null,
|
||||||
|
)
|
||||||
|
|
||||||
|
first.writeToParcel(parcel, 0)
|
||||||
|
second.writeToParcel(parcel, 0)
|
||||||
|
third.writeToParcel(parcel, 0)
|
||||||
|
|
||||||
|
val bytes = parcel.marshall()
|
||||||
|
parcel.unmarshall(bytes, 0, bytes.size)
|
||||||
|
parcel.setDataPosition(0)
|
||||||
|
|
||||||
|
val first2 = readFromParcel<UnsignedTypes>(parcel)
|
||||||
|
val second2 = readFromParcel<UnsignedTypes>(parcel)
|
||||||
|
val third2 = readFromParcel<UnsignedTypes>(parcel)
|
||||||
|
|
||||||
|
assert(first == first2)
|
||||||
|
assert(second == second2)
|
||||||
|
assert(third == third2)
|
||||||
|
}
|
||||||
+5
@@ -35,6 +35,11 @@ public class ParcelizeBoxTestGenerated extends AbstractParcelizeBoxTest {
|
|||||||
runTest("plugins/parcelize/parcelize-compiler/testData/box/allPrimitiveTypes.kt");
|
runTest("plugins/parcelize/parcelize-compiler/testData/box/allPrimitiveTypes.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("allUnsignedTypes.kt")
|
||||||
|
public void testAllUnsignedTypes() throws Exception {
|
||||||
|
runTest("plugins/parcelize/parcelize-compiler/testData/box/allUnsignedTypes.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("arraySimple.kt")
|
@TestMetadata("arraySimple.kt")
|
||||||
public void testArraySimple() throws Exception {
|
public void testArraySimple() throws Exception {
|
||||||
runTest("plugins/parcelize/parcelize-compiler/testData/box/arraySimple.kt");
|
runTest("plugins/parcelize/parcelize-compiler/testData/box/arraySimple.kt");
|
||||||
|
|||||||
+5
@@ -35,6 +35,11 @@ public class ParcelizeIrBoxTestGenerated extends AbstractParcelizeIrBoxTest {
|
|||||||
runTest("plugins/parcelize/parcelize-compiler/testData/box/allPrimitiveTypes.kt");
|
runTest("plugins/parcelize/parcelize-compiler/testData/box/allPrimitiveTypes.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("allUnsignedTypes.kt")
|
||||||
|
public void testAllUnsignedTypes() throws Exception {
|
||||||
|
runTest("plugins/parcelize/parcelize-compiler/testData/box/allUnsignedTypes.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("arraySimple.kt")
|
@TestMetadata("arraySimple.kt")
|
||||||
public void testArraySimple() throws Exception {
|
public void testArraySimple() throws Exception {
|
||||||
runTest("plugins/parcelize/parcelize-compiler/testData/box/arraySimple.kt");
|
runTest("plugins/parcelize/parcelize-compiler/testData/box/arraySimple.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user