Skip to content

Commit 5eb7843

Browse files
authored
Merge pull request #52 from coder-mike/51-private-heaps
2 parents fae1b0f + 5750d6d commit 5eb7843

File tree

10 files changed

+195
-126
lines changed

10 files changed

+195
-126
lines changed

dist-c/microvium.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,7 +1365,7 @@ static uint16_t getBucketOffsetEnd(TsBucket* bucket);
13651365
static uint16_t getSectionSize(VM* vm, mvm_TeBytecodeSection section);
13661366
static Value vm_intToStr(VM* vm, int32_t i);
13671367
static Value vm_newStringFromCStrNT(VM* vm, const char* s);
1368-
static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader);
1368+
static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader, void* context);
13691369
static LongPtr vm_toStringUtf8_long(VM* vm, Value value, size_t* out_sizeBytes);
13701370
static LongPtr vm_findScopedVariable(VM* vm, uint16_t index);
13711371
static Value vm_cloneContainer(VM* vm, Value* pArr);
@@ -1516,6 +1516,17 @@ static int32_t mvm_float64ToInt32(MVM_FLOAT64 value);
15161516
#define MVM_PORT_INT32_OVERFLOW_CHECKS 1
15171517
#endif
15181518

1519+
// Backwards compatibility with non-contextual malloc
1520+
#ifndef MVM_CONTEXTUAL_MALLOC
1521+
#define MVM_CONTEXTUAL_MALLOC(size, context) MVM_MALLOC(size)
1522+
#endif
1523+
1524+
// Backwards compatibility with non-contextual free
1525+
#ifndef MVM_CONTEXTUAL_FREE
1526+
#define MVM_CONTEXTUAL_FREE(size, context) MVM_FREE(size)
1527+
#endif
1528+
1529+
15191530

15201531

15211532

@@ -4076,7 +4087,7 @@ TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE lpBytecode, size_t byteco
40764087
return MVM_E_BYTECODE_REQUIRES_FLOAT_SUPPORT;
40774088
}
40784089

4079-
err = vm_validatePortFileMacros(lpBytecode, &header);
4090+
err = vm_validatePortFileMacros(lpBytecode, &header, context);
40804091
if (err) return err;
40814092

