Extract object traversals (#4724)
(cherry picked from commit c3131ea2f51c31881ea068f28035f4d015daf609)
This commit is contained in:
committed by
Space
parent
248e340cd9
commit
27e4b21020
@@ -46,6 +46,7 @@
|
||||
#include "MemoryPrivate.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "Natives.h"
|
||||
#include "ObjectTraversal.hpp"
|
||||
#include "Porting.h"
|
||||
#include "Runtime.h"
|
||||
#include "Utils.hpp"
|
||||
@@ -977,31 +978,6 @@ inline container_size_t objectSize(const ObjHeader* obj) {
|
||||
return alignUp(size, kObjectAlignment);
|
||||
}
|
||||
|
||||
template <typename func>
|
||||
inline void traverseObjectFields(ObjHeader* obj, func process) {
|
||||
const TypeInfo* typeInfo = obj->type_info();
|
||||
if (typeInfo != theArrayTypeInfo) {
|
||||
for (int index = 0; index < typeInfo->objOffsetsCount_; index++) {
|
||||
ObjHeader** location = reinterpret_cast<ObjHeader**>(
|
||||
reinterpret_cast<uintptr_t>(obj) + typeInfo->objOffsets_[index]);
|
||||
process(location);
|
||||
}
|
||||
} else {
|
||||
ArrayHeader* array = obj->array();
|
||||
for (uint32_t index = 0; index < array->count_; index++) {
|
||||
process(ArrayAddressOfElementAt(array, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename func>
|
||||
inline void traverseReferredObjects(ObjHeader* obj, func process) {
|
||||
traverseObjectFields(obj, [process](ObjHeader** location) {
|
||||
ObjHeader* ref = *location;
|
||||
if (ref != nullptr) process(ref);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename func>
|
||||
inline void traverseContainerObjects(ContainerHeader* container, func process) {
|
||||
RuntimeAssert(!isAggregatingFrozenContainer(container), "Must not be called on such containers");
|
||||
@@ -1016,7 +992,7 @@ inline void traverseContainerObjects(ContainerHeader* container, func process) {
|
||||
template <typename func>
|
||||
inline void traverseContainerObjectFields(ContainerHeader* container, func process) {
|
||||
traverseContainerObjects(container, [process](ObjHeader* obj) {
|
||||
traverseObjectFields(obj, process);
|
||||
kotlin::traverseObjectFields(obj, process);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2870,7 +2846,7 @@ void runFreezeHooksRecursive(ObjHeader* root) {
|
||||
|
||||
kotlin::RunFreezeHooks(obj);
|
||||
|
||||
traverseReferredObjects(obj, [&seen, &toVisit](ObjHeader* field) {
|
||||
kotlin::traverseReferredObjects(obj, [&seen, &toVisit](ObjHeader* field) {
|
||||
auto wasNotSeenYet = seen.insert(field).second;
|
||||
// Only iterating on unseen objects which containers will get frozen by freezeCyclic or freezeAcyclic.
|
||||
if (wasNotSeenYet && canFreeze(containerFor(field))) {
|
||||
@@ -2997,7 +2973,7 @@ CycleDetectorRootset CycleDetector::collectRootset() {
|
||||
continue;
|
||||
rootset.roots.push_back(candidate);
|
||||
rootset.heldRefs.emplace_back(candidate);
|
||||
traverseReferredObjects(candidate, [&rootset, candidate](KRef field) {
|
||||
kotlin::traverseReferredObjects(candidate, [&rootset, candidate](KRef field) {
|
||||
rootset.rootToFields[candidate].push_back(field);
|
||||
// TODO: There's currently a race here:
|
||||
// some other thread might null this field and destroy it in GC before
|
||||
@@ -3022,7 +2998,7 @@ KStdVector<KRef> findCycleWithDFS(KRef root, const CycleDetectorRootset& rootset
|
||||
return;
|
||||
}
|
||||
|
||||
traverseReferredObjects(obj, process);
|
||||
kotlin::traverseReferredObjects(obj, process);
|
||||
};
|
||||
|
||||
KStdVector<KStdVector<KRef>> toVisit;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef RUNTIME_OBJECT_TRAVERSAL_H
|
||||
#define RUNTIME_OBJECT_TRAVERSAL_H
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "Memory.h"
|
||||
#include "Natives.h"
|
||||
#include "Types.h"
|
||||
|
||||
namespace kotlin {
|
||||
|
||||
// TODO: Consider an iterator/ranges based approaches for traversals.
|
||||
|
||||
template <typename F>
|
||||
void traverseObjectFields(ObjHeader* object, F process) noexcept(noexcept(process(std::declval<ObjHeader**>()))) {
|
||||
const TypeInfo* typeInfo = object->type_info();
|
||||
// Only consider arrays of objects, not arrays of primitives.
|
||||
if (typeInfo != theArrayTypeInfo) {
|
||||
for (int index = 0; index < typeInfo->objOffsetsCount_; index++) {
|
||||
process(reinterpret_cast<ObjHeader**>(reinterpret_cast<uintptr_t>(object) + typeInfo->objOffsets_[index]));
|
||||
}
|
||||
} else {
|
||||
ArrayHeader* array = object->array();
|
||||
for (uint32_t index = 0; index < array->count_; index++) {
|
||||
process(ArrayAddressOfElementAt(array, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void traverseReferredObjects(ObjHeader* object, F process) noexcept(noexcept(process(std::declval<ObjHeader*>()))) {
|
||||
traverseObjectFields(object, [&process](ObjHeader** location) noexcept(noexcept(process(std::declval<ObjHeader*>()))) {
|
||||
if (ObjHeader* ref = *location) {
|
||||
process(ref);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace kotlin
|
||||
|
||||
#endif // RUNTIME_OBJECT_TRAVERSAL_H
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "ObjectTraversal.hpp"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
using ::testing::_;
|
||||
|
||||
namespace {
|
||||
|
||||
template <size_t Count>
|
||||
class Object : private Pinned {
|
||||
public:
|
||||
Object() {
|
||||
header_.typeInfoOrMeta_ = &type_;
|
||||
type_.typeInfo_ = &type_;
|
||||
type_.objOffsetsCount_ = Count;
|
||||
type_.objOffsets_ = fieldOffsets_.data();
|
||||
for (size_t i = 0; i < Count; ++i) {
|
||||
fieldOffsets_[i] = reinterpret_cast<uintptr_t>(&fields_[i]) - reinterpret_cast<uintptr_t>(&header_);
|
||||
}
|
||||
}
|
||||
|
||||
ObjHeader* header() { return &header_; }
|
||||
|
||||
ObjHeader*& operator[](size_t index) { return fields_[index]; }
|
||||
|
||||
private:
|
||||
ObjHeader header_;
|
||||
TypeInfo type_;
|
||||
std::array<int32_t, Count> fieldOffsets_;
|
||||
std::array<ObjHeader*, Count> fields_{};
|
||||
};
|
||||
|
||||
template <size_t Count>
|
||||
class Array : private Pinned {
|
||||
public:
|
||||
Array() {
|
||||
header_.typeInfoOrMeta_ = const_cast<TypeInfo*>(theArrayTypeInfo);
|
||||
header_.count_ = Count;
|
||||
}
|
||||
|
||||
ObjHeader* header() { return header_.obj(); }
|
||||
|
||||
ObjHeader*& operator[](size_t index) { return fields_[index]; }
|
||||
|
||||
private:
|
||||
ArrayHeader header_;
|
||||
std::array<ObjHeader*, Count> fields_{};
|
||||
};
|
||||
|
||||
struct CallableWithExceptions {
|
||||
void operator()(ObjHeader*) noexcept(false) {}
|
||||
void operator()(ObjHeader**) noexcept(false) {}
|
||||
};
|
||||
|
||||
struct CallableWithoutExceptions {
|
||||
void operator()(ObjHeader*) noexcept {}
|
||||
void operator()(ObjHeader**) noexcept {}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseFieldsExceptions) {
|
||||
static_assert(
|
||||
noexcept(traverseObjectFields(std::declval<ObjHeader*>(), std::declval<CallableWithoutExceptions>())),
|
||||
"Callable is noexcept, so traverse is noexcept");
|
||||
static_assert(
|
||||
!noexcept(traverseObjectFields(std::declval<ObjHeader*>(), std::declval<CallableWithExceptions>())),
|
||||
"Callable is noexcept(false), so traverse is noexcept(false)");
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseEmptyObjectFields) {
|
||||
Object<0> object;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(_)).Times(0);
|
||||
traverseObjectFields(object.header(), [&process](ObjHeader** field) { process.Call(field); });
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseObjectFields) {
|
||||
ObjHeader field1;
|
||||
ObjHeader field3;
|
||||
Object<3> object;
|
||||
object[0] = &field1;
|
||||
object[2] = &field3;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(&object[0]));
|
||||
EXPECT_CALL(process, Call(&object[1]));
|
||||
EXPECT_CALL(process, Call(&object[2]));
|
||||
traverseObjectFields(object.header(), [&process](ObjHeader** field) { process.Call(field); });
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseObjectFieldsWithException) {
|
||||
constexpr int kException = 1;
|
||||
|
||||
ObjHeader field1;
|
||||
ObjHeader field2;
|
||||
ObjHeader field3;
|
||||
Object<3> object;
|
||||
object[0] = &field1;
|
||||
object[1] = &field2;
|
||||
object[2] = &field3;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(&object[0]));
|
||||
EXPECT_CALL(process, Call(&object[1])).WillOnce([]() { throw kException; });
|
||||
EXPECT_CALL(process, Call(&object[2])).Times(0);
|
||||
try {
|
||||
traverseObjectFields(object.header(), [&process](ObjHeader** field) { process.Call(field); });
|
||||
} catch (int exception) {
|
||||
EXPECT_THAT(exception, kException);
|
||||
} catch (...) {
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseEmptyArrayFields) {
|
||||
Array<0> array;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(_)).Times(0);
|
||||
traverseObjectFields(array.header(), [&process](ObjHeader** field) { process.Call(field); });
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseArrayFields) {
|
||||
ObjHeader element1;
|
||||
ObjHeader element3;
|
||||
Array<3> array;
|
||||
array[0] = &element1;
|
||||
array[2] = &element3;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(&array[0]));
|
||||
EXPECT_CALL(process, Call(&array[1]));
|
||||
EXPECT_CALL(process, Call(&array[2]));
|
||||
traverseObjectFields(array.header(), [&process](ObjHeader** field) { process.Call(field); });
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseArrayFieldsWithException) {
|
||||
constexpr int kException = 1;
|
||||
|
||||
ObjHeader element1;
|
||||
ObjHeader element2;
|
||||
ObjHeader element3;
|
||||
Array<3> array;
|
||||
array[0] = &element1;
|
||||
array[1] = &element2;
|
||||
array[2] = &element3;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader**)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(&array[0]));
|
||||
EXPECT_CALL(process, Call(&array[1])).WillOnce([]() { throw kException; });
|
||||
EXPECT_CALL(process, Call(&array[2])).Times(0);
|
||||
try {
|
||||
traverseObjectFields(array.header(), [&process](ObjHeader** field) { process.Call(field); });
|
||||
} catch (int exception) {
|
||||
EXPECT_THAT(exception, kException);
|
||||
} catch (...) {
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseRefsExceptions) {
|
||||
static_assert(
|
||||
noexcept(traverseReferredObjects(std::declval<ObjHeader*>(), std::declval<CallableWithoutExceptions>())),
|
||||
"Callable is noexcept, so traverse is noexcept");
|
||||
static_assert(
|
||||
!noexcept(traverseReferredObjects(std::declval<ObjHeader*>(), std::declval<CallableWithExceptions>())),
|
||||
"Callable is noexcept(false), so traverse is noexcept(false)");
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseEmptyObjectRefs) {
|
||||
Object<0> object;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(_)).Times(0);
|
||||
traverseReferredObjects(object.header(), [&process](ObjHeader* field) { process.Call(field); });
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseObjectRefs) {
|
||||
ObjHeader field1;
|
||||
ObjHeader field3;
|
||||
Object<3> object;
|
||||
object[0] = &field1;
|
||||
object[2] = &field3;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(&field1));
|
||||
EXPECT_CALL(process, Call(&field3));
|
||||
traverseReferredObjects(object.header(), [&process](ObjHeader* field) { process.Call(field); });
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseObjectRefsWithException) {
|
||||
constexpr int kException = 1;
|
||||
|
||||
ObjHeader field1;
|
||||
ObjHeader field2;
|
||||
ObjHeader field3;
|
||||
Object<3> object;
|
||||
object[0] = &field1;
|
||||
object[1] = &field2;
|
||||
object[2] = &field3;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(&field1));
|
||||
EXPECT_CALL(process, Call(&field2)).WillOnce([]() { throw kException; });
|
||||
EXPECT_CALL(process, Call(&field3)).Times(0);
|
||||
try {
|
||||
traverseReferredObjects(object.header(), [&process](ObjHeader* field) { process.Call(field); });
|
||||
} catch (int exception) {
|
||||
EXPECT_THAT(exception, kException);
|
||||
} catch (...) {
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseEmptyArrayRefs) {
|
||||
Array<0> array;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(_)).Times(0);
|
||||
traverseReferredObjects(array.header(), [&process](ObjHeader* field) { process.Call(field); });
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseArrayRefs) {
|
||||
ObjHeader element1;
|
||||
ObjHeader element3;
|
||||
Array<3> array;
|
||||
array[0] = &element1;
|
||||
array[2] = &element3;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(&element1));
|
||||
EXPECT_CALL(process, Call(&element3));
|
||||
traverseReferredObjects(array.header(), [&process](ObjHeader* field) { process.Call(field); });
|
||||
}
|
||||
|
||||
TEST(ObjectTraversalTest, TraverseArrayRefsWithException) {
|
||||
constexpr int kException = 1;
|
||||
|
||||
ObjHeader element1;
|
||||
ObjHeader element2;
|
||||
ObjHeader element3;
|
||||
Array<3> array;
|
||||
array[0] = &element1;
|
||||
array[1] = &element2;
|
||||
array[2] = &element3;
|
||||
testing::StrictMock<testing::MockFunction<void(ObjHeader*)>> process;
|
||||
|
||||
EXPECT_CALL(process, Call(&element1));
|
||||
EXPECT_CALL(process, Call(&element2)).WillOnce([]() { throw kException; });
|
||||
EXPECT_CALL(process, Call(&element3)).Times(0);
|
||||
try {
|
||||
traverseReferredObjects(array.header(), [&process](ObjHeader* field) { process.Call(field); });
|
||||
} catch (int exception) {
|
||||
EXPECT_THAT(exception, kException);
|
||||
} catch (...) {
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
}
|
||||
@@ -9,28 +9,38 @@
|
||||
|
||||
namespace {
|
||||
|
||||
TypeInfo theAnyTypeInfoImpl = {};
|
||||
TypeInfo theArrayTypeInfoImpl = {};
|
||||
TypeInfo theBooleanArrayTypeInfoImpl = {};
|
||||
TypeInfo theByteArrayTypeInfoImpl = {};
|
||||
TypeInfo theCharArrayTypeInfoImpl = {};
|
||||
TypeInfo theDoubleArrayTypeInfoImpl = {};
|
||||
TypeInfo theFloatArrayTypeInfoImpl = {};
|
||||
TypeInfo theForeignObjCObjectTypeInfoImpl = {};
|
||||
TypeInfo theFreezableAtomicReferenceTypeInfoImpl = {};
|
||||
TypeInfo theIntArrayTypeInfoImpl = {};
|
||||
TypeInfo theLongArrayTypeInfoImpl = {};
|
||||
TypeInfo theNativePtrArrayTypeInfoImpl = {};
|
||||
TypeInfo theObjCObjectWrapperTypeInfoImpl = {};
|
||||
TypeInfo theOpaqueFunctionTypeInfoImpl = {};
|
||||
TypeInfo theShortArrayTypeInfoImpl = {};
|
||||
TypeInfo theStringTypeInfoImpl = {};
|
||||
TypeInfo theThrowableTypeInfoImpl = {};
|
||||
TypeInfo theUnitTypeInfoImpl = {};
|
||||
TypeInfo theWorkerBoundReferenceTypeInfoImpl = {};
|
||||
TypeInfo theCleanerImplTypeInfoImpl = {};
|
||||
class TypeInfoImpl {
|
||||
public:
|
||||
TypeInfoImpl() { type_.typeInfo_ = &type_; }
|
||||
|
||||
ArrayHeader theEmptyStringImpl = { &theStringTypeInfoImpl, /* element count */ 0 };
|
||||
TypeInfo* type() { return &type_; }
|
||||
|
||||
private:
|
||||
TypeInfo type_;
|
||||
};
|
||||
|
||||
TypeInfoImpl theAnyTypeInfoImpl;
|
||||
TypeInfoImpl theArrayTypeInfoImpl;
|
||||
TypeInfoImpl theBooleanArrayTypeInfoImpl;
|
||||
TypeInfoImpl theByteArrayTypeInfoImpl;
|
||||
TypeInfoImpl theCharArrayTypeInfoImpl;
|
||||
TypeInfoImpl theDoubleArrayTypeInfoImpl;
|
||||
TypeInfoImpl theFloatArrayTypeInfoImpl;
|
||||
TypeInfoImpl theForeignObjCObjectTypeInfoImpl;
|
||||
TypeInfoImpl theFreezableAtomicReferenceTypeInfoImpl;
|
||||
TypeInfoImpl theIntArrayTypeInfoImpl;
|
||||
TypeInfoImpl theLongArrayTypeInfoImpl;
|
||||
TypeInfoImpl theNativePtrArrayTypeInfoImpl;
|
||||
TypeInfoImpl theObjCObjectWrapperTypeInfoImpl;
|
||||
TypeInfoImpl theOpaqueFunctionTypeInfoImpl;
|
||||
TypeInfoImpl theShortArrayTypeInfoImpl;
|
||||
TypeInfoImpl theStringTypeInfoImpl;
|
||||
TypeInfoImpl theThrowableTypeInfoImpl;
|
||||
TypeInfoImpl theUnitTypeInfoImpl;
|
||||
TypeInfoImpl theWorkerBoundReferenceTypeInfoImpl;
|
||||
TypeInfoImpl theCleanerImplTypeInfoImpl;
|
||||
|
||||
ArrayHeader theEmptyStringImpl = {theStringTypeInfoImpl.type(), /* element count */ 0};
|
||||
|
||||
template <class T>
|
||||
struct KBox {
|
||||
@@ -48,28 +58,28 @@ extern "C" {
|
||||
// Set to 1 to enable runtime assertions.
|
||||
extern const int KonanNeedDebugInfo = 1;
|
||||
|
||||
extern const TypeInfo* theAnyTypeInfo = &theAnyTypeInfoImpl;
|
||||
extern const TypeInfo* theArrayTypeInfo = &theArrayTypeInfoImpl;
|
||||
extern const TypeInfo* theBooleanArrayTypeInfo = &theBooleanArrayTypeInfoImpl;
|
||||
extern const TypeInfo* theByteArrayTypeInfo = &theByteArrayTypeInfoImpl;
|
||||
extern const TypeInfo* theCharArrayTypeInfo = &theCharArrayTypeInfoImpl;
|
||||
extern const TypeInfo* theDoubleArrayTypeInfo = &theDoubleArrayTypeInfoImpl;
|
||||
extern const TypeInfo* theFloatArrayTypeInfo = &theFloatArrayTypeInfoImpl;
|
||||
extern const TypeInfo* theForeignObjCObjectTypeInfo = &theForeignObjCObjectTypeInfoImpl;
|
||||
extern const TypeInfo* theFreezableAtomicReferenceTypeInfo = &theFreezableAtomicReferenceTypeInfoImpl;
|
||||
extern const TypeInfo* theIntArrayTypeInfo = &theIntArrayTypeInfoImpl;
|
||||
extern const TypeInfo* theLongArrayTypeInfo = &theLongArrayTypeInfoImpl;
|
||||
extern const TypeInfo* theNativePtrArrayTypeInfo = &theNativePtrArrayTypeInfoImpl;
|
||||
extern const TypeInfo* theObjCObjectWrapperTypeInfo = &theObjCObjectWrapperTypeInfoImpl;
|
||||
extern const TypeInfo* theOpaqueFunctionTypeInfo = &theOpaqueFunctionTypeInfoImpl;
|
||||
extern const TypeInfo* theShortArrayTypeInfo = &theShortArrayTypeInfoImpl;
|
||||
extern const TypeInfo* theStringTypeInfo = &theStringTypeInfoImpl;
|
||||
extern const TypeInfo* theThrowableTypeInfo = &theThrowableTypeInfoImpl;
|
||||
extern const TypeInfo* theUnitTypeInfo = &theUnitTypeInfoImpl;
|
||||
extern const TypeInfo* theWorkerBoundReferenceTypeInfo = &theWorkerBoundReferenceTypeInfoImpl;
|
||||
extern const TypeInfo* theCleanerImplTypeInfo = &theCleanerImplTypeInfoImpl;
|
||||
extern const TypeInfo* theAnyTypeInfo = theAnyTypeInfoImpl.type();
|
||||
extern const TypeInfo* theArrayTypeInfo = theArrayTypeInfoImpl.type();
|
||||
extern const TypeInfo* theBooleanArrayTypeInfo = theBooleanArrayTypeInfoImpl.type();
|
||||
extern const TypeInfo* theByteArrayTypeInfo = theByteArrayTypeInfoImpl.type();
|
||||
extern const TypeInfo* theCharArrayTypeInfo = theCharArrayTypeInfoImpl.type();
|
||||
extern const TypeInfo* theDoubleArrayTypeInfo = theDoubleArrayTypeInfoImpl.type();
|
||||
extern const TypeInfo* theFloatArrayTypeInfo = theFloatArrayTypeInfoImpl.type();
|
||||
extern const TypeInfo* theForeignObjCObjectTypeInfo = theForeignObjCObjectTypeInfoImpl.type();
|
||||
extern const TypeInfo* theFreezableAtomicReferenceTypeInfo = theFreezableAtomicReferenceTypeInfoImpl.type();
|
||||
extern const TypeInfo* theIntArrayTypeInfo = theIntArrayTypeInfoImpl.type();
|
||||
extern const TypeInfo* theLongArrayTypeInfo = theLongArrayTypeInfoImpl.type();
|
||||
extern const TypeInfo* theNativePtrArrayTypeInfo = theNativePtrArrayTypeInfoImpl.type();
|
||||
extern const TypeInfo* theObjCObjectWrapperTypeInfo = theObjCObjectWrapperTypeInfoImpl.type();
|
||||
extern const TypeInfo* theOpaqueFunctionTypeInfo = theOpaqueFunctionTypeInfoImpl.type();
|
||||
extern const TypeInfo* theShortArrayTypeInfo = theShortArrayTypeInfoImpl.type();
|
||||
extern const TypeInfo* theStringTypeInfo = theStringTypeInfoImpl.type();
|
||||
extern const TypeInfo* theThrowableTypeInfo = theThrowableTypeInfoImpl.type();
|
||||
extern const TypeInfo* theUnitTypeInfo = theUnitTypeInfoImpl.type();
|
||||
extern const TypeInfo* theWorkerBoundReferenceTypeInfo = theWorkerBoundReferenceTypeInfoImpl.type();
|
||||
extern const TypeInfo* theCleanerImplTypeInfo = theCleanerImplTypeInfoImpl.type();
|
||||
|
||||
extern const ArrayHeader theEmptyArray = { &theArrayTypeInfoImpl, /* element count */0 };
|
||||
extern const ArrayHeader theEmptyArray = {theArrayTypeInfoImpl.type(), /* element count */ 0};
|
||||
|
||||
OBJ_GETTER0(TheEmptyString) {
|
||||
RETURN_OBJ(theEmptyStringImpl.obj());
|
||||
|
||||
Reference in New Issue
Block a user