mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-26 15:32:16 +00:00
external gc interface
This commit is contained in:
parent
41abc0166c
commit
0dcfcc7935
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
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. */
|
||||
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. */
|
||||
|
@ -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. */
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
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