Path: blob/master/runtime/gc_glue_java/ArrayletObjectModel.hpp
5990 views
/*******************************************************************************1* Copyright (c) 1991, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/2122/**23* @file24* @ingroup GC_Base25*/26#if !defined(ARRAYLETOBJECTMODEL_)27#define ARRAYLETOBJECTMODEL_2829#include "j9.h"30#include "j9cfg.h"31#include "j9consts.h"32#include "modron.h"33#include "modronopt.h"3435#include "ArrayletObjectModelBase.hpp"36#include "Bits.hpp"37#include "ForwardedHeader.hpp"38#include "Math.hpp"39#include "SlotObject.hpp"4041class GC_ArrayletObjectModel : public GC_ArrayletObjectModelBase42{43/*44* Data members45*/46private:47protected:48public:4950/*51* Function members52*/53private:54void AssertBadElementSize();55protected:56/* forward declare methods from parent class to avoid namespace issues */57MMINLINE uintptr_t58getSpineSizeWithoutHeader(ArrayLayout layout, uintptr_t numberArraylets, uintptr_t dataSize, bool alignData)59{60return GC_ArrayletObjectModelBase::getSpineSizeWithoutHeader(layout, numberArraylets, dataSize, alignData);61}62public:63/* forward declare methods from parent class to avoid namespace issues */64MMINLINE uintptr_t65arrayletSize(J9IndexableObject *objPtr, uintptr_t index, uintptr_t dataSizeInBytes, uintptr_t numberOfArraylets)66{67return GC_ArrayletObjectModelBase::arrayletSize(objPtr, index, dataSizeInBytes, numberOfArraylets);68}69MMINLINE uintptr_t70numArraylets(uintptr_t unadjustedDataSizeInBytes)71{72return GC_ArrayletObjectModelBase::numArraylets(unadjustedDataSizeInBytes);73}7475/**76* Get the header size for contiguous arrays77* @return header size78*/79MMINLINE uintptr_t80contiguousHeaderSize()81{82return compressObjectReferences() ? sizeof(J9IndexableObjectContiguousCompressed) : sizeof(J9IndexableObjectContiguousFull);83}8485/**86* Get the header size for discontiguous arrays87* @return header size88*/89MMINLINE uintptr_t90discontiguousHeaderSize()91{92return compressObjectReferences() ? sizeof(J9IndexableObjectDiscontiguousCompressed) : sizeof(J9IndexableObjectDiscontiguousFull);93}9495/**96* Get the spine size for the given indexable object97* @param objPtr Pointer to an array object98* @return The total size in bytes of objPtr's array spine;99* includes header, arraylet ptrs, and (if present) padding & inline data100*/101MMINLINE uintptr_t102getSpineSize(J9IndexableObject* objPtr)103{104ArrayLayout layout = getArrayLayout(objPtr);105return getSpineSize(objPtr, layout);106}107108/**109* Get the spine size for an arraylet with these properties110* @param J9Class The class of the array.111* @param layout The layout of the indexable object112* @param numberArraylets Number of arraylets for this indexable object113* @param dataSize How many elements are in the indexable object114* @param alignData Should the data section be aligned115* @return spineSize The actual size in byte of the spine116*/117MMINLINE uintptr_t118getSpineSize(J9Class *clazzPtr, ArrayLayout layout, uintptr_t numberArraylets, uintptr_t dataSize, bool alignData)119{120uintptr_t result = getHeaderSize(clazzPtr, layout) + getSpineSizeWithoutHeader(layout, numberArraylets, dataSize, alignData);121return result;122}123124/**125* Return the total number of arraylets for the given indexable object126* @param objPtr Pointer to an array object127* @return the number of arraylets used for an array of dataSizeInBytes bytes128*/129MMINLINE uintptr_t130numArraylets(J9IndexableObject *objPtr)131{132return numArraylets(getDataSizeInBytes(objPtr));133}134135/**136* Check the given indexable object is inline contiguous137* @param objPtr Pointer to an array object138* @return true of array is inline contiguous139*/140MMINLINE bool141isInlineContiguousArraylet(J9IndexableObject *objPtr)142{143return (getArrayLayout(objPtr) == InlineContiguous);144}145146/**147* Get the number of discontiguous arraylets for the given indexable object148* @param objPtr Pointer to an array object149* @return the number of arraylets stored external to the spine.150*/151MMINLINE uintptr_t152numExternalArraylets(J9IndexableObject *objPtr)153{154ArrayLayout layout = getArrayLayout(objPtr);155if (layout == InlineContiguous) {156return 0;157}158159uintptr_t numberArraylets = numArraylets(objPtr);160if (layout == Hybrid) {161/* last arrayoid pointer points into spine (remainder data is contiguous with header) */162numberArraylets -= 1;163} else if (layout == Discontiguous) {164/* Data fits exactly within a whole number of arraylets */165AssertArrayletIsDiscontiguous(objPtr);166}167168return numberArraylets;169}170171/**172* Get the total number of bytes consumed by arraylets external to the173* given indexable object.174* @param objPtr Pointer to an array object175* @return the number of bytes consumed external to the spine176*/177MMINLINE uintptr_t178externalArrayletsSize(J9IndexableObject *objPtr)179{180uintptr_t numberArraylets = numExternalArraylets(objPtr);181182return numberArraylets * _omrVM->_arrayletLeafSize;183}184185/**186* Determine if the specified array object includes any arraylet leaf pointers.187* @param objPtr[in] the object to test188* @return true if the array has any internal or external leaf pointers, false otherwise189*/190MMINLINE bool191hasArrayletLeafPointers(J9IndexableObject *objPtr)192{193/* Contiguous arraylet has no implicit leaf pointer */194return !isInlineContiguousArraylet(objPtr);195}196197198/**199* Get the size in bytes for the arraylet at index in indexable object objPtr200* @param objPtr Pointer to an array object201* @param index the index in question. 0<=index<numArraylets(objPtr)202* @return the size of the indexth arraylet203*/204MMINLINE uintptr_t205arrayletSize(J9IndexableObject *objPtr, uintptr_t index)206{207uintptr_t dataSizeInBytes = getDataSizeInBytes(objPtr);208uintptr_t numberOfArraylets = numArraylets(dataSizeInBytes);209return arrayletSize(objPtr, index, dataSizeInBytes, numberOfArraylets);210}211212/**213* Get the spine size for the given indexable object214* @param objPtr Pointer to an array object215* @param layout layout for array object216* @return The total size in bytes of objPtr's array spine;217* includes header, arraylet ptrs, and (if present) padding & inline data218*/219MMINLINE uintptr_t220getSpineSize(J9IndexableObject* objPtr, ArrayLayout layout)221{222return getSpineSize(J9GC_J9OBJECT_CLAZZ(objPtr, this), layout, getSizeInElements(objPtr));223}224225/**226* Get the spine size for the given indexable object227* @param clazzPtr Pointer to the preserved J9Class, used in scavenger228* @param layout layout for array object229* @param size Size of indexable object230* @return The total size in bytes of objPtr's array spine;231* includes header, arraylet ptrs, and (if present) padding & inline data232*/233MMINLINE uintptr_t234getSpineSize(J9Class *clazzPtr, ArrayLayout layout, uintptr_t size)235{236return getHeaderSize(clazzPtr, layout) + getSpineSizeWithoutHeader(clazzPtr, layout, size);237}238239/**240* Get the spine size without header for the given indexable object241* @param clazzPtr Pointer to the preserved J9Class, used in scavenger242* @param layout layout for array object243* @param size Size of indexable object244* @return The size in bytes of objPtr's array spine without header;245* includes arraylet ptrs, padding and inline data (if any is present)246*/247MMINLINE uintptr_t248getSpineSizeWithoutHeader(J9Class *clazzPtr, ArrayLayout layout, uintptr_t size)249{250uintptr_t dataSize = getDataSizeInBytes(clazzPtr, size);251uintptr_t numberArraylets = numArraylets(dataSize);252bool alignData = shouldAlignSpineDataSection(clazzPtr);253return getSpineSizeWithoutHeader(layout, numberArraylets, dataSize, alignData);254}255256#if defined(J9VM_GC_ENABLE_DOUBLE_MAP)257/**258* Checks if arraylet falls into corner case of discontigous data259* Arraylet possible cases:260* 0: Empty arraylets, in this case the array is represented as261* an arraylet however it does not contain any data, but it262* does contain an arrayoid (leaf pointer) that points to NULL.263* Even though this case is represented as a discontiguous arraylet264* internally due to its implementation, it is actually a contiguous265* array with length zero.266* 1: The total data size in arraylet is between 0 and region267* size. Small enough to make the arraylet layout contiguous,268* in which case this function is unreachable.269* 2: The total data size in arraylet is exacly the same size270* of a region. In this case we do not need to double271* map since we already have a contiguous representation of the272* data at first leaf.273* 3: Similar to first case, the data portion is slightly smaller than274* a region size, however not small enough to include header and data275* at the same region to make it contiguous. In which case we would276* have one leaf, where we also do not need to double map.277* 4: The total data size in arraylet is stricly greater than one region;278* however, not multiple of region size. Since with enabled double map279* layout is always discontiguous, we would have 2 or more arraylet leaves280* therefore we always double map.281* 5: The total data size in arraylet is stricly greater than one region and282* multiple of region size. Here we would have 2 or more arraylet leaves283* containing data. We always double map in this case.284*285* @param spine Pointer to an array indexable object spine286* @return false in case corner cases 0, 2 or 3 are valid. On the other hand,287* if cases 4 or 5 are true, the function returns true.288*/289MMINLINE bool290isArrayletDataDiscontiguous(J9IndexableObject *spine)291{292return numArraylets(spine) > 1;293}294295/**296* Checks if arraylet falls into corner case of contiguous data297*298* @param spine Pointer to an array indexable object spine299* @return true in case corner cases 2 or 3 are valid. On the other hand,300* if cases 0, 4 or 5 are true, the function returns false.301*/302MMINLINE bool303isArrayletDataContiguous(J9IndexableObject *spine)304{305return (1 == numArraylets(spine)) && (getSizeInElements(spine) > 0);306}307#endif /* J9VM_GC_ENABLE_DOUBLE_MAP */308309/**310* We can't use memcpy because it may be not atomic for pointers, use this function instead311* Copy data in uintptr_t words312* If length is not times of uintptr_t one more word is copied313* @param destAddr address copy to314* @param sourceAddr address copy from315* @param lengthInBytes requested size in bytes316*/317MMINLINE void318copyInWords (uintptr_t *destAddr, uintptr_t *sourceAddr, uintptr_t lengthInBytes)319{320uintptr_t lengthInWords = lengthInBytes / sizeof(uintptr_t);321while (lengthInWords--) {322*destAddr++ = *sourceAddr++;323}324}325326/**327* Returns the header size of an arraylet for a given layout.328* This method is actually private, but since ArrayLayout is public,329* it cannot be declared in private section.330* @param layout Layout of the arraylet331* @return Size of header in bytes332*/333MMINLINE uintptr_t334getHeaderSize(J9Class *clazzPtr, ArrayLayout layout)335{336uintptr_t headerSize = contiguousHeaderSize();337if (layout != InlineContiguous) {338headerSize = discontiguousHeaderSize();339}340return headerSize;341}342343/**344* Returns the size of data in an indexable object, in bytes, including leaves and alignment345* padding, excluding the header, or UDATA_MAX if an overflow occurs.346* @param arrayPtr Pointer to the indexable object whose size is required347* @return Aligned size in bytes excluding the header, or UDATA_MAX if an overflow occurs348*/349MMINLINE uintptr_t350getDataSizeInBytes(J9IndexableObject *arrayPtr)351{352return getDataSizeInBytes(J9GC_J9OBJECT_CLAZZ(arrayPtr, this), getSizeInElements(arrayPtr));353}354355/**356* Returns the size of data in an indexable object, in bytes, including leaves and alignment357* padding, excluding the header, or UDATA_MAX if an overflow occurs.358* @param clazzPtr Pointer to the class of the object359* @param numberOfElements size from indexable object360* @return Aligned size in bytes excluding the header, or UDATA_MAX if an overflow occurs361*/362MMINLINE uintptr_t363getDataSizeInBytes(J9Class *clazzPtr, uintptr_t numberOfElements)364{365uintptr_t stride = J9ARRAYCLASS_GET_STRIDE(clazzPtr);366uintptr_t size = numberOfElements * stride;367uintptr_t alignedSize = UDATA_MAX;368if ((size / stride) == numberOfElements) {369alignedSize = MM_Math::roundToSizeofUDATA(size);370if (alignedSize < size) {371alignedSize = UDATA_MAX;372}373}374return alignedSize;375}376377/**378* Get the size from the header for the given indexable object379* @param objPtr Pointer to an array object380* @return the size381*/382MMINLINE uintptr_t383getArraySize(J9IndexableObject *objPtr)384{385return compressObjectReferences()386? ((J9IndexableObjectContiguousCompressed*)objPtr)->size387: ((J9IndexableObjectContiguousFull*)objPtr)->size;388}389390/**391* Get the layout for the given indexable object392* @param objPtr Pointer to an array object393* @return the ArrayLayout for objectPtr394*/395MMINLINE ArrayLayout396getArrayLayout(J9IndexableObject *objPtr)397{398GC_ArrayletObjectModel::ArrayLayout layout = GC_ArrayletObjectModel::InlineContiguous;399/* Trivial check for InlineContiguous. */400if (0 != getArraySize(objPtr)) {401return GC_ArrayletObjectModel::InlineContiguous;402}403404/* Check if the objPtr is in the allowed arraylet range. */405if (((uintptr_t)objPtr >= (uintptr_t)_arrayletRangeBase) && ((uintptr_t)objPtr < (uintptr_t)_arrayletRangeTop)) {406uintptr_t dataSizeInBytes = getDataSizeInBytes(objPtr);407J9Class* clazz = J9GC_J9OBJECT_CLAZZ(objPtr, this);408layout = getArrayletLayout(clazz, dataSizeInBytes);409}410return layout;411}412413MMINLINE ArrayLayout414getArrayletLayout(J9Class* clazz, uintptr_t dataSizeInBytes)415{416return getArrayletLayout(clazz, dataSizeInBytes, _largestDesirableArraySpineSize);417}418419/**420* Get the layout of an indexable object given it's class, data size in bytes and the subspace's largestDesirableSpine.421* @param clazz The class of the object stored in the array.422* @param dataSizeInBytes the size in bytes of the data of the array.423* @param largestDesirableSpine The largest desirable spine of the arraylet.424*/425ArrayLayout getArrayletLayout(J9Class* clazz, uintptr_t dataSizeInBytes, uintptr_t largestDesirableSpine);426427/**428* Perform a safe memcpy of one array to another.429* Assumes that destObject and srcObject have the same shape and size.430*/431MMINLINE void432memcpyArray(J9IndexableObject *destObject, J9IndexableObject *srcObject)433{434if (InlineContiguous == getArrayLayout(srcObject)) {435/* assume that destObject must have the same shape! */436uintptr_t sizeInBytes = getSizeInBytesWithoutHeader(srcObject);437uintptr_t* srcData = (uintptr_t*)getDataPointerForContiguous(srcObject);438uintptr_t* destData = (uintptr_t*)getDataPointerForContiguous(destObject);439copyInWords(destData, srcData, sizeInBytes);440} else {441bool const compressed = compressObjectReferences();442uintptr_t arrayletCount = numArraylets(srcObject);443fj9object_t *srcArraylets = getArrayoidPointer(srcObject);444fj9object_t *destArraylets = getArrayoidPointer(destObject);445for (uintptr_t i = 0; i < arrayletCount; i++) {446GC_SlotObject srcSlotObject(_omrVM, GC_SlotObject::addToSlotAddress(srcArraylets, i, compressed));447GC_SlotObject destSlotObject(_omrVM, GC_SlotObject::addToSlotAddress(destArraylets, i, compressed));448void* srcLeafAddress = srcSlotObject.readReferenceFromSlot();449void* destLeafAddress = destSlotObject.readReferenceFromSlot();450451452uintptr_t copySize = _omrVM->_arrayletLeafSize;453454if (i == arrayletCount - 1) {455copySize = arrayletSize(srcObject, i);456}457copyInWords((uintptr_t *)destLeafAddress, (uintptr_t *)srcLeafAddress, copySize);458}459}460}461462/**463* Perform a memcpy from a primitive array to native memory.464* Assumes that the destination is large enough.465*466* @param destData the native memory buffer to copy into467* @param srcObject the array to copy from468* @param elementIndex the start index (element-relative) to copy from469* @param elementCount the number of elements of data to copy470*/471/*MMINLINE*/ void472memcpyFromArray(void *destData, J9IndexableObject *srcObject, int32_t elementIndex, int32_t elementCount)473{474uintptr_t elementSize = J9ARRAYCLASS_GET_STRIDE(J9GC_J9OBJECT_CLAZZ(srcObject, this));475if (isInlineContiguousArraylet(srcObject)) {476// If the data is stored contiguously, then a simple copy is sufficient.477void* srcData = getDataPointerForContiguous(srcObject);478switch(elementSize)479{480case 1:481// byte/boolean482{483uint8_t* srcCursor = ((uint8_t*)srcData) + elementIndex;484uint8_t* destCursor = (uint8_t*)destData;485int32_t count = elementCount;486while(count--) {487*destCursor++ = *srcCursor++;488}489}490break;491492case 2:493// short/char494{495uint16_t* srcCursor = ((uint16_t*)srcData) + elementIndex;496uint16_t* destCursor = (uint16_t*)destData;497int32_t count = elementCount;498while(count--) {499*destCursor++ = *srcCursor++;500}501}502break;503504case 4:505// int/float506{507uint32_t* srcCursor = ((uint32_t*)srcData) + elementIndex;508uint32_t* destCursor = (uint32_t*)destData;509uint32_t count = elementCount;510while(count--) {511*destCursor++ = *srcCursor++;512}513}514break;515516case 8:517// long/double518{519U_64* srcCursor = ((U_64*)srcData) + elementIndex;520U_64* destCursor = (U_64*)destData;521uint32_t count = elementCount;522while(count--) {523*destCursor++ = *srcCursor++;524}525}526break;527528default :529// unreachable since we currently do not expect anything other than primitive arrays to be using them.530{531AssertBadElementSize();532}533break;534}535} else {536bool const compressed = compressObjectReferences();537fj9object_t *srcArraylets = getArrayoidPointer(srcObject);538void *outerDestCursor = destData;539uint32_t outerCount = elementCount;540uintptr_t arrayletLeafElements = _omrVM->_arrayletLeafSize / elementSize;541uintptr_t arrayletIndex = elementIndex / arrayletLeafElements;542uintptr_t arrayletElementOffset = elementIndex % arrayletLeafElements;543while(outerCount > 0) {544GC_SlotObject srcSlotObject(_omrVM, GC_SlotObject::addToSlotAddress(srcArraylets, arrayletIndex++, compressed));545void* srcLeafAddress = srcSlotObject.readReferenceFromSlot();546uint32_t innerCount = outerCount;547// Can we fulfill the remainder of the copy from this page?548if (innerCount + arrayletElementOffset > arrayletLeafElements) {549// Copy as much as we can550innerCount = (uint32_t)(arrayletLeafElements - arrayletElementOffset);551}552switch(elementSize)553{554case 1:555// byte/boolean556{557uint8_t* srcCursor = ((uint8_t*)srcLeafAddress) + arrayletElementOffset;558uint8_t* destCursor = (uint8_t*)outerDestCursor;559outerCount -= innerCount; // Decrement the outer count before looping560while(innerCount--) {561*destCursor++ = *srcCursor++;562}563outerDestCursor = (void*)destCursor;564arrayletElementOffset = 0; // Consumed565}566break;567568case 2:569// short/char570{571uint16_t* srcCursor = ((uint16_t*)srcLeafAddress) + arrayletElementOffset;572uint16_t* destCursor = (uint16_t*)outerDestCursor;573outerCount -= innerCount; // Decrement the outer count before looping574while(innerCount--) {575*destCursor++ = *srcCursor++;576}577outerDestCursor = (void*)destCursor;578arrayletElementOffset = 0;579}580break;581582case 4:583// int/float584{585uint32_t* srcCursor = ((uint32_t*)srcLeafAddress) + arrayletElementOffset;586uint32_t* destCursor = (uint32_t*)outerDestCursor;587outerCount -= innerCount; // Decrement the outer count before looping588while(innerCount--) {589*destCursor++ = *srcCursor++;590}591outerDestCursor = (void*)destCursor;592arrayletElementOffset = 0;593}594break;595596case 8:597// long/double598{599U_64* srcCursor = ((U_64*)srcLeafAddress) + arrayletElementOffset;600U_64* destCursor = (U_64*)outerDestCursor;601outerCount -= innerCount; // Decrement the outer count before looping602while(innerCount--) {603*destCursor++ = *srcCursor++;604}605outerDestCursor = (void*)destCursor;606arrayletElementOffset = 0;607}608break;609610default :611// unreachable since we currently do not expect anything other than primitive arrays to be using them.612{613AssertBadElementSize();614}615break;616}617}618}619}620621/**622* Perform a memcpy from native memory to a primitive array.623* Assumes that the destination is large enough.624*625* @param destObject the array to copy to626* @param elementIndex the start index (element-relative) to copy from627* @param elementCount the number of elements of data to copy628* @param srcData the native memory buffer to copy from629*/630MMINLINE void631memcpyToArray(J9IndexableObject *destObject, int32_t elementIndex, int32_t elementCount, void *srcData)632{633uintptr_t elementSize = J9ARRAYCLASS_GET_STRIDE(J9GC_J9OBJECT_CLAZZ(destObject, this));634if (isInlineContiguousArraylet(destObject)) {635// If the data is stored contiguously, then a simple copy is sufficient.636void* destData = getDataPointerForContiguous(destObject);637switch(elementSize)638{639case 1:640// byte/boolean641{642uint8_t* srcCursor = (uint8_t*)srcData;643uint8_t* destCursor = ((uint8_t*)destData) + elementIndex;644int32_t count = elementCount;645while(count--) {646*destCursor++ = *srcCursor++;647}648}649break;650651case 2:652// short/char653{654uint16_t* srcCursor = (uint16_t*)srcData;655uint16_t* destCursor = ((uint16_t*)destData) + elementIndex;656int32_t count = elementCount;657while(count--) {658*destCursor++ = *srcCursor++;659}660}661break;662663case 4:664// int/float665{666uint32_t* srcCursor = (uint32_t*)srcData;667uint32_t* destCursor = ((uint32_t*)destData) + elementIndex;668uint32_t count = elementCount;669while(count--) {670*destCursor++ = *srcCursor++;671}672}673break;674675case 8:676// long/double677{678U_64* srcCursor = (U_64*)srcData;679U_64* destCursor = ((U_64*)destData) + elementIndex;680uint32_t count = elementCount;681while(count--) {682*destCursor++ = *srcCursor++;683}684}685break;686687default :688// unreachable since we currently do not expect anything other than primitive arrays to be using them.689{690AssertBadElementSize();691}692break;693}694} else {695bool const compressed = compressObjectReferences();696fj9object_t *destArraylets = getArrayoidPointer(destObject);697void *outerSrcCursor = srcData;698uint32_t outerCount = elementCount;699uintptr_t arrayletLeafElements = _omrVM->_arrayletLeafSize / elementSize;700uintptr_t arrayletIndex = elementIndex / arrayletLeafElements;701uintptr_t arrayletElementOffset = elementIndex % arrayletLeafElements;702while(outerCount > 0) {703GC_SlotObject destSlotObject(_omrVM, GC_SlotObject::addToSlotAddress(destArraylets, arrayletIndex++, compressed));704void* destLeafAddress = destSlotObject.readReferenceFromSlot();705uint32_t innerCount = outerCount;706// Can we fulfill the remainder of the copy from this page?707if (innerCount + arrayletElementOffset > arrayletLeafElements) {708// Copy as much as we can709innerCount = (uint32_t)(arrayletLeafElements - arrayletElementOffset);710}711switch(elementSize)712{713case 1:714// byte/boolean715{716uint8_t* srcCursor = (uint8_t*)outerSrcCursor;717uint8_t* destCursor = ((uint8_t*)destLeafAddress) + arrayletElementOffset;718outerCount -= innerCount; // Decrement the outer count before looping719while(innerCount--) {720*destCursor++ = *srcCursor++;721}722outerSrcCursor = (void*)srcCursor;723arrayletElementOffset = 0; // Consumed724}725break;726727case 2:728// short/char729{730uint16_t* srcCursor = (uint16_t*)outerSrcCursor;731uint16_t* destCursor = ((uint16_t*)destLeafAddress) + arrayletElementOffset;732outerCount -= innerCount; // Decrement the outer count before looping733while(innerCount--) {734*destCursor++ = *srcCursor++;735}736outerSrcCursor = (void*)srcCursor;737arrayletElementOffset = 0;738}739break;740741case 4:742// int/float743{744uint32_t* srcCursor = (uint32_t*)outerSrcCursor;745uint32_t* destCursor = ((uint32_t*)destLeafAddress) + arrayletElementOffset;746outerCount -= innerCount; // Decrement the outer count before looping747while(innerCount--) {748*destCursor++ = *srcCursor++;749}750outerSrcCursor = (void*)srcCursor;751arrayletElementOffset = 0;752}753break;754755case 8:756// long/double757{758U_64* srcCursor = (U_64*)outerSrcCursor;759U_64* destCursor = ((U_64*)destLeafAddress) + arrayletElementOffset;760outerCount -= innerCount; // Decrement the outer count before looping761while(innerCount--) {762*destCursor++ = *srcCursor++;763}764outerSrcCursor = (void*)srcCursor;765arrayletElementOffset = 0;766}767break;768769default :770// unreachable since we currently do not expect anything other than primitive arrays to be using them.771{772AssertBadElementSize();773}774break;775}776}777}778}779780/**781* Returns the size of an indexable object, in bytes, excluding the header.782* @param arrayPtr Pointer to the indexable object whose size is required783* @return Size of object in bytes excluding the header784*/785MMINLINE uintptr_t786getSizeInBytesWithoutHeader(J9IndexableObject *arrayPtr)787{788ArrayLayout layout = getArrayLayout(arrayPtr);789return getSpineSizeWithoutHeader(J9GC_J9OBJECT_CLAZZ(arrayPtr, this), layout, getSizeInElements(arrayPtr));790}791792/**793* Returns the size of an indexable object, in bytes, including the header.794* WARNING: This implementation assumes the arraylet layout is InlineContiguous.795* @param clazz Pointer to the preserved indexable object class which may not be intact796* @param numberOfElements size from indexable object797* @return Size of object in bytes including the header798*/799MMINLINE uintptr_t800getSizeInBytesWithHeader(J9Class *clazz, uintptr_t numberOfElements)801{802ArrayLayout layout = InlineContiguous;803if(0 == numberOfElements) {804layout = Discontiguous;805}806return getSizeInBytesWithHeader(clazz, layout, numberOfElements);807}808809/**810* Returns the size of an indexable object, in bytes, including the header.811* @param clazz Pointer to the preserved indexable object class which may not be intact812* @param flags Indexable object flags813* @param numberOfElements size from indexable object814* @return Size of object in bytes including the header815*/816MMINLINE uintptr_t817getSizeInBytesWithHeader(J9Class *clazz, ArrayLayout layout, uintptr_t numberOfElements)818{819return getSpineSize(clazz, layout, numberOfElements);820}821822/**823* Returns the size of an indexable object, in bytes, including the header.824* @param arrayPtr Pointer to the indexable object whose size is required825* @return Size of object in bytes including the header826*/827MMINLINE uintptr_t828getSizeInBytesWithHeader(J9IndexableObject *arrayPtr)829{830ArrayLayout layout = getArrayLayout(arrayPtr);831return getSpineSize(J9GC_J9OBJECT_CLAZZ(arrayPtr, this), layout, getSizeInElements(arrayPtr));832}833834#if defined(J9VM_ENV_DATA64)835836/**837* Gets data pointer of a contiguous indexable object.838* Helper to get dataAddr field of contiguous indexable objects.839*840* @return Pointer which points to indexable object data841*/842MMINLINE void **843dataAddrSlotForContiguous(J9IndexableObject *arrayPtr)844{845AssertContiguousArrayletLayout(arrayPtr);846bool const compressed = compressObjectReferences();847void **dataAddrPtr = NULL;848if (compressed) {849dataAddrPtr = &((J9IndexableObjectContiguousCompressed *)arrayPtr)->dataAddr;850} else {851dataAddrPtr = &((J9IndexableObjectContiguousFull *)arrayPtr)->dataAddr;852}853return dataAddrPtr;854}855856/**857* Gets data pointer of a discontiguous indexable object.858* Helper to get dataAddr field of discontiguous indexable objects.859*860* @return Pointer which points to discontiguous indexable object data861*/862MMINLINE void **863dataAddrSlotForDiscontiguous(J9IndexableObject *arrayPtr)864{865/* If double mapping is enabled only, arraylet will have a discontiguous layout.866* If sparse-heap is enabled, arraylet will have a contiguous layout. For now867* we can't simply Assert only the discontiguous case because there could also868* exist hybrid arraylets (which will be dicontinued in the future) */869bool const compressed = compressObjectReferences();870void **dataAddrPtr = NULL;871if (compressed) {872dataAddrPtr = &((J9IndexableObjectDiscontiguousCompressed *)arrayPtr)->dataAddr;873} else {874dataAddrPtr = &((J9IndexableObjectDiscontiguousFull *)arrayPtr)->dataAddr;875}876return dataAddrPtr;877}878879/**880* Sets data pointer of a contiguous indexable object.881* Sets the data pointer of a contiguous indexable object; in this case882* dataAddr will point directly into the data right after dataAddr field883* (data resides in heap).884*885* @param arrayPtr Pointer to the indexable object whose size is required886*/887MMINLINE void888setDataAddrForContiguous(J9IndexableObject *arrayPtr)889{890void **dataAddrPtr = dataAddrSlotForContiguous(arrayPtr);891void *dataAddr = (void *)((uintptr_t)arrayPtr + contiguousHeaderSize());892*dataAddrPtr = dataAddr;893}894895/**896* Sets data pointer of a discontiguous indexable object.897* Sets the data pointer of a discontiguous indexable object; in this case898* dataAddr will point to the contiguous representation of the data899* which resides outside the heap (assuming double map/sparse-heap is enabled).900* In case double map is disabled, the dataAddr will point to the first arrayoid901* of the discontiguous indexable object, which also resides right after dataAddr902* field.903*904* @param arrayPtr Pointer to the indexable object whose size is required905* @param address Pointer which points to indexable object data906*/907MMINLINE void908setDataAddrForDiscontiguous(J9IndexableObject *arrayPtr, void *address)909{910/* If double mapping is enabled only, arraylet will have a discontiguous layout.911* If sparse-heap is enabled, arraylet will have a contiguous layout. For now912* we can't simply Assert only the discontiguous case because there could also913* exist hybrid arraylets (which will be dicontinued in the future) */914void *calculatedDataAddr = address;915void **dataAddrPtr = dataAddrSlotForDiscontiguous(arrayPtr);916917/* If calculatedDataAddr is NULL then we make dataAddr point to the first arrayoid */918/* Later on, when sparse-heap is enabled by default, we must assert dataAddr is not NULL */919if (NULL == calculatedDataAddr) {920calculatedDataAddr = (void *)((uintptr_t)arrayPtr + discontiguousHeaderSize());921}922923*dataAddrPtr = calculatedDataAddr;924}925926/**927* Asserts that an indexable object pointer is indeed an indexable object928*929* @param arrayPtr Pointer to the indexable object930*/931void AssertArrayPtrIsIndexable(J9IndexableObject *arrayPtr);932933/**934* Asserts that the dataAddr field of the indexable object is correct.935*936* @param arrayPtr Pointer to the indexable object937* @param dataAddr Pointer to indexable object data address938*/939MMINLINE bool940isCorrectDataAddrContiguous(J9IndexableObject *arrayPtr, void *dataAddr)941{942return dataAddr == (void *)((uintptr_t)arrayPtr + contiguousHeaderSize());943}944945/**946* Asserts that an indexable object pointer is indeed an indexable object947*948* @param arrayPtr Pointer to the indexable object949* @param dataAddr Pointer to indexable object data address950*/951MMINLINE bool952isCorrectDataAddrDiscontiguous(J9IndexableObject *arrayPtr, void *dataAddr)953{954return dataAddr == (void *)((uintptr_t)arrayPtr + discontiguousHeaderSize());955}956957/**958* Returns data pointer associated with a contiguous Indexable object.959* Data pointer will always be pointing at the arraylet data. In this960* case the data pointer will be pointing to address immediately after961* the header.962*963* @param arrayPtr Pointer to the indexable object whose size is required964* @return data address associated with the Indexable object965*/966MMINLINE void *967getDataAddrForContiguous(J9IndexableObject *arrayPtr)968{969void *dataAddr = *dataAddrSlotForContiguous(arrayPtr);970return dataAddr;971}972973/**974* Returns data pointer associated with a discontiguous Indexable object.975* Data pointer will always be pointing at the arraylet data. In this976* case the data pointer will be pointing to address immediately after977* the header (the arrayoid), except when double mapping or sparse-heap978* is enabled. In these cases, the data pointer will point to the979* contiguous representation of the data; hence returning that pointer.980*981* @param arrayPtr Pointer to the indexable object whose size is required982* @return data address associated with the Indexable object983*/984MMINLINE void *985getDataAddrForDiscontiguous(J9IndexableObject *arrayPtr)986{987/* If double mapping is enabled only, arraylet will have a discontiguous layout.988* If sparse-heap is enabled, arraylet will have a contiguous layout. For now we989* Assert only the discontiguous case until sparse-heap is introduced. */990AssertDiscontiguousArrayletLayout(arrayPtr);991void *dataAddr = *dataAddrSlotForDiscontiguous(arrayPtr);992return dataAddr;993}994995/**996* Returns data pointer associated with the Indexable object.997* Data pointer will always be pointing at the arraylet data. In all998* cases the data pointer will be pointing to address immediately after999* the header, except when double mapping is enabled. In this case,1000* if double mapping is enabled and arraylet was double mapped1001* successfully the data pointer will point to the contiguous1002* representation of the data; hence returning that pointer.1003*1004* @param arrayPtr Pointer to the indexable object whose size is required1005* @return data address associated with the Indexable object1006*/1007MMINLINE void *1008getDataAddrForIndexableObject(J9IndexableObject *arrayPtr)1009{1010return (InlineContiguous == getArrayLayout(arrayPtr))1011? getDataAddrForContiguous(arrayPtr)1012: getDataAddrForDiscontiguous(arrayPtr);1013}10141015/**1016* Asserts that the dataAddr field of the indexable object is correct.1017*1018* @param arrayPtr Pointer to the indexable object1019*/1020MMINLINE bool1021isCorrectDataAddr(J9IndexableObject *arrayPtr)1022{10231024return (InlineContiguous == getArrayLayout(arrayPtr))1025? isCorrectDataAddrForContiguousArraylet(arrayPtr)1026: isCorrectDataAddrForDiscontiguousArraylet(arrayPtr);1027}10281029/**1030* Asserts that the dataAddr field of the contiguous arraylet is correct.1031*1032* @param arrayPtr Pointer to the indexable object1033*/1034MMINLINE bool1035isCorrectDataAddrForContiguousArraylet(J9IndexableObject *arrayPtr)1036{1037void *dataAddr = getDataAddrForContiguous(arrayPtr);1038return isCorrectDataAddrContiguous(arrayPtr, dataAddr);1039}10401041/**1042* Asserts that the dataAddr field of the indexable object is correct.1043*1044* @param arrayPtr Pointer to the indexable object1045*/1046MMINLINE bool1047isCorrectDataAddrForDiscontiguousArraylet(J9IndexableObject *arrayPtr)1048{1049void *dataAddr = getDataAddrForDiscontiguous(arrayPtr);1050return isCorrectDataAddrDiscontiguous(arrayPtr, dataAddr);1051}1052#endif /* J9VM_ENV_DATA64 */10531054/**1055* External fixup dataAddr API to update pointer of indexable objects.1056* Used in concurrent GCs in case of mutator and GC thread races.1057* Sets the dataAddr of either a contiguous or discomtiguous indexable1058* object.1059*1060* @param forwardedHeader Forwarded header of arrayPtr1061* @param arrayPtr Pointer to the indexable object whose size is required1062*/1063MMINLINE void1064fixupDataAddr(MM_ForwardedHeader *forwardedHeader, omrobjectptr_t arrayPtr)1065{1066#if defined(J9VM_ENV_DATA64)1067J9IndexableObject *j9ArrayPtr = (J9IndexableObject *)arrayPtr;10681069if (InlineContiguous == getPreservedArrayLayout(forwardedHeader)) {1070setDataAddrForContiguous(j9ArrayPtr);1071} else {1072setDataAddrForDiscontiguous(j9ArrayPtr, NULL);1073}1074#endif /* J9VM_ENV_DATA64 */1075}1076/**1077* External fixup dataAddr API to update pointer of indexable objects.1078* Sets the dataAddr of either a contiguous or discomtiguous indexable1079* object.1080*1081* @param arrayPtr Pointer to the indexable object whose size is required1082*/1083MMINLINE void1084fixupDataAddr(omrobjectptr_t arrayPtr)1085{1086#if defined(J9VM_ENV_DATA64)1087J9IndexableObject *j9ArrayPtr = (J9IndexableObject *)arrayPtr;1088AssertArrayPtrIsIndexable(j9ArrayPtr);10891090if (InlineContiguous == getArrayLayout(j9ArrayPtr)) {1091setDataAddrForContiguous(j9ArrayPtr);1092} else {1093setDataAddrForDiscontiguous(j9ArrayPtr, NULL);1094}1095#endif /* J9VM_ENV_DATA64 */1096}10971098/**1099* Extract the class pointer from an unforwarded object.1100*1101* This method will assert if the object has been marked as forwarded.1102*1103* @param[in] pointer to forwardedHeader the MM_ForwardedHeader instance encapsulating the object1104* @return pointer to the J9Class from the object encapsulated by forwardedHeader1105* @see MM_ForwardingHeader::isForwardedObject()1106*/1107MMINLINE J9Class *1108getPreservedClass(MM_ForwardedHeader *forwardedHeader)1109{1110return (J9Class *)((uintptr_t)(forwardedHeader->getPreservedSlot()) & J9GC_J9OBJECT_CLAZZ_ADDRESS_MASK);1111}11121113/**1114* Get the preserved hashcode offset of forwarded header.1115*1116* @param[in] Pointer to forwardedHeader the MM_ForwardedHeader instance encapsulating the object1117*/1118MMINLINE uintptr_t1119getPreservedHashcodeOffset(MM_ForwardedHeader *forwardedHeader)1120{1121J9Class *clazz = getPreservedClass(forwardedHeader);1122return getHashcodeOffset(clazz, getPreservedArrayLayout(forwardedHeader), getPreservedIndexableSize(forwardedHeader));1123}11241125/**1126* Extract the size (as getSizeInElements()) from an unforwarded object1127*1128* This method will assert if the object is not indexable or has been marked as forwarded.1129*1130* @param[in] forwardedHeader pointer to the MM_ForwardedHeader instance encapsulating the object1131* @return the size (#elements) of the array encapsulated by forwardedHeader1132* @see MM_ForwardingHeader::isForwardedObject()1133*/1134MMINLINE uint32_t1135getPreservedIndexableSize(MM_ForwardedHeader *forwardedHeader)1136{1137/* Asserts if object is indeed indexable */1138ForwardedHeaderAssert(J9GC_CLASS_IS_ARRAY(getPreservedClass(forwardedHeader)));11391140/* in compressed headers, the size of the object is stored in the low-order half of the uintptr_t read when we read clazz1141* so read it from there instead of the heap (since the heap copy would have been over-written by the forwarding1142* pointer if another thread copied the object underneath us). In non-compressed, this field should still be readable1143* out of the heap.1144*/1145uint32_t size = 0;1146#if defined (OMR_GC_COMPRESSED_POINTERS)1147if (compressObjectReferences()) {1148size = forwardedHeader->getPreservedOverlap();1149} else1150#endif /* defined (OMR_GC_COMPRESSED_POINTERS) */1151{1152size = ((J9IndexableObjectContiguousFull *)forwardedHeader->getObject())->size;1153}11541155if (0 == size) {1156/* Discontiguous */1157if (compressObjectReferences()) {1158size = ((J9IndexableObjectDiscontiguousCompressed *)forwardedHeader->getObject())->size;1159} else {1160size = ((J9IndexableObjectDiscontiguousFull *)forwardedHeader->getObject())->size;1161}1162}11631164return size;1165}11661167/**1168* Extract the array layout from preserved info in Forwarded header1169* (this mimics getArrayLayout())1170*1171* @param[in] forwardedHeader pointer to the MM_ForwardedHeader instance encapsulating the object1172* @return the ArrayLayout for the forwarded object1173*/1174MMINLINE ArrayLayout1175getPreservedArrayLayout(MM_ForwardedHeader *forwardedHeader)1176{1177J9Class *clazz = getPreservedClass(forwardedHeader);1178/* Asserts if object is indeed indexable */1179ForwardedHeaderAssert(J9GC_CLASS_IS_ARRAY(clazz));11801181ArrayLayout layout = InlineContiguous;1182uint32_t size = 0;1183#if defined (OMR_GC_COMPRESSED_POINTERS)1184if (compressObjectReferences()) {1185size = forwardedHeader->getPreservedOverlap();1186} else1187#endif /* defined (OMR_GC_COMPRESSED_POINTERS) */1188{1189size = ((J9IndexableObjectContiguousFull *)forwardedHeader->getObject())->size;1190}11911192if (0 == size) {1193/* we know we are dealing with heap object, so we don't need to check against _arrayletRangeBase/Top, like getArrayLayout does */1194uintptr_t dataSizeInBytes = getDataSizeInBytes(clazz, getPreservedIndexableSize(forwardedHeader));1195layout = getArrayletLayout(clazz, dataSizeInBytes);1196}11971198return layout;1199}12001201/**1202* Calculates object size in bytes with header and object's hashcode offset1203*1204* @param[in] forwardedHeader pointer to the MM_ForwardedHeader instance encapsulating the object1205* @param[out] hashcodeOffset hashcode offset of object being moved1206*1207* @return object size in bytes with header1208*/1209MMINLINE uintptr_t1210calculateObjectSizeAndHashcode(MM_ForwardedHeader *forwardedHeader, uintptr_t *hashcodeOffset)1211{1212J9Class* clazz = getPreservedClass(forwardedHeader);1213uintptr_t numberOfElements = (uintptr_t)getPreservedIndexableSize(forwardedHeader);1214uintptr_t dataSizeInBytes = getDataSizeInBytes(clazz, numberOfElements);1215ArrayLayout layout = getArrayletLayout(clazz, dataSizeInBytes);1216*hashcodeOffset = getHashcodeOffset(clazz, layout, numberOfElements);1217return getSizeInBytesWithHeader(clazz, layout, numberOfElements);1218}12191220/**1221* Returns the header size of a given indexable object. The arraylet layout is determined base on "small" size.1222* @param arrayPtr Ptr to an array for which header size will be returned1223* @return Size of header in bytes1224*/1225MMINLINE uintptr_t1226getHeaderSize(J9IndexableObject *arrayPtr)1227{1228uintptr_t headerSize = 0;1229if (compressObjectReferences()) {1230uintptr_t size = ((J9IndexableObjectContiguousCompressed *)arrayPtr)->size;1231headerSize = sizeof(J9IndexableObjectContiguousCompressed);1232if (0 == size) {1233headerSize = sizeof(J9IndexableObjectDiscontiguousCompressed);1234}1235} else {1236uintptr_t size = ((J9IndexableObjectContiguousFull *)arrayPtr)->size;1237headerSize = sizeof(J9IndexableObjectContiguousFull);1238if (0 == size) {1239headerSize = sizeof(J9IndexableObjectDiscontiguousFull);1240}1241}1242return headerSize;1243}12441245/**1246* Returns the address of first arraylet leaf slot in the spine1247* @param arrayPtr Ptr to an array1248* @return Arrayoid ptr1249*/1250MMINLINE fj9object_t *1251getArrayoidPointer(J9IndexableObject *arrayPtr)1252{1253return (fj9object_t *)((uintptr_t)arrayPtr + (compressObjectReferences() ? sizeof(J9IndexableObjectDiscontiguousCompressed) : sizeof(J9IndexableObjectDiscontiguousFull)));1254}12551256/**1257* Returns the address of first data slot in the array1258* @param arrayPtr Ptr to an array1259* @return Address of first data slot in the array1260*/1261MMINLINE void *1262getDataPointerForContiguous(J9IndexableObject *arrayPtr)1263{1264return (void *)((uintptr_t)arrayPtr + contiguousHeaderSize());1265}126612671268/**1269* Returns the offset of the hashcode slot, in bytes, from the beginning of the header.1270* WARNING: This implementation assumes the arraylet layout is InlineContiguous.1271* @param clazzPtr Pointer to the class of the object1272* @param numberOfElements size from indexable object1273* @return offset of the hashcode slot1274*/1275MMINLINE uintptr_t1276getHashcodeOffset(J9Class *clazzPtr, uintptr_t numberOfElements)1277{1278ArrayLayout layout = InlineContiguous;1279if(0 == numberOfElements) {1280layout = Discontiguous;1281}1282return getHashcodeOffset(clazzPtr, layout, numberOfElements);1283}12841285/**1286* Returns the offset of the hashcode slot, in bytes, from the beginning of the header.1287* @param clazzPtr Pointer to the class of the object1288* @param layout The layout of the indexable object1289* @param numberOfElements size from indexable object1290* @return offset of the hashcode slot1291*/1292MMINLINE uintptr_t1293getHashcodeOffset(J9Class *clazzPtr, ArrayLayout layout, uintptr_t numberOfElements)1294{1295/* Don't call getDataSizeInBytes() since that rounds up to uintptr_t. */1296uintptr_t dataSize = numberOfElements * J9ARRAYCLASS_GET_STRIDE(clazzPtr);1297uintptr_t numberOfArraylets = numArraylets(dataSize);1298bool alignData = shouldAlignSpineDataSection(clazzPtr);1299uintptr_t spineSize = getSpineSize(clazzPtr, layout, numberOfArraylets, dataSize, alignData);1300return MM_Math::roundToSizeofU32(spineSize);1301}13021303/**1304* Returns the offset of the hashcode slot, in bytes, from the beginning of the header.1305* @param arrayPtr Pointer to the array1306* @return offset of the hashcode slot1307*/1308MMINLINE uintptr_t1309getHashcodeOffset(J9IndexableObject *arrayPtr)1310{1311ArrayLayout layout = getArrayLayout(arrayPtr);1312return getHashcodeOffset(J9GC_J9OBJECT_CLAZZ(arrayPtr, this), layout, getSizeInElements(arrayPtr));1313}13141315MMINLINE void1316expandArrayletSubSpaceRange(MM_MemorySubSpace * subSpace, void * rangeBase, void * rangeTop, uintptr_t largestDesirableArraySpineSize)1317{1318GC_ArrayletObjectModelBase::expandArrayletSubSpaceRange(subSpace, rangeBase, rangeTop, largestDesirableArraySpineSize);1319}13201321/**1322* Updates leaf pointers that point to an address located within the indexable object. For example,1323* when the array layout is either inline continuous or hybrid, there will be leaf pointers that point1324* to data that is contained within the indexable object. These internal leaf pointers are updated by1325* calculating their offset within the source arraylet, then updating the destination arraylet pointers1326* to use the same offset.1327*1328* @param destinationPtr Pointer to the new indexable object1329* @param sourcePtr Pointer to the original indexable object that was copied1330*/1331void fixupInternalLeafPointersAfterCopy(J9IndexableObject *destinationPtr, J9IndexableObject *sourcePtr);13321333/**1334* Initialize the receiver, a new instance of GC_ObjectModel1335*1336* @return true on success, false on failure1337*/1338bool initialize(MM_GCExtensionsBase *extensions);13391340/**1341* Tear down the receiver1342*/1343void tearDown(MM_GCExtensionsBase *extensions);13441345/**1346* Asserts that an Arraylet has indeed discontiguous layout1347*/1348void AssertArrayletIsDiscontiguous(J9IndexableObject *objPtr);13491350/**1351* Asserts that an Arraylet has true contiguous layout1352*/1353void AssertContiguousArrayletLayout(J9IndexableObject *objPtr);13541355/**1356* Asserts that an Arraylet has true discontiguous layout1357*/1358void AssertDiscontiguousArrayletLayout(J9IndexableObject *objPtr);1359};1360#endif /* ARRAYLETOBJECTMODEL_ */136113621363