external gc interface

This commit is contained in:
dcode 2019-03-28 11:07:47 +01:00
parent 41abc0166c
commit 0dcfcc7935
8 changed files with 2573 additions and 17 deletions

View File

@ -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);
}
}
} }
} }

View File

@ -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. */

View File

@ -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. */

View File

@ -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);
} }
} }

View File

@ -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";

File diff suppressed because it is too large Load Diff

39
tests/compiler/gc.ts Normal file
View 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);
}

File diff suppressed because it is too large Load Diff