src/Array.cpp
author David Anderson <dvander@alliedmods.net>
Sun Nov 18 16:10:43 2012 -0800 (2012-11-18)
changeset 218 ccb089827f87
parent 209 5cbec15fadfe
child 234 26e3035cdffc
permissions -rw-r--r--
Re-add postinc support.
     1 /* vim: set ts=4 sw=4 tw=99 et:
     2  *
     3  * Copyright (C) 2012 David Anderson
     4  *
     5  * This file is part of SourcePawn.
     6  *
     7  * SourcePawn is free software: you can redistribute it and/or modify it under
     8  * the terms of the GNU General Public License as published by the Free
     9  * Software Foundation, either version 3 of the License, or (at your option)
    10  * any later version.
    11  * 
    12  * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY
    13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License along with
    17  * SourcePawn. If not, see http://www.gnu.org/licenses/.
    18  */
    19 #include <string.h>
    20 #include "Array.h"
    21 #include "FixedArray.h"
    22 #include "Heap.h"
    23 #include "Zone.h"
    24 #include "Structures.h"
    25 #include "Heap-inl.h"
    26 
    27 using namespace ke;
    28 
    29 ArrayMap *
    30 ArrayMap::New(Zone *zone, Handle<ArrayType> type)
    31 {
    32     Local<Map> obj(zone, zone->allocateMap(MapKind_Array, sizeof(ArrayMap), MapKind_MetaArray));
    33     if (!obj)
    34         return NULL;
    35 
    36     Local<ArrayMap> map(zone, ArrayMap::cast(obj));
    37     map->type_ = type;
    38     return map;
    39 }
    40 
    41 ArrayMap *
    42 ArrayMap::Attach(Zone *zone, Handle<ArrayType> type)
    43 {
    44     if (type->newMap())
    45         return ArrayMap::cast(type->newMap());
    46 
    47     Local<ArrayMap> map(zone, ArrayMap::New(zone, type));
    48     if (!map)
    49         return NULL;
    50 
    51     // Ensure the inner type has an ArrayMap if needed.
    52     if (type->contained()->isArray()) {
    53         Local<ArrayType> contained(zone, ArrayType::cast(type->contained()));
    54         if (!ArrayMap::Attach(zone, contained))
    55             return NULL;
    56     }
    57 
    58     type->setNewMap(map);
    59     return map;
    60 }
    61 
    62 Array *
    63 Array::NewRaw(Zone *zone, Handle<ArrayMap> map, unsigned nelements, Heap::Tenure tenure)
    64 {
    65     if (nelements > (INT_MAX / SizeForElement(map->type()))) {
    66         zone->reportError(Message_ArraySizeTooBig);
    67         return NULL;
    68     }
    69 
    70     size_t bytes = sizeof(Array);
    71     if (map->type()->hasInlineElements())
    72         bytes += SizeForElement(map->type()) * nelements;
    73 
    74     Local<Array> array(zone, Array::cast(zone->allocate(map, bytes, tenure)));
    75     if (!array)
    76         return NULL;
    77 
    78     array->length_ = 0;
    79     array->elements_ = NULL;
    80 
    81     if (map->type()->hasInlineElements()) {
    82         // This must be a fixed array containing no traceables.
    83         assert(nelements > 0);
    84         assert(map->type()->isFixedLength());
    85         assert(!map->type()->contained()->isTraceable());
    86 
    87         // Compute an interior |elements_| pointer, such that if elements_
    88         // is accessed like a FixedArray, we reach the appropriate vector.
    89         array->elements_ = Address((Array *)array + 1) - ByteArray::offsetToElements();
    90         array->length_ = nelements;
    91         memset(array->bytes(), 0, SizeForElement(map->type()) * nelements);
    92         return array;
    93     }
    94 
    95     // If there is nothing to allocate, we don't bother creating elements yet.
    96     if (!nelements)
    97         return array;
    98 
    99     if (!map->type()->contained()->isTraceable()) {
   100         size_t nbytes = SizeForElement(map->type()) * nelements;
   101         Local<ByteArray> elements(zone, ByteArray::New(zone, nbytes, tenure));
   102         if (!elements)
   103             return NULL;
   104 
   105         array->updateLengthAndElements(zone, nelements, elements);
   106         return array;
   107     }
   108 
   109     Local<FixedArray> elements(zone, FixedArray::New(zone, nelements, tenure));
   110     if (!elements)
   111         return NULL;
   112 
   113     array->updateLengthAndElements(zone, nelements, elements);
   114 
   115     // If this is an array of structs, we're required to allocate those structs now.
   116     if (map->type()->contained()->isStruct()) {
   117         Local<StructType> childType(zone, StructType::cast(map->type()->contained()));
   118         Local<StructMap> childMap(zone, childType->newMap());
   119         Local<Struct> child(zone);
   120         for (unsigned i = 0; i < nelements; i++) {
   121             child = Struct::New(zone, childMap, tenure);
   122             if (!child)
   123                 return NULL;
   124 
   125             elements->set(zone, i, child);
   126         }
   127     }
   128 
   129     return array;
   130 }
   131 
   132 static Array *
   133 ConstructSizedArray(Zone *zone, Handle<ArrayMap> map, int dims[MAX_ARRAY_DEPTH],
   134                     unsigned level, Heap::Tenure tenure)
   135 {
   136     Local<Array> array(zone, Array::NewRaw(zone, map, dims[level], tenure));
   137     if (!array)
   138         return NULL;
   139 
   140     if (map->type()->contained()->isArray()) {
   141         Local<ArrayType> childType(zone, ArrayType::cast(map->type()->contained()));
   142         Local<ArrayMap> childMap(zone, childType->newMap());
   143         Local<Array> child(zone);
   144 
   145         for (unsigned i = 0; i < unsigned(dims[level]); i++) {
   146             child = ConstructSizedArray(zone, childMap, dims, level + 1, tenure);
   147             if (!child)
   148                 return NULL;
   149 
   150             array->writeObject(zone, i, child);
   151         }
   152     }
   153 
   154     return array;
   155 }
   156 
   157 Array *
   158 Array::NewSized(Zone *zone, Handle<ArrayMap> map, int dims[MAX_ARRAY_DEPTH], Heap::Tenure tenure)
   159 {
   160     for (size_t i = 0; i < map->type()->levels(); i++) {
   161         if (dims[i] < 0) {
   162             zone->reportError(Message_InvalidDimensionSize, dims[i], i);
   163             return NULL;
   164         }
   165     }
   166 
   167     return ConstructSizedArray(zone, map, dims, 0, tenure);
   168 }
   169 
   170 static Array *
   171 ConstructFixedArray(Zone *zone, Handle<ArrayMap> map, Heap::Tenure tenure)
   172 {
   173     unsigned length = map->type()->fixedLength();
   174     Local<Array> array(zone, Array::NewRaw(zone, map, length, tenure));
   175     if (!array)
   176         return NULL;
   177 
   178     if (map->type()->contained()->isArray()) {
   179         Local<ArrayType> childType(zone, ArrayType::cast(map->type()->contained()));
   180         Local<ArrayMap> childMap(zone, childType->newMap());
   181         Local<Array> child(zone);
   182 
   183         if (childMap->type()->isFixedLength()) {
   184             for (unsigned i = 0; i < length; i++) {
   185                 child = ConstructFixedArray(zone, childMap, tenure);
   186                 if (!child)
   187                     return NULL;
   188 
   189                 array->writeObject(zone, i, child);
   190             }
   191         } else {
   192             for (unsigned i = 0; i < length; i++) {
   193                 child = Array::NewRaw(zone, childMap, 0, tenure);
   194                 if (!child)
   195                     return NULL;
   196 
   197                 array->writeObject(zone, i, child);
   198             }
   199         }
   200     }
   201 
   202     return array;
   203 }
   204 
   205 Array *
   206 Array::NewFixed(Zone *zone, Handle<ArrayMap> map, Heap::Tenure tenure)
   207 {
   208     return ConstructFixedArray(zone, map, tenure);
   209 }
   210 
   211 Array *
   212 Array::NewString(Zone *zone, Handle<ArrayMap> map, Address buffer, size_t length, Heap::Tenure tenure)
   213 {
   214     assert(map->type()->isCharArray());
   215 
   216     Local<Array> array(zone, Array::New1D(zone, map, length, tenure));
   217     if (!array)
   218         return NULL;
   219 
   220     assert(array->length() >= length);
   221     memcpy(array->chars(), buffer, length);
   222     return array;
   223 }
   224 
   225 Array *
   226 Array::New1D(Zone *zone, Handle<ArrayMap> map, uint32 elements, Heap::Tenure tenure)
   227 {
   228     assert(map->type()->levels());
   229     return Array::NewRaw(zone, map, elements, tenure);
   230 }
   231 
   232 Array *
   233 Array::NewEmpty(Zone *zone, Handle<ArrayMap> map, Heap::Tenure tenure)
   234 {
   235     assert(!map->type()->isFixedLength());
   236 
   237     return Array::NewRaw(zone, map, 0, tenure);
   238 }
   239 
   240 Array *
   241 Array::NewExternal(Zone *zone, Handle<ArrayMap> map, void *buffer, unsigned length,
   242                    Heap::Tenure tenure)
   243 {
   244     Local<Array> array(zone, Array::cast(zone->allocate(map, sizeof(Array), tenure)));
   245     if (!array)
   246         return NULL;
   247 
   248     array->length_ = length;
   249     array->elements_ = Address(buffer) - ByteArray::offsetToElements();
   250     return array;
   251 }
   252