mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-28 16:32:15 +00:00
external gc interface
This commit is contained in:
parent
41abc0166c
commit
0dcfcc7935
@ -1,16 +1,48 @@
|
|||||||
/// <reference path="./collector/index.d.ts" />
|
/// <reference path="./collector/index.d.ts" />
|
||||||
|
|
||||||
|
import { E_NOTIMPLEMENTED } from "./util/error";
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
@lazy
|
||||||
|
var GC_ROOT = new Set<usize>();
|
||||||
|
|
||||||
/** Garbage collector interface. */
|
/** Garbage collector interface. */
|
||||||
export namespace gc {
|
export namespace gc {
|
||||||
|
|
||||||
/** Whether the garbage collector interface is implemented. */
|
/** Whether the garbage collector interface is implemented. */
|
||||||
// @ts-ignore: decorator
|
// @ts-ignore: decorator
|
||||||
@lazy
|
@lazy
|
||||||
export const IMPLEMENTED: bool = isDefined(__ref_collect);
|
export const implemented: bool = isDefined(__ref_collect);
|
||||||
|
|
||||||
/** Performs a full garbage collection cycle. */
|
/** Performs a full garbage collection cycle. */
|
||||||
export function collect(): void {
|
export function collect(): void {
|
||||||
if (isDefined(__ref_collect)) __ref_collect();
|
if (isDefined(__ref_collect)) __ref_collect();
|
||||||
else WARNING("missing implementation: gc.collect");
|
else throw new Error(E_NOTIMPLEMENTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Retains a reference, making sure that it doesn't become collected. */
|
||||||
|
export function retain(ref: usize): void {
|
||||||
|
var root = GC_ROOT;
|
||||||
|
if (!root.has(ref)) {
|
||||||
|
root.add(ref);
|
||||||
|
if (implemented) {
|
||||||
|
if (isDefined(__ref_link)) __ref_link(ref, changetype<usize>(root));
|
||||||
|
else if (isDefined(__ref_retain)) __ref_retain(ref);
|
||||||
|
else assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Releases a reference, allowing it to become collected. */
|
||||||
|
export function release(ref: usize): void {
|
||||||
|
var root = GC_ROOT;
|
||||||
|
if (root.has(ref)) {
|
||||||
|
root.delete(ref);
|
||||||
|
if (implemented) {
|
||||||
|
if (isDefined(__ref_link)) __ref_unlink(ref, changetype<usize>(root));
|
||||||
|
else if (isDefined(__ref_retain)) __ref_release(ref);
|
||||||
|
else assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
std/assembly/index.d.ts
vendored
20
std/assembly/index.d.ts
vendored
@ -974,6 +974,8 @@ declare function bswap16<T = i8 | u8 | i16 | u16 | i32 | u32>(value: T): T;
|
|||||||
|
|
||||||
/** Memory operations. */
|
/** Memory operations. */
|
||||||
declare namespace memory {
|
declare namespace memory {
|
||||||
|
/** Whether the memory managed interface is implemented. */
|
||||||
|
export const implemented: bool;
|
||||||
/** Returns the current memory size in units of pages. One page is 64kb. */
|
/** Returns the current memory size in units of pages. One page is 64kb. */
|
||||||
export function size(): i32;
|
export function size(): i32;
|
||||||
/** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */
|
/** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */
|
||||||
@ -985,9 +987,9 @@ declare namespace memory {
|
|||||||
/** Repeats `src` of length `srcLength` `count` times at `dst`. */
|
/** Repeats `src` of length `srcLength` `count` times at `dst`. */
|
||||||
export function repeat(dst: usize, src: usize, srcLength: usize, count: usize): void;
|
export function repeat(dst: usize, src: usize, srcLength: usize, count: usize): void;
|
||||||
/** Copies elements from a passive element segment to a table. */
|
/** Copies elements from a passive element segment to a table. */
|
||||||
// export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void;
|
export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void;
|
||||||
/** Prevents further use of a passive element segment. */
|
/** Prevents further use of a passive element segment. */
|
||||||
// export function drop(segmentIndex: u32): void;
|
export function drop(segmentIndex: u32): void;
|
||||||
/** Copies elements from one region of a table to another region. */
|
/** Copies elements from one region of a table to another region. */
|
||||||
export function allocate(size: usize): usize;
|
export function allocate(size: usize): usize;
|
||||||
/** Disposes a chunk of memory by its pointer. */
|
/** Disposes a chunk of memory by its pointer. */
|
||||||
@ -1000,20 +1002,24 @@ declare namespace memory {
|
|||||||
|
|
||||||
/** Garbage collector operations. */
|
/** Garbage collector operations. */
|
||||||
declare namespace gc {
|
declare namespace gc {
|
||||||
/** Allocates a managed object identified by its visitor function. */
|
/** Whether the garbage collector interface is implemented. */
|
||||||
export function allocate(size: usize, visitFn: (ref: usize) => void): usize;
|
export const implemented: bool;
|
||||||
/** Performs a full garbage collection cycle. */
|
/** Performs a full garbage collection cycle. */
|
||||||
export function collect(): void;
|
export function collect(): void;
|
||||||
|
/** Retains a reference, making sure that it doesn't become collected. */
|
||||||
|
export function retain(ref: usize): void;
|
||||||
|
/** Releases a reference, allowing it to become collected. */
|
||||||
|
export function release(ref: usize): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Table operations. */
|
/** Table operations. */
|
||||||
declare namespace table {
|
declare namespace table {
|
||||||
/** Copies elements from a passive element segment to a table. */
|
/** Copies elements from a passive element segment to a table. */
|
||||||
// export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void;
|
export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void;
|
||||||
/** Prevents further use of a passive element segment. */
|
/** Prevents further use of a passive element segment. */
|
||||||
// export function drop(elementIndex: u32): void;
|
export function drop(elementIndex: u32): void;
|
||||||
/** Copies elements from one region of a table to another region. */
|
/** Copies elements from one region of a table to another region. */
|
||||||
// export function copy(dest: u32, src: u32, n: u32): void;
|
export function copy(dest: u32, src: u32, n: u32): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Class representing a generic, fixed-length raw binary data buffer. */
|
/** Class representing a generic, fixed-length raw binary data buffer. */
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/// <reference path="./allocator/index.d.ts" />
|
/// <reference path="./allocator/index.d.ts" />
|
||||||
|
|
||||||
import { memcmp, memmove, memset } from "./util/memory";
|
import { memcmp, memmove, memset } from "./util/memory";
|
||||||
|
import { E_NOTIMPLEMENTED } from "./util/error";
|
||||||
|
|
||||||
// @ts-ignore: decorator
|
// @ts-ignore: decorator
|
||||||
@builtin
|
@builtin
|
||||||
@ -9,6 +10,11 @@ export declare const HEAP_BASE: usize;
|
|||||||
/** Memory manager interface. */
|
/** Memory manager interface. */
|
||||||
export namespace memory {
|
export namespace memory {
|
||||||
|
|
||||||
|
/** Whether the memory managed interface is implemented. */
|
||||||
|
// @ts-ignore: decorator
|
||||||
|
@lazy
|
||||||
|
export const implemented: bool = isDefined(__mem_allocate);
|
||||||
|
|
||||||
/** Gets the size of the memory in pages. */
|
/** Gets the size of the memory in pages. */
|
||||||
// @ts-ignore: decorator
|
// @ts-ignore: decorator
|
||||||
@builtin
|
@builtin
|
||||||
@ -37,14 +43,14 @@ export namespace memory {
|
|||||||
// @ts-ignore: decorator
|
// @ts-ignore: decorator
|
||||||
@unsafe
|
@unsafe
|
||||||
export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void {
|
export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void {
|
||||||
unreachable(); // not yet implemented
|
throw new Error(E_NOTIMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Drops a memory segment. */
|
/** Drops a memory segment. */
|
||||||
// @ts-ignore: decorator
|
// @ts-ignore: decorator
|
||||||
@unsafe
|
@unsafe
|
||||||
export function drop(segmentIndex: u32): void {
|
export function drop(segmentIndex: u32): void {
|
||||||
unreachable(); // not yet implemented
|
throw new Error(E_NOTIMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Dynamically allocates a section of memory and returns its address. */
|
/** Dynamically allocates a section of memory and returns its address. */
|
||||||
@ -52,7 +58,7 @@ export namespace memory {
|
|||||||
@unsafe
|
@unsafe
|
||||||
export function allocate(size: usize): usize {
|
export function allocate(size: usize): usize {
|
||||||
if (isDefined(__mem_allocate)) return __mem_allocate(size);
|
if (isDefined(__mem_allocate)) return __mem_allocate(size);
|
||||||
else return <usize>unreachable();
|
else throw new Error(E_NOTIMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Dynamically frees a section of memory by the previously allocated address. */
|
/** Dynamically frees a section of memory by the previously allocated address. */
|
||||||
@ -60,7 +66,7 @@ export namespace memory {
|
|||||||
@unsafe
|
@unsafe
|
||||||
export function free(ptr: usize): void {
|
export function free(ptr: usize): void {
|
||||||
if (isDefined(__mem_free)) __mem_free(ptr);
|
if (isDefined(__mem_free)) __mem_free(ptr);
|
||||||
else unreachable();
|
else throw new Error(E_NOTIMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resets the memory to its initial state. Arena allocator only. */
|
/** Resets the memory to its initial state. Arena allocator only. */
|
||||||
@ -68,7 +74,7 @@ export namespace memory {
|
|||||||
@unsafe
|
@unsafe
|
||||||
export function reset(): void {
|
export function reset(): void {
|
||||||
if (isDefined(__mem_reset)) __mem_reset();
|
if (isDefined(__mem_reset)) __mem_reset();
|
||||||
else unreachable();
|
else throw new Error(E_NOTIMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Repeats a section of memory at a specific address. */
|
/** Repeats a section of memory at a specific address. */
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
|
import { E_NOTIMPLEMENTED } from "./util/error";
|
||||||
|
|
||||||
export namespace table {
|
export namespace table {
|
||||||
|
|
||||||
export function copy(dst: u32, src: u32, n: u32): void {
|
export function copy(dst: u32, src: u32, n: u32): void {
|
||||||
ERROR("not implemented: table.copy");
|
throw new Error(E_NOTIMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void {
|
export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void {
|
||||||
ERROR("not implemented: table.init");
|
throw new Error(E_NOTIMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function drop(elementIndex: u32): void {
|
export function drop(elementIndex: u32): void {
|
||||||
ERROR("not implemented: table.drop");
|
throw new Error(E_NOTIMPLEMENTED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,3 +16,7 @@ export const E_EMPTYARRAY: string = "Array is empty";
|
|||||||
// @ts-ignore: decorator
|
// @ts-ignore: decorator
|
||||||
@lazy @inline
|
@lazy @inline
|
||||||
export const E_HOLEYARRAY: string = "Element type must be nullable if array is holey";
|
export const E_HOLEYARRAY: string = "Element type must be nullable if array is holey";
|
||||||
|
|
||||||
|
// @ts-ignore: decorator
|
||||||
|
@lazy @inline
|
||||||
|
export const E_NOTIMPLEMENTED: string = "Not implemented";
|
||||||
|
1118
tests/compiler/gc.optimized.wat
Normal file
1118
tests/compiler/gc.optimized.wat
Normal file
File diff suppressed because it is too large
Load Diff
39
tests/compiler/gc.ts
Normal file
39
tests/compiler/gc.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import "allocator/arena";
|
||||||
|
import { link_count, unlink_count, collect_count } from "./gc/_dummy";
|
||||||
|
export { gc };
|
||||||
|
|
||||||
|
class Ref {}
|
||||||
|
|
||||||
|
@start export function main(): void {
|
||||||
|
var ref = new Ref();
|
||||||
|
|
||||||
|
assert(gc.implemented);
|
||||||
|
|
||||||
|
var previous_link_count = link_count;
|
||||||
|
var previous_unlink_count = unlink_count;
|
||||||
|
var previous_collect_count = collect_count;
|
||||||
|
|
||||||
|
gc.retain(changetype<usize>(ref));
|
||||||
|
|
||||||
|
assert(link_count == previous_link_count + 1);
|
||||||
|
assert(unlink_count == previous_unlink_count);
|
||||||
|
assert(collect_count == previous_collect_count);
|
||||||
|
previous_link_count = link_count;
|
||||||
|
previous_unlink_count = unlink_count;
|
||||||
|
previous_collect_count = collect_count;
|
||||||
|
|
||||||
|
gc.release(changetype<usize>(ref));
|
||||||
|
|
||||||
|
assert(link_count == previous_link_count);
|
||||||
|
assert(unlink_count == previous_unlink_count + 1);
|
||||||
|
assert(collect_count == previous_collect_count);
|
||||||
|
previous_link_count = link_count;
|
||||||
|
previous_unlink_count = unlink_count;
|
||||||
|
previous_collect_count = collect_count;
|
||||||
|
|
||||||
|
gc.collect();
|
||||||
|
|
||||||
|
assert(link_count == previous_link_count);
|
||||||
|
assert(unlink_count == previous_unlink_count);
|
||||||
|
assert(collect_count == previous_collect_count + 1);
|
||||||
|
}
|
1349
tests/compiler/gc.untouched.wat
Normal file
1349
tests/compiler/gc.untouched.wat
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user