532c116cd2
This CL implements `NativeFinalizer` in the GC. `FinalizerEntry`s are extended to track `external_size` and in which `Heap::Space` the finalizable value is. On attaching a native finalizer, the external size is added to the relevant heap. When the finalizable value is promoted from new to old space, the external size is promoted as well. And when a native finalizer is run or is detached, the external size is removed from the relevant heap again. In contrast to Dart `Finalizer`s, `NativeFinalizer`s are run on isolate shutdown. When the `NativeFinalizer`s themselves are collected, the finalizers are not run. Users should stick the native finalizer in a global variable to ensure finalization. We will revisit this design when we add send and exit support, because there is a design space to explore what to do in that case. This current solution promises the least to users. In this implementation native finalizers have a Dart entry to clean up the entries from the `all_entries` field of the finalizer. We should consider using another data structure that avoids the need for this Dart entry. See the TODO left in the code. Bug: https://github.com/dart-lang/sdk/issues/47777 TEST=runtime/tests/vm/dart(_2)/isolates/fast_object_copy_test.dart TEST=runtime/vm/object_test.cc TEST=tests/ffi(_2)/vmspecific_native_finalizer_* Change-Id: I8f594c80c3c344ad83e1f2de10de028eb8456121 Cq-Include-Trybots: luci.dart.try:vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-ffi-android-debug-arm64c-try,dart-sdk-mac-arm64-try,vm-kernel-mac-release-arm64-try,pkg-mac-release-arm64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try,vm-kernel-win-debug-x64c-try,vm-kernel-win-debug-x64-try,vm-kernel-precomp-win-debug-x64c-try,vm-kernel-nnbd-win-release-ia32-try,vm-ffi-android-debug-arm-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-mac-debug-x64-try,vm-kernel-nnbd-mac-debug-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,benchmark-linux-try,flutter-frontend-try,pkg-linux-debug-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-gcc-linux-try,vm-kernel-optcounter-threshold-linux-release-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64c-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/236320 Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Daco Harkes <dacoharkes@google.com>
468 lines
23 KiB
C++
468 lines
23 KiB
C++
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_CLASS_ID_H_
|
|
#define RUNTIME_VM_CLASS_ID_H_
|
|
|
|
// This header defines the list of VM implementation classes and their ids.
|
|
//
|
|
// Note: we assume that all builds of Dart VM use exactly the same class ids
|
|
// for these classes.
|
|
|
|
#include "platform/assert.h"
|
|
|
|
namespace dart {
|
|
|
|
// Size of the class-id part of the object header. See UntaggedObject.
|
|
typedef uint16_t ClassIdTagType;
|
|
|
|
// Classes that are not subclasses of Instance and only handled by the VM,
|
|
// but do not require any special handling other than being a predefined class.
|
|
#define CLASS_LIST_INTERNAL_ONLY(V) \
|
|
V(Class) \
|
|
V(PatchClass) \
|
|
V(Function) \
|
|
V(TypeParameters) \
|
|
V(ClosureData) \
|
|
V(FfiTrampolineData) \
|
|
V(Field) \
|
|
V(Script) \
|
|
V(Library) \
|
|
V(Namespace) \
|
|
V(KernelProgramInfo) \
|
|
V(WeakSerializationReference) \
|
|
V(Code) \
|
|
V(Instructions) \
|
|
V(InstructionsSection) \
|
|
V(InstructionsTable) \
|
|
V(ObjectPool) \
|
|
V(PcDescriptors) \
|
|
V(CodeSourceMap) \
|
|
V(CompressedStackMaps) \
|
|
V(LocalVarDescriptors) \
|
|
V(ExceptionHandlers) \
|
|
V(Context) \
|
|
V(ContextScope) \
|
|
V(Sentinel) \
|
|
V(SingleTargetCache) \
|
|
V(UnlinkedCall) \
|
|
V(MonomorphicSmiableCall) \
|
|
V(CallSiteData) \
|
|
V(ICData) \
|
|
V(MegamorphicCache) \
|
|
V(SubtypeTestCache) \
|
|
V(LoadingUnit) \
|
|
V(Error) \
|
|
V(ApiError) \
|
|
V(LanguageError) \
|
|
V(UnhandledException) \
|
|
V(UnwindError)
|
|
|
|
// Classes that are subclasses of Instance and neither part of a specific cid
|
|
// grouping like strings, arrays, etc. nor require special handling outside of
|
|
// being a predefined class.
|
|
#define CLASS_LIST_INSTANCE_SINGLETONS(V) \
|
|
V(Instance) \
|
|
V(LibraryPrefix) \
|
|
V(TypeArguments) \
|
|
V(AbstractType) \
|
|
V(Type) \
|
|
V(FinalizerBase) \
|
|
V(Finalizer) \
|
|
V(NativeFinalizer) \
|
|
V(FinalizerEntry) \
|
|
V(FunctionType) \
|
|
V(TypeRef) \
|
|
V(TypeParameter) \
|
|
V(Closure) \
|
|
V(Number) \
|
|
V(Integer) \
|
|
V(Smi) \
|
|
V(Mint) \
|
|
V(Double) \
|
|
V(Bool) \
|
|
V(Float32x4) \
|
|
V(Int32x4) \
|
|
V(Float64x2) \
|
|
V(TypedDataBase) \
|
|
V(TypedData) \
|
|
V(ExternalTypedData) \
|
|
V(TypedDataView) \
|
|
V(Pointer) \
|
|
V(DynamicLibrary) \
|
|
V(Capability) \
|
|
V(ReceivePort) \
|
|
V(SendPort) \
|
|
V(StackTrace) \
|
|
V(RegExp) \
|
|
V(WeakProperty) \
|
|
V(WeakReference) \
|
|
V(MirrorReference) \
|
|
V(FutureOr) \
|
|
V(UserTag) \
|
|
V(TransferableTypedData)
|
|
|
|
#define CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY_NOR_MAP(V) \
|
|
CLASS_LIST_INTERNAL_ONLY(V) CLASS_LIST_INSTANCE_SINGLETONS(V)
|
|
|
|
#define CLASS_LIST_MAPS(V) \
|
|
V(LinkedHashMap) \
|
|
V(ImmutableLinkedHashMap)
|
|
|
|
#define CLASS_LIST_SETS(V) \
|
|
V(LinkedHashSet) \
|
|
V(ImmutableLinkedHashSet)
|
|
|
|
#define CLASS_LIST_FIXED_LENGTH_ARRAYS(V) \
|
|
V(Array) \
|
|
V(ImmutableArray)
|
|
|
|
#define CLASS_LIST_ARRAYS(V) \
|
|
CLASS_LIST_FIXED_LENGTH_ARRAYS(V) \
|
|
V(GrowableObjectArray)
|
|
|
|
#define CLASS_LIST_STRINGS(V) \
|
|
V(String) \
|
|
V(OneByteString) \
|
|
V(TwoByteString) \
|
|
V(ExternalOneByteString) \
|
|
V(ExternalTwoByteString)
|
|
|
|
#define CLASS_LIST_TYPED_DATA(V) \
|
|
V(Int8Array) \
|
|
V(Uint8Array) \
|
|
V(Uint8ClampedArray) \
|
|
V(Int16Array) \
|
|
V(Uint16Array) \
|
|
V(Int32Array) \
|
|
V(Uint32Array) \
|
|
V(Int64Array) \
|
|
V(Uint64Array) \
|
|
V(Float32Array) \
|
|
V(Float64Array) \
|
|
V(Float32x4Array) \
|
|
V(Int32x4Array) \
|
|
V(Float64x2Array)
|
|
|
|
#define CLASS_LIST_FFI_NUMERIC_FIXED_SIZE(V) \
|
|
V(Int8) \
|
|
V(Int16) \
|
|
V(Int32) \
|
|
V(Int64) \
|
|
V(Uint8) \
|
|
V(Uint16) \
|
|
V(Uint32) \
|
|
V(Uint64) \
|
|
V(Float) \
|
|
V(Double)
|
|
|
|
#define CLASS_LIST_FFI_TYPE_MARKER(V) \
|
|
CLASS_LIST_FFI_NUMERIC_FIXED_SIZE(V) \
|
|
V(Void) \
|
|
V(Handle) \
|
|
V(Bool)
|
|
|
|
#define CLASS_LIST_FFI(V) \
|
|
V(NativeFunction) \
|
|
CLASS_LIST_FFI_TYPE_MARKER(V) \
|
|
V(NativeType) \
|
|
V(Struct)
|
|
|
|
#define DART_CLASS_LIST_TYPED_DATA(V) \
|
|
V(Int8) \
|
|
V(Uint8) \
|
|
V(Uint8Clamped) \
|
|
V(Int16) \
|
|
V(Uint16) \
|
|
V(Int32) \
|
|
V(Uint32) \
|
|
V(Int64) \
|
|
V(Uint64) \
|
|
V(Float32) \
|
|
V(Float64) \
|
|
V(Float32x4) \
|
|
V(Int32x4) \
|
|
V(Float64x2)
|
|
|
|
#define CLASS_LIST_FOR_HANDLES(V) \
|
|
CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY_NOR_MAP(V) \
|
|
V(LinkedHashMap) \
|
|
V(LinkedHashSet) \
|
|
V(Array) \
|
|
V(GrowableObjectArray) \
|
|
V(String)
|
|
|
|
#define CLASS_LIST_NO_OBJECT(V) \
|
|
CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY_NOR_MAP(V) \
|
|
CLASS_LIST_MAPS(V) \
|
|
CLASS_LIST_SETS(V) \
|
|
CLASS_LIST_ARRAYS(V) \
|
|
CLASS_LIST_STRINGS(V)
|
|
|
|
#define CLASS_LIST(V) \
|
|
V(Object) \
|
|
CLASS_LIST_NO_OBJECT(V)
|
|
|
|
enum ClassId : intptr_t {
|
|
// Illegal class id.
|
|
kIllegalCid = 0,
|
|
|
|
// Pseudo class id for native pointers, the heap should never see an
|
|
// object with this class id.
|
|
kNativePointer,
|
|
|
|
// The following entries describes classes for pseudo-objects in the heap
|
|
// that should never be reachable from live objects. Free list elements
|
|
// maintain the free list for old space, and forwarding corpses are used to
|
|
// implement one-way become.
|
|
kFreeListElement,
|
|
kForwardingCorpse,
|
|
|
|
// List of Ids for predefined classes.
|
|
#define DEFINE_OBJECT_KIND(clazz) k##clazz##Cid,
|
|
CLASS_LIST(DEFINE_OBJECT_KIND)
|
|
#undef DEFINE_OBJECT_KIND
|
|
|
|
// clang-format off
|
|
#define DEFINE_OBJECT_KIND(clazz) kFfi##clazz##Cid,
|
|
CLASS_LIST_FFI(DEFINE_OBJECT_KIND)
|
|
#undef DEFINE_OBJECT_KIND
|
|
|
|
#define DEFINE_OBJECT_KIND(clazz) \
|
|
kTypedData##clazz##Cid, \
|
|
kTypedData##clazz##ViewCid, \
|
|
kExternalTypedData##clazz##Cid,
|
|
CLASS_LIST_TYPED_DATA(DEFINE_OBJECT_KIND)
|
|
#undef DEFINE_OBJECT_KIND
|
|
kByteDataViewCid,
|
|
|
|
kByteBufferCid,
|
|
// clang-format on
|
|
|
|
// The following entries do not describe a predefined class, but instead
|
|
// are class indexes for pre-allocated instances (Null, dynamic, void, Never).
|
|
kNullCid,
|
|
kDynamicCid,
|
|
kVoidCid,
|
|
kNeverCid,
|
|
|
|
kNumPredefinedCids,
|
|
};
|
|
|
|
// Keep these in sync with the cid numbering above.
|
|
const int kTypedDataCidRemainderInternal = 0;
|
|
const int kTypedDataCidRemainderView = 1;
|
|
const int kTypedDataCidRemainderExternal = 2;
|
|
|
|
// Class Id predicates.
|
|
|
|
bool IsInternalOnlyClassId(intptr_t index);
|
|
bool IsErrorClassId(intptr_t index);
|
|
bool IsNumberClassId(intptr_t index);
|
|
bool IsIntegerClassId(intptr_t index);
|
|
bool IsStringClassId(intptr_t index);
|
|
bool IsOneByteStringClassId(intptr_t index);
|
|
bool IsExternalStringClassId(intptr_t index);
|
|
bool IsBuiltinListClassId(intptr_t index);
|
|
bool IsTypeClassId(intptr_t index);
|
|
bool IsTypedDataBaseClassId(intptr_t index);
|
|
bool IsTypedDataClassId(intptr_t index);
|
|
bool IsTypedDataViewClassId(intptr_t index);
|
|
bool IsExternalTypedDataClassId(intptr_t index);
|
|
bool IsFfiPointerClassId(intptr_t index);
|
|
bool IsFfiTypeClassId(intptr_t index);
|
|
bool IsFfiDynamicLibraryClassId(intptr_t index);
|
|
bool IsInternalVMdefinedClassId(intptr_t index);
|
|
bool IsImplicitFieldClassId(intptr_t index);
|
|
|
|
// Should be used for looping over non-Object internal-only cids.
|
|
constexpr intptr_t kFirstInternalOnlyCid = kClassCid;
|
|
constexpr intptr_t kLastInternalOnlyCid = kUnwindErrorCid;
|
|
// Use the currently surrounding cids to check that no new classes have been
|
|
// added to the beginning or end of CLASS_LIST_INTERNAL_ONLY without adjusting
|
|
// the above definitions.
|
|
COMPILE_ASSERT(kFirstInternalOnlyCid == kObjectCid + 1);
|
|
COMPILE_ASSERT(kInstanceCid == kLastInternalOnlyCid + 1);
|
|
|
|
// Returns true for any class id that either does not correspond to a real
|
|
// class, like kIllegalCid or kForwardingCorpse, or that is internal to the VM
|
|
// and should not be exposed directly to user code.
|
|
inline bool IsInternalOnlyClassId(intptr_t index) {
|
|
// Fix the condition below if these become non-contiguous.
|
|
COMPILE_ASSERT(kIllegalCid + 1 == kNativePointer &&
|
|
kIllegalCid + 2 == kFreeListElement &&
|
|
kIllegalCid + 3 == kForwardingCorpse &&
|
|
kIllegalCid + 4 == kObjectCid &&
|
|
kIllegalCid + 5 == kFirstInternalOnlyCid);
|
|
return index <= kLastInternalOnlyCid;
|
|
}
|
|
|
|
inline bool IsErrorClassId(intptr_t index) {
|
|
// Make sure this function is updated when new Error types are added.
|
|
COMPILE_ASSERT(kApiErrorCid == kErrorCid + 1 &&
|
|
kLanguageErrorCid == kErrorCid + 2 &&
|
|
kUnhandledExceptionCid == kErrorCid + 3 &&
|
|
kUnwindErrorCid == kErrorCid + 4 &&
|
|
// Change if needed for detecting a new error added at the end.
|
|
kLastInternalOnlyCid == kUnwindErrorCid);
|
|
return (index >= kErrorCid && index <= kUnwindErrorCid);
|
|
}
|
|
|
|
inline bool IsNumberClassId(intptr_t index) {
|
|
// Make sure this function is updated when new Number types are added.
|
|
COMPILE_ASSERT(kIntegerCid == kNumberCid + 1 && kSmiCid == kNumberCid + 2 &&
|
|
kMintCid == kNumberCid + 3 && kDoubleCid == kNumberCid + 4);
|
|
return (index >= kNumberCid && index <= kDoubleCid);
|
|
}
|
|
|
|
inline bool IsIntegerClassId(intptr_t index) {
|
|
// Make sure this function is updated when new Integer types are added.
|
|
COMPILE_ASSERT(kSmiCid == kIntegerCid + 1 && kMintCid == kIntegerCid + 2);
|
|
return (index >= kIntegerCid && index <= kMintCid);
|
|
}
|
|
|
|
// Make sure this check is updated when new StringCid types are added.
|
|
COMPILE_ASSERT(kOneByteStringCid == kStringCid + 1 &&
|
|
kTwoByteStringCid == kStringCid + 2 &&
|
|
kExternalOneByteStringCid == kStringCid + 3 &&
|
|
kExternalTwoByteStringCid == kStringCid + 4);
|
|
|
|
inline bool IsStringClassId(intptr_t index) {
|
|
return (index >= kStringCid && index <= kExternalTwoByteStringCid);
|
|
}
|
|
|
|
inline bool IsOneByteStringClassId(intptr_t index) {
|
|
return (index == kOneByteStringCid || index == kExternalOneByteStringCid);
|
|
}
|
|
|
|
inline bool IsExternalStringClassId(intptr_t index) {
|
|
return (index == kExternalOneByteStringCid ||
|
|
index == kExternalTwoByteStringCid);
|
|
}
|
|
|
|
inline bool IsArrayClassId(intptr_t index) {
|
|
COMPILE_ASSERT(kImmutableArrayCid == kArrayCid + 1);
|
|
COMPILE_ASSERT(kGrowableObjectArrayCid == kArrayCid + 2);
|
|
return (index >= kArrayCid && index <= kGrowableObjectArrayCid);
|
|
}
|
|
|
|
inline bool IsBuiltinListClassId(intptr_t index) {
|
|
// Make sure this function is updated when new builtin List types are added.
|
|
return (IsArrayClassId(index) || IsTypedDataBaseClassId(index) ||
|
|
(index == kByteBufferCid));
|
|
}
|
|
|
|
inline bool IsTypeClassId(intptr_t index) {
|
|
// Only Type and FunctionType can be encountered as instance types at runtime.
|
|
return index == kTypeCid || index == kFunctionTypeCid;
|
|
}
|
|
|
|
inline bool IsTypedDataBaseClassId(intptr_t index) {
|
|
// Make sure this is updated when new TypedData types are added.
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 3 == kTypedDataUint8ArrayCid);
|
|
return index >= kTypedDataInt8ArrayCid && index < kByteDataViewCid;
|
|
}
|
|
|
|
inline bool IsTypedDataClassId(intptr_t index) {
|
|
// Make sure this is updated when new TypedData types are added.
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 3 == kTypedDataUint8ArrayCid);
|
|
return IsTypedDataBaseClassId(index) && ((index - kTypedDataInt8ArrayCid) %
|
|
3) == kTypedDataCidRemainderInternal;
|
|
}
|
|
|
|
inline bool IsTypedDataViewClassId(intptr_t index) {
|
|
// Make sure this is updated when new TypedData types are added.
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayViewCid + 3 == kTypedDataUint8ArrayViewCid);
|
|
|
|
const bool is_byte_data_view = index == kByteDataViewCid;
|
|
return is_byte_data_view ||
|
|
(IsTypedDataBaseClassId(index) &&
|
|
((index - kTypedDataInt8ArrayCid) % 3) == kTypedDataCidRemainderView);
|
|
}
|
|
|
|
inline bool IsExternalTypedDataClassId(intptr_t index) {
|
|
// Make sure this is updated when new TypedData types are added.
|
|
COMPILE_ASSERT(kExternalTypedDataInt8ArrayCid + 3 ==
|
|
kExternalTypedDataUint8ArrayCid);
|
|
|
|
return IsTypedDataBaseClassId(index) && ((index - kTypedDataInt8ArrayCid) %
|
|
3) == kTypedDataCidRemainderExternal;
|
|
}
|
|
|
|
inline bool IsFfiTypeClassId(intptr_t index) {
|
|
switch (index) {
|
|
case kPointerCid:
|
|
case kFfiNativeFunctionCid:
|
|
#define CASE_FFI_CID(name) case kFfi##name##Cid:
|
|
CLASS_LIST_FFI_TYPE_MARKER(CASE_FFI_CID)
|
|
#undef CASE_FFI_CID
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
inline bool IsFfiPredefinedClassId(classid_t class_id) {
|
|
switch (class_id) {
|
|
case kPointerCid:
|
|
case kDynamicLibraryCid:
|
|
#define CASE_FFI_CID(name) case kFfi##name##Cid:
|
|
CLASS_LIST_FFI(CASE_FFI_CID)
|
|
#undef CASE_FFI_CID
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
inline bool IsFfiPointerClassId(intptr_t index) {
|
|
return index == kPointerCid;
|
|
}
|
|
|
|
inline bool IsFfiDynamicLibraryClassId(intptr_t index) {
|
|
return index == kDynamicLibraryCid;
|
|
}
|
|
|
|
inline bool IsInternalVMdefinedClassId(intptr_t index) {
|
|
return ((index < kNumPredefinedCids) && !IsImplicitFieldClassId(index));
|
|
}
|
|
|
|
// This is a set of classes that are not Dart classes whose representation
|
|
// is defined by the VM but are used in the VM code by computing the
|
|
// implicit field offsets of the various fields in the dart object.
|
|
inline bool IsImplicitFieldClassId(intptr_t index) {
|
|
return index == kByteBufferCid;
|
|
}
|
|
|
|
// Make sure the following checks are updated when adding new TypedData types.
|
|
|
|
// Ensure that each typed data type comes in internal/view/external variants
|
|
// next to each other.
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 1 == kTypedDataInt8ArrayViewCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 2 == kExternalTypedDataInt8ArrayCid);
|
|
|
|
// Ensure the order of the typed data members in 3-step.
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 1 * 3 == kTypedDataUint8ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 2 * 3 ==
|
|
kTypedDataUint8ClampedArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 3 * 3 == kTypedDataInt16ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 4 * 3 == kTypedDataUint16ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 5 * 3 == kTypedDataInt32ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 6 * 3 == kTypedDataUint32ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 7 * 3 == kTypedDataInt64ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 8 * 3 == kTypedDataUint64ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 9 * 3 == kTypedDataFloat32ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 10 * 3 == kTypedDataFloat64ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 11 * 3 == kTypedDataFloat32x4ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 12 * 3 == kTypedDataInt32x4ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 13 * 3 == kTypedDataFloat64x2ArrayCid);
|
|
COMPILE_ASSERT(kTypedDataInt8ArrayCid + 14 * 3 == kByteDataViewCid);
|
|
COMPILE_ASSERT(kByteBufferCid + 1 == kNullCid);
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_CLASS_ID_H_
|