diff --git a/std/assembly/array.ts b/std/assembly/array.ts index ac91a75c..c500c878 100644 --- a/std/assembly/array.ts +++ b/std/assembly/array.ts @@ -1,4 +1,4 @@ -import { ALLOCATE, REALLOCATE, DISCARD, LINK, REGISTER, MAX_BYTELENGTH, ArrayBufferView } from "./runtime"; +import { ALLOCATE, REALLOCATE, DISCARD, LINK, REGISTER, MAX_BYTELENGTH, ArrayBufferView, UNLINK } from "./runtime"; import { ArrayBuffer } from "./arraybuffer"; import { COMPARATOR, SORT } from "./util/sort"; import { itoa, dtoa, itoa_stream, dtoa_stream, MAX_DOUBLE_LENGTH } from "./util/number"; @@ -73,11 +73,14 @@ export class Array extends ArrayBufferView { @operator("[]=") // unchecked is built-in private __set(index: i32, value: T): void { ensureCapacity(this, index + 1, alignof()); - store(this.dataStart + (index << alignof()), - isManaged() - ? LINK(value, this) - : value - ); + if (isManaged()) { + let offset = this.dataStart + (index << alignof()); + let oldValue = load(offset); + store(offset, LINK(value, this)); + UNLINK(oldValue, this); // order is important + } else { + store(this.dataStart + (index << alignof()), value); + } if (index >= this.length_) this.length_ = index + 1; } @@ -324,20 +327,21 @@ export class Array extends ArrayBufferView { var length = this.length_; start = start < 0 ? max(length + start, 0) : min(start, length); deleteCount = max(min(deleteCount, length - start), 0); - var splice = new Array(deleteCount); - var spliceStart = splice.dataStart; + var result = new Array(deleteCount); + var resultStart = result.dataStart; var thisStart = this.dataStart; var thisBase = thisStart + (start << alignof()); for (let i = 0; i < deleteCount; ++i) { - let element = load(thisBase + (i << alignof())); - store(spliceStart + (i << alignof()), - isManaged() - ? LINK>(element, splice) - : element - ); + let deleted = load(thisBase + (i << alignof())); + if (isManaged()) { + store(resultStart + (i << alignof()), LINK>(deleted, result)); + UNLINK(deleted, this); // order is important + } else { + store(resultStart + (i << alignof()), deleted); + } } memory.copy( - splice.dataStart, + result.dataStart, thisBase, deleteCount << alignof() ); @@ -350,7 +354,7 @@ export class Array extends ArrayBufferView { ); } this.length_ = length - deleteCount; - return splice; + return result; } reverse(): Array { diff --git a/std/assembly/fixedarray.ts b/std/assembly/fixedarray.ts index 954c4e11..b888eeaa 100644 --- a/std/assembly/fixedarray.ts +++ b/std/assembly/fixedarray.ts @@ -1,4 +1,4 @@ -import { ALLOCATE, REGISTER, MAX_BYTELENGTH, HEADER, HEADER_SIZE, LINK } from "./runtime"; +import { ALLOCATE, REGISTER, MAX_BYTELENGTH, HEADER, HEADER_SIZE, LINK, UNLINK } from "./runtime"; // NOTE: DO NOT USE YET! @@ -22,16 +22,12 @@ export class FixedArray { @operator("[]") private __get(index: i32): T { if (index >= this.length) throw new RangeError("Offset out of bounds"); - return load(changetype(this) + (index << alignof())); + return this.__unchecked_get(index); } @operator("[]=") private __set(index: i32, value: T): void { if (index >= this.length) throw new RangeError("Offset out of bounds"); - store(changetype(this) + (index << alignof()), - isManaged() - ? LINK(value, this) - : value - ); + return this.__unchecked_set(index, value); } @operator("{}") private __unchecked_get(index: i32): T { @@ -39,10 +35,13 @@ export class FixedArray { } @operator("{}=") private __unchecked_set(index: i32, value: T): void { - store(changetype(this) + (index << alignof()), - isManaged() - ? LINK(value, this) - : value - ); + if (isManaged()) { + let offset = changetype(this) + (index << alignof()); + let oldValue = load(offset); + store(offset, LINK(value, this)); + UNLINK(oldValue, this); // order is important + } else { + store(changetype(this) + (index << alignof()), value); + } } } diff --git a/std/assembly/map.ts b/std/assembly/map.ts index 48dcb309..93784ff1 100644 --- a/std/assembly/map.ts +++ b/std/assembly/map.ts @@ -1,4 +1,4 @@ -import { LINK } from "./runtime"; +import { LINK, UNLINK } from "./runtime"; import { HASH } from "./util/hash"; // A deterministic hash map based on CloseTable from https://github.com/jorendorff/dht @@ -102,9 +102,15 @@ export class Map { set(key: K, value: V): void { var hashCode = HASH(key); - var entry = this.find(key, hashCode); + var entry = this.find(key, hashCode); // unmanaged! if (entry) { - entry.value = value; + if (isManaged()) { + let oldValue = entry.value; + entry.value = LINK(value, this); + UNLINK(oldValue, this); // order is important + } else { + entry.value = value; + } } else { // check if rehashing is necessary if (this.entriesOffset == this.entriesCapacity) { @@ -119,13 +125,9 @@ export class Map { entry = changetype>( changetype(entries) + this.entriesOffset++ * ENTRY_SIZE() ); - // link with the map (entry is unmanaged) - entry.key = isManaged() - ? LINK(key, this) - : key; - entry.value = isManaged() - ? LINK(value, this) - : value; + // link with the map + entry.key = isManaged() ? LINK(key, this) : key; + entry.value = isManaged() ? LINK(value, this) : value; ++this.entriesCount; // link with previous entry in bucket let bucketPtrBase = changetype(this.buckets) + (hashCode & this.bucketsMask) * BUCKET_SIZE; @@ -137,6 +139,8 @@ export class Map { delete(key: K): bool { var entry = this.find(key, HASH(key)); if (!entry) return false; + if (isManaged()) UNLINK(entry.key, this); + if (isManaged()) UNLINK(entry.value, this); entry.taggedNext |= EMPTY; --this.entriesCount; // check if rehashing is appropriate diff --git a/std/assembly/set.ts b/std/assembly/set.ts index 991741ec..8d070e17 100644 --- a/std/assembly/set.ts +++ b/std/assembly/set.ts @@ -1,4 +1,4 @@ -import { LINK } from "./runtime"; +import { LINK, UNLINK } from "./runtime"; import { HASH } from "./util/hash"; // A deterministic hash set based on CloseTable from https://github.com/jorendorff/dht @@ -93,7 +93,7 @@ export class Set { add(key: K): void { var hashCode = HASH(key); - var entry = this.find(key, hashCode); + var entry = this.find(key, hashCode); // unmanaged! if (!entry) { // check if rehashing is necessary if (this.entriesOffset == this.entriesCapacity) { @@ -108,10 +108,8 @@ export class Set { entry = changetype>( changetype(entries) + this.entriesOffset++ * ENTRY_SIZE() ); - // link with the set itself (entry is unmanaged) - entry.key = isManaged() - ? LINK(key, this) - : key; + // link with the set + entry.key = isManaged() ? LINK(key, this) : key; ++this.entriesCount; // link with previous entry in bucket let bucketPtrBase = changetype(this.buckets) + (hashCode & this.bucketsMask) * BUCKET_SIZE; @@ -123,6 +121,7 @@ export class Set { delete(key: K): bool { var entry = this.find(key, HASH(key)); if (!entry) return false; + if (isManaged()) UNLINK(entry.key, this); entry.taggedNext |= EMPTY; --this.entriesCount; // check if rehashing is appropriate diff --git a/tests/compiler/std/array.optimized.wat b/tests/compiler/std/array.optimized.wat index 81675dfd..c814bd71 100644 --- a/tests/compiler/std/array.optimized.wat +++ b/tests/compiler/std/array.optimized.wat @@ -2298,7 +2298,7 @@ if i32.const 0 i32.const 208 - i32.const 200 + i32.const 203 i32.const 20 call $~lib/env/abort unreachable @@ -2565,7 +2565,7 @@ if i32.const 0 i32.const 208 - i32.const 261 + i32.const 264 i32.const 20 call $~lib/env/abort unreachable @@ -4096,7 +4096,7 @@ if i32.const 0 i32.const 208 - i32.const 374 + i32.const 378 i32.const 4 call $~lib/env/abort unreachable @@ -4592,7 +4592,7 @@ if i32.const 0 i32.const 208 - i32.const 374 + i32.const 378 i32.const 4 call $~lib/env/abort unreachable @@ -5111,7 +5111,7 @@ if i32.const 0 i32.const 208 - i32.const 374 + i32.const 378 i32.const 4 call $~lib/env/abort unreachable @@ -5436,7 +5436,7 @@ if i32.const 0 i32.const 208 - i32.const 374 + i32.const 378 i32.const 4 call $~lib/env/abort unreachable diff --git a/tests/compiler/std/array.untouched.wat b/tests/compiler/std/array.untouched.wat index 45a5bbf6..09ee0b98 100644 --- a/tests/compiler/std/array.untouched.wat +++ b/tests/compiler/std/array.untouched.wat @@ -2967,7 +2967,7 @@ if i32.const 0 i32.const 208 - i32.const 200 + i32.const 203 i32.const 20 call $~lib/env/abort unreachable @@ -3326,7 +3326,7 @@ if i32.const 0 i32.const 208 - i32.const 261 + i32.const 264 i32.const 20 call $~lib/env/abort unreachable @@ -5302,7 +5302,7 @@ if i32.const 0 i32.const 208 - i32.const 374 + i32.const 378 i32.const 4 call $~lib/env/abort unreachable @@ -5912,7 +5912,7 @@ if i32.const 0 i32.const 208 - i32.const 374 + i32.const 378 i32.const 4 call $~lib/env/abort unreachable @@ -6547,7 +6547,7 @@ if i32.const 0 i32.const 208 - i32.const 374 + i32.const 378 i32.const 4 call $~lib/env/abort unreachable @@ -7051,7 +7051,7 @@ if i32.const 0 i32.const 208 - i32.const 374 + i32.const 378 i32.const 4 call $~lib/env/abort unreachable @@ -7626,7 +7626,7 @@ if i32.const 0 i32.const 208 - i32.const 374 + i32.const 378 i32.const 4 call $~lib/env/abort unreachable @@ -7990,7 +7990,7 @@ if i32.const 0 i32.const 208 - i32.const 374 + i32.const 378 i32.const 4 call $~lib/env/abort unreachable @@ -8247,7 +8247,7 @@ if i32.const 0 i32.const 208 - i32.const 374 + i32.const 378 i32.const 4 call $~lib/env/abort unreachable