/src/moddable/xs/sources/xsMemory.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2016-2026 Moddable Tech, Inc. |
3 | | * |
4 | | * This file is part of the Moddable SDK Runtime. |
5 | | * |
6 | | * The Moddable SDK Runtime is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation, either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * The Moddable SDK Runtime is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public License |
17 | | * along with the Moddable SDK Runtime. If not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | * This file incorporates work covered by the following copyright and |
20 | | * permission notice: |
21 | | * |
22 | | * Copyright (C) 2010-2016 Marvell International Ltd. |
23 | | * Copyright (C) 2002-2010 Kinoma, Inc. |
24 | | * |
25 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
26 | | * you may not use this file except in compliance with the License. |
27 | | * You may obtain a copy of the License at |
28 | | * |
29 | | * http://www.apache.org/licenses/LICENSE-2.0 |
30 | | * |
31 | | * Unless required by applicable law or agreed to in writing, software |
32 | | * distributed under the License is distributed on an "AS IS" BASIS, |
33 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
34 | | * See the License for the specific language governing permissions and |
35 | | * limitations under the License. |
36 | | */ |
37 | | |
38 | | #include "xsAll.h" |
39 | | |
40 | | #ifndef mxReport |
41 | | #define mxReport 0 |
42 | | #endif |
43 | | #ifndef mxStress |
44 | | #define mxStress 0 |
45 | | #endif |
46 | | #ifndef mxNoChunks |
47 | | #define mxNoChunks 0 |
48 | | #endif |
49 | | #ifndef mxPoisonSlots |
50 | | #define mxPoisonSlots 0 |
51 | | #endif |
52 | | |
53 | | #if mxNoChunks |
54 | | #if defined(__has_feature) |
55 | | #if __has_feature(address_sanitizer) |
56 | | #define mxPoisonChunks 1 |
57 | | #endif |
58 | | #elif defined(__SANITIZE_ADDRESS__) |
59 | | #define mxPoisonChunks 1 |
60 | | #endif |
61 | | #endif |
62 | | #ifndef mxPoisonChunks |
63 | | #define mxPoisonChunks 0 |
64 | | #endif |
65 | | |
66 | | #if mxPoisonSlots || mxPoisonChunks |
67 | | #include <sanitizer/asan_interface.h> |
68 | | #endif |
69 | | |
70 | | #if mxStress |
71 | | int gxStress = 0; |
72 | | |
73 | | static int fxShouldStress() |
74 | 73.8M | { |
75 | 73.8M | if (!gxStress) |
76 | 73.8M | return 0; |
77 | | |
78 | 0 | if (gxStress > 0) |
79 | 0 | return 1; |
80 | | |
81 | 0 | gxStress += 1; |
82 | 0 | return 0 == gxStress; |
83 | 0 | } |
84 | | #endif |
85 | | |
86 | | #if mxNoChunks |
87 | | #if FUZZING |
88 | | extern void *fxMemMalloc_noforcefail(size_t size); |
89 | | #define c_malloc_noforcefail(size) fxMemMalloc_noforcefail(size) |
90 | | #else |
91 | | #define c_malloc_noforcefail c_malloc |
92 | | #endif |
93 | | #endif |
94 | | |
95 | 161M | #define mxChunkFlag 0x80000000 |
96 | | |
97 | | static txSize fxAdjustChunkSize(txMachine* the, txSize size); |
98 | | static void* fxCheckChunk(txMachine* the, txChunk* chunk, txSize size, txSize offset); |
99 | | static void* fxFindChunk(txMachine* the, txSize size, txBoolean *once); |
100 | | static void* fxGrowChunk(txMachine* the, txSize size); |
101 | | static void* fxGrowChunks(txMachine* the, txSize theSize); |
102 | | /* static */ void fxGrowSlots(txMachine* the, txSize theCount); |
103 | | static void fxMark(txMachine* the, void (*theMarker)(txMachine*, txSlot*)); |
104 | | #if mxKeysGarbageCollection |
105 | | static void fxMarkID(txMachine* the, txID id); |
106 | | #endif |
107 | | static void fxMarkFinalizationRegistry(txMachine* the, txSlot* registry); |
108 | | static void fxMarkInstance(txMachine* the, txSlot* theCurrent, void (*theMarker)(txMachine*, txSlot*)); |
109 | | static void fxMarkReference(txMachine* the, txSlot* theSlot); |
110 | | static void fxMarkValue(txMachine* the, txSlot* theSlot); |
111 | | static void fxMarkWeakStuff(txMachine* the); |
112 | | static void fxSweep(txMachine* the); |
113 | | static void fxSweepValue(txMachine* the, txSlot* theSlot); |
114 | | |
115 | | #ifdef mxNever |
116 | | |
117 | | typedef struct sxSample txSample; |
118 | | struct sxSample { |
119 | | struct timespec time; |
120 | | txNumber duration; |
121 | | long count; |
122 | | char* label; |
123 | | }; |
124 | | |
125 | | void reportTime(txSample* theSample) |
126 | | { |
127 | | fprintf(stderr, " %s %ld %f", theSample->label, theSample->count, theSample->duration); |
128 | | } |
129 | | |
130 | | void startTime(txSample* theSample) |
131 | | { |
132 | | clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &(theSample->time)); |
133 | | } |
134 | | |
135 | | void stopTime(txSample* theSample) |
136 | | { |
137 | | struct timespec time; |
138 | | clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time); |
139 | | theSample->duration += ((double)(time.tv_sec) - (double)(theSample->time.tv_sec)) |
140 | | + (((double)(time.tv_nsec) - (double)(theSample->time.tv_nsec)) / 1000000000.0); |
141 | | theSample->count++; |
142 | | } |
143 | | |
144 | | txSample gxLifeTime = { { 0, 0 }, 0, 0, "life" }; |
145 | | txSample gxMarkTime = { { 0, 0 }, 0, 0, "mark" }; |
146 | | txSample gxSweepChunkTime = { { 0, 0 }, 0, 0, "sweep chunk" }; |
147 | | txSample gxSweepSlotTime = { { 0, 0 }, 0, 0, "sweep slot" }; |
148 | | txSample gxCompactChunkTime = { { 0, 0 }, 0, 0, "compact" }; |
149 | | txSample gxChunksGarbageCollectionTime = { { 0, 0 }, 0, 0, "chunks" }; |
150 | | txSample gxKeysGarbageCollectionTime = { { 0, 0 }, 0, 0, "keys" }; |
151 | | txSample gxSlotsGarbageCollectionTime = { { 0, 0 }, 0, 0, "slots" }; |
152 | | txSample gxForcedGarbageCollectionTime = { { 0, 0 }, 0, 0, "forced" }; |
153 | | #endif |
154 | | |
155 | | txSize fxAddChunkSizes(txMachine* the, txSize a, txSize b) |
156 | 13.4M | { |
157 | 13.4M | txSize c; |
158 | 13.4M | #if __has_builtin(__builtin_add_overflow) |
159 | 13.4M | if (__builtin_add_overflow(a, b, &c)) { |
160 | | #else |
161 | | c = a + b; |
162 | | if (((a ^ c) & (b ^ c)) < 0) { |
163 | | #endif |
164 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
165 | 0 | } |
166 | 13.4M | return c; |
167 | 13.4M | } |
168 | | |
169 | | txSize fxAdjustChunkSize(txMachine* the, txSize size) |
170 | 11.4M | { |
171 | 11.4M | txSize adjust = sizeof(txChunk); |
172 | 11.4M | txSize modulo = size & (sizeof(size_t) - 1); |
173 | 11.4M | if (modulo) |
174 | 6.35M | adjust += sizeof(size_t) - modulo; |
175 | 11.4M | return fxAddChunkSizes(the, size, adjust); |
176 | 11.4M | } |
177 | | |
178 | | void fxAllocate(txMachine* the, txCreation* theCreation) |
179 | 6.51k | { |
180 | | #ifdef mxNever |
181 | | startTime(&gxLifeTime); |
182 | | #endif |
183 | 6.51k | #if mxStress |
184 | 6.51k | gxStress = 0; |
185 | 6.51k | #endif |
186 | | |
187 | 6.51k | the->currentChunksSize = 0; |
188 | 6.51k | the->peakChunksSize = 0; |
189 | 6.51k | the->maximumChunksSize = 0; |
190 | 6.51k | the->minimumChunksSize = theCreation->incrementalChunkSize; |
191 | | |
192 | 6.51k | the->currentHeapCount = 0; |
193 | 6.51k | the->peakHeapCount = 0; |
194 | 6.51k | the->maximumHeapCount = 0; |
195 | 6.51k | the->minimumHeapCount = theCreation->incrementalHeapCount; |
196 | | |
197 | 6.51k | the->firstBlock = C_NULL; |
198 | 6.51k | the->firstHeap = C_NULL; |
199 | | |
200 | | #if mxNoChunks |
201 | | the->maximumChunksSize = theCreation->initialChunkSize; |
202 | | #else |
203 | 6.51k | fxGrowChunks(the, theCreation->initialChunkSize); |
204 | 6.51k | #endif |
205 | | |
206 | 6.51k | the->stackBottom = fxAllocateSlots(the, theCreation->stackCount); |
207 | 6.51k | the->stackTop = the->stackBottom + theCreation->stackCount; |
208 | 6.51k | the->stackIntrinsics = the->stackTop; |
209 | 6.51k | the->stackPrototypes = the->stackTop - XS_INTRINSICS_COUNT; |
210 | 6.51k | the->stack = the->stackTop; |
211 | | #ifdef mxInstrument |
212 | | the->stackPeak = the->stackTop; |
213 | | #endif |
214 | | |
215 | 6.51k | fxGrowSlots(the, theCreation->initialHeapCount); |
216 | | |
217 | 6.51k | the->keyCount = (txID)theCreation->initialKeyCount; |
218 | 6.51k | the->keyDelta = (txID)theCreation->incrementalKeyCount; |
219 | 6.51k | the->keyIndex = 0; |
220 | 6.51k | the->keyArray = (txSlot **)c_malloc_uint32(theCreation->initialKeyCount * sizeof(txSlot*)); |
221 | 6.51k | if (!the->keyArray) |
222 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
223 | | |
224 | 6.51k | the->nameModulo = theCreation->nameModulo; |
225 | 6.51k | the->nameTable = (txSlot **)c_malloc_uint32(theCreation->nameModulo * sizeof(txSlot*)); |
226 | 6.51k | if (!the->nameTable) |
227 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
228 | | |
229 | 6.51k | the->symbolModulo = theCreation->symbolModulo; |
230 | 6.51k | the->symbolTable = (txSlot **)c_malloc_uint32(theCreation->symbolModulo * sizeof(txSlot*)); |
231 | 6.51k | if (!the->symbolTable) |
232 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
233 | | |
234 | 6.51k | fxAllocateStringInfoCache(the); |
235 | | |
236 | 6.51k | the->stackLimit = fxCStackLimit(); |
237 | | |
238 | 6.51k | the->cRoot = C_NULL; |
239 | 6.51k | the->parserBufferSize = theCreation->parserBufferSize; |
240 | 6.51k | the->parserTableModulo = theCreation->parserTableModulo; |
241 | | |
242 | 6.51k | #ifdef mxDebug |
243 | 6.51k | the->pathCount = 256; |
244 | 6.51k | the->pathValue = c_malloc(the->pathCount); |
245 | 6.51k | if (!the->pathValue) |
246 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
247 | 6.51k | #endif |
248 | 6.51k | } |
249 | | |
250 | | void* fxCheckChunk(txMachine* the, txChunk* chunk, txSize size, txSize offset) |
251 | 11.3M | { |
252 | 11.3M | if (chunk) { |
253 | 11.3M | txByte* data = (txByte*)chunk; |
254 | | #if mxNoChunks |
255 | | chunk->size = size; |
256 | | the->currentChunksSize += size; |
257 | | #else |
258 | 11.3M | txSize capacity = (txSize)(chunk->temporary - data); |
259 | 11.3M | #ifdef mxSnapshot |
260 | 11.3M | #if INTPTR_MAX == INT64_MAX |
261 | 11.3M | chunk->dummy = 0; |
262 | 11.3M | #endif |
263 | | #ifdef mxSnapshotRandomInit |
264 | | arc4random_buf(data + sizeof(txChunk), offset); |
265 | | #endif |
266 | 11.3M | #endif |
267 | 11.3M | offset += sizeof(txChunk); |
268 | 11.3M | c_memset(data + offset, 0, capacity - offset); |
269 | 11.3M | chunk->size = size; |
270 | 11.3M | the->currentChunksSize += capacity; |
271 | 11.3M | #endif |
272 | 11.3M | if (the->peakChunksSize < the->currentChunksSize) |
273 | 8.13M | the->peakChunksSize = the->currentChunksSize; |
274 | 11.3M | return data + sizeof(txChunk); |
275 | 11.3M | } |
276 | 0 | fxReport(the, "# Chunk allocation: failed for %ld bytes\n", (long)size); |
277 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
278 | 0 | return C_NULL; |
279 | 11.3M | } |
280 | | |
281 | | #if defined(__clang__) || defined (__GNUC__) |
282 | | __attribute__((no_sanitize_address)) |
283 | | #endif |
284 | | void fxCheckCStack(txMachine* the) |
285 | 192M | { |
286 | 192M | char x; |
287 | 192M | char *stack = &x; |
288 | 192M | if (stack <= the->stackLimit) { |
289 | 0 | fxAbort(the, XS_NATIVE_STACK_OVERFLOW_EXIT); |
290 | 0 | } |
291 | 192M | } |
292 | | |
293 | | void fxCollect(txMachine* the, txFlag theFlag) |
294 | 12.4k | { |
295 | 12.4k | txSize aCount; |
296 | 12.4k | txSlot* freeSlot; |
297 | 12.4k | txSlot* aSlot; |
298 | 12.4k | txSlot* bSlot; |
299 | 12.4k | txSlot* cSlot; |
300 | | |
301 | 12.4k | if ((the->collectFlag & XS_COLLECTING_FLAG) == 0) { |
302 | 0 | the->collectFlag |= XS_SKIPPED_COLLECT_FLAG; |
303 | 0 | return; |
304 | 0 | } |
305 | 12.4k | the->collectFlag |= theFlag & (XS_COLLECT_KEYS_FLAG | XS_ORGANIC_FLAG); |
306 | | |
307 | | #ifdef mxNever |
308 | | if (theFlag & XS_ORGANIC_FLAG) { |
309 | | if (theFlag & XS_COMPACT_FLAG) |
310 | | startTime(&gxChunksGarbageCollectionTime); |
311 | | else if (theFlag & XS_COLLECT_KEYS_FLAG) |
312 | | startTime(&gxKeysGarbageCollectionTime); |
313 | | else |
314 | | startTime(&gxSlotsGarbageCollectionTime); |
315 | | } |
316 | | else |
317 | | startTime(&gxForcedGarbageCollectionTime); |
318 | | startTime(&gxMarkTime); |
319 | | #endif |
320 | 12.4k | if (theFlag & XS_COMPACT_FLAG) { |
321 | 4.31k | fxInvalidateStringInfoCache(the); |
322 | 4.31k | fxMark(the, fxMarkValue); |
323 | 4.31k | fxMarkWeakStuff(the); |
324 | | #ifdef mxNever |
325 | | stopTime(&gxMarkTime); |
326 | | #endif |
327 | 4.31k | fxSweep(the); |
328 | 4.31k | } |
329 | 8.16k | else { |
330 | 8.16k | fxMark(the, fxMarkReference); |
331 | 8.16k | fxMarkWeakStuff(the); |
332 | | #ifdef mxNever |
333 | | stopTime(&gxMarkTime); |
334 | | startTime(&gxSweepSlotTime); |
335 | | #endif |
336 | 8.16k | aCount = 0; |
337 | 8.16k | freeSlot = C_NULL; |
338 | 8.16k | aSlot = the->firstHeap; |
339 | 27.9k | while (aSlot) { |
340 | 19.7k | bSlot = aSlot + 1; |
341 | 19.7k | cSlot = aSlot->value.reference; |
342 | 647M | while (bSlot < cSlot) { |
343 | 647M | if (bSlot->flag & XS_MARK_FLAG) { |
344 | 456M | bSlot->flag &= ~XS_MARK_FLAG; |
345 | | |
346 | 456M | if (bSlot->kind == XS_REFERENCE_KIND) { |
347 | 115M | mxCheck(the, bSlot->value.reference->kind == XS_INSTANCE_KIND); |
348 | 115M | } |
349 | | |
350 | 456M | aCount++; |
351 | 456M | } |
352 | 191M | else { |
353 | 191M | if (bSlot->kind == XS_HOST_KIND) { |
354 | 449 | if (bSlot->flag & XS_HOST_HOOKS_FLAG) { |
355 | 0 | if (bSlot->value.host.variant.hooks->destructor) |
356 | 0 | (*(bSlot->value.host.variant.hooks->destructor))(bSlot->value.host.data); |
357 | 0 | } |
358 | 449 | else if (bSlot->value.host.variant.destructor) |
359 | 0 | (*(bSlot->value.host.variant.destructor))(bSlot->value.host.data); |
360 | 449 | } |
361 | | #if mxInstrument |
362 | | if ((bSlot->kind == XS_MODULE_KIND) && (bSlot->ID == XS_MODULE_BEHAVIOR)) |
363 | | the->loadedModulesCount--; |
364 | | #endif |
365 | 191M | bSlot->kind = XS_UNDEFINED_KIND; |
366 | 191M | bSlot->next = freeSlot; |
367 | | #if mxPoisonSlots |
368 | | ASAN_POISON_MEMORY_REGION(&bSlot->value, sizeof(bSlot->value)); |
369 | | #endif |
370 | 191M | freeSlot = bSlot; |
371 | 191M | } |
372 | 647M | bSlot++; |
373 | 647M | } |
374 | 19.7k | aSlot = aSlot->next; |
375 | 19.7k | } |
376 | 8.16k | the->currentHeapCount = aCount; |
377 | 8.16k | the->freeHeap = freeSlot; |
378 | | #ifdef mxNever |
379 | | stopTime(&gxSweepSlotTime); |
380 | | #endif |
381 | 8.16k | } |
382 | | |
383 | 12.4k | aSlot = the->stack; |
384 | 28.8M | while (aSlot < the->stackTop) { |
385 | 28.8M | aSlot->flag &= ~XS_MARK_FLAG; |
386 | 28.8M | aSlot++; |
387 | 28.8M | } |
388 | | |
389 | 12.4k | the->collectFlag &= ~(XS_COLLECT_KEYS_FLAG | XS_ORGANIC_FLAG); |
390 | | |
391 | 12.4k | if (theFlag & XS_COMPACT_FLAG) { |
392 | 4.31k | if ((the->maximumChunksSize - the->currentChunksSize) < the->minimumChunksSize) |
393 | 1.18k | the->collectFlag |= XS_TRASHING_CHUNKS_FLAG; |
394 | 3.12k | else |
395 | 3.12k | the->collectFlag &= ~XS_TRASHING_CHUNKS_FLAG; |
396 | 4.31k | } |
397 | | |
398 | 12.4k | if ((the->maximumHeapCount - the->currentHeapCount) < the->minimumHeapCount) |
399 | 11.4k | the->collectFlag |= XS_TRASHING_SLOTS_FLAG; |
400 | 990 | else |
401 | 990 | the->collectFlag &= ~XS_TRASHING_SLOTS_FLAG; |
402 | | |
403 | | #ifdef mxNever |
404 | | if (theFlag & XS_ORGANIC_FLAG) { |
405 | | if (theFlag & XS_COMPACT_FLAG) |
406 | | stopTime(&gxChunksGarbageCollectionTime); |
407 | | else if (theFlag & XS_COLLECT_KEYS_FLAG) |
408 | | stopTime(&gxKeysGarbageCollectionTime); |
409 | | else |
410 | | stopTime(&gxSlotsGarbageCollectionTime); |
411 | | } |
412 | | else |
413 | | stopTime(&gxForcedGarbageCollectionTime); |
414 | | #endif |
415 | | |
416 | | #if mxReport |
417 | | if (theFlag) |
418 | | fxReport(the, "# Chunk collection: reserved %ld used %ld peak %ld bytes\n", |
419 | | (long)the->maximumChunksSize, (long)the->currentChunksSize, (long)the->peakChunksSize); |
420 | | aCount = 0; |
421 | | aSlot = the->firstHeap; |
422 | | while (aSlot) { |
423 | | aCount++; |
424 | | aSlot = aSlot->next; |
425 | | } |
426 | | fxReport(the, "# Slot collection: reserved %ld used %ld peak %ld bytes %d\n", |
427 | | (long)((the->maximumHeapCount - aCount) * sizeof(txSlot)), |
428 | | (long)(the->currentHeapCount * sizeof(txSlot)), |
429 | | (long)(the->peakHeapCount * sizeof(txSlot)), |
430 | | the->collectFlag & XS_TRASHING_SLOTS_FLAG); |
431 | | #endif |
432 | | #ifdef mxInstrument |
433 | | the->garbageCollectionCount++; |
434 | | #endif |
435 | 12.4k | #if defined(mxInstrument) || defined(mxProfile) |
436 | 12.4k | fxCheckProfiler(the, C_NULL); |
437 | 12.4k | #endif |
438 | 12.4k | } |
439 | | |
440 | | txSlot* fxDuplicateSlot(txMachine* the, txSlot* theSlot) |
441 | 0 | { |
442 | 0 | txSlot* result; |
443 | | |
444 | 0 | result = fxNewSlot(the); |
445 | 0 | result->ID = theSlot->ID; |
446 | 0 | result->kind = theSlot->kind; |
447 | 0 | result->flag = theSlot->flag & ~XS_MARK_FLAG; |
448 | 0 | result->value = theSlot->value; |
449 | 0 | return result; |
450 | 0 | } |
451 | | |
452 | | void* fxFindChunk(txMachine* the, txSize size, txBoolean *once) |
453 | 11.3M | { |
454 | 11.3M | txBlock* block; |
455 | 11.3M | txChunk* chunk; |
456 | 11.3M | #if mxStress |
457 | 11.3M | if (fxShouldStress()) { |
458 | 0 | if (*once) { |
459 | 0 | fxCollect(the, XS_COMPACT_FLAG | XS_ORGANIC_FLAG); |
460 | 0 | *once = 0; |
461 | 0 | } |
462 | 0 | } |
463 | 11.3M | #endif |
464 | | #if mxNoChunks |
465 | | if ((the->currentChunksSize + size > the->maximumChunksSize)) { |
466 | | fxCollect(the, XS_COMPACT_FLAG | XS_ORGANIC_FLAG); |
467 | | if (the->collectFlag & XS_TRASHING_CHUNKS_FLAG) |
468 | | the->maximumChunksSize += the->minimumChunksSize; |
469 | | } |
470 | | chunk = c_malloc_noforcefail(size); |
471 | | if (!chunk) |
472 | | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
473 | | chunk->size = size; |
474 | | chunk->temporary = (txByte*)the->firstBlock; |
475 | | the->firstBlock = (txBlock*)chunk; |
476 | | return chunk; |
477 | | #endif |
478 | 11.3M | again: |
479 | 11.3M | block = the->firstBlock; |
480 | 13.1M | while (block) { |
481 | 13.1M | if ((block->current + size) <= block->limit) { |
482 | 11.3M | chunk = (txChunk*)(block->current); |
483 | 11.3M | block->current += size; |
484 | 11.3M | chunk->temporary = block->current; |
485 | 11.3M | return chunk; |
486 | 11.3M | } |
487 | 1.71M | block = block->nextBlock; |
488 | 1.71M | } |
489 | 4.59k | if (*once) { |
490 | 4.31k | txBoolean wasThrashing = ((the->collectFlag & XS_TRASHING_CHUNKS_FLAG) != 0), isThrashing; |
491 | 4.31k | fxCollect(the, XS_COMPACT_FLAG | XS_ORGANIC_FLAG); |
492 | 4.31k | isThrashing = ((the->collectFlag & XS_TRASHING_CHUNKS_FLAG) != 0); |
493 | 4.31k | *once = 0; |
494 | 4.31k | if (wasThrashing && isThrashing) |
495 | 327 | return C_NULL; |
496 | 3.98k | goto again; |
497 | 4.31k | } |
498 | 287 | return C_NULL; |
499 | 4.59k | } |
500 | | |
501 | | txSlot* fxFindKey(txMachine* the) |
502 | 3.70M | { |
503 | 3.70M | #if mxKeysGarbageCollection |
504 | 3.70M | txBoolean once = 1; |
505 | 3.70M | #endif |
506 | 3.70M | txID id; |
507 | 3.70M | txSlot* result; |
508 | 3.70M | more: |
509 | 3.70M | id = the->keyIndex; |
510 | 3.70M | if (id < the->keyCount) { |
511 | 3.68M | result = fxNewSlot(the); |
512 | 3.68M | result->ID = id; |
513 | 3.68M | the->keyArray[id - the->keyOffset] = result; |
514 | 3.68M | the->keyIndex++; |
515 | 3.68M | return result; |
516 | 3.68M | } |
517 | 25.2k | #if mxKeysGarbageCollection |
518 | 32.0k | again: |
519 | 32.0k | result = the->keyholeList; |
520 | 32.0k | if (result) { |
521 | 25.0k | the->keyholeCount--; |
522 | 25.0k | the->keyholeList = result->next; |
523 | 25.0k | result->next = C_NULL; |
524 | 25.0k | return result; |
525 | 25.0k | } |
526 | 6.98k | if (once) { |
527 | 6.79k | fxCollect(the, XS_COLLECT_KEYS_FLAG | XS_ORGANIC_FLAG); |
528 | 6.79k | once = 0; |
529 | 6.79k | goto again; |
530 | 6.79k | } |
531 | 195 | #endif |
532 | 195 | else { |
533 | 195 | fxGrowKeys(the, 1); |
534 | 195 | goto more; |
535 | 195 | } |
536 | 0 | return C_NULL; |
537 | 6.98k | } |
538 | | |
539 | | void fxFree(txMachine* the) |
540 | 6.51k | { |
541 | 6.51k | txSlot* aHeap; |
542 | | |
543 | | #if mxAliasInstance |
544 | | if (the->aliasArray) |
545 | | c_free_uint32(the->aliasArray); |
546 | | the->aliasArray = C_NULL; |
547 | | #endif |
548 | | |
549 | 6.51k | fxFreeStringInfoCache(the); |
550 | | |
551 | 6.51k | if (the->symbolTable) |
552 | 6.51k | c_free_uint32(the->symbolTable); |
553 | 6.51k | the->symbolTable = C_NULL; |
554 | 6.51k | if (the->nameTable) |
555 | 6.51k | c_free_uint32(the->nameTable); |
556 | 6.51k | the->nameTable = C_NULL; |
557 | 6.51k | if (the->keyArray) |
558 | 6.51k | c_free_uint32(the->keyArray); |
559 | 6.51k | the->keyArray = C_NULL; |
560 | | |
561 | 13.8k | while (the->firstHeap) { |
562 | 7.29k | aHeap = the->firstHeap; |
563 | 7.29k | the->firstHeap = aHeap->next; |
564 | 7.29k | fxFreeSlots(the, aHeap); |
565 | 7.29k | } |
566 | 6.51k | the->firstHeap = C_NULL; |
567 | | |
568 | 6.51k | if (the->stackBottom) |
569 | 6.51k | fxFreeSlots(the, the->stackBottom); |
570 | 6.51k | the->stackBottom = C_NULL; |
571 | 6.51k | the->stackTop = C_NULL; |
572 | 6.51k | the->stackIntrinsics = C_NULL; |
573 | 6.51k | the->stackPrototypes = C_NULL; |
574 | 6.51k | the->stack = C_NULL; |
575 | | |
576 | | #if mxNoChunks |
577 | | { |
578 | | txChunk** address; |
579 | | txChunk* chunk; |
580 | | address = (txChunk**)&(the->firstBlock); |
581 | | while ((chunk = *address)) { |
582 | | *address = (txChunk*)(chunk->temporary); |
583 | | c_free(chunk); |
584 | | } |
585 | | } |
586 | | #else |
587 | 6.51k | { |
588 | 6.51k | txBlock* aBlock; |
589 | 13.6k | while (the->firstBlock) { |
590 | 7.13k | aBlock = the->firstBlock; |
591 | 7.13k | the->firstBlock = aBlock->nextBlock; |
592 | 7.13k | fxFreeChunks(the, aBlock); |
593 | 7.13k | } |
594 | 6.51k | the->firstBlock = C_NULL; |
595 | 6.51k | } |
596 | 6.51k | #endif |
597 | | |
598 | 6.51k | #ifdef mxDebug |
599 | 6.51k | if (the->pathValue) |
600 | 6.51k | c_free(the->pathValue); |
601 | 6.51k | the->pathValue = C_NULL; |
602 | 6.51k | #endif |
603 | | |
604 | | #ifdef mxNever |
605 | | stopTime(&gxLifeTime); |
606 | | // fprintf(stderr, "###"); |
607 | | // reportTime(&gxLifeTime); |
608 | | // reportTime(&gxForcedGarbageCollectionTime); |
609 | | // reportTime(&gxChunksGarbageCollectionTime); |
610 | | // reportTime(&gxKeysGarbageCollectionTime); |
611 | | // reportTime(&gxSlotsGarbageCollectionTime); |
612 | | // reportTime(&gxMarkTime); |
613 | | // reportTime(&gxSweepChunkTime); |
614 | | // reportTime(&gxSweepSlotTime); |
615 | | // reportTime(&gxCompactChunkTime); |
616 | | // fprintf(stderr, "\n"); |
617 | | #endif |
618 | 6.51k | } |
619 | | |
620 | | void* fxGrowChunk(txMachine* the, txSize size) |
621 | 614 | { |
622 | 614 | txBlock* block = fxGrowChunks(the, size); |
623 | 614 | txChunk* chunk = C_NULL; |
624 | 614 | if (block) { |
625 | 614 | chunk = (txChunk*)(block->current); |
626 | 614 | block->current += size; |
627 | 614 | chunk->temporary = block->current; |
628 | 614 | } |
629 | 614 | return chunk; |
630 | 614 | } |
631 | | |
632 | | void* fxGrowChunks(txMachine* the, txSize size) |
633 | 7.13k | { |
634 | 7.13k | txByte* buffer; |
635 | 7.13k | txBlock* block = C_NULL; |
636 | | |
637 | 7.13k | if (!the->minimumChunksSize && the->firstBlock) { |
638 | 0 | fxReport(the, "# Chunk allocation: %d bytes failed in fixed size heap\n", size); |
639 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
640 | 0 | } |
641 | | |
642 | 7.13k | if ((the->firstBlock != C_NULL) && (!(the->collectFlag & XS_SKIPPED_COLLECT_FLAG))) { |
643 | 614 | txSize modulo = size % (the->minimumChunksSize ? the->minimumChunksSize : 16); |
644 | 614 | if (modulo) |
645 | 611 | size = fxAddChunkSizes(the, size, the->minimumChunksSize - modulo); |
646 | 614 | } |
647 | 7.13k | size = fxAddChunkSizes(the, size, sizeof(txBlock)); |
648 | 7.13k | buffer = fxAllocateChunks(the, size); |
649 | 7.13k | if (buffer) { |
650 | 7.13k | #ifdef mxSnapshot |
651 | 7.13k | c_memset(buffer, 0, size); |
652 | 7.13k | #endif |
653 | 7.13k | if ((the->firstBlock != C_NULL) && (the->firstBlock->limit == buffer)) { |
654 | 0 | the->firstBlock->limit += size; |
655 | 0 | block = the->firstBlock; |
656 | 0 | } |
657 | 7.13k | else { |
658 | 7.13k | block = (txBlock*)buffer; |
659 | 7.13k | block->nextBlock = the->firstBlock; |
660 | 7.13k | block->current = buffer + sizeof(txBlock); |
661 | 7.13k | block->limit = buffer + size; |
662 | 7.13k | block->temporary = C_NULL; |
663 | 7.13k | the->firstBlock = block; |
664 | 7.13k | size -= sizeof(txBlock); |
665 | 7.13k | } |
666 | 7.13k | the->maximumChunksSize = fxAddChunkSizes(the, the->maximumChunksSize, size); |
667 | | #if mxReport |
668 | | fxReport(the, "# Chunk allocation: reserved %ld used %ld peak %ld bytes\n", |
669 | | (long)the->maximumChunksSize, (long)the->currentChunksSize, (long)the->peakChunksSize); |
670 | | #endif |
671 | 7.13k | } |
672 | 7.13k | the->collectFlag &= ~XS_TRASHING_CHUNKS_FLAG; |
673 | 7.13k | return block; |
674 | 7.13k | } |
675 | | |
676 | | void fxGrowKeys(txMachine* the, txID theCount) |
677 | 195 | { |
678 | 195 | if (the->keyDelta > 0) { |
679 | 195 | txID keyDelta = (theCount > the->keyDelta) ? theCount : the->keyDelta; |
680 | 195 | txID keyCount = (the->keyCount + keyDelta) - the->keyOffset; |
681 | 195 | txSlot** keyArray = c_realloc(the->keyArray, keyCount * sizeof(txSlot*)); |
682 | 195 | if (keyArray == C_NULL) |
683 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
684 | 195 | the->keyArray = keyArray; |
685 | 195 | the->keyCount = keyCount + the->keyOffset; |
686 | 195 | } |
687 | 0 | else |
688 | 0 | fxAbort(the, XS_NO_MORE_KEYS_EXIT); |
689 | 195 | } |
690 | | |
691 | | void fxGrowSlots(txMachine* the, txSize theCount) |
692 | 7.29k | { |
693 | 7.29k | txSlot* aHeap; |
694 | 7.29k | txSlot* aSlot; |
695 | | |
696 | 7.29k | aHeap = fxAllocateSlots(the, theCount); |
697 | 7.29k | if (!aHeap) { |
698 | 0 | fxReport(the, "# Slot allocation: failed for %ld bytes\n", theCount * sizeof(txSlot)); |
699 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
700 | 0 | } |
701 | 7.29k | if ((void *)-1 == aHeap) |
702 | 0 | return; |
703 | | |
704 | 7.29k | if (the->firstHeap && the->growHeapDirection) { |
705 | 0 | if (the->growHeapDirection > 0) { |
706 | 0 | the->firstHeap->value.reference = aHeap + theCount; |
707 | 0 | the->maximumHeapCount += theCount; |
708 | 0 | theCount -= 1; |
709 | 0 | aSlot = aHeap; |
710 | 0 | } |
711 | 0 | else { |
712 | 0 | *aHeap = *(the->firstHeap); |
713 | 0 | the->maximumHeapCount += theCount; |
714 | 0 | theCount -= 1; |
715 | 0 | the->firstHeap = aHeap; |
716 | 0 | aSlot = aHeap + 1; |
717 | 0 | } |
718 | 0 | } |
719 | 7.29k | else { |
720 | 7.29k | the->maximumHeapCount += theCount - 1; |
721 | 7.29k | aHeap->next = the->firstHeap; |
722 | 7.29k | aHeap->ID = 0; |
723 | 7.29k | aHeap->flag = 0; |
724 | 7.29k | aHeap->kind = 0; |
725 | 7.29k | aHeap->value.reference = aHeap + theCount; |
726 | 7.29k | theCount -= 2; |
727 | 7.29k | the->firstHeap = aHeap; |
728 | 7.29k | aSlot = aHeap + 1; |
729 | 7.29k | } |
730 | 238M | while (theCount--) { |
731 | 238M | txSlot* next = aSlot + 1; |
732 | 238M | aSlot->next = next; |
733 | 238M | aSlot->flag = XS_NO_FLAG; |
734 | 238M | aSlot->kind = XS_UNDEFINED_KIND; |
735 | | #if mxPoisonSlots |
736 | | ASAN_POISON_MEMORY_REGION(&aSlot->value, sizeof(aSlot->value)); |
737 | | #endif |
738 | 238M | aSlot = next; |
739 | 238M | } |
740 | 7.29k | aSlot->next = the->freeHeap; |
741 | 7.29k | aSlot->flag = XS_NO_FLAG; |
742 | 7.29k | aSlot->kind = XS_UNDEFINED_KIND; |
743 | | #if mxPoisonSlots |
744 | | ASAN_POISON_MEMORY_REGION(&aSlot->value, sizeof(aSlot->value)); |
745 | | #endif |
746 | 7.29k | the->freeHeap = aHeap + 1; |
747 | 7.29k | the->collectFlag &= ~XS_TRASHING_SLOTS_FLAG; |
748 | | #if mxReport |
749 | | fxReport(the, "# Slot allocation: reserved %ld used %ld peak %ld bytes\n", |
750 | | (long)(the->maximumHeapCount * sizeof(txSlot)), |
751 | | (long)(the->currentHeapCount * sizeof(txSlot)), |
752 | | (long)(the->peakHeapCount * sizeof(txSlot))); |
753 | | #endif |
754 | 7.29k | } |
755 | | |
756 | | void fxMark(txMachine* the, void (*theMarker)(txMachine*, txSlot*)) |
757 | 12.4k | { |
758 | 12.4k | txSlot** p; |
759 | 12.4k | txSlot** q; |
760 | 12.4k | txSlot* slot; |
761 | | |
762 | | #if mxAliasInstance |
763 | | p = the->aliasArray; |
764 | | q = p + the->aliasCount; |
765 | | while (p < q) { |
766 | | if ((slot = *p)) { |
767 | | (*theMarker)(the, slot); |
768 | | slot->flag |= XS_MARK_FLAG; |
769 | | } |
770 | | p++; |
771 | | } |
772 | | #endif |
773 | | |
774 | 12.4k | slot = the->stackTop; |
775 | 28.8M | while (slot > the->stack) { |
776 | 28.8M | slot--; |
777 | 28.8M | (*theMarker)(the, slot); |
778 | 28.8M | } |
779 | 12.4k | slot = the->cRoot; |
780 | 12.4k | while (slot) { |
781 | 0 | (*theMarker)(the, slot); |
782 | 0 | slot = slot->next; |
783 | 0 | } |
784 | | |
785 | 12.4k | #if mxKeysGarbageCollection |
786 | 12.4k | if (the->collectFlag & XS_COLLECT_KEYS_FLAG) { |
787 | 6.79k | txInteger deletions = 0; |
788 | 6.79k | p = the->keyArray; |
789 | 6.79k | q = p + the->keyIndex - the->keyOffset; |
790 | 6.96M | while (p < q) { |
791 | 6.95M | slot = *p++; |
792 | 6.95M | if (!(slot->flag & XS_MARK_FLAG)) { |
793 | 514k | if (slot->flag & XS_DONT_DELETE_FLAG) |
794 | 489k | slot->flag |= XS_MARK_FLAG; |
795 | 25.5k | else if (slot->flag & XS_DONT_ENUM_FLAG) |
796 | 25.5k | deletions++; |
797 | 514k | } |
798 | 6.95M | } |
799 | | |
800 | | // fprintf(stderr, "\n### KEYS GC %d", deletions); |
801 | 6.79k | p = the->nameTable; |
802 | 6.79k | q = the->nameTable + the->nameModulo; |
803 | 9.65M | while ((p < q) && deletions) { |
804 | 9.64M | txSlot** address = p; |
805 | 14.4M | while (((slot = *address)) && deletions) { |
806 | 4.84M | if (slot->flag & XS_MARK_FLAG) |
807 | 4.82M | address = &(slot->next); |
808 | 25.5k | else { |
809 | 25.5k | *address = slot->next; |
810 | 25.5k | deletions--; |
811 | 25.5k | } |
812 | 4.84M | } |
813 | 9.64M | p++; |
814 | 9.64M | } |
815 | | // fprintf(stderr, " => %d", deletions); |
816 | | |
817 | 6.79k | the->keyholeCount = 0; |
818 | 6.79k | the->keyholeList = C_NULL; |
819 | 6.79k | p = the->keyArray; |
820 | 6.79k | q = p + the->keyIndex - the->keyOffset; |
821 | 6.96M | while (p < q) { |
822 | 6.95M | slot = *p; |
823 | 6.95M | if (slot->flag & XS_MARK_FLAG) |
824 | 6.93M | (*theMarker)(the, slot); |
825 | 25.5k | else { |
826 | | // if (slot->kind != XS_UNDEFINED_KIND) { |
827 | | // fxIDToString(the, slot->ID, the->nameBuffer, sizeof(the->nameBuffer)); |
828 | | // fprintf(stderr, "\n%p %d %s", slot, slot->ID, the->nameBuffer); |
829 | | // } |
830 | 25.5k | slot->flag = XS_INTERNAL_FLAG | XS_MARK_FLAG; |
831 | 25.5k | slot->next = the->keyholeList; |
832 | 25.5k | slot->kind = XS_UNDEFINED_KIND; |
833 | 25.5k | the->keyholeCount++; |
834 | 25.5k | the->keyholeList = slot; |
835 | 25.5k | } |
836 | 6.95M | p++; |
837 | 6.95M | } |
838 | | // fprintf(stderr, "\n"); |
839 | 6.79k | } |
840 | 5.68k | else |
841 | 5.68k | #endif |
842 | 5.68k | { |
843 | 5.68k | p = the->keyArray; |
844 | 5.68k | q = p + the->keyIndex - the->keyOffset; |
845 | 3.28M | while (p < q) { |
846 | 3.27M | slot = *p++; |
847 | 3.27M | slot->flag |= XS_MARK_FLAG; |
848 | 3.27M | (*theMarker)(the, slot); |
849 | 3.27M | } |
850 | 5.68k | } |
851 | 12.4k | } |
852 | | |
853 | | #if mxKeysGarbageCollection |
854 | | void fxMarkID(txMachine* the, txID id) |
855 | 49.3M | { |
856 | 49.3M | txSlot* slot; |
857 | 49.3M | if (id == XS_NO_ID) |
858 | 25.7M | return; |
859 | 23.5M | if (id < the->keyOffset) |
860 | 0 | return; |
861 | 23.5M | id -= the->keyOffset; |
862 | 23.5M | slot = the->keyArray[id]; |
863 | 23.5M | slot->flag |= XS_MARK_FLAG; |
864 | 23.5M | } |
865 | | #endif |
866 | | |
867 | | void fxMarkFinalizationRegistry(txMachine* the, txSlot* registry) |
868 | 0 | { |
869 | 0 | txSlot* slot = registry->value.finalizationRegistry.callback->next; |
870 | 0 | txSlot* instance; |
871 | 0 | while (slot) { |
872 | 0 | slot = slot->next; |
873 | 0 | if (slot) { |
874 | 0 | instance = slot->value.finalizationCell.target; |
875 | 0 | if (instance && !(instance->flag & XS_MARK_FLAG)) { |
876 | 0 | slot->value.finalizationCell.target = C_NULL; |
877 | 0 | registry->value.finalizationRegistry.flags |= XS_FINALIZATION_REGISTRY_CHANGED; |
878 | 0 | } |
879 | 0 | instance = slot->value.finalizationCell.token; |
880 | 0 | if (instance && !(instance->flag & XS_MARK_FLAG)) |
881 | 0 | slot->value.finalizationCell.token = C_NULL; |
882 | 0 | slot = slot->next; |
883 | 0 | } |
884 | 0 | } |
885 | 0 | } |
886 | | |
887 | | void fxMarkInstance(txMachine* the, txSlot* theCurrent, void (*theMarker)(txMachine*, txSlot*)) |
888 | 77.6M | { |
889 | 77.6M | txSlot* aProperty; |
890 | 77.6M | txSlot* aTemporary; |
891 | | |
892 | 77.6M | mxCheck(the, theCurrent->kind == XS_INSTANCE_KIND); |
893 | 77.6M | aProperty = theCurrent; |
894 | 77.6M | theCurrent->value.instance.garbage = C_NULL; |
895 | 764M | for (;;) { |
896 | 764M | if (aProperty) { |
897 | 568M | if (!(aProperty->flag & XS_MARK_FLAG)) { |
898 | 568M | aProperty->flag |= XS_MARK_FLAG; |
899 | 568M | switch (aProperty->kind) { |
900 | 195M | case XS_INSTANCE_KIND: |
901 | 195M | aTemporary = aProperty->value.instance.prototype; |
902 | 195M | if (aTemporary && !(aTemporary->flag & XS_MARK_FLAG)) { |
903 | 49.9k | aProperty->value.instance.prototype = theCurrent; |
904 | 49.9k | theCurrent = aTemporary; |
905 | 49.9k | theCurrent->value.instance.garbage = aProperty; |
906 | 49.9k | aProperty = theCurrent; |
907 | 49.9k | } |
908 | 195M | else |
909 | 195M | aProperty = aProperty->next; |
910 | 195M | break; |
911 | 118M | case XS_REFERENCE_KIND: |
912 | 118M | #if mxKeysGarbageCollection |
913 | 118M | if (the->collectFlag & XS_COLLECT_KEYS_FLAG) { |
914 | 29.2M | if (!(aProperty->flag & XS_INTERNAL_FLAG)) |
915 | 29.2M | fxMarkID(the, aProperty->ID); |
916 | 29.2M | } |
917 | 118M | #endif |
918 | 118M | aTemporary = aProperty->value.reference; |
919 | 118M | if (!(aTemporary->flag & XS_MARK_FLAG)) { |
920 | 118M | aProperty->value.reference = theCurrent; |
921 | 118M | theCurrent = aTemporary; |
922 | 118M | theCurrent->value.instance.garbage = aProperty; |
923 | 118M | aProperty = theCurrent; |
924 | 118M | } |
925 | 923k | else |
926 | 923k | aProperty = aProperty->next; |
927 | 118M | break; |
928 | | |
929 | 0 | case XS_PROXY_KIND: |
930 | 0 | aTemporary = aProperty->value.proxy.handler; |
931 | 0 | if (aTemporary && !(aTemporary->flag & XS_MARK_FLAG)) { |
932 | 0 | aProperty->flag |= XS_INSPECTOR_FLAG; |
933 | 0 | aProperty->value.proxy.handler = theCurrent; |
934 | 0 | theCurrent = aTemporary; |
935 | 0 | theCurrent->value.instance.garbage = aProperty; |
936 | 0 | aProperty = theCurrent; |
937 | 0 | } |
938 | 0 | else { |
939 | 0 | aTemporary = aProperty->value.proxy.target; |
940 | 0 | if (aTemporary && !(aTemporary->flag & XS_MARK_FLAG)) { |
941 | 0 | aProperty->value.proxy.target = theCurrent; |
942 | 0 | theCurrent = aTemporary; |
943 | 0 | theCurrent->value.instance.garbage = aProperty; |
944 | 0 | aProperty = theCurrent; |
945 | 0 | } |
946 | 0 | else |
947 | 0 | aProperty = aProperty->next; |
948 | 0 | } |
949 | 0 | break; |
950 | | |
951 | 0 | case XS_CLOSURE_KIND: |
952 | 0 | #if mxKeysGarbageCollection |
953 | 0 | if (the->collectFlag & XS_COLLECT_KEYS_FLAG) { |
954 | 0 | if (!(aProperty->flag & XS_INTERNAL_FLAG)) |
955 | 0 | fxMarkID(the, aProperty->ID); |
956 | 0 | } |
957 | 0 | #endif |
958 | 0 | aTemporary = aProperty->value.closure; |
959 | 0 | if (aTemporary && !(aTemporary->flag & XS_MARK_FLAG)) { |
960 | 0 | aTemporary->flag |= XS_MARK_FLAG; |
961 | 0 | if (aTemporary->kind == XS_REFERENCE_KIND) { |
962 | 0 | aTemporary = aTemporary->value.reference; |
963 | 0 | if (!(aTemporary->flag & XS_MARK_FLAG)) { |
964 | 0 | aProperty->value.closure->value.reference = theCurrent; |
965 | 0 | theCurrent = aTemporary; |
966 | 0 | theCurrent->value.instance.garbage = aProperty; |
967 | 0 | aProperty = theCurrent; |
968 | | |
969 | 0 | } |
970 | 0 | } |
971 | 0 | else { |
972 | 0 | (*theMarker)(the, aTemporary); |
973 | 0 | aProperty = aProperty->next; |
974 | 0 | } |
975 | 0 | } |
976 | 0 | else |
977 | 0 | aProperty = aProperty->next; |
978 | 0 | break; |
979 | | |
980 | 7.41M | case XS_CALLBACK_KIND: |
981 | 7.41M | case XS_CODE_KIND: |
982 | 7.41M | #if mxKeysGarbageCollection |
983 | 7.41M | if (the->collectFlag & XS_COLLECT_KEYS_FLAG) |
984 | 4.03M | fxMarkID(the, aProperty->ID); |
985 | 7.41M | #endif |
986 | 7.41M | (*theMarker)(the, aProperty); |
987 | 7.41M | aProperty = aProperty->next; |
988 | 7.41M | break; |
989 | | |
990 | 246M | default: |
991 | 246M | #if mxKeysGarbageCollection |
992 | 246M | if (the->collectFlag & XS_COLLECT_KEYS_FLAG) { |
993 | 48.1M | if (!(aProperty->flag & XS_INTERNAL_FLAG)) |
994 | 12.1M | fxMarkID(the, aProperty->ID); |
995 | 48.1M | } |
996 | 246M | #endif |
997 | 246M | (*theMarker)(the, aProperty); |
998 | 246M | aProperty = aProperty->next; |
999 | 246M | break; |
1000 | 568M | } |
1001 | 568M | } |
1002 | 0 | else |
1003 | 0 | aProperty = aProperty->next; |
1004 | 568M | } |
1005 | 195M | else if (theCurrent->value.instance.garbage) { |
1006 | 118M | aProperty = theCurrent->value.instance.garbage; |
1007 | 118M | theCurrent->value.instance.garbage = C_NULL; |
1008 | 118M | switch (aProperty->kind) { |
1009 | 49.9k | case XS_INSTANCE_KIND: |
1010 | 49.9k | aTemporary = aProperty->value.instance.prototype; |
1011 | 49.9k | aProperty->value.instance.prototype = theCurrent; |
1012 | 49.9k | theCurrent = aTemporary; |
1013 | 49.9k | aProperty = aProperty->next; |
1014 | 49.9k | break; |
1015 | 118M | case XS_REFERENCE_KIND: |
1016 | 118M | aTemporary = aProperty->value.reference; |
1017 | 118M | aProperty->value.reference = theCurrent; |
1018 | 118M | theCurrent = aTemporary; |
1019 | 118M | aProperty = aProperty->next; |
1020 | 118M | break; |
1021 | 0 | case XS_PROXY_KIND: |
1022 | 0 | if (aProperty->flag & XS_INSPECTOR_FLAG) { |
1023 | 0 | aProperty->flag &= ~XS_INSPECTOR_FLAG; |
1024 | 0 | aTemporary = aProperty->value.proxy.handler; |
1025 | 0 | aProperty->value.proxy.handler = theCurrent; |
1026 | 0 | theCurrent = aTemporary; |
1027 | | |
1028 | 0 | aTemporary = aProperty->value.proxy.target; |
1029 | 0 | if (aTemporary && !(aTemporary->flag & XS_MARK_FLAG)) { |
1030 | 0 | aProperty->value.proxy.target = theCurrent; |
1031 | 0 | theCurrent = aTemporary; |
1032 | 0 | theCurrent->value.instance.garbage = aProperty; |
1033 | 0 | aProperty = theCurrent; |
1034 | 0 | } |
1035 | 0 | else { |
1036 | 0 | aProperty = aProperty->next; |
1037 | 0 | } |
1038 | 0 | } |
1039 | 0 | else { |
1040 | 0 | aTemporary = aProperty->value.proxy.target; |
1041 | 0 | aProperty->value.proxy.target = theCurrent; |
1042 | 0 | theCurrent = aTemporary; |
1043 | 0 | aProperty = aProperty->next; |
1044 | 0 | } |
1045 | 0 | break; |
1046 | 0 | case XS_CLOSURE_KIND: |
1047 | 0 | aTemporary = aProperty->value.closure->value.reference; |
1048 | 0 | aProperty->value.closure->value.reference = theCurrent; |
1049 | 0 | theCurrent = aTemporary; |
1050 | 0 | aProperty = aProperty->next; |
1051 | 0 | break; |
1052 | 118M | } |
1053 | 118M | } |
1054 | 77.6M | else |
1055 | 77.6M | break; |
1056 | 764M | } |
1057 | 77.6M | } |
1058 | | |
1059 | | void fxMarkReference(txMachine* the, txSlot* theSlot) |
1060 | 248M | { |
1061 | 248M | txSlot* aSlot; |
1062 | 248M | switch (theSlot->kind) { |
1063 | 33.9M | case XS_REFERENCE_KIND: |
1064 | 33.9M | aSlot = theSlot->value.reference; |
1065 | 33.9M | if (!(aSlot->flag & XS_MARK_FLAG)) |
1066 | 33.0M | fxMarkInstance(the, aSlot, fxMarkReference); |
1067 | 33.9M | break; |
1068 | 0 | case XS_CLOSURE_KIND: |
1069 | 0 | aSlot = theSlot->value.closure; |
1070 | 0 | if (aSlot && (!(aSlot->flag & XS_MARK_FLAG))) { |
1071 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1072 | 0 | fxMarkReference(the, aSlot); |
1073 | 0 | } |
1074 | 0 | break; |
1075 | 0 | case XS_INSTANCE_KIND: |
1076 | 0 | if (!(theSlot->flag & XS_MARK_FLAG)) |
1077 | 0 | fxMarkInstance(the, theSlot, fxMarkReference); |
1078 | 0 | break; |
1079 | 473k | case XS_ACCESSOR_KIND: |
1080 | 473k | aSlot = theSlot->value.accessor.getter; |
1081 | 473k | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1082 | 465k | fxCheckCStack(the); |
1083 | 465k | fxMarkInstance(the, aSlot, fxMarkReference); |
1084 | 465k | } |
1085 | 473k | aSlot = theSlot->value.accessor.setter; |
1086 | 473k | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1087 | 65.3k | fxCheckCStack(the); |
1088 | 65.3k | fxMarkInstance(the, aSlot, fxMarkReference); |
1089 | 65.3k | } |
1090 | 473k | break; |
1091 | 0 | case XS_ARGUMENTS_SLOPPY_KIND: |
1092 | 0 | case XS_ARGUMENTS_STRICT_KIND: |
1093 | 138M | case XS_ARRAY_KIND: |
1094 | 138M | case XS_STACK_KIND: |
1095 | 138M | fxCheckCStack(the); |
1096 | 138M | if ((aSlot = theSlot->value.array.address)) { |
1097 | 18.1M | txIndex aLength = (((txChunk*)(((txByte*)aSlot) - sizeof(txChunk)))->size) / sizeof(txSlot); |
1098 | 18.1M | if (aLength > theSlot->value.array.length) |
1099 | 0 | aLength = theSlot->value.array.length; |
1100 | 53.6M | while (aLength) { |
1101 | 35.4M | fxMarkReference(the, aSlot); |
1102 | 35.4M | aSlot++; |
1103 | 35.4M | aLength--; |
1104 | 35.4M | } |
1105 | 18.1M | } |
1106 | 138M | break; |
1107 | 4.85M | case XS_CALLBACK_KIND: |
1108 | 4.85M | aSlot = theSlot->value.callback.closures; |
1109 | 4.85M | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1110 | 0 | fxCheckCStack(the); |
1111 | 0 | fxMarkInstance(the, aSlot, fxMarkReference); |
1112 | 0 | } |
1113 | 4.85M | break; |
1114 | 0 | case XS_CODE_KIND: |
1115 | | // continue |
1116 | 16.3k | case XS_CODE_X_KIND: |
1117 | 16.3k | aSlot = theSlot->value.code.closures; |
1118 | 16.3k | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1119 | 0 | fxCheckCStack(the); |
1120 | 0 | fxMarkInstance(the, aSlot, fxMarkReference); |
1121 | 0 | } |
1122 | 16.3k | break; |
1123 | 4.85M | case XS_HOME_KIND: |
1124 | 4.85M | aSlot = theSlot->value.home.object; |
1125 | 4.85M | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1126 | 0 | fxCheckCStack(the); |
1127 | 0 | fxMarkInstance(the, aSlot, fxMarkReference); |
1128 | 0 | } |
1129 | 4.85M | aSlot = theSlot->value.home.module; |
1130 | 4.85M | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1131 | 0 | fxCheckCStack(the); |
1132 | 0 | fxMarkInstance(the, aSlot, fxMarkReference); |
1133 | 0 | } |
1134 | 4.85M | break; |
1135 | 0 | case XS_MODULE_KIND: |
1136 | 8.16k | case XS_PROGRAM_KIND: |
1137 | 8.16k | #if mxKeysGarbageCollection |
1138 | 8.16k | if (the->collectFlag & XS_COLLECT_KEYS_FLAG) |
1139 | 6.79k | fxMarkID(the, theSlot->value.module.id); |
1140 | 8.16k | #endif |
1141 | 8.16k | aSlot = theSlot->value.module.realm; |
1142 | 8.16k | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1143 | 8.16k | fxCheckCStack(the); |
1144 | 8.16k | fxMarkInstance(the, aSlot, fxMarkReference); |
1145 | 8.16k | } |
1146 | 8.16k | break; |
1147 | 0 | case XS_EXPORT_KIND: |
1148 | 0 | aSlot = theSlot->value.export.closure; |
1149 | 0 | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1150 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1151 | 0 | fxMarkReference(the, aSlot); |
1152 | 0 | } |
1153 | 0 | aSlot = theSlot->value.export.module; |
1154 | 0 | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) |
1155 | 0 | fxMarkInstance(the, aSlot, fxMarkReference); |
1156 | 0 | break; |
1157 | 8.16k | case XS_HOST_KIND: |
1158 | 8.16k | if (theSlot->value.host.data) { |
1159 | 0 | if ((theSlot->flag & XS_HOST_HOOKS_FLAG) && (theSlot->value.host.variant.hooks->marker)) |
1160 | 0 | (*theSlot->value.host.variant.hooks->marker)(the, theSlot->value.host.data, fxMarkReference); |
1161 | 0 | } |
1162 | 8.16k | break; |
1163 | 0 | case XS_PROXY_KIND: |
1164 | 0 | aSlot = theSlot->value.proxy.handler; |
1165 | 0 | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) |
1166 | 0 | fxMarkInstance(the, aSlot, fxMarkReference); |
1167 | 0 | aSlot = theSlot->value.proxy.target; |
1168 | 0 | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) |
1169 | 0 | fxMarkInstance(the, aSlot, fxMarkReference); |
1170 | 0 | break; |
1171 | | |
1172 | 0 | case XS_BREAKPOINT_KIND: |
1173 | 0 | aSlot = theSlot->value.breakpoint.info; |
1174 | 0 | if (aSlot && (!(aSlot->flag & XS_MARK_FLAG))) |
1175 | 0 | fxMarkInstance(the, aSlot, fxMarkValue); |
1176 | 0 | break; |
1177 | 12 | case XS_ERROR_KIND: |
1178 | 12 | aSlot = theSlot->value.error.info; |
1179 | 12 | if (aSlot && (!(aSlot->flag & XS_MARK_FLAG))) |
1180 | 10 | fxMarkInstance(the, aSlot, fxMarkReference); |
1181 | 12 | break; |
1182 | 0 | case XS_ASYNC_DISPOSABLE_STACK_KIND: |
1183 | 0 | case XS_DISPOSABLE_STACK_KIND: |
1184 | 24.5k | case XS_LIST_KIND: |
1185 | 24.5k | fxCheckCStack(the); |
1186 | 24.5k | aSlot = theSlot->value.list.first; |
1187 | 24.5k | while (aSlot) { |
1188 | 0 | if (!(aSlot->flag & XS_MARK_FLAG)) { |
1189 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1190 | 0 | fxMarkReference(the, aSlot); |
1191 | 0 | } |
1192 | 0 | aSlot = aSlot->next; |
1193 | 0 | } |
1194 | 24.5k | break; |
1195 | | |
1196 | 0 | case XS_PRIVATE_KIND: |
1197 | 0 | fxCheckCStack(the); |
1198 | 0 | aSlot = theSlot->value.private.check; |
1199 | 0 | if (!(aSlot->flag & XS_MARK_FLAG)) |
1200 | 0 | fxMarkInstance(the, aSlot, fxMarkReference); |
1201 | 0 | aSlot = theSlot->value.private.first; |
1202 | 0 | while (aSlot) { |
1203 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1204 | 0 | fxMarkReference(the, aSlot); |
1205 | 0 | aSlot = aSlot->next; |
1206 | 0 | } |
1207 | 0 | break; |
1208 | | |
1209 | 0 | case XS_MAP_KIND: |
1210 | 0 | case XS_SET_KIND: |
1211 | 0 | { |
1212 | 0 | txSlot** anAddress = theSlot->value.table.address; |
1213 | 0 | txInteger aLength = theSlot->value.table.length; |
1214 | 0 | while (aLength) { |
1215 | 0 | aSlot = *anAddress; |
1216 | 0 | while (aSlot) { |
1217 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1218 | 0 | aSlot = aSlot->next; |
1219 | 0 | } |
1220 | 0 | anAddress++; |
1221 | 0 | aLength--; |
1222 | 0 | } |
1223 | 0 | } |
1224 | 0 | break; |
1225 | 0 | case XS_WEAK_MAP_KIND: |
1226 | 0 | case XS_WEAK_SET_KIND: |
1227 | 0 | aSlot = theSlot->value.weakList.first; |
1228 | 0 | while (aSlot) { |
1229 | 0 | if (!(aSlot->flag & XS_MARK_FLAG)) { |
1230 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1231 | 0 | fxMarkReference(the, aSlot); |
1232 | 0 | } |
1233 | 0 | aSlot = aSlot->next; |
1234 | 0 | } |
1235 | 0 | break; |
1236 | 0 | case XS_WEAK_ENTRY_KIND: |
1237 | 0 | aSlot = theSlot->value.weakEntry.check; |
1238 | 0 | if (aSlot->flag & XS_MARK_FLAG) { |
1239 | 0 | aSlot = theSlot->value.weakEntry.value; |
1240 | 0 | if (!(aSlot->flag & XS_MARK_FLAG)) { |
1241 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1242 | 0 | fxMarkReference(the, aSlot); |
1243 | 0 | } |
1244 | 0 | } |
1245 | 0 | break; |
1246 | 0 | case XS_WEAK_REF_KIND: |
1247 | 0 | aSlot = theSlot->value.weakRef.target; |
1248 | 0 | if (aSlot) { |
1249 | 0 | #ifdef mxSnapshot |
1250 | 0 | if (the->collectFlag & XS_ORGANIC_FLAG) { |
1251 | 0 | fxMarkReference(the, aSlot); |
1252 | 0 | } |
1253 | 0 | else { |
1254 | 0 | theSlot->value.weakRef.link = the->firstWeakRefLink; |
1255 | 0 | the->firstWeakRefLink = theSlot; |
1256 | 0 | } |
1257 | | #else |
1258 | | theSlot->value.weakRef.link = the->firstWeakRefLink; |
1259 | | the->firstWeakRefLink = theSlot; |
1260 | | #endif |
1261 | 0 | } |
1262 | 0 | break; |
1263 | 0 | case XS_FINALIZATION_REGISTRY_KIND: |
1264 | 0 | aSlot = theSlot->value.finalizationRegistry.callback; |
1265 | 0 | if (aSlot) { |
1266 | 0 | fxCheckCStack(the); |
1267 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1268 | 0 | fxMarkReference(the, aSlot); |
1269 | 0 | aSlot = aSlot->next; |
1270 | 0 | while (aSlot) { |
1271 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1272 | 0 | fxMarkReference(the, aSlot); // holdings |
1273 | 0 | aSlot = aSlot->next; |
1274 | 0 | if (aSlot) { |
1275 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1276 | | // weak target and token |
1277 | 0 | aSlot = aSlot->next; |
1278 | 0 | } |
1279 | 0 | } |
1280 | 0 | } |
1281 | 0 | break; |
1282 | | |
1283 | 0 | case XS_HOST_INSPECTOR_KIND: |
1284 | 0 | aSlot = theSlot->value.hostInspector.cache; |
1285 | 0 | if (!(aSlot->flag & XS_MARK_FLAG)) |
1286 | 0 | fxMarkInstance(the, aSlot, fxMarkReference); |
1287 | 0 | break; |
1288 | 0 | #if mxKeysGarbageCollection |
1289 | 245k | case XS_SYMBOL_KIND: |
1290 | 245k | if (the->collectFlag & XS_COLLECT_KEYS_FLAG) { |
1291 | 203k | if (!(theSlot->flag & XS_INTERNAL_FLAG)) |
1292 | 101k | fxMarkID(the, theSlot->value.symbol); |
1293 | 203k | } |
1294 | 245k | break; |
1295 | 3.89M | case XS_AT_KIND: |
1296 | 3.89M | if (the->collectFlag & XS_COLLECT_KEYS_FLAG) |
1297 | 3.86M | fxMarkID(the, theSlot->value.at.id); |
1298 | 3.89M | break; |
1299 | 248M | #endif |
1300 | 248M | } |
1301 | 248M | } |
1302 | | |
1303 | | void fxMarkValue(txMachine* the, txSlot* theSlot) |
1304 | 181M | { |
1305 | 181M | #define mxMarkChunk(_THE_DATA) \ |
1306 | 181M | ((txChunk*)(((txByte*)_THE_DATA) - sizeof(txChunk)))->size |= mxChunkFlag |
1307 | | |
1308 | 181M | txSlot* aSlot; |
1309 | 181M | switch (theSlot->kind) { |
1310 | 4.26M | case XS_STRING_KIND: |
1311 | 4.26M | mxMarkChunk(theSlot->value.string); |
1312 | 4.26M | break; |
1313 | 0 | case XS_BIGINT_KIND: |
1314 | 0 | mxMarkChunk(theSlot->value.bigint.data); |
1315 | 0 | break; |
1316 | 44.1M | case XS_REFERENCE_KIND: |
1317 | 44.1M | aSlot = theSlot->value.reference; |
1318 | 44.1M | if (!(aSlot->flag & XS_MARK_FLAG)) |
1319 | 43.7M | fxMarkInstance(the, aSlot, fxMarkValue); |
1320 | 44.1M | break; |
1321 | 0 | case XS_CLOSURE_KIND: |
1322 | 0 | aSlot = theSlot->value.closure; |
1323 | 0 | if (aSlot && (!(aSlot->flag & XS_MARK_FLAG))) { |
1324 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1325 | 0 | fxMarkValue(the, aSlot); |
1326 | 0 | } |
1327 | 0 | break; |
1328 | 0 | case XS_INSTANCE_KIND: |
1329 | 0 | if (!(theSlot->flag & XS_MARK_FLAG)) |
1330 | 0 | fxMarkInstance(the, theSlot, fxMarkValue); |
1331 | 0 | break; |
1332 | | |
1333 | 0 | case XS_ARGUMENTS_SLOPPY_KIND: |
1334 | 0 | case XS_ARGUMENTS_STRICT_KIND: |
1335 | 43.8M | case XS_ARRAY_KIND: |
1336 | 43.8M | case XS_STACK_KIND: |
1337 | 43.8M | fxCheckCStack(the); |
1338 | 43.8M | if ((aSlot = theSlot->value.array.address)) { |
1339 | 35.6M | txChunk* chunk = (txChunk*)(((txByte*)aSlot) - sizeof(txChunk)); |
1340 | 35.6M | if (!(chunk->size & mxChunkFlag)) { |
1341 | 35.6M | txIndex aLength = chunk->size / sizeof(txSlot); |
1342 | 35.6M | if (aLength > theSlot->value.array.length) |
1343 | 0 | aLength = theSlot->value.array.length; |
1344 | 136M | while (aLength) { |
1345 | 100M | fxMarkValue(the, aSlot); |
1346 | 100M | aSlot++; |
1347 | 100M | aLength--; |
1348 | 100M | } |
1349 | 35.6M | mxMarkChunk(theSlot->value.array.address); |
1350 | 35.6M | } |
1351 | 35.6M | } |
1352 | 43.8M | break; |
1353 | 144 | case XS_ARRAY_BUFFER_KIND: |
1354 | 144 | if (theSlot->value.arrayBuffer.address) |
1355 | 107 | mxMarkChunk(theSlot->value.arrayBuffer.address); |
1356 | 144 | break; |
1357 | 2.56M | case XS_CALLBACK_KIND: |
1358 | 2.56M | aSlot = theSlot->value.callback.closures; |
1359 | 2.56M | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1360 | 0 | fxCheckCStack(the); |
1361 | 0 | fxMarkInstance(the, aSlot, fxMarkValue); |
1362 | 0 | } |
1363 | 2.56M | break; |
1364 | 0 | case XS_CODE_KIND: |
1365 | 0 | mxMarkChunk(theSlot->value.code.address); |
1366 | | /* continue */ |
1367 | 0 | mxFallThrough; |
1368 | 8.62k | case XS_CODE_X_KIND: |
1369 | 8.62k | aSlot = theSlot->value.code.closures; |
1370 | 8.62k | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1371 | 0 | fxCheckCStack(the); |
1372 | 0 | fxMarkInstance(the, aSlot, fxMarkValue); |
1373 | 0 | } |
1374 | 8.62k | break; |
1375 | 4.31k | case XS_GLOBAL_KIND: |
1376 | 4.31k | mxMarkChunk(theSlot->value.table.address); |
1377 | 4.31k | break; |
1378 | 4.41k | case XS_HOST_KIND: |
1379 | 4.41k | if (theSlot->value.host.data) { |
1380 | 105 | if ((theSlot->flag & XS_HOST_HOOKS_FLAG) && (theSlot->value.host.variant.hooks->marker)) |
1381 | 0 | (*theSlot->value.host.variant.hooks->marker)(the, theSlot->value.host.data, fxMarkValue); |
1382 | 105 | if (theSlot->flag & XS_HOST_CHUNK_FLAG) |
1383 | 105 | mxMarkChunk(theSlot->value.host.data); |
1384 | 105 | } |
1385 | 4.41k | break; |
1386 | 0 | case XS_IDS_KIND: |
1387 | 0 | if (theSlot->value.IDs) |
1388 | 0 | mxMarkChunk(theSlot->value.IDs); |
1389 | 0 | break; |
1390 | 0 | case XS_PROXY_KIND: |
1391 | 0 | aSlot = theSlot->value.proxy.handler; |
1392 | 0 | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) |
1393 | 0 | fxMarkInstance(the, aSlot, fxMarkValue); |
1394 | 0 | aSlot = theSlot->value.proxy.target; |
1395 | 0 | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) |
1396 | 0 | fxMarkInstance(the, aSlot, fxMarkValue); |
1397 | 0 | break; |
1398 | 0 | case XS_REGEXP_KIND: |
1399 | 0 | if (theSlot->value.regexp.code) |
1400 | 0 | mxMarkChunk(theSlot->value.regexp.code); |
1401 | 0 | if (theSlot->value.regexp.data) |
1402 | 0 | mxMarkChunk(theSlot->value.regexp.data); |
1403 | 0 | break; |
1404 | | |
1405 | 250k | case XS_ACCESSOR_KIND: |
1406 | 250k | aSlot = theSlot->value.accessor.getter; |
1407 | 250k | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1408 | 245k | fxCheckCStack(the); |
1409 | 245k | fxMarkInstance(the, aSlot, fxMarkValue); |
1410 | 245k | } |
1411 | 250k | aSlot = theSlot->value.accessor.setter; |
1412 | 250k | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1413 | 34.4k | fxCheckCStack(the); |
1414 | 34.4k | fxMarkInstance(the, aSlot, fxMarkValue); |
1415 | 34.4k | } |
1416 | 250k | break; |
1417 | 2.56M | case XS_HOME_KIND: |
1418 | 2.56M | aSlot = theSlot->value.home.object; |
1419 | 2.56M | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1420 | 0 | fxCheckCStack(the); |
1421 | 0 | fxMarkInstance(the, aSlot, fxMarkValue); |
1422 | 0 | } |
1423 | 2.56M | aSlot = theSlot->value.home.module; |
1424 | 2.56M | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1425 | 0 | fxCheckCStack(the); |
1426 | 0 | fxMarkInstance(the, aSlot, fxMarkValue); |
1427 | 0 | } |
1428 | 2.56M | break; |
1429 | 0 | case XS_MODULE_KIND: |
1430 | 4.31k | case XS_PROGRAM_KIND: |
1431 | 4.31k | #if mxKeysGarbageCollection |
1432 | 4.31k | if (the->collectFlag & XS_COLLECT_KEYS_FLAG) |
1433 | 0 | fxMarkID(the, theSlot->value.module.id); |
1434 | 4.31k | #endif |
1435 | 4.31k | aSlot = theSlot->value.module.realm; |
1436 | 4.31k | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1437 | 4.31k | fxCheckCStack(the); |
1438 | 4.31k | fxMarkInstance(the, aSlot, fxMarkValue); |
1439 | 4.31k | } |
1440 | 4.31k | break; |
1441 | 0 | case XS_EXPORT_KIND: |
1442 | 0 | aSlot = theSlot->value.export.closure; |
1443 | 0 | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) { |
1444 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1445 | 0 | fxMarkValue(the, aSlot); |
1446 | 0 | } |
1447 | 0 | aSlot = theSlot->value.export.module; |
1448 | 0 | if (aSlot && !(aSlot->flag & XS_MARK_FLAG)) |
1449 | 0 | fxMarkInstance(the, aSlot, fxMarkValue); |
1450 | 0 | break; |
1451 | 2.44M | case XS_KEY_KIND: |
1452 | 2.44M | if (theSlot->value.key.string) |
1453 | 2.44M | mxMarkChunk(theSlot->value.key.string); |
1454 | 2.44M | break; |
1455 | | |
1456 | 0 | case XS_BREAKPOINT_KIND: |
1457 | 0 | aSlot = theSlot->value.breakpoint.info; |
1458 | 0 | if (aSlot && (!(aSlot->flag & XS_MARK_FLAG))) |
1459 | 0 | fxMarkInstance(the, aSlot, fxMarkValue); |
1460 | 0 | break; |
1461 | 28 | case XS_ERROR_KIND: |
1462 | 28 | aSlot = theSlot->value.error.info; |
1463 | 28 | if (aSlot && (!(aSlot->flag & XS_MARK_FLAG))) |
1464 | 28 | fxMarkInstance(the, aSlot, fxMarkValue); |
1465 | 28 | break; |
1466 | 0 | case XS_ASYNC_DISPOSABLE_STACK_KIND: |
1467 | 0 | case XS_DISPOSABLE_STACK_KIND: |
1468 | 12.9k | case XS_LIST_KIND: |
1469 | 12.9k | fxCheckCStack(the); |
1470 | 12.9k | aSlot = theSlot->value.list.first; |
1471 | 12.9k | while (aSlot) { |
1472 | 0 | if (!(aSlot->flag & XS_MARK_FLAG)) { |
1473 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1474 | 0 | fxMarkValue(the, aSlot); |
1475 | 0 | } |
1476 | 0 | aSlot = aSlot->next; |
1477 | 0 | } |
1478 | 12.9k | break; |
1479 | | |
1480 | 0 | case XS_PRIVATE_KIND: |
1481 | 0 | fxCheckCStack(the); |
1482 | 0 | aSlot = theSlot->value.private.check; |
1483 | 0 | if (!(aSlot->flag & XS_MARK_FLAG)) |
1484 | 0 | fxMarkInstance(the, aSlot, fxMarkValue); |
1485 | 0 | aSlot = theSlot->value.private.first; |
1486 | 0 | while (aSlot) { |
1487 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1488 | 0 | fxMarkValue(the, aSlot); |
1489 | 0 | aSlot = aSlot->next; |
1490 | 0 | } |
1491 | 0 | break; |
1492 | | |
1493 | 0 | case XS_MAP_KIND: |
1494 | 0 | case XS_SET_KIND: |
1495 | 0 | { |
1496 | 0 | txSlot** anAddress = theSlot->value.table.address; |
1497 | 0 | txInteger aLength = theSlot->value.table.length; |
1498 | 0 | while (aLength) { |
1499 | 0 | aSlot = *anAddress; |
1500 | 0 | while (aSlot) { |
1501 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1502 | 0 | aSlot = aSlot->next; |
1503 | 0 | } |
1504 | 0 | anAddress++; |
1505 | 0 | aLength--; |
1506 | 0 | } |
1507 | 0 | } |
1508 | 0 | mxMarkChunk(theSlot->value.table.address); |
1509 | 0 | break; |
1510 | | |
1511 | 0 | case XS_WEAK_MAP_KIND: |
1512 | 0 | case XS_WEAK_SET_KIND: |
1513 | 0 | aSlot = theSlot->value.weakList.first; |
1514 | 0 | while (aSlot) { |
1515 | 0 | if (!(aSlot->flag & XS_MARK_FLAG)) { |
1516 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1517 | 0 | fxMarkValue(the, aSlot); |
1518 | 0 | } |
1519 | 0 | aSlot = aSlot->next; |
1520 | 0 | } |
1521 | 0 | break; |
1522 | 0 | case XS_WEAK_ENTRY_KIND: |
1523 | 0 | aSlot = theSlot->value.weakEntry.check; |
1524 | 0 | if (aSlot->flag & XS_MARK_FLAG) { |
1525 | 0 | aSlot = theSlot->value.weakEntry.value; |
1526 | 0 | if (!(aSlot->flag & XS_MARK_FLAG)) { |
1527 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1528 | 0 | fxMarkValue(the, aSlot); |
1529 | 0 | } |
1530 | 0 | } |
1531 | 0 | break; |
1532 | 0 | case XS_WEAK_REF_KIND: |
1533 | 0 | aSlot = theSlot->value.weakRef.target; |
1534 | 0 | if (aSlot) { |
1535 | 0 | #ifdef mxSnapshot |
1536 | 0 | if (the->collectFlag & XS_ORGANIC_FLAG) { |
1537 | 0 | fxMarkValue(the, aSlot); |
1538 | 0 | } |
1539 | 0 | else { |
1540 | 0 | theSlot->value.weakRef.link = the->firstWeakRefLink; |
1541 | 0 | the->firstWeakRefLink = theSlot; |
1542 | 0 | } |
1543 | | #else |
1544 | | theSlot->value.weakRef.link = the->firstWeakRefLink; |
1545 | | the->firstWeakRefLink = theSlot; |
1546 | | #endif |
1547 | 0 | } |
1548 | 0 | break; |
1549 | 0 | case XS_FINALIZATION_REGISTRY_KIND: |
1550 | 0 | aSlot = theSlot->value.finalizationRegistry.callback; |
1551 | 0 | if (aSlot) { |
1552 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1553 | 0 | fxMarkValue(the, aSlot); |
1554 | 0 | aSlot = aSlot->next; |
1555 | 0 | while (aSlot) { |
1556 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1557 | 0 | fxCheckCStack(the); |
1558 | 0 | fxMarkValue(the, aSlot); // holdings |
1559 | 0 | aSlot = aSlot->next; |
1560 | 0 | if (aSlot) { |
1561 | 0 | aSlot->flag |= XS_MARK_FLAG; |
1562 | | // weak target and token |
1563 | 0 | aSlot = aSlot->next; |
1564 | 0 | } |
1565 | 0 | } |
1566 | 0 | } |
1567 | 0 | break; |
1568 | | |
1569 | 0 | case XS_HOST_INSPECTOR_KIND: |
1570 | 0 | aSlot = theSlot->value.hostInspector.cache; |
1571 | 0 | if (!(aSlot->flag & XS_MARK_FLAG)) |
1572 | 0 | fxMarkInstance(the, aSlot, fxMarkValue); |
1573 | 0 | break; |
1574 | | |
1575 | 0 | #if mxKeysGarbageCollection |
1576 | 129k | case XS_SYMBOL_KIND: |
1577 | 129k | if (the->collectFlag & XS_COLLECT_KEYS_FLAG) { |
1578 | 0 | if (!(theSlot->flag & XS_INTERNAL_FLAG)) |
1579 | 0 | fxMarkID(the, theSlot->value.symbol); |
1580 | 0 | } |
1581 | 129k | break; |
1582 | 770k | case XS_AT_KIND: |
1583 | 770k | if (the->collectFlag & XS_COLLECT_KEYS_FLAG) |
1584 | 0 | fxMarkID(the, theSlot->value.at.id); |
1585 | 770k | break; |
1586 | 181M | #endif |
1587 | 181M | } |
1588 | 181M | } |
1589 | | |
1590 | | void fxMarkWeakStuff(txMachine* the) |
1591 | 12.4k | { |
1592 | 12.4k | txSlot* slot; |
1593 | 12.4k | txSlot** address; |
1594 | | |
1595 | 12.4k | { |
1596 | 12.4k | txSlot* list; |
1597 | 12.4k | txSlot** listAddress = &the->firstWeakListLink; |
1598 | 12.4k | while ((list = *listAddress)) { |
1599 | 0 | if (list->flag & XS_MARK_FLAG) { |
1600 | 0 | txSlot* listEntry; |
1601 | 0 | txSlot** listEntryAddress = &list->value.weakList.first; |
1602 | 0 | while ((listEntry = *listEntryAddress)) { |
1603 | 0 | txSlot* value = listEntry->value.weakEntry.value; |
1604 | 0 | if ((value->flag & XS_MARK_FLAG) && (value->kind != XS_UNINITIALIZED_KIND)) { |
1605 | 0 | listEntryAddress = &listEntry->next; |
1606 | 0 | } |
1607 | 0 | else { |
1608 | 0 | listEntry->flag &= ~XS_MARK_FLAG; |
1609 | 0 | *listEntryAddress = listEntry->next; |
1610 | 0 | } |
1611 | 0 | } |
1612 | 0 | listAddress = &list->value.weakList.link; |
1613 | 0 | } |
1614 | 0 | else { |
1615 | 0 | txSlot* listEntry = list->value.weakList.first; |
1616 | 0 | while (listEntry) { |
1617 | 0 | txSlot* key = listEntry->value.weakEntry.check; |
1618 | 0 | if (key->flag & XS_MARK_FLAG) { |
1619 | 0 | txSlot* keyEntry; |
1620 | 0 | txSlot** keyEntryAddress = &key->next; |
1621 | 0 | while ((keyEntry = *keyEntryAddress)) { |
1622 | 0 | if (!(keyEntry->flag & XS_INTERNAL_FLAG)) |
1623 | 0 | break; |
1624 | 0 | if ((keyEntry->kind == XS_WEAK_ENTRY_KIND) && (keyEntry->value.weakEntry.check == list)) { |
1625 | 0 | keyEntry->flag &= ~XS_MARK_FLAG; |
1626 | 0 | *keyEntryAddress = keyEntry->next; |
1627 | 0 | break; |
1628 | 0 | } |
1629 | 0 | keyEntryAddress = &keyEntry->next; |
1630 | 0 | } |
1631 | 0 | } |
1632 | 0 | listEntry = listEntry->next; |
1633 | 0 | } |
1634 | 0 | *listAddress = list->value.weakList.link; |
1635 | 0 | } |
1636 | 0 | } |
1637 | 12.4k | } |
1638 | 12.4k | address = &the->firstWeakRefLink; |
1639 | 12.4k | while ((slot = *address)) { |
1640 | 0 | if (!(slot->value.weakRef.target->flag & XS_MARK_FLAG)) |
1641 | 0 | slot->value.weakRef.target = C_NULL; |
1642 | 0 | *address = C_NULL; |
1643 | 0 | address = &(slot->value.weakRef.link); |
1644 | 0 | } |
1645 | | |
1646 | 12.4k | if (mxFinalizationRegistries.kind == XS_REFERENCE_KIND) { |
1647 | 12.4k | slot = mxFinalizationRegistries.value.reference->next; |
1648 | 12.4k | while (slot) { |
1649 | 0 | fxMarkFinalizationRegistry(the, slot->value.closure); |
1650 | 0 | slot = slot->next; |
1651 | 0 | } |
1652 | 12.4k | } |
1653 | 12.4k | } |
1654 | | |
1655 | | txSize fxMultiplyChunkSizes(txMachine* the, txSize a, txSize b) |
1656 | 4.35M | { |
1657 | 4.35M | txSize c; |
1658 | 4.35M | #if __has_builtin(__builtin_mul_overflow) |
1659 | 4.35M | if (__builtin_mul_overflow(a, b, &c) || (c < 0)) { |
1660 | | #else |
1661 | | txNumber C = (txNumber)a * (txNumber)b; |
1662 | | c = (txSize)C; |
1663 | | if ((C > (txNumber)0x7FFFFFFF) || (C < (txNumber)0)) { |
1664 | | #endif |
1665 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
1666 | 0 | } |
1667 | 4.35M | return c; |
1668 | 4.35M | } |
1669 | | |
1670 | | void* fxNewChunk(txMachine* the, txSize size) |
1671 | 11.3M | { |
1672 | 11.3M | txSize offset = size; |
1673 | 11.3M | txChunk* chunk; |
1674 | 11.3M | txBoolean once = 1; |
1675 | 11.3M | size = fxAdjustChunkSize(the, size); |
1676 | 11.3M | chunk = fxFindChunk(the, size, &once); |
1677 | 11.3M | if (!chunk) { |
1678 | 614 | chunk = fxGrowChunk(the, size); |
1679 | 614 | } |
1680 | 11.3M | #ifdef mxMetering |
1681 | 11.3M | the->meterIndex += size * XS_CHUNK_ALLOCATION_METERING; |
1682 | 11.3M | #endif |
1683 | 11.3M | return fxCheckChunk(the, chunk, size, offset); |
1684 | 11.3M | } |
1685 | | |
1686 | | void* fxNewGrowableChunk(txMachine* the, txSize size, txSize capacity) |
1687 | 0 | { |
1688 | | #if mxNoChunks |
1689 | | return fxNewChunk(the, size); |
1690 | | #else |
1691 | 0 | txSize offset = size; |
1692 | 0 | txChunk* chunk; |
1693 | 0 | txBoolean once = 1; |
1694 | 0 | size = fxAdjustChunkSize(the, size); |
1695 | 0 | capacity = fxAdjustChunkSize(the, capacity); |
1696 | 0 | chunk = fxFindChunk(the, capacity, &once); |
1697 | 0 | if (!chunk) { |
1698 | 0 | chunk = fxGrowChunk(the, capacity); |
1699 | 0 | if (!chunk) { |
1700 | 0 | chunk = fxFindChunk(the, size, &once); |
1701 | 0 | if (!chunk) { |
1702 | 0 | chunk = fxGrowChunk(the, size); |
1703 | 0 | } |
1704 | 0 | } |
1705 | 0 | } |
1706 | 0 | #ifdef mxMetering |
1707 | 0 | the->meterIndex += size * XS_CHUNK_ALLOCATION_METERING; |
1708 | 0 | #endif |
1709 | 0 | return fxCheckChunk(the, chunk, size, offset); |
1710 | 0 | #endif |
1711 | 0 | } |
1712 | | |
1713 | | txSlot* fxNewSlot(txMachine* the) |
1714 | 62.4M | { |
1715 | 62.4M | txSlot* aSlot; |
1716 | 62.4M | txBoolean once = 1, allocate; |
1717 | | |
1718 | 62.4M | #if mxStress |
1719 | 62.4M | if (fxShouldStress()) { |
1720 | 0 | fxCollect(the, XS_COMPACT_FLAG | XS_ORGANIC_FLAG); |
1721 | 0 | once = 0; |
1722 | 0 | } |
1723 | 62.4M | #endif |
1724 | 62.4M | again: |
1725 | 62.4M | aSlot = the->freeHeap; |
1726 | 62.4M | if (aSlot) { |
1727 | 62.4M | the->freeHeap = aSlot->next; |
1728 | 62.4M | aSlot->next = C_NULL; |
1729 | 62.4M | aSlot->ID = XS_NO_ID; |
1730 | 62.4M | aSlot->flag = XS_NO_FLAG; |
1731 | 62.4M | #ifdef mxSnapshot |
1732 | 62.4M | #if mx32bitID |
1733 | 62.4M | aSlot->dummy = 0; |
1734 | | #elif INTPTR_MAX == INT64_MAX |
1735 | | aSlot->dummy = 0; |
1736 | | #endif |
1737 | 62.4M | #endif |
1738 | | #if mxPoisonSlots |
1739 | | ASAN_UNPOISON_MEMORY_REGION(&aSlot->value, sizeof(aSlot->value)); |
1740 | | #endif |
1741 | 62.4M | the->currentHeapCount++; |
1742 | 62.4M | if (the->peakHeapCount < the->currentHeapCount) |
1743 | 57.8M | the->peakHeapCount = the->currentHeapCount; |
1744 | 62.4M | #ifdef mxMetering |
1745 | 62.4M | the->meterIndex += XS_SLOT_ALLOCATION_METERING; |
1746 | 62.4M | #endif |
1747 | 62.4M | return aSlot; |
1748 | 62.4M | } |
1749 | 1.53k | if (once) { |
1750 | 1.37k | txBoolean wasThrashing = ((the->collectFlag & XS_TRASHING_SLOTS_FLAG) != 0), isThrashing; |
1751 | | |
1752 | 1.37k | fxCollect(the, XS_ORGANIC_FLAG); |
1753 | | |
1754 | 1.37k | isThrashing = ((the->collectFlag & XS_TRASHING_SLOTS_FLAG) != 0); |
1755 | 1.37k | allocate = wasThrashing && isThrashing; |
1756 | | |
1757 | 1.37k | once = 0; |
1758 | 1.37k | } |
1759 | 157 | else |
1760 | 157 | allocate = 1; |
1761 | 1.53k | if (allocate) { |
1762 | 773 | if (!the->minimumHeapCount) { |
1763 | 0 | fxReport(the, "# Slot allocation: failed in fixed size heap\n"); |
1764 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
1765 | 0 | } |
1766 | 773 | fxGrowSlots(the, !(the->collectFlag & XS_SKIPPED_COLLECT_FLAG) ? the->minimumHeapCount : 64); |
1767 | 773 | } |
1768 | 1.53k | goto again; |
1769 | 0 | return C_NULL; |
1770 | 62.4M | } |
1771 | | |
1772 | | void* fxRenewChunk(txMachine* the, void* theData, txSize size) |
1773 | 8.28k | { |
1774 | | #if mxNoChunks |
1775 | | txByte* aData = ((txByte*)theData) - sizeof(txChunk); |
1776 | | txChunk* aChunk = (txChunk*)aData; |
1777 | | size = fxAdjustChunkSize(the, size); |
1778 | | if (size <= aChunk->size) { |
1779 | | #if mxPoisonChunks |
1780 | | ASAN_POISON_MEMORY_REGION(aData + size, aChunk->size - size); |
1781 | | #endif |
1782 | | aChunk->size = size; |
1783 | | return theData; |
1784 | | } |
1785 | | return C_NULL; |
1786 | | #else |
1787 | 8.28k | txByte* aData = ((txByte*)theData) - sizeof(txChunk); |
1788 | 8.28k | txChunk* aChunk = (txChunk*)aData; |
1789 | 8.28k | txSize capacity = (txSize)(aChunk->temporary - aData); |
1790 | 8.28k | txBlock* aBlock = the->firstBlock; |
1791 | 8.28k | size = fxAdjustChunkSize(the, size); |
1792 | 8.28k | if (size <= capacity) { |
1793 | 0 | the->currentChunksSize += size - aChunk->size; |
1794 | 0 | if (the->peakChunksSize < the->currentChunksSize) |
1795 | 0 | the->peakChunksSize = the->currentChunksSize; |
1796 | 0 | aChunk->size = size; |
1797 | 0 | #ifdef mxMetering |
1798 | 0 | the->meterIndex += size * XS_CHUNK_ALLOCATION_METERING; |
1799 | 0 | #endif |
1800 | 0 | return theData; |
1801 | 0 | } |
1802 | 20.8k | while (aBlock) { |
1803 | 12.7k | if (aChunk->temporary == aBlock->current) { |
1804 | 144 | txSize delta = size - capacity; |
1805 | 144 | if (aBlock->current + delta <= aBlock->limit) { |
1806 | 134 | the->currentChunksSize += size - aChunk->size; |
1807 | 134 | if (the->peakChunksSize < the->currentChunksSize) |
1808 | 44 | the->peakChunksSize = the->currentChunksSize; |
1809 | 134 | aBlock->current += delta; |
1810 | 134 | aChunk->temporary = aBlock->current; |
1811 | 134 | aChunk->size = size; |
1812 | 134 | #ifdef mxSnapshot |
1813 | 134 | c_memset(aData + capacity, 0, delta); |
1814 | 134 | #endif |
1815 | 134 | #ifdef mxMetering |
1816 | 134 | the->meterIndex += size * XS_CHUNK_ALLOCATION_METERING; |
1817 | 134 | #endif |
1818 | 134 | return theData; |
1819 | 134 | } |
1820 | 10 | else { |
1821 | 10 | return C_NULL; |
1822 | 10 | } |
1823 | 144 | } |
1824 | 12.5k | aBlock = aBlock->nextBlock; |
1825 | 12.5k | } |
1826 | 8.13k | return C_NULL; |
1827 | 8.28k | #endif |
1828 | 8.28k | } |
1829 | | |
1830 | | #if mxAliasInstance |
1831 | | void fxShare(txMachine* the) |
1832 | | { |
1833 | | txID aliasCount = 0; |
1834 | | txSlot *heap, *slot, *limit; |
1835 | | |
1836 | | heap = the->firstHeap; |
1837 | | while (heap) { |
1838 | | slot = heap + 1; |
1839 | | limit = heap->value.reference; |
1840 | | while (slot < limit) { |
1841 | | if (slot->kind == XS_INSTANCE_KIND) { |
1842 | | txBoolean frozen = (slot->flag & XS_DONT_PATCH_FLAG) ? 1 : 0; |
1843 | | if (frozen) { |
1844 | | txSlot *property = slot->next; |
1845 | | while (property) { |
1846 | | if (property->kind == XS_ARRAY_KIND) { |
1847 | | txSlot* item = property->value.array.address; |
1848 | | txInteger length = (txInteger)fxGetIndexSize(the, property); |
1849 | | while (length > 0) { |
1850 | | if (item->kind != XS_ACCESSOR_KIND) |
1851 | | if (!(item->flag & XS_DONT_SET_FLAG)) |
1852 | | frozen = 0; |
1853 | | if (!(item->flag & XS_DONT_DELETE_FLAG)) |
1854 | | frozen = 0; |
1855 | | item++; |
1856 | | length--; |
1857 | | } |
1858 | | } |
1859 | | else { |
1860 | | if (property->kind != XS_ACCESSOR_KIND) |
1861 | | if (!(property->flag & XS_DONT_SET_FLAG)) |
1862 | | frozen = 0; |
1863 | | if (!(property->flag & XS_DONT_DELETE_FLAG)) |
1864 | | frozen = 0; |
1865 | | } |
1866 | | property = property->next; |
1867 | | } |
1868 | | } |
1869 | | if (frozen) |
1870 | | slot->ID = XS_NO_ID; |
1871 | | else |
1872 | | slot->ID = aliasCount++; |
1873 | | } |
1874 | | else if (slot->kind == XS_CLOSURE_KIND) { |
1875 | | txSlot* closure = slot->value.closure; |
1876 | | if (closure->flag & XS_DONT_SET_FLAG) |
1877 | | closure->flag |= XS_DONT_DELETE_FLAG; |
1878 | | else { |
1879 | | if (closure->ID == XS_NO_ID) |
1880 | | closure->ID = aliasCount++; |
1881 | | slot->flag &= ~XS_DONT_SET_FLAG; |
1882 | | } |
1883 | | } |
1884 | | slot->flag |= XS_MARK_FLAG; |
1885 | | slot++; |
1886 | | } |
1887 | | heap = heap->next; |
1888 | | } |
1889 | | the->aliasCount = aliasCount; |
1890 | | /* |
1891 | | fxReport(the, "# Share\n"); |
1892 | | fxReport(the, "# \tSlots: %ld\n", the->currentHeapCount); |
1893 | | fxReport(the, "# \t\tSymbols: %ld\n", the->keyIndex); |
1894 | | fxReport(the, "# \t\tInstances: %ld\n", aliasCount); |
1895 | | fxReport(the, "# \tChunks: %ld bytes\n", the->currentChunksSize); |
1896 | | */ |
1897 | | } |
1898 | | #endif |
1899 | | |
1900 | | void fxSweep(txMachine* the) |
1901 | 4.31k | { |
1902 | 4.31k | txSize aTotal; |
1903 | | #if mxNoChunks |
1904 | | txChunk** address; |
1905 | | txChunk* chunk; |
1906 | | #else |
1907 | 4.31k | txBlock* aBlock; |
1908 | 4.31k | txByte* limit; |
1909 | 4.31k | txByte* next; |
1910 | 4.31k | #endif |
1911 | 4.31k | txByte* current; |
1912 | 4.31k | txByte* temporary; |
1913 | 4.31k | txSize aSize; |
1914 | 4.31k | txByte** aCodeAddress; |
1915 | 4.31k | txSlot* aSlot; |
1916 | 4.31k | txSlot* bSlot; |
1917 | 4.31k | txSlot* cSlot; |
1918 | 4.31k | txSlot* freeSlot; |
1919 | 4.31k | txJump* jump; |
1920 | | |
1921 | | #ifdef mxNever |
1922 | | startTime(&gxSweepChunkTime); |
1923 | | #endif |
1924 | | |
1925 | 4.31k | aTotal = 0; |
1926 | | #if mxNoChunks |
1927 | | address = (txChunk**)&(the->firstBlock); |
1928 | | while ((chunk = *address)) { |
1929 | | aSize = chunk->size; |
1930 | | if (aSize & mxChunkFlag) { |
1931 | | aSize &= ~mxChunkFlag; |
1932 | | temporary = c_malloc_noforcefail(aSize); |
1933 | | if (!temporary) |
1934 | | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); // should never happen |
1935 | | c_memcpy(temporary, chunk, aSize); |
1936 | | ((txChunk*)temporary)->size = aSize; |
1937 | | chunk->temporary = temporary; |
1938 | | address = (txChunk**)&(((txChunk*)temporary)->temporary); |
1939 | | aTotal += aSize; |
1940 | | } |
1941 | | else { |
1942 | | *address = (txChunk*)(chunk->temporary); |
1943 | | c_free(chunk); |
1944 | | } |
1945 | | } |
1946 | | #else |
1947 | 4.31k | aBlock = the->firstBlock; |
1948 | 14.9k | while (aBlock) { |
1949 | 10.6k | current = ((txByte*)aBlock) + sizeof(txBlock); |
1950 | 10.6k | limit = aBlock->current; |
1951 | 10.6k | temporary = current; |
1952 | 43.5M | while (current < limit) { |
1953 | 43.5M | aSize = ((txChunk*)current)->size; |
1954 | 43.5M | next = ((txChunk*)current)->temporary; |
1955 | 43.5M | if (aSize & mxChunkFlag) { |
1956 | 40.1M | aSize &= ~mxChunkFlag; |
1957 | 40.1M | ((txChunk*)current)->temporary = temporary; |
1958 | 40.1M | temporary += aSize; |
1959 | 40.1M | aTotal += aSize; |
1960 | 40.1M | } |
1961 | 3.46M | else { |
1962 | 3.46M | ((txChunk*)current)->temporary = C_NULL; |
1963 | 3.46M | } |
1964 | 43.5M | ((txChunk*)current)->size = (txSize)(next - current); |
1965 | 43.5M | current = next; |
1966 | 43.5M | } |
1967 | 10.6k | aBlock->temporary = temporary; |
1968 | 10.6k | aBlock = aBlock->nextBlock; |
1969 | 10.6k | } |
1970 | 4.31k | #endif |
1971 | 4.31k | the->currentChunksSize = aTotal; |
1972 | | |
1973 | 4.31k | aCodeAddress = &(the->code); |
1974 | 4.31k | aSlot = the->frame; |
1975 | 12.8k | while (aSlot) { |
1976 | 8.58k | mxCheck(the, aSlot->kind == XS_FRAME_KIND); |
1977 | 8.58k | if ((aSlot->flag & XS_C_FLAG) == 0) { |
1978 | 0 | bSlot = (aSlot + 2)->value.reference->next; |
1979 | 0 | if (bSlot->kind == XS_CODE_KIND) { |
1980 | 0 | current = bSlot->value.code.address; |
1981 | 0 | temporary = (txByte*)(((txChunk*)(current - sizeof(txChunk)))->temporary); |
1982 | 0 | if (temporary) { |
1983 | 0 | temporary += sizeof(txChunk); |
1984 | 0 | *aCodeAddress += temporary - current; |
1985 | 0 | } |
1986 | 0 | } |
1987 | 0 | } |
1988 | 8.58k | else { |
1989 | 8.58k | current = *aCodeAddress; |
1990 | 8.58k | if (current) { |
1991 | 0 | temporary = (txByte*)(((txChunk*)(current - sizeof(txChunk)))->temporary); |
1992 | 0 | if (temporary) |
1993 | 0 | *aCodeAddress = temporary + sizeof(txChunk); |
1994 | 0 | } |
1995 | 8.58k | } |
1996 | 8.58k | aCodeAddress = &(aSlot->value.frame.code); |
1997 | 8.58k | aSlot = aSlot->next; |
1998 | 8.58k | } |
1999 | | |
2000 | 4.31k | jump = the->firstJump; |
2001 | 17.2k | while (jump) { |
2002 | 12.9k | if (jump->flag) { |
2003 | 0 | aSlot = jump->frame; |
2004 | 0 | bSlot = (aSlot + 2)->value.reference->next; |
2005 | 0 | if (bSlot->kind == XS_CODE_KIND) { |
2006 | 0 | current = bSlot->value.code.address; |
2007 | 0 | temporary = (txByte*)(((txChunk*)(current - sizeof(txChunk)))->temporary); |
2008 | 0 | if (temporary) { |
2009 | 0 | temporary += sizeof(txChunk); |
2010 | 0 | jump->code += temporary - current; |
2011 | 0 | } |
2012 | 0 | } |
2013 | 0 | } |
2014 | 12.9k | else { |
2015 | 12.9k | current = jump->code; |
2016 | 12.9k | if (current) { |
2017 | 0 | temporary = (txByte*)(((txChunk*)(current - sizeof(txChunk)))->temporary); |
2018 | 0 | if (temporary) |
2019 | 0 | jump->code = temporary + sizeof(txChunk); |
2020 | 0 | } |
2021 | 12.9k | } |
2022 | 12.9k | jump = jump->nextJump; |
2023 | 12.9k | } |
2024 | | |
2025 | 4.31k | aSlot = the->stack; |
2026 | 9.36M | while (aSlot < the->stackTop) { |
2027 | 9.36M | fxSweepValue(the, aSlot); |
2028 | 9.36M | aSlot++; |
2029 | 9.36M | } |
2030 | 4.31k | aSlot = the->cRoot; |
2031 | 4.31k | while (aSlot) { |
2032 | 0 | fxSweepValue(the, aSlot); |
2033 | 0 | aSlot = aSlot->next; |
2034 | 0 | } |
2035 | | |
2036 | | #ifdef mxNever |
2037 | | stopTime(&gxSweepChunkTime); |
2038 | | startTime(&gxSweepSlotTime); |
2039 | | #endif |
2040 | | |
2041 | 4.31k | aTotal = 0; |
2042 | 4.31k | freeSlot = C_NULL; |
2043 | 4.31k | aSlot = the->firstHeap; |
2044 | 11.5k | while (aSlot) { |
2045 | 7.22k | bSlot = aSlot + 1; |
2046 | 7.22k | cSlot = aSlot->value.reference; |
2047 | 236M | while (bSlot < cSlot) { |
2048 | 236M | if (bSlot->flag & XS_MARK_FLAG) { |
2049 | 122M | bSlot->flag &= ~XS_MARK_FLAG; |
2050 | 122M | fxSweepValue(the, bSlot); |
2051 | 122M | aTotal++; |
2052 | 122M | } |
2053 | 113M | else { |
2054 | 113M | #ifndef mxLink |
2055 | 113M | if (bSlot->kind == XS_HOST_KIND) { |
2056 | 471 | if (bSlot->flag & XS_HOST_HOOKS_FLAG) { |
2057 | 0 | if (bSlot->value.host.variant.hooks->destructor) |
2058 | 0 | (*(bSlot->value.host.variant.hooks->destructor))(bSlot->value.host.data); |
2059 | 0 | } |
2060 | 471 | else if (bSlot->value.host.variant.destructor) |
2061 | 0 | (*(bSlot->value.host.variant.destructor))(bSlot->value.host.data); |
2062 | 471 | } |
2063 | 113M | #endif |
2064 | | // if (bSlot->kind == XS_MODULE_KIND) { |
2065 | | // char* name = fxGetKeyName(the, bSlot->value.module.id); |
2066 | | // fprintf(stderr, "gc module %d %s\n", bSlot->value.module.id, name); |
2067 | | // } |
2068 | | #if mxInstrument |
2069 | | if ((bSlot->kind == XS_MODULE_KIND) && (bSlot->ID == XS_MODULE_BEHAVIOR)) |
2070 | | the->loadedModulesCount--; |
2071 | | #endif |
2072 | 113M | bSlot->kind = XS_UNDEFINED_KIND; |
2073 | 113M | bSlot->next = freeSlot; |
2074 | | #if mxPoisonSlots |
2075 | | ASAN_POISON_MEMORY_REGION(&bSlot->value, sizeof(bSlot->value)); |
2076 | | #endif |
2077 | 113M | freeSlot = bSlot; |
2078 | 113M | } |
2079 | 236M | bSlot++; |
2080 | 236M | } |
2081 | 7.22k | aSlot = aSlot->next; |
2082 | 7.22k | } |
2083 | 4.31k | the->currentHeapCount = aTotal; |
2084 | 4.31k | the->freeHeap = freeSlot; |
2085 | | |
2086 | | #ifdef mxNever |
2087 | | stopTime(&gxSweepSlotTime); |
2088 | | startTime(&gxCompactChunkTime); |
2089 | | #endif |
2090 | | |
2091 | | #if mxNoChunks |
2092 | | address = (txChunk**)&(the->firstBlock); |
2093 | | while ((chunk = *address)) { |
2094 | | aSize = chunk->size; |
2095 | | if (aSize & mxChunkFlag) { |
2096 | | *address = (txChunk*)(chunk->temporary); |
2097 | | c_free(chunk); |
2098 | | } |
2099 | | else { |
2100 | | address = (txChunk**)&(chunk->temporary); |
2101 | | } |
2102 | | } |
2103 | | #else |
2104 | 4.31k | aBlock = the->firstBlock; |
2105 | 14.9k | while (aBlock) { |
2106 | 10.6k | txByte* former = C_NULL; |
2107 | 10.6k | current = ((txByte*)aBlock) + sizeof(txBlock); |
2108 | 10.6k | limit = aBlock->current; |
2109 | 43.5M | while (current < limit) { |
2110 | 43.5M | aSize = ((txChunk*)current)->size; |
2111 | 43.5M | next = current + aSize; |
2112 | 43.5M | if ((temporary = ((txChunk*)current)->temporary)) { |
2113 | 40.1M | if (former) { |
2114 | 40.1M | ((txChunk*)former)->temporary = temporary; |
2115 | 40.1M | ((txChunk*)former)->size = (txSize)(temporary - former); |
2116 | 40.1M | } |
2117 | 40.1M | if (temporary != current) |
2118 | 2.03M | c_memmove(temporary, current, aSize); |
2119 | 40.1M | former = temporary; |
2120 | 40.1M | } |
2121 | 43.5M | current = next; |
2122 | 43.5M | } |
2123 | 10.6k | if (former) { |
2124 | 10.5k | ((txChunk*)former)->temporary = aBlock->temporary; |
2125 | 10.5k | ((txChunk*)former)->size = (txSize)(aBlock->temporary - former); |
2126 | 10.5k | } |
2127 | 10.6k | aBlock->current = aBlock->temporary; |
2128 | 10.6k | aBlock->temporary = C_NULL; |
2129 | 10.6k | aBlock = aBlock->nextBlock; |
2130 | 10.6k | } |
2131 | 4.31k | #endif |
2132 | | |
2133 | | #ifdef mxNever |
2134 | | stopTime(&gxCompactChunkTime); |
2135 | | #endif |
2136 | 4.31k | } |
2137 | | |
2138 | | void fxSweepValue(txMachine* the, txSlot* theSlot) |
2139 | 232M | { |
2140 | 232M | txSlot* aSlot; |
2141 | 232M | txByte* data; |
2142 | | |
2143 | 232M | #define mxSweepChunk(_THE_DATA, _THE_DATA_TYPE) \ |
2144 | 232M | if ((data = (txByte*)(((txChunk*)(((txByte*)(_THE_DATA)) - sizeof(txChunk)))->temporary))) \ |
2145 | 42.4M | ((_THE_DATA)) = (_THE_DATA_TYPE)(data + sizeof(txChunk)) |
2146 | | |
2147 | 232M | switch (theSlot->kind) { |
2148 | 4.26M | case XS_STRING_KIND: |
2149 | 4.26M | mxSweepChunk(theSlot->value.string, txString); |
2150 | 4.26M | break; |
2151 | 0 | case XS_BIGINT_KIND: |
2152 | 0 | mxSweepChunk(theSlot->value.bigint.data, txU4*); |
2153 | 0 | break; |
2154 | | |
2155 | 0 | case XS_ARGUMENTS_SLOPPY_KIND: |
2156 | 0 | case XS_ARGUMENTS_STRICT_KIND: |
2157 | 43.8M | case XS_ARRAY_KIND: |
2158 | 43.8M | case XS_STACK_KIND: |
2159 | 43.8M | if ((aSlot = theSlot->value.array.address)) { |
2160 | | #if mxNoChunks |
2161 | | mxSweepChunk(theSlot->value.array.address, txSlot*); |
2162 | | aSlot = theSlot->value.array.address; |
2163 | | #endif |
2164 | 35.6M | txChunk* chunk = (txChunk*)(((txByte*)aSlot) - sizeof(txChunk)); |
2165 | 35.6M | txIndex aLength = chunk->size / sizeof(txSlot); |
2166 | 35.6M | if (aLength > theSlot->value.array.length) |
2167 | 0 | aLength = theSlot->value.array.length; |
2168 | 136M | while (aLength) { |
2169 | 100M | fxSweepValue(the, aSlot); |
2170 | 100M | aSlot++; |
2171 | 100M | aLength--; |
2172 | 100M | } |
2173 | | #if mxNoChunks |
2174 | | #else |
2175 | 35.6M | mxSweepChunk(theSlot->value.array.address, txSlot*); |
2176 | 35.6M | #endif |
2177 | 35.6M | } |
2178 | 43.8M | break; |
2179 | 144 | case XS_ARRAY_BUFFER_KIND: |
2180 | 144 | if (theSlot->value.arrayBuffer.address) |
2181 | 107 | mxSweepChunk(theSlot->value.arrayBuffer.address, txByte*); |
2182 | 144 | break; |
2183 | 0 | case XS_CODE_KIND: |
2184 | 0 | mxSweepChunk(theSlot->value.code.address, txByte*); |
2185 | 0 | break; |
2186 | 4.31k | case XS_GLOBAL_KIND: |
2187 | 4.31k | mxSweepChunk(theSlot->value.table.address, txSlot**); |
2188 | 4.31k | break; |
2189 | 4.41k | case XS_HOST_KIND: |
2190 | 4.41k | if (theSlot->value.host.data) { |
2191 | 105 | if (theSlot->flag & XS_HOST_CHUNK_FLAG) |
2192 | 105 | mxSweepChunk(theSlot->value.host.data, void*); |
2193 | 105 | } |
2194 | 4.41k | break; |
2195 | 0 | case XS_IDS_KIND: |
2196 | 0 | if (theSlot->value.IDs) |
2197 | 0 | mxSweepChunk(theSlot->value.IDs, txID*); |
2198 | 0 | break; |
2199 | 0 | case XS_REGEXP_KIND: |
2200 | 0 | if (theSlot->value.regexp.code) |
2201 | 0 | mxSweepChunk(theSlot->value.regexp.code, void*); |
2202 | 0 | if (theSlot->value.regexp.data) |
2203 | 0 | mxSweepChunk(theSlot->value.regexp.data, void*); |
2204 | 0 | break; |
2205 | 2.44M | case XS_KEY_KIND: |
2206 | 2.44M | if (theSlot->value.key.string) |
2207 | 2.44M | mxSweepChunk(theSlot->value.key.string, txString); |
2208 | 2.44M | break; |
2209 | 0 | case XS_MAP_KIND: |
2210 | 0 | case XS_SET_KIND: |
2211 | 0 | mxSweepChunk(theSlot->value.table.address, txSlot**); |
2212 | 0 | break; |
2213 | 232M | } |
2214 | 232M | } |