[K/N] Support of collectReferenceFieldValues function
This commit is contained in:
@@ -849,6 +849,13 @@ standaloneTest("cleaner_basic") {
|
||||
flags = ['-tr', '-opt-in=kotlin.native.internal.InternalForKotlinNative']
|
||||
}
|
||||
|
||||
standaloneTest("collect_reference_field_values") {
|
||||
enabled = (project.testTarget != 'wasm32') && // Cleaners need workers
|
||||
!isNoopGC
|
||||
source = "runtime/basic/collectReferenceFieldValues.kt"
|
||||
flags = ['-tr', '-opt-in=kotlin.native.internal.InternalForKotlinNative']
|
||||
}
|
||||
|
||||
standaloneTest("cleaner_workers") {
|
||||
enabled = (project.testTarget != 'wasm32') && // Cleaners need workers
|
||||
!isNoopGC
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
import kotlin.native.internal.*
|
||||
import kotlin.test.*
|
||||
|
||||
value class BoxInt(val x: Int)
|
||||
value class BoxBoxInt(val x: BoxInt)
|
||||
data class A(val x: Int)
|
||||
value class BoxA(val x: A)
|
||||
value class BoxBoxA(val x: BoxA)
|
||||
|
||||
|
||||
class X(
|
||||
val a: Int,
|
||||
val b: List<Int>,
|
||||
val c: IntArray,
|
||||
val d: Array<Int>,
|
||||
val e: Array<Any>,
|
||||
val f: BoxInt,
|
||||
val g: BoxBoxInt,
|
||||
val h: A,
|
||||
val i: BoxA,
|
||||
val j: BoxBoxA,
|
||||
val k: Any?,
|
||||
val l: Any?,
|
||||
val m: Any?,
|
||||
val n: Any?,
|
||||
val o: IntArray?
|
||||
) {
|
||||
val p by lazy { 1 }
|
||||
lateinit var q: IntArray
|
||||
lateinit var r: IntArray
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `big class with mixed values`() {
|
||||
val lst = listOf(1, 2, 3)
|
||||
val ia = intArrayOf(1, 2, 3)
|
||||
val ia2 = intArrayOf(3, 4, 5)
|
||||
val ai = arrayOf(1, 2, 3)
|
||||
val astr: Array<Any> = arrayOf("123", "456", 1, 2, 3)
|
||||
val bi = BoxInt(2)
|
||||
val bbi = BoxBoxInt(BoxInt(3))
|
||||
val a1 = A(1)
|
||||
val a2 = A(2)
|
||||
val a3 = A(3)
|
||||
val a6 = A(3)
|
||||
val x = X(
|
||||
1, lst, ia, ai, astr, bi, bbi,
|
||||
a1, BoxA(a2), BoxBoxA(BoxA(a3)),
|
||||
4, BoxInt(5), BoxA(a6), null, null
|
||||
)
|
||||
x.r = ia2
|
||||
val fields = x.collectReferenceFieldValues()
|
||||
assertEquals(12, fields.size)
|
||||
// not using assertContains because of ===
|
||||
assertTrue(fields.any { it === lst }, "Should contain list $lst")
|
||||
assertTrue(fields.any { it === ia }, "Should contain int array $ia")
|
||||
assertTrue(fields.any { it === ia2 }, "Should contain int array $ia2")
|
||||
assertTrue(fields.any { it === ai }, "Should contain array of int $ai")
|
||||
assertTrue(fields.any { it === astr }, "Should contain array of string $astr")
|
||||
assertTrue(fields.any { it === a1 }, "Should contain A(1)")
|
||||
assertTrue(fields.any { it === a2 }, "Should contain A(2)")
|
||||
assertTrue(fields.any { it === a3 }, "Should contain A(3)")
|
||||
assertTrue(fields.any { it.toString().startsWith("Lazy value not initialized yet") }, "Should contain lazy delegate")
|
||||
assertContains(fields, 4)
|
||||
assertContains(fields, BoxInt(5))
|
||||
assertContains(fields, BoxA(a6))}
|
||||
|
||||
@Test
|
||||
fun `call on primitive`() {
|
||||
assertEquals(1.collectReferenceFieldValues(), emptyList<Any>())
|
||||
assertEquals(123456.collectReferenceFieldValues(), emptyList<Any>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `call on value over primitive class`() {
|
||||
assertEquals(BoxInt(1).collectReferenceFieldValues(), emptyList<Any>())
|
||||
assertEquals(BoxBoxInt(BoxInt(1)).collectReferenceFieldValues(), emptyList<Any>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `call on value class`() {
|
||||
val a1 = A(1)
|
||||
assertEquals(BoxA(a1).collectReferenceFieldValues(), listOf(a1))
|
||||
assertEquals(BoxBoxA(BoxA(a1)).collectReferenceFieldValues(), listOf(a1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `call on String`() {
|
||||
assertEquals("1234".collectReferenceFieldValues(), emptyList<Any>())
|
||||
assertEquals("".collectReferenceFieldValues(), emptyList<Any>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `call on primitive array`() {
|
||||
assertEquals(intArrayOf(1, 2, 3).collectReferenceFieldValues(), emptyList<Any>())
|
||||
assertEquals(intArrayOf().collectReferenceFieldValues(), emptyList<Any>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `call on array`() {
|
||||
assertEquals(arrayOf(1, 2, 3).collectReferenceFieldValues(), listOf<Any>(1, 2, 3))
|
||||
assertEquals(arrayOf(null, "10", null, 3).collectReferenceFieldValues(), listOf<Any>("10", 3))
|
||||
assertEquals(arrayOf<Any>().collectReferenceFieldValues(), emptyList<Any>())
|
||||
assertEquals(emptyArray<Any>().collectReferenceFieldValues(), emptyList<Any>())
|
||||
assertEquals(emptyArray<Any?>().collectReferenceFieldValues(), emptyList<Any>())
|
||||
}
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
#include "KAssert.h"
|
||||
#include "TypeInfo.h"
|
||||
#include "Memory.h"
|
||||
#include "Types.h"
|
||||
#include "KString.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -39,4 +42,14 @@ InterfaceTableRecord const* LookupInterfaceTableRecord(InterfaceTableRecord cons
|
||||
return interfaceTable + l;
|
||||
}
|
||||
|
||||
RUNTIME_NOTHROW int Kotlin_internal_reflect_getObjectReferenceFieldsCount(ObjHeader* object) {
|
||||
auto *info = object->type_info();
|
||||
if (info->IsArray()) return 0;
|
||||
return info->objOffsetsCount_;
|
||||
}
|
||||
|
||||
RUNTIME_NOTHROW OBJ_GETTER(Kotlin_internal_reflect_getObjectReferenceFieldByIndex, ObjHeader* object, int index) {
|
||||
RETURN_OBJ(*reinterpret_cast<ObjHeader**>(reinterpret_cast<uintptr_t>(object) + object->type_info()->objOffsets_[index]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,3 +19,36 @@ internal fun debugDescription(kClass: KClass<*>, identity: Int): String {
|
||||
val identityStr = unsignedIdentity.toString(16)
|
||||
return "$className@$identityStr"
|
||||
}
|
||||
|
||||
@GCUnsafeCall("Kotlin_internal_reflect_getObjectReferenceFieldsCount")
|
||||
private external fun getObjectReferenceFieldsCount(o: Any): Int
|
||||
@GCUnsafeCall("Kotlin_internal_reflect_getObjectReferenceFieldByIndex")
|
||||
private external fun getObjectReferenceFieldByIndex(o: Any, index: Int): Any?
|
||||
|
||||
/**
|
||||
* Returns [List] of non-null reference fields of the object.
|
||||
*
|
||||
* This function is intended to be used for testing and debugging purposes.
|
||||
* - It heavily relies on internal ABI details. No compatibility guarantees on exact list contents are provided.
|
||||
* - Order and representation of object's fields are subject to change.
|
||||
* - Performance characteristics of the implementation
|
||||
*
|
||||
* Limitations:
|
||||
* - Primitives (unboxed [Int], [Double], [Float], etc.) are not included in the result.
|
||||
* - Non-boxed value classes over primitives are not included in the result.
|
||||
* - Non-boxed value classes over references are included in the result as the underlying reference type.
|
||||
* - Synthetic fields (e.g. special fields for delegation) are included in the result.
|
||||
* - There is no way to find which reference in the result corresponds to which field.
|
||||
*
|
||||
* For `Array<T>` list of all its non-null elements is returned.
|
||||
* For primitive arrays ([IntArray], [DoubleArray], [FloatArray], etc.) empty list is returned.
|
||||
*/
|
||||
@InternalForKotlinNative
|
||||
public fun Any.collectReferenceFieldValues() : List<Any> {
|
||||
return when {
|
||||
this is Array<*> -> this.filterNotNull()
|
||||
else -> (0 until getObjectReferenceFieldsCount(this@collectReferenceFieldValues)).mapNotNull {
|
||||
getObjectReferenceFieldByIndex(this@collectReferenceFieldValues, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user