Welcome to mirror list, hosted at ThFree Co, Russian Federation.

memory.ts « runtime « wasm « mono « src - github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 1653b94af2b72d16da5607d767a53d5b0434178b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
import monoWasmThreads from "consts:monoWasmThreads";
import { Module, runtimeHelpers } from "./imports";
import { mono_assert } from "./types";
import { VoidPtr, NativePointer, ManagedPointer } from "./types/emscripten";
import * as cuint64 from "./cuint64";
import cwraps, { I52Error } from "./cwraps";

const alloca_stack: Array<VoidPtr> = [];
const alloca_buffer_size = 32 * 1024;
let alloca_base: VoidPtr, alloca_offset: VoidPtr, alloca_limit: VoidPtr;
let HEAPI64: BigInt64Array = <any>null;

function _ensure_allocated(): void {
    if (alloca_base)
        return;
    alloca_base = Module._malloc(alloca_buffer_size);
    alloca_offset = alloca_base;
    alloca_limit = <VoidPtr>(<any>alloca_base + alloca_buffer_size);
}

const is_bigint_supported = typeof BigInt !== "undefined" && typeof BigInt64Array !== "undefined";

export function temp_malloc(size: number): VoidPtr {
    _ensure_allocated();
    if (!alloca_stack.length)
        throw new Error("No temp frames have been created at this point");

    const result = alloca_offset;
    alloca_offset += <any>size;
    if (alloca_offset >= alloca_limit)
        throw new Error("Out of temp storage space");
    return result;
}

export function _create_temp_frame(): void {
    _ensure_allocated();
    alloca_stack.push(alloca_offset);
}

export function _release_temp_frame(): void {
    if (!alloca_stack.length)
        throw new Error("No temp frames have been created at this point");

    alloca_offset = <VoidPtr>alloca_stack.pop();
}

type _MemOffset = number | VoidPtr | NativePointer | ManagedPointer;
type _NumberOrPointer = number | VoidPtr | NativePointer | ManagedPointer;

function assert_int_in_range(value: Number, min: Number, max: Number) {
    mono_assert(Number.isSafeInteger(value), () => `Value is not an integer: ${value} (${typeof (value)})`);
    mono_assert(value >= min && value <= max, () => `Overflow: value ${value} is out of ${min} ${max} range`);
}

export function _zero_region(byteOffset: VoidPtr, sizeBytes: number): void {
    if (((<any>byteOffset % 4) === 0) && ((sizeBytes % 4) === 0))
        Module.HEAP32.fill(0, <any>byteOffset >>> 2, sizeBytes >>> 2);
    else
        Module.HEAP8.fill(0, <any>byteOffset, sizeBytes);
}

export function setB32(offset: _MemOffset, value: number | boolean): void {
    const boolValue = !!value;
    if (typeof (value) === "number")
        assert_int_in_range(value, 0, 1);
    Module.HEAP32[<any>offset >>> 2] = boolValue ? 1 : 0;
}

export function setU8(offset: _MemOffset, value: number): void {
    assert_int_in_range(value, 0, 0xFF);
    Module.HEAPU8[<any>offset] = value;
}

export function setU16(offset: _MemOffset, value: number): void {
    assert_int_in_range(value, 0, 0xFFFF);
    Module.HEAPU16[<any>offset >>> 1] = value;
}

export function setU32_unchecked(offset: _MemOffset, value: _NumberOrPointer): void {
    Module.HEAPU32[<any>offset >>> 2] = <number><any>value;
}

export function setU32(offset: _MemOffset, value: _NumberOrPointer): void {
    assert_int_in_range(<any>value, 0, 0xFFFF_FFFF);
    Module.HEAPU32[<any>offset >>> 2] = <number><any>value;
}

export function setI8(offset: _MemOffset, value: number): void {
    assert_int_in_range(value, -0x80, 0x7F);
    Module.HEAP8[<any>offset] = value;
}

export function setI16(offset: _MemOffset, value: number): void {
    assert_int_in_range(value, -0x8000, 0x7FFF);
    Module.HEAP16[<any>offset >>> 1] = value;
}

export function setI32_unchecked(offset: _MemOffset, value: number): void {
    Module.HEAP32[<any>offset >>> 2] = value;
}

export function setI32(offset: _MemOffset, value: number): void {
    assert_int_in_range(<any>value, -0x8000_0000, 0x7FFF_FFFF);
    Module.HEAP32[<any>offset >>> 2] = value;
}

function autoThrowI52(error: I52Error) {
    if (error === I52Error.NONE)
        return;

    switch (error) {
        case I52Error.NON_INTEGRAL:
            throw new Error("value was not an integer");
        case I52Error.OUT_OF_RANGE:
            throw new Error("value out of range");
        default:
            throw new Error("unknown internal error");
    }
}

/**
 * Throws for values which are not 52 bit integer. See Number.isSafeInteger()
 */
export function setI52(offset: _MemOffset, value: number): void {
    mono_assert(Number.isSafeInteger(value), () => `Value is not a safe integer: ${value} (${typeof (value)})`);
    const error = cwraps.mono_wasm_f64_to_i52(<any>offset, value);
    autoThrowI52(error);
}

/**
 * Throws for values which are not 52 bit integer or are negative. See Number.isSafeInteger().
 */
export function setU52(offset: _MemOffset, value: number): void {
    mono_assert(Number.isSafeInteger(value), () => `Value is not a safe integer: ${value} (${typeof (value)})`);
    mono_assert(value >= 0, "Can't convert negative Number into UInt64");
    const error = cwraps.mono_wasm_f64_to_u52(<any>offset, value);
    autoThrowI52(error);
}

export function setI64Big(offset: _MemOffset, value: bigint): void {
    mono_assert(is_bigint_supported, "BigInt is not supported.");
    mono_assert(typeof value === "bigint", () => `Value is not an bigint: ${value} (${typeof (value)})`);
    mono_assert(value >= min_int64_big && value <= max_int64_big, () => `Overflow: value ${value} is out of ${min_int64_big} ${max_int64_big} range`);

    HEAPI64[<any>offset >>> 3] = value;
}

export function setF32(offset: _MemOffset, value: number): void {
    mono_assert(typeof value === "number", () => `Value is not a Number: ${value} (${typeof (value)})`);
    Module.HEAPF32[<any>offset >>> 2] = value;
}

export function setF64(offset: _MemOffset, value: number): void {
    mono_assert(typeof value === "number", () => `Value is not a Number: ${value} (${typeof (value)})`);
    Module.HEAPF64[<any>offset >>> 3] = value;
}


export function getB32(offset: _MemOffset): boolean {
    return !!(Module.HEAP32[<any>offset >>> 2]);
}

export function getU8(offset: _MemOffset): number {
    return Module.HEAPU8[<any>offset];
}

export function getU16(offset: _MemOffset): number {
    return Module.HEAPU16[<any>offset >>> 1];
}

export function getU32(offset: _MemOffset): number {
    return Module.HEAPU32[<any>offset >>> 2];
}

export function getI8(offset: _MemOffset): number {
    return Module.HEAP8[<any>offset];
}

export function getI16(offset: _MemOffset): number {
    return Module.HEAP16[<any>offset >>> 1];
}

export function getI32(offset: _MemOffset): number {
    return Module.HEAP32[<any>offset >>> 2];
}

/**
 * Throws for Number.MIN_SAFE_INTEGER > value > Number.MAX_SAFE_INTEGER
 */
export function getI52(offset: _MemOffset): number {
    const result = cwraps.mono_wasm_i52_to_f64(<any>offset, runtimeHelpers._i52_error_scratch_buffer);
    const error = getI32(runtimeHelpers._i52_error_scratch_buffer);
    autoThrowI52(error);
    return result;
}

/**
 * Throws for 0 > value > Number.MAX_SAFE_INTEGER
 */
export function getU52(offset: _MemOffset): number {
    const result = cwraps.mono_wasm_u52_to_f64(<any>offset, runtimeHelpers._i52_error_scratch_buffer);
    const error = getI32(runtimeHelpers._i52_error_scratch_buffer);
    autoThrowI52(error);
    return result;
}

export function getI64Big(offset: _MemOffset): bigint {
    mono_assert(is_bigint_supported, "BigInt is not supported.");
    return HEAPI64[<any>offset >>> 3];
}

export function getF32(offset: _MemOffset): number {
    return Module.HEAPF32[<any>offset >>> 2];
}

export function getF64(offset: _MemOffset): number {
    return Module.HEAPF64[<any>offset >>> 3];
}

let max_int64_big: BigInt;
let min_int64_big: BigInt;
export function afterUpdateGlobalBufferAndViews(buffer: ArrayBufferLike): void {
    if (is_bigint_supported) {
        max_int64_big = BigInt("9223372036854775807");
        min_int64_big = BigInt("-9223372036854775808");
        HEAPI64 = new BigInt64Array(buffer);
    }
}

export function getCU64(offset: _MemOffset): cuint64.CUInt64 {
    const lo = getU32(offset);
    const hi = getU32(<any>offset + 4);
    return cuint64.pack32(lo, hi);
}

export function setCU64(offset: _MemOffset, value: cuint64.CUInt64): void {
    const [lo, hi] = cuint64.unpack32(value);
    setU32_unchecked(offset, lo);
    setU32_unchecked(<any>offset + 4, hi);
}

/// Allocates a new buffer of the given size on the Emscripten stack and passes a pointer to it to the callback.
/// Returns the result of the callback.  As usual with stack allocations, the buffer is freed when the callback returns.
/// Do not attempt to use the stack pointer after the callback is finished.
export function withStackAlloc<TResult>(bytesWanted: number, f: (ptr: VoidPtr) => TResult): TResult;
export function withStackAlloc<T1, TResult>(bytesWanted: number, f: (ptr: VoidPtr, ud1: T1) => TResult, ud1: T1): TResult;
export function withStackAlloc<T1, T2, TResult>(bytesWanted: number, f: (ptr: VoidPtr, ud1: T1, ud2: T2) => TResult, ud1: T1, ud2: T2): TResult;
export function withStackAlloc<T1, T2, T3, TResult>(bytesWanted: number, f: (ptr: VoidPtr, ud1: T1, ud2: T2, ud3: T3) => TResult, ud1: T1, ud2: T2, ud3: T3): TResult;
export function withStackAlloc<T1, T2, T3, TResult>(bytesWanted: number, f: (ptr: VoidPtr, ud1?: T1, ud2?: T2, ud3?: T3) => TResult, ud1?: T1, ud2?: T2, ud3?: T3): TResult {
    const sp = Module.stackSave();
    const ptr = Module.stackAlloc(bytesWanted);
    try {
        return f(ptr, ud1, ud2, ud3);
    } finally {
        Module.stackRestore(sp);
    }
}

// @bytes must be a typed array. space is allocated for it in the native heap
//  and it is copied to that location. returns the address of the allocation.
export function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr {
    const memoryOffset = Module._malloc(bytes.length);
    const heapBytes = new Uint8Array(Module.HEAPU8.buffer, <any>memoryOffset, bytes.length);
    heapBytes.set(bytes);
    return memoryOffset;
}

const BuiltinAtomics = globalThis.Atomics;

export const Atomics = monoWasmThreads ? {
    storeI32(offset: _MemOffset, value: number): void {

        BuiltinAtomics.store(Module.HEAP32, <any>offset >>> 2, value);
    },
    notifyI32(offset: _MemOffset, count: number): void {
        BuiltinAtomics.notify(Module.HEAP32, <any>offset >>> 2, count);
    }
} : {
    storeI32: setI32,
    notifyI32: () => { /*empty*/ }
};