Get rid of uneeded parts of RTTI. (#2435)
This commit is contained in:
+12
-36
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty
|
|||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.isAnnotationClass
|
import org.jetbrains.kotlin.ir.util.isAnnotationClass
|
||||||
import org.jetbrains.kotlin.ir.util.isInterface
|
import org.jetbrains.kotlin.ir.util.isInterface
|
||||||
|
import org.jetbrains.kotlin.konan.KonanAbiVersion
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||||
@@ -59,18 +60,17 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
result = result or TF_ACYCLIC
|
result = result or TF_ACYCLIC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (classDescriptor.isInterface)
|
||||||
|
result = result or TF_INTERFACE
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class FieldTableRecord(val nameSignature: LocalHash, fieldOffset: Int) :
|
|
||||||
Struct(runtime.fieldTableRecordType, nameSignature, Int32(fieldOffset))
|
|
||||||
|
|
||||||
inner class MethodTableRecord(val nameSignature: LocalHash, methodEntryPoint: ConstPointer?) :
|
inner class MethodTableRecord(val nameSignature: LocalHash, methodEntryPoint: ConstPointer?) :
|
||||||
Struct(runtime.methodTableRecordType, nameSignature, methodEntryPoint)
|
Struct(runtime.methodTableRecordType, nameSignature, methodEntryPoint)
|
||||||
|
|
||||||
private inner class TypeInfo(
|
private inner class TypeInfo(
|
||||||
selfPtr: ConstPointer,
|
selfPtr: ConstPointer,
|
||||||
name: ConstValue,
|
extendedInfo: ConstPointer,
|
||||||
size: Int,
|
size: Int,
|
||||||
superType: ConstValue,
|
superType: ConstValue,
|
||||||
objOffsets: ConstValue,
|
objOffsets: ConstValue,
|
||||||
@@ -79,12 +79,9 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
interfacesCount: Int,
|
interfacesCount: Int,
|
||||||
methods: ConstValue,
|
methods: ConstValue,
|
||||||
methodsCount: Int,
|
methodsCount: Int,
|
||||||
fields: ConstValue,
|
|
||||||
fieldsCount: Int,
|
|
||||||
packageName: String?,
|
packageName: String?,
|
||||||
relativeName: String?,
|
relativeName: String?,
|
||||||
flags: Int,
|
flags: Int,
|
||||||
extendedInfo: ConstPointer,
|
|
||||||
writableTypeInfo: ConstPointer?) :
|
writableTypeInfo: ConstPointer?) :
|
||||||
|
|
||||||
Struct(
|
Struct(
|
||||||
@@ -92,7 +89,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
|
|
||||||
selfPtr,
|
selfPtr,
|
||||||
|
|
||||||
name,
|
extendedInfo,
|
||||||
|
|
||||||
|
Int32(KonanAbiVersion.CURRENT.version),
|
||||||
|
|
||||||
Int32(size),
|
Int32(size),
|
||||||
|
|
||||||
superType,
|
superType,
|
||||||
@@ -106,16 +106,11 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
methods,
|
methods,
|
||||||
Int32(methodsCount),
|
Int32(methodsCount),
|
||||||
|
|
||||||
fields,
|
|
||||||
Int32(fieldsCount),
|
|
||||||
|
|
||||||
kotlinStringLiteral(packageName),
|
kotlinStringLiteral(packageName),
|
||||||
kotlinStringLiteral(relativeName),
|
kotlinStringLiteral(relativeName),
|
||||||
|
|
||||||
Int32(flags),
|
Int32(flags),
|
||||||
|
|
||||||
extendedInfo,
|
|
||||||
|
|
||||||
*listOfNotNull(writableTypeInfo).toTypedArray()
|
*listOfNotNull(writableTypeInfo).toTypedArray()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -180,8 +175,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
|
|
||||||
val bodyType = llvmDeclarations.bodyType
|
val bodyType = llvmDeclarations.bodyType
|
||||||
|
|
||||||
val name = className.globalHash
|
|
||||||
|
|
||||||
val size = getInstanceSize(bodyType, className)
|
val size = getInstanceSize(bodyType, className)
|
||||||
|
|
||||||
val superTypeOrAny = classDesc.getSuperClassNotAny() ?: context.ir.symbols.any.owner
|
val superTypeOrAny = classDesc.getSuperClassNotAny() ?: context.ir.symbols.any.owner
|
||||||
@@ -192,7 +185,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className",
|
val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className",
|
||||||
pointerType(runtime.typeInfoType), interfaces)
|
pointerType(runtime.typeInfoType), interfaces)
|
||||||
|
|
||||||
// TODO: reuse offsets obtained for 'fields' below
|
|
||||||
val objOffsets = getStructElements(bodyType).mapIndexedNotNull { index, type ->
|
val objOffsets = getStructElements(bodyType).mapIndexedNotNull { index, type ->
|
||||||
if (isObjectType(type)) {
|
if (isObjectType(type)) {
|
||||||
LLVMOffsetOfElement(llvmTargetData, bodyType, index)
|
LLVMOffsetOfElement(llvmTargetData, bodyType, index)
|
||||||
@@ -210,16 +202,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
objOffsets.size
|
objOffsets.size
|
||||||
}
|
}
|
||||||
|
|
||||||
val fields = llvmDeclarations.fields.mapIndexed { index, field ->
|
|
||||||
// Note: using FQ name because a class may have multiple fields with the same name due to property overriding
|
|
||||||
val nameSignature = field.fqNameSafe.localHash // FIXME: add signature
|
|
||||||
val fieldOffset = LLVMOffsetOfElement(llvmTargetData, bodyType, index)
|
|
||||||
FieldTableRecord(nameSignature, fieldOffset.toInt())
|
|
||||||
}.sortedBy { it.nameSignature.value }
|
|
||||||
|
|
||||||
val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className",
|
|
||||||
runtime.fieldTableRecordType, fields)
|
|
||||||
|
|
||||||
val methods = if (classDesc.isAbstract()) {
|
val methods = if (classDesc.isAbstract()) {
|
||||||
emptyList()
|
emptyList()
|
||||||
} else {
|
} else {
|
||||||
@@ -233,17 +215,15 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
val typeInfoGlobal = llvmDeclarations.typeInfoGlobal
|
val typeInfoGlobal = llvmDeclarations.typeInfoGlobal
|
||||||
val typeInfo = TypeInfo(
|
val typeInfo = TypeInfo(
|
||||||
classDesc.typeInfoPtr,
|
classDesc.typeInfoPtr,
|
||||||
name,
|
makeExtendedInfo(classDesc),
|
||||||
size,
|
size,
|
||||||
superType,
|
superType,
|
||||||
objOffsetsPtr, objOffsetsCount,
|
objOffsetsPtr, objOffsetsCount,
|
||||||
interfacesPtr, interfaces.size,
|
interfacesPtr, interfaces.size,
|
||||||
methodsPtr, methods.size,
|
methodsPtr, methods.size,
|
||||||
fieldsPtr, if (classDesc.isInterface) -1 else fields.size,
|
|
||||||
reflectionInfo.packageName,
|
reflectionInfo.packageName,
|
||||||
reflectionInfo.relativeName,
|
reflectionInfo.relativeName,
|
||||||
flagsFromClass(classDesc),
|
flagsFromClass(classDesc),
|
||||||
makeExtendedInfo(classDesc),
|
|
||||||
llvmDeclarations.writableTypeInfoGlobal?.pointer
|
llvmDeclarations.writableTypeInfoGlobal?.pointer
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -335,8 +315,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
): ConstPointer {
|
): ConstPointer {
|
||||||
assert(descriptor.isInterface)
|
assert(descriptor.isInterface)
|
||||||
|
|
||||||
val name = "".globalHash
|
|
||||||
|
|
||||||
val size = 0
|
val size = 0
|
||||||
|
|
||||||
val superClass = context.ir.symbols.any.owner
|
val superClass = context.ir.symbols.any.owner
|
||||||
@@ -349,8 +327,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
assert(superClass.declarations.all { it !is IrProperty && it !is IrField })
|
assert(superClass.declarations.all { it !is IrProperty && it !is IrField })
|
||||||
val objOffsetsPtr = NullPointer(int32Type)
|
val objOffsetsPtr = NullPointer(int32Type)
|
||||||
val objOffsetsCount = 0
|
val objOffsetsCount = 0
|
||||||
val fieldsPtr = NullPointer(runtime.fieldTableRecordType)
|
|
||||||
val fieldsCount = 0
|
|
||||||
|
|
||||||
val methods = (methodTableRecords(superClass) + methodImpls.map { (method, impl) ->
|
val methods = (methodTableRecords(superClass) + methodImpls.map { (method, impl) ->
|
||||||
assert(method.containingDeclaration == descriptor)
|
assert(method.containingDeclaration == descriptor)
|
||||||
@@ -377,17 +353,15 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
val result = typeInfoWithVtableGlobal.pointer.getElementPtr(0)
|
val result = typeInfoWithVtableGlobal.pointer.getElementPtr(0)
|
||||||
val typeInfoWithVtable = Struct(TypeInfo(
|
val typeInfoWithVtable = Struct(TypeInfo(
|
||||||
selfPtr = result,
|
selfPtr = result,
|
||||||
name = name,
|
extendedInfo = NullPointer(runtime.extendedTypeInfoType),
|
||||||
size = size,
|
size = size,
|
||||||
superType = superClass.typeInfoPtr,
|
superType = superClass.typeInfoPtr,
|
||||||
objOffsets = objOffsetsPtr, objOffsetsCount = objOffsetsCount,
|
objOffsets = objOffsetsPtr, objOffsetsCount = objOffsetsCount,
|
||||||
interfaces = interfacesPtr, interfacesCount = interfaces.size,
|
interfaces = interfacesPtr, interfacesCount = interfaces.size,
|
||||||
methods = methodsPtr, methodsCount = methods.size,
|
methods = methodsPtr, methodsCount = methods.size,
|
||||||
fields = fieldsPtr, fieldsCount = fieldsCount,
|
|
||||||
packageName = reflectionInfo.packageName,
|
packageName = reflectionInfo.packageName,
|
||||||
relativeName = reflectionInfo.relativeName,
|
relativeName = reflectionInfo.relativeName,
|
||||||
flags = flagsFromClass(descriptor) or (if (immutable) TF_IMMUTABLE else 0),
|
flags = flagsFromClass(descriptor) or (if (immutable) TF_IMMUTABLE else 0),
|
||||||
extendedInfo = NullPointer(runtime.extendedTypeInfoType),
|
|
||||||
writableTypeInfo = writableTypeInfo
|
writableTypeInfo = writableTypeInfo
|
||||||
), vtable)
|
), vtable)
|
||||||
|
|
||||||
@@ -417,5 +391,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep in sync with Konan_TypeFlags in TypeInfo.h.
|
||||||
private const val TF_IMMUTABLE = 1
|
private const val TF_IMMUTABLE = 1
|
||||||
private const val TF_ACYCLIC = 2
|
private const val TF_ACYCLIC = 2
|
||||||
|
private const val TF_INTERFACE = 4
|
||||||
|
|||||||
-1
@@ -22,7 +22,6 @@ class Runtime(bitcodeFile: String) {
|
|||||||
val typeInfoType = getStructType("TypeInfo")
|
val typeInfoType = getStructType("TypeInfo")
|
||||||
val extendedTypeInfoType = getStructType("ExtendedTypeInfo")
|
val extendedTypeInfoType = getStructType("ExtendedTypeInfo")
|
||||||
val writableTypeInfoType = getStructTypeOrNull("WritableTypeInfo")
|
val writableTypeInfoType = getStructTypeOrNull("WritableTypeInfo")
|
||||||
val fieldTableRecordType = getStructType("FieldTableRecord")
|
|
||||||
val methodTableRecordType = getStructType("MethodTableRecord")
|
val methodTableRecordType = getStructType("MethodTableRecord")
|
||||||
val globalHashType = getStructType("GlobalHash")
|
val globalHashType = getStructType("GlobalHash")
|
||||||
|
|
||||||
|
|||||||
@@ -671,7 +671,6 @@ static const TypeInfo* createTypeInfo(
|
|||||||
|
|
||||||
result->flags_ = 0;
|
result->flags_ = 0;
|
||||||
|
|
||||||
MakeGlobalHash(nullptr, 0, &result->name_);
|
|
||||||
result->instanceSize_ = superType->instanceSize_;
|
result->instanceSize_ = superType->instanceSize_;
|
||||||
result->superType_ = superType;
|
result->superType_ = superType;
|
||||||
result->objOffsets_ = superType->objOffsets_;
|
result->objOffsets_ = superType->objOffsets_;
|
||||||
@@ -705,9 +704,6 @@ static const TypeInfo* createTypeInfo(
|
|||||||
result->openMethods_ = openMethods_;
|
result->openMethods_ = openMethods_;
|
||||||
result->openMethodsCount_ = methodTable.size();
|
result->openMethodsCount_ = methodTable.size();
|
||||||
|
|
||||||
result->fields_ = nullptr;
|
|
||||||
result->fieldsCount_ = 0;
|
|
||||||
|
|
||||||
result->packageName_ = nullptr;
|
result->packageName_ = nullptr;
|
||||||
result->relativeName_ = nullptr; // TODO: add some info.
|
result->relativeName_ = nullptr; // TODO: add some info.
|
||||||
result->writableInfo_ = (WritableTypeInfo*)konanAllocMemory(sizeof(WritableTypeInfo));
|
result->writableInfo_ = (WritableTypeInfo*)konanAllocMemory(sizeof(WritableTypeInfo));
|
||||||
|
|||||||
@@ -24,24 +24,6 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#if USE_BINARY_SEARCH
|
#if USE_BINARY_SEARCH
|
||||||
|
|
||||||
int LookupFieldOffset(const TypeInfo* info, FieldNameHash nameSignature) {
|
|
||||||
int bottom = 0;
|
|
||||||
int top = info->fieldsCount_ - 1;
|
|
||||||
|
|
||||||
while (bottom <= top) {
|
|
||||||
int middle = (bottom + top) / 2;
|
|
||||||
if (info->fields_[middle].nameSignature_ < nameSignature)
|
|
||||||
bottom = middle + 1;
|
|
||||||
else if (info->fields_[middle].nameSignature_ == nameSignature)
|
|
||||||
return info->fields_[middle].fieldOffset_;
|
|
||||||
else
|
|
||||||
top = middle - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeAssert(false, "Unknown field");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) {
|
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) {
|
||||||
int bottom = 0;
|
int bottom = 0;
|
||||||
int top = info->openMethodsCount_ - 1;
|
int top = info->openMethodsCount_ - 1;
|
||||||
@@ -62,16 +44,6 @@ void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
int LookupFieldOffset(const TypeInfo* info, FieldNameHash nameSignature) {
|
|
||||||
for (int i = 0; i < info->fieldsCount_; ++i) {
|
|
||||||
if (info->fields_[i].nameSignature_ == nameSignature) {
|
|
||||||
return info->fields_[i].fieldOffset_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RuntimeAssert(false, "Unknown field");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) {
|
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) {
|
||||||
for (int i = 0; i < info->openMethodsCount_; ++i) {
|
for (int i = 0; i < info->openMethodsCount_; ++i) {
|
||||||
if (info->openMethods_[i].nameSignature_ == nameSignature) {
|
if (info->openMethods_[i].nameSignature_ == nameSignature) {
|
||||||
|
|||||||
@@ -36,12 +36,6 @@ struct MethodTableRecord {
|
|||||||
void* methodEntryPoint_;
|
void* methodEntryPoint_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An element of sorted by hash in-place array representing field offsets.
|
|
||||||
struct FieldTableRecord {
|
|
||||||
FieldNameHash nameSignature_;
|
|
||||||
int fieldOffset_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Type for runtime representation of Konan object.
|
// Type for runtime representation of Konan object.
|
||||||
// Keep in sync with runtimeTypeMap in RTTIGenerator.
|
// Keep in sync with runtimeTypeMap in RTTIGenerator.
|
||||||
enum Konan_RuntimeType {
|
enum Konan_RuntimeType {
|
||||||
@@ -59,7 +53,8 @@ enum Konan_RuntimeType {
|
|||||||
|
|
||||||
enum Konan_TypeFlags {
|
enum Konan_TypeFlags {
|
||||||
TF_IMMUTABLE = 1 << 0,
|
TF_IMMUTABLE = 1 << 0,
|
||||||
TF_ACYCLIC = 1 << 1
|
TF_ACYCLIC = 1 << 1,
|
||||||
|
TF_INTERFACE = 1 << 2
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Konan_MetaFlags {
|
enum Konan_MetaFlags {
|
||||||
@@ -84,8 +79,10 @@ struct ExtendedTypeInfo {
|
|||||||
struct TypeInfo {
|
struct TypeInfo {
|
||||||
// Reference to self, to allow simple obtaining TypeInfo via meta-object.
|
// Reference to self, to allow simple obtaining TypeInfo via meta-object.
|
||||||
const TypeInfo* typeInfo_;
|
const TypeInfo* typeInfo_;
|
||||||
// Hash of class name.
|
// Extended RTTI, to retain cross-version debuggability, since ABI version 5 shall always be at the second position.
|
||||||
ClassNameHash name_;
|
const ExtendedTypeInfo* extendedInfo_;
|
||||||
|
// ABI version.
|
||||||
|
uint32_t abiVersion_;
|
||||||
// Negative value marks array class/string, and it is negated element size.
|
// Negative value marks array class/string, and it is negated element size.
|
||||||
int32_t instanceSize_;
|
int32_t instanceSize_;
|
||||||
// Must be pointer to Any for array classes, and null for Any.
|
// Must be pointer to Any for array classes, and null for Any.
|
||||||
@@ -100,9 +97,6 @@ struct TypeInfo {
|
|||||||
// Null for abstract classes and interfaces.
|
// Null for abstract classes and interfaces.
|
||||||
const MethodTableRecord* openMethods_;
|
const MethodTableRecord* openMethods_;
|
||||||
uint32_t openMethodsCount_;
|
uint32_t openMethodsCount_;
|
||||||
const FieldTableRecord* fields_;
|
|
||||||
// Is negative to mark an interface.
|
|
||||||
int32_t fieldsCount_;
|
|
||||||
|
|
||||||
// String for the fully qualified dot-separated name of the package containing class,
|
// String for the fully qualified dot-separated name of the package containing class,
|
||||||
// or `null` if the class is local or anonymous.
|
// or `null` if the class is local or anonymous.
|
||||||
@@ -116,9 +110,6 @@ struct TypeInfo {
|
|||||||
// Various flags.
|
// Various flags.
|
||||||
int32_t flags_;
|
int32_t flags_;
|
||||||
|
|
||||||
// Extended RTTI.
|
|
||||||
const ExtendedTypeInfo* extendedInfo_;
|
|
||||||
|
|
||||||
#if KONAN_TYPE_INFO_HAS_WRITABLE_PART
|
#if KONAN_TYPE_INFO_HAS_WRITABLE_PART
|
||||||
WritableTypeInfo* writableInfo_;
|
WritableTypeInfo* writableInfo_;
|
||||||
#endif
|
#endif
|
||||||
@@ -139,16 +130,12 @@ struct TypeInfo {
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
// Find offset of given hash in table.
|
// Find open method by its hash. Other methods are resolved in compile-time.
|
||||||
// Note, that we use attribute const, which assumes function doesn't
|
// Note, that we use attribute const, which assumes function doesn't
|
||||||
// dereference global memory, while this function does. However, it seems
|
// dereference global memory, while this function does. However, it seems
|
||||||
// to be safe, as actual result of this computation depends only on 'type_info'
|
// to be safe, as actual result of this computation depends only on 'type_info'
|
||||||
// and 'hash' numeric values and doesn't really depends on global memory state
|
// and 'hash' numeric values and doesn't really depends on global memory state
|
||||||
// (as TypeInfo is compile time constant and type info pointers are stable).
|
// (as TypeInfo is compile time constant and type info pointers are stable).
|
||||||
int LookupFieldOffset(const TypeInfo* type_info, FieldNameHash hash) RUNTIME_CONST;
|
|
||||||
|
|
||||||
// Find open method by its hash. Other methods are resolved in compile-time.
|
|
||||||
// See comment in LookupFieldOffset().
|
|
||||||
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) RUNTIME_CONST;
|
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) RUNTIME_CONST;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) {
|
|||||||
RuntimeAssert(obj != nullptr, "must not be null");
|
RuntimeAssert(obj != nullptr, "must not be null");
|
||||||
const TypeInfo* obj_type_info = obj->type_info();
|
const TypeInfo* obj_type_info = obj->type_info();
|
||||||
// If it is an interface - check in list of implemented interfaces.
|
// If it is an interface - check in list of implemented interfaces.
|
||||||
if (type_info->fieldsCount_ < 0) {
|
if ((type_info->flags_ & TF_INTERFACE) != 0) {
|
||||||
for (int i = 0; i < obj_type_info->implementedInterfacesCount_; ++i) {
|
for (int i = 0; i < obj_type_info->implementedInterfacesCount_; ++i) {
|
||||||
if (obj_type_info->implementedInterfaces_[i] == type_info) {
|
if (obj_type_info->implementedInterfaces_[i] == type_info) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user