40824093
uint16_t importTableSize = header.sectionOffsets[vm_sectionAfter(vm, BCS_IMPORT_TABLE)] - header.sectionOffsets[BCS_IMPORT_TABLE];
@@ -7674,7 +7685,7 @@ void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb) {
76747685
* point to actual bytecode, whereas pHeader should point to a local copy that's
76757686
* been validated.
76767687
*/
7677-
static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader) {
7688+
static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader, void* context) {
76787689
uint32_t x1 = 0x12345678;
76797690
uint32_t x2 = 0x12345678;
76807691
uint32_t x3 = 0x87654321;
@@ -7718,8 +7729,8 @@ static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsByt
77187729
if ((!MVM_NATIVE_POINTER_IS_16_BIT) && (sizeof(void*) == 2)) return MVM_E_EXPECTED_POINTER_SIZE_NOT_TO_BE_16_BIT;
77197730

77207731
#if MVM_USE_SINGLE_RAM_PAGE
7721-
void* ptr = MVM_MALLOC(2);
7722-
MVM_FREE(ptr);
7732+
void* ptr = MVM_CONTEXTUAL_MALLOC(2, context);
7733+
MVM_CONTEXTUAL_FREE(ptr, context);
77237734
if ((intptr_t)ptr - (intptr_t)MVM_RAM_PAGE_ADDR > 0xffff) return MVM_E_MALLOC_NOT_WITHIN_RAM_PAGE;
77247735
#endif // MVM_USE_SINGLE_RAM_PAGE
77257736

@@ -7781,7 +7792,7 @@ static TeError vm_newError(VM* vm, TeError err) {
77817792
}
77827793

77837794
static void* vm_malloc(VM* vm, size_t size) {
7784-
void* result = MVM_MALLOC(size);
7795+
void* result = MVM_CONTEXTUAL_MALLOC(size, vm->context);
77857796

77867797
#if MVM_SAFE_MODE && MVM_USE_SINGLE_RAM_PAGE
77877798
// See comment on MVM_RAM_PAGE_ADDR in microvium_port_example.h
@@ -7792,12 +7803,15 @@ static void* vm_malloc(VM* vm, size_t size) {
77927803

77937804
// Note: mvm_free frees the VM, while vm_free is the counterpart to vm_malloc
77947805
static void vm_free(VM* vm, void* ptr) {
7806+
// Capture the context before freeing the ptr, since the pointer could be the vm
7807+
void* context = vm->context;
7808+
77957809
#if MVM_SAFE_MODE && MVM_USE_SINGLE_RAM_PAGE
77967810
// See comment on MVM_RAM_PAGE_ADDR in microvium_port_example.h
77977811
VM_ASSERT(vm, !ptr || ((intptr_t)ptr - (intptr_t)MVM_RAM_PAGE_ADDR <= 0xFFFF));
77987812
#endif
77997813

7800-
MVM_FREE(ptr);
7814+
MVM_CONTEXTUAL_FREE(ptr, context);
78017815
}
78027816

78037817
static mvm_TeError vm_uint8ArrayNew(VM* vm, Value* slot) {

dist-c/microvium.h

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ typedef enum mvm_TeType {
9090
VM_T_END,
9191
} mvm_TeType;
9292

93+
// Prefix to attach to exported microvium API functions. If a user doesn't
94+
// specify this, we just set it up as the empty macro.
95+
#ifndef MVM_EXPORT
96+
#define MVM_EXPORT
97+
#endif
98+
9399
typedef struct mvm_VM mvm_VM;
94100

95101
typedef mvm_TeError (*mvm_TfHostFunction)(mvm_VM* vm, mvm_HostFunctionID hostFunctionID, mvm_Value* result, mvm_Value* args, uint8_t argCount);
@@ -169,12 +175,12 @@ extern "C" {
169175
* @param context Any value. The context for a VM can be retrieved later using
170176
* `mvm_getContext`. It can be used to attach user-defined data to a VM.
171177
*/
172-
mvm_TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE snapshotBytecode, size_t bytecodeSize, void* context, mvm_TfResolveImport resolveImport);
178+
MVM_EXPORT mvm_TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE snapshotBytecode, size_t bytecodeSize, void* context, mvm_TfResolveImport resolveImport);
173179

174180
/**
175181
* Free all memory associated with a VM. The VM must not be used again after freeing.
176182
*/
177-
void mvm_free(mvm_VM* vm);
183+
MVM_EXPORT void mvm_free(mvm_VM* vm);
178184

179185
/**
180186
* Call a function in the VM
@@ -189,21 +195,21 @@ void mvm_free(mvm_VM* vm);
189195
* MVM_E_UNCAUGHT_EXCEPTION and the exception value will be put into
190196
* `out_result`
191197
*/
192-
mvm_TeError mvm_call(mvm_VM* vm, mvm_Value func, mvm_Value* out_result, mvm_Value* args, uint8_t argCount);
198+
MVM_EXPORT mvm_TeError mvm_call(mvm_VM* vm, mvm_Value func, mvm_Value* out_result, mvm_Value* args, uint8_t argCount);
193199

194-
void* mvm_getContext(mvm_VM* vm);
200+
MVM_EXPORT void* mvm_getContext(mvm_VM* vm);
195201

196-
void mvm_initializeHandle(mvm_VM* vm, mvm_Handle* handle); // Handle must be released by mvm_releaseHandle
197-
void mvm_cloneHandle(mvm_VM* vm, mvm_Handle* target, const mvm_Handle* source); // Target must be released by mvm_releaseHandle
198-
mvm_TeError mvm_releaseHandle(mvm_VM* vm, mvm_Handle* handle);
199-
static inline mvm_Value mvm_handleGet(const mvm_Handle* handle) { return handle->_value; }
200-
static inline void mvm_handleSet(mvm_Handle* handle, mvm_Value value) { handle->_value = value; }
202+
MVM_EXPORT void mvm_initializeHandle(mvm_VM* vm, mvm_Handle* handle); // Handle must be released by mvm_releaseHandle
203+
MVM_EXPORT void mvm_cloneHandle(mvm_VM* vm, mvm_Handle* target, const mvm_Handle* source); // Target must be released by mvm_releaseHandle
204+
MVM_EXPORT mvm_TeError mvm_releaseHandle(mvm_VM* vm, mvm_Handle* handle);
205+
MVM_EXPORT static inline mvm_Value mvm_handleGet(const mvm_Handle* handle) { return handle->_value; }
206+
MVM_EXPORT static inline void mvm_handleSet(mvm_Handle* handle, mvm_Value value) { handle->_value = value; }
201207

202208
/**
203209
* Roughly like the `typeof` operator in JS, except with distinct values for
204210
* null and arrays
205211
*/
206-
mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value);
212+
MVM_EXPORT mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value);
207213

208214
/**
209215
* Converts the value to a string encoded as UTF-8.
@@ -226,21 +232,21 @@ mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value);
226232
* [memory-management.md](https://github.com/coder-mike/microvium/blob/master/doc/native-vm/memory-management.md)
227233
* for details.
228234
*/
229-
const char* mvm_toStringUtf8(mvm_VM* vm, mvm_Value value, size_t* out_sizeBytes);
235+
MVM_EXPORT const char* mvm_toStringUtf8(mvm_VM* vm, mvm_Value value, size_t* out_sizeBytes);
230236

231237
/**
232238
* Convert the value to a bool based on its truthiness.
233239
*
234240
* See https://developer.mozilla.org/en-US/docs/Glossary/Truthy
235241
*/
236-
bool mvm_toBool(mvm_VM* vm, mvm_Value value);
242+
MVM_EXPORT bool mvm_toBool(mvm_VM* vm, mvm_Value value);
237243

238244
/**
239245
* Converts the value to a 32-bit signed integer.
240246
*
241247
* The result of this should be the same as `value|0` in JavaScript code.
242248
*/
243-
int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value);
249+
MVM_EXPORT int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value);
244250

245251
#if MVM_SUPPORT_FLOAT
246252
/**
@@ -250,7 +256,7 @@ int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value);
250256
*
251257
* For efficiency, use mvm_toInt32 instead if your value is an integer.
252258
*/
253-
MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value);
259+
MVM_EXPORT MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value);
254260

255261
/**
256262
* Create a number value in the VM.
@@ -260,23 +266,23 @@ MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value);
260266
* Design note: mvm_newNumber creates a number *from* a float64, so it's named
261267
* `newNumber` and not `newFloat64`
262268
*/
263-
mvm_Value mvm_newNumber(mvm_VM* vm, MVM_FLOAT64 value);
269+
MVM_EXPORT mvm_Value mvm_newNumber(mvm_VM* vm, MVM_FLOAT64 value);
264270
#endif
265271

266-
bool mvm_isNaN(mvm_Value value);
272+
MVM_EXPORT bool mvm_isNaN(mvm_Value value);
267273

268274
extern const mvm_Value mvm_undefined;
269275
extern const mvm_Value mvm_null;
270-
mvm_Value mvm_newBoolean(bool value);
271-
mvm_Value mvm_newInt32(mvm_VM* vm, int32_t value);
276+
MVM_EXPORT mvm_Value mvm_newBoolean(bool value);
277+
MVM_EXPORT mvm_Value mvm_newInt32(mvm_VM* vm, int32_t value);
272278

273279
/**
274280
* Create a new string in Microvium memory.
275281
*
276282
* @param valueUtf8 The a pointer to the string content.
277283
* @param sizeBytes The size in bytes of the string, excluding any null terminator.
278284
*/
279-
mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes);
285+
MVM_EXPORT mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes);
280286

281287
/**
282288
* A Uint8Array in Microvium is an efficient buffer of bytes. It is mutable but
@@ -288,7 +294,7 @@ mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes);
288294
*
289295
* See also: mvm_uint8ArrayToBytes
290296
*/
291-
mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size);
297+
MVM_EXPORT mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size);
292298

293299
/**
294300
* Given a Uint8Array, this will give a pointer to its data and its size (in
@@ -302,7 +308,7 @@ mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size);
302308
*
303309
* See also: mvm_newUint8Array
304310
*/
305-
mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t** out_data, size_t* out_size);
311+
MVM_EXPORT mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t** out_data, size_t* out_size);
306312

307313
/**
308314
* Resolves (finds) the values exported by the VM, identified by ID.
@@ -315,7 +321,7 @@ mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t
315321
* captured by a mvm_Handle. In typical usage, exports will each be function
316322
* values, but any value type is valid.
317323
*/
318-
mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value* results, uint8_t count);
324+
MVM_EXPORT mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value* results, uint8_t count);
319325

320326
/**
321327
* Run a garbage collection cycle.
@@ -327,12 +333,12 @@ mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value*
327333
* of needed as the amount of space used after the last compaction, and then
328334
* adding blocks as-necessary.
329335
*/
330-
void mvm_runGC(mvm_VM* vm, bool squeeze);
336+
MVM_EXPORT void mvm_runGC(mvm_VM* vm, bool squeeze);
331337

332338
/**
333339
* Compares two values for equality. The same semantics as JavaScript `===`
334340
*/
335-
bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b);
341+
MVM_EXPORT bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b);
336342

337343
/**
338344
* The current bytecode address being executed (relative to the beginning of the
@@ -341,12 +347,12 @@ bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b);
341347
* This value can be looked up in the map file generated by the CLI flag
342348
* `--map-file`
343349
*/
344-
uint16_t mvm_getCurrentAddress(mvm_VM* vm);
350+
MVM_EXPORT uint16_t mvm_getCurrentAddress(mvm_VM* vm);
345351

346352
/**
347353
* Get stats about the VM memory
348354
*/
349-
void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats);
355+
MVM_EXPORT void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats);
350356

351357
#if MVM_INCLUDE_SNAPSHOT_CAPABILITY
352358
/**
@@ -368,7 +374,7 @@ void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats);
368374
* Note: The result is malloc'd on the host heap, and so needs to be freed with
369375
* a call to *free*.
370376
*/
371-
void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size);
377+
MVM_EXPORT void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size);
372378
#endif // MVM_INCLUDE_SNAPSHOT_CAPABILITY
373379

374380
#if MVM_INCLUDE_DEBUG_CAPABILITY
@@ -389,12 +395,12 @@ void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size);
389395
* Setting a breakpoint a second time on the same address of an existing active
390396
* breakpoint will have no effect.
391397
*/
392-
void mvm_dbg_setBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress);
398+
MVM_EXPORT void mvm_dbg_setBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress);
393399

394400
/**
395401
* Remove a breakpoint added by mvm_dbg_setBreakpoint
396402
*/
397-
void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress);
403+
MVM_EXPORT void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress);
398404

399405
/**
400406
* Set the function to be called when any breakpoint is hit.
@@ -412,7 +418,7 @@ void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress);
412418
* still active. This should *NOT* be used to continue execution, but could
413419
* theoretically be used to evaluate debug watch expressions.
414420
*/
415-
void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb);
421+
MVM_EXPORT void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb);
416422
#endif // MVM_INCLUDE_DEBUG_CAPABILITY
417423

418424
#ifdef __cplusplus

dist-c/microvium_port_example.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,13 @@ static uint16_t crc16(MVM_LONG_PTR_TYPE lp, uint16_t size) {
279279
/**
280280
* Implementation of malloc and free to use.
281281
*
282-
* Note that MVM_FREE needs to accept null pointers as well.
282+
* Note that MVM_CONTEXTUAL_FREE needs to accept null pointers as well.
283283
*
284-
* If MVM_USE_SINGLE_RAM_PAGE is set, pointers returned by MVM_MALLOC must
285-
* always be within 64kB of MVM_RAM_PAGE_ADDR.
284+
* If MVM_USE_SINGLE_RAM_PAGE is set, pointers returned by MVM_CONTEXTUAL_MALLOC
285+
* must always be within 64kB of MVM_RAM_PAGE_ADDR.
286+
*
287+
* The `context` passed to these macros is whatever value that the host passes
288+
* to `mvm_restore`. It can be any value that fits in a pointer.
286289
*/
287-
#define MVM_MALLOC(size) malloc(size)
288-
#define MVM_FREE(ptr) free(ptr)
290+
#define MVM_CONTEXTUAL_MALLOC(size, context) malloc(size)
291+
#define MVM_CONTEXTUAL_FREE(ptr, context) free(ptr)

native-vm/microvium.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2596,7 +2596,7 @@ TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE lpBytecode, size_t byteco
25962596
return MVM_E_BYTECODE_REQUIRES_FLOAT_SUPPORT;
25972597
}
25982598

2599-
err = vm_validatePortFileMacros(lpBytecode, &header);
2599+
err = vm_validatePortFileMacros(lpBytecode, &header, context);
26002600
if (err) return err;
26012601

26022602
uint16_t importTableSize = header.sectionOffsets[vm_sectionAfter(vm, BCS_IMPORT_TABLE)] - header.sectionOffsets[BCS_IMPORT_TABLE];
@@ -6194,7 +6194,7 @@ void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb) {
61946194
* point to actual bytecode, whereas pHeader should point to a local copy that's
61956195
* been validated.
61966196
*/
6197-
static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader) {
6197+
static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader, void* context) {
61986198
uint32_t x1 = 0x12345678;
61996199
uint32_t x2 = 0x12345678;
62006200
uint32_t x3 = 0x87654321;
@@ -6238,8 +6238,8 @@ static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsByt
62386238
if ((!MVM_NATIVE_POINTER_IS_16_BIT) && (sizeof(void*) == 2)) return MVM_E_EXPECTED_POINTER_SIZE_NOT_TO_BE_16_BIT;
62396239

62406240
#if MVM_USE_SINGLE_RAM_PAGE
6241-
void* ptr = MVM_MALLOC(2);
6242-
MVM_FREE(ptr);
6241+
void* ptr = MVM_CONTEXTUAL_MALLOC(2, context);
6242+
MVM_CONTEXTUAL_FREE(ptr, context);
62436243
if ((intptr_t)ptr - (intptr_t)MVM_RAM_PAGE_ADDR > 0xffff) return MVM_E_MALLOC_NOT_WITHIN_RAM_PAGE;
62446244
#endif // MVM_USE_SINGLE_RAM_PAGE
62456245

@@ -6301,7 +6301,7 @@ static TeError vm_newError(VM* vm, TeError err) {
63016301
}
63026302

63036303
static void* vm_malloc(VM* vm, size_t size) {
6304-
void* result = MVM_MALLOC(size);
6304+
void* result = MVM_CONTEXTUAL_MALLOC(size, vm->context);
63056305

63066306
#if MVM_SAFE_MODE && MVM_USE_SINGLE_RAM_PAGE
63076307
// See comment on MVM_RAM_PAGE_ADDR in microvium_port_example.h
@@ -6312,12 +6312,15 @@ static void* vm_malloc(VM* vm, size_t size) {
63126312

63136313
// Note: mvm_free frees the VM, while vm_free is the counterpart to vm_malloc
63146314
static void vm_free(VM* vm, void* ptr) {
6315+
// Capture the context before freeing the ptr, since the pointer could be the vm
6316+
void* context = vm->context;
6317+
63156318
#if MVM_SAFE_MODE && MVM_USE_SINGLE_RAM_PAGE
63166319
// See comment on MVM_RAM_PAGE_ADDR in microvium_port_example.h
63176320
VM_ASSERT(vm, !ptr || ((intptr_t)ptr - (intptr_t)MVM_RAM_PAGE_ADDR <= 0xFFFF));
63186321
#endif
63196322

6320-
MVM_FREE(ptr);
6323+
MVM_CONTEXTUAL_FREE(ptr, context);
63216324
}
63226325

63236326
static mvm_TeError vm_uint8ArrayNew(VM* vm, Value* slot) {

0 commit comments

Comments
 (0)