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" />
import { E_NOTIMPLEMENTED } from "./util/error";
// @ts-ignore
@lazy
var GC_ROOT = new Set<usize>();
/** Garbage collector interface. */
export namespace gc {
/** Whether the garbage collector interface is implemented. */
// @ts-ignore: decorator
@lazy
export const IMPLEMENTED: bool = isDefined(__ref_collect);
export const implemented: bool = isDefined(__ref_collect);
/** Performs a full garbage collection cycle. */
export function collect(): void {
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. */
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. */
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. */
@ -985,9 +987,9 @@ declare namespace memory {
/** Repeats `src` of length `srcLength` `count` times at `dst`. */
export function repeat(dst: usize, src: usize, srcLength: usize, count: usize): void;
/** 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. */
// export function drop(segmentIndex: u32): void;
export function drop(segmentIndex: u32): void;
/** Copies elements from one region of a table to another region. */
export function allocate(size: usize): usize;
/** Disposes a chunk of memory by its pointer. */
@ -1000,20 +1002,24 @@ declare namespace memory {
/** Garbage collector operations. */
declare namespace gc {
/** Allocates a managed object identified by its visitor function. */
export function allocate(size: usize, visitFn: (ref: usize) => void): usize;
/** Whether the garbage collector interface is implemented. */
export const implemented: bool;
/** Performs a full garbage collection cycle. */
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. */
declare namespace 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. */
// export function drop(elementIndex: u32): void;
export function drop(elementIndex: u32): void;
/** 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. */

View File

@ -1,6 +1,7 @@
/// <reference path="./allocator/index.d.ts" />
import { memcmp, memmove, memset } from "./util/memory";
import { E_NOTIMPLEMENTED } from "./util/error";
// @ts-ignore: decorator
@builtin
@ -9,6 +10,11 @@ export declare const HEAP_BASE: usize;
/** Memory manager interface. */
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. */
// @ts-ignore: decorator
@builtin
@ -37,14 +43,14 @@ export namespace memory {
// @ts-ignore: decorator
@unsafe
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. */
// @ts-ignore: decorator
@unsafe
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. */
@ -52,7 +58,7 @@ export namespace memory {
@unsafe
export function allocate(size: usize): usize {
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. */
@ -60,7 +66,7 @@ export namespace memory {
@unsafe
export function free(ptr: usize): void {
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. */
@ -68,7 +74,7 @@ export namespace memory {
@unsafe
export function reset(): void {
if (isDefined(__mem_reset)) __mem_reset();
else unreachable();
else throw new Error(E_NOTIMPLEMENTED);
}
/** 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 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 {
ERROR("not implemented: table.init");
throw new Error(E_NOTIMPLEMENTED);
}
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
@lazy @inline
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