warpo 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -89,7 +89,15 @@
89
89
  "items": {
90
90
  "type": "string",
91
91
  "description": "Name of a feature to disable.",
92
- "enum": ["mutable-globals", "sign-extension", "nontrapping-f2i", "bulk-memory", "multi-value", "tail-call"]
92
+ "enum": [
93
+ "mutable-globals",
94
+ "sign-extension",
95
+ "nontrapping-f2i",
96
+ "bulk-memory",
97
+ "multi-value",
98
+ "tail-call",
99
+ "return-to-br"
100
+ ]
93
101
  }
94
102
  },
95
103
  "enable": {
@@ -98,7 +106,15 @@
98
106
  "items": {
99
107
  "type": "string",
100
108
  "description": "Name of a feature to enable.",
101
- "enum": ["mutable-globals", "sign-extension", "nontrapping-f2i", "bulk-memory", "multi-value", "tail-call"]
109
+ "enum": [
110
+ "mutable-globals",
111
+ "sign-extension",
112
+ "nontrapping-f2i",
113
+ "bulk-memory",
114
+ "multi-value",
115
+ "tail-call",
116
+ "return-to-br"
117
+ ]
102
118
  }
103
119
  },
104
120
  "debug": {
@@ -57,6 +57,41 @@ function ENTRY_SIZE<K, V>(): usize {
57
57
  return size;
58
58
  }
59
59
 
60
+ /**
61
+ * Forward-only history of entries layouts: the map keeps the latest version,
62
+ * while each iterator keeps the version matching its cursor. Old versions retain
63
+ * removed slot indices, not obsolete entries buffers or references to their contents.
64
+ */
65
+ class MapIteratorVersion {
66
+ private nextVersion: MapIteratorVersion | null = null;
67
+ // For versions with a successor, null marks clear(); otherwise indices are sorted in the old layout.
68
+ private removedIndices: StaticArray<i32> | null = null;
69
+
70
+ transition(removedIndices: StaticArray<i32> | null): MapIteratorVersion {
71
+ this.removedIndices = removedIndices;
72
+ return (this.nextVersion = new MapIteratorVersion());
73
+ }
74
+
75
+ translateIndex(index: i32): i32 {
76
+ const removedIndices = this.removedIndices;
77
+ if (!removedIndices) return 0;
78
+ // New cursor = old cursor - number of removed slots strictly before it.
79
+ // A removed slot at index is unvisited and must not move the boundary backward.
80
+ let lower = 0;
81
+ let upper = removedIndices.length;
82
+ while (lower < upper) {
83
+ const middle = lower + ((upper - lower) >> 1);
84
+ if (unchecked(removedIndices[middle]) < index) lower = middle + 1;
85
+ else upper = middle;
86
+ }
87
+ return index - lower;
88
+ }
89
+
90
+ getNext(): MapIteratorVersion {
91
+ return assert(this.nextVersion);
92
+ }
93
+ }
94
+
60
95
  // @ts-ignore: decorator
61
96
  @lazy
62
97
  const GET_START = Symbol();
@@ -65,11 +100,33 @@ const GET_START = Symbol();
65
100
  @lazy
66
101
  const GET_ENTRIES_OFFSET = Symbol();
67
102
 
103
+ // @ts-ignore: decorator
104
+ @lazy
105
+ const GET_ITERATOR_VERSION = Symbol();
106
+
68
107
  class MapIterator<K, V> implements Iterator<[K, V]> {
108
+ // Next physical slot in this.version's layout, not the number of values returned.
69
109
  private i: i32 = 0;
70
- constructor(private map: Map<K, V>) {}
110
+ private map: Map<K, V> | null;
111
+ private version: MapIteratorVersion | null;
112
+
113
+ constructor(map: Map<K, V>) {
114
+ this.map = map;
115
+ this.version = map[GET_ITERATOR_VERSION]();
116
+ }
117
+
71
118
  next(): IteratorResult<[K, V]> {
72
119
  const map = this.map;
120
+ if (!map) return IteratorResult.done<[K, V]>();
121
+ // Apply every missed compaction or clear before accessing the current entries buffer.
122
+ const currentVersion = map[GET_ITERATOR_VERSION]();
123
+ let version = assert(this.version);
124
+ while (version != currentVersion) {
125
+ this.i = version.translateIndex(this.i);
126
+ version = version.getNext();
127
+ }
128
+ this.version = version;
129
+
73
130
  const start = map[GET_START]();
74
131
  const size = map[GET_ENTRIES_OFFSET]();
75
132
  for (let i = this.i; i < size; ++i) {
@@ -79,6 +136,10 @@ class MapIterator<K, V> implements Iterator<[K, V]> {
79
136
  return IteratorResult.fromValue<[K, V]>([entry.key, entry.value]);
80
137
  }
81
138
  }
139
+ // Once done, later insertions must not revive this iterator.
140
+ // Release the map and any layout history still retained by this iterator.
141
+ this.map = null;
142
+ this.version = null;
82
143
  return IteratorResult.done<[K, V]>();
83
144
  }
84
145
  }
@@ -93,6 +154,8 @@ export class Map<K, V> implements Iterable<[K, V]> {
93
154
  private entriesCapacity: i32 = INITIAL_CAPACITY;
94
155
  private entriesOffset: i32 = 0;
95
156
  private entriesCount: i32 = 0;
157
+ // Lazy state: maps that never create iterators need no layout-change history.
158
+ private iteratorVersion: MapIteratorVersion | null = null;
96
159
 
97
160
  constructor() {
98
161
  /* nop */
@@ -107,6 +170,12 @@ export class Map<K, V> implements Iterable<[K, V]> {
107
170
  [GET_ENTRIES_OFFSET](): i32 {
108
171
  return this.entriesOffset;
109
172
  }
173
+ @inline
174
+ [GET_ITERATOR_VERSION](): MapIteratorVersion {
175
+ let iteratorVersion = this.iteratorVersion;
176
+ if (!iteratorVersion) this.iteratorVersion = iteratorVersion = new MapIteratorVersion();
177
+ return iteratorVersion;
178
+ }
110
179
 
111
180
  get size(): i32 {
112
181
  return this.entriesCount;
@@ -116,6 +185,9 @@ export class Map<K, V> implements Iterable<[K, V]> {
116
185
  this.buckets = new ArrayBuffer(INITIAL_CAPACITY * <i32>BUCKET_SIZE);
117
186
  this.bucketsMask = INITIAL_CAPACITY - 1;
118
187
  this.entries = new ArrayBuffer(INITIAL_CAPACITY * <i32>ENTRY_SIZE<K, V>());
188
+ // Live iterators must restart at entries inserted after clear(), rather than keep their old indices.
189
+ let iteratorVersion = this.iteratorVersion;
190
+ if (iteratorVersion) this.iteratorVersion = iteratorVersion.transition(null);
119
191
  this.entriesCapacity = INITIAL_CAPACITY;
120
192
  this.entriesOffset = 0;
121
193
  this.entriesCount = 0;
@@ -206,9 +278,17 @@ export class Map<K, V> implements Iterable<[K, V]> {
206
278
  let newEntries = new ArrayBuffer(newEntriesCapacity * <i32>ENTRY_SIZE<K, V>());
207
279
 
208
280
  // copy old entries to new entries
209
- let oldPtr = changetype<usize>(this.entries);
281
+ let oldStart = changetype<usize>(this.entries);
282
+ let oldPtr = oldStart;
210
283
  let oldEnd = oldPtr + <usize>this.entriesOffset * ENTRY_SIZE<K, V>();
211
284
  let newPtr = changetype<usize>(newEntries);
285
+ let iteratorVersion = this.iteratorVersion;
286
+ // Record deleted slots only if iterator state exists; growth without holes leaves indices unchanged.
287
+ let removedIndices: StaticArray<i32> | null =
288
+ iteratorVersion && this.entriesOffset != this.entriesCount
289
+ ? new StaticArray<i32>(this.entriesOffset - this.entriesCount)
290
+ : null;
291
+ let removedCount = 0;
212
292
  while (oldPtr != oldEnd) {
213
293
  let oldEntry = changetype<MapEntry<K, V>>(oldPtr);
214
294
  if (!(oldEntry.taggedNext & EMPTY)) {
@@ -221,6 +301,9 @@ export class Map<K, V> implements Iterable<[K, V]> {
221
301
  newEntry.taggedNext = load<usize>(newBucketPtrBase);
222
302
  store<usize>(newBucketPtrBase, newPtr);
223
303
  newPtr += ENTRY_SIZE<K, V>();
304
+ } else if (removedIndices) {
305
+ // Old slots are scanned in order, keeping the indices sorted for the lower-bound search.
306
+ unchecked((removedIndices[removedCount++] = <i32>((oldPtr - oldStart) / ENTRY_SIZE<K, V>())));
224
307
  }
225
308
  oldPtr += ENTRY_SIZE<K, V>();
226
309
  }
@@ -228,6 +311,7 @@ export class Map<K, V> implements Iterable<[K, V]> {
228
311
  this.buckets = newBuckets;
229
312
  this.bucketsMask = newBucketsMask;
230
313
  this.entries = newEntries;
314
+ if (iteratorVersion && removedIndices) this.iteratorVersion = iteratorVersion.transition(removedIndices);
231
315
  this.entriesCapacity = newEntriesCapacity;
232
316
  this.entriesOffset = this.entriesCount;
233
317
  }
@@ -276,6 +360,9 @@ export class Map<K, V> implements Iterable<[K, V]> {
276
360
 
277
361
  @unsafe private __visit(cookie: u32): void {
278
362
  __visit(changetype<usize>(this.buckets), cookie);
363
+ // Map uses a custom visitor, so its managed iterator-version field must be traced explicitly.
364
+ let iteratorVersion = this.iteratorVersion;
365
+ if (iteratorVersion) __visit(changetype<usize>(iteratorVersion), cookie);
279
366
  let entries = changetype<usize>(this.entries);
280
367
  if (isManaged<K>() || isManaged<V>()) {
281
368
  let cur = entries;
@@ -2,6 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import * as api from "./__warpo_create";
5
+ import { memory_expose } from "warpo/memory";
5
6
 
6
7
  export class ModuleResolve {
7
8
  packageName!: string;
@@ -13,5 +14,6 @@ export class ModuleResolve {
13
14
 
14
15
  export type Fn = (task: ModuleResolve) => void;
15
16
  export function onModuleResolve(fn: Fn): void {
17
+ memory_expose<ModuleResolve>();
16
18
  api.onModuleResolve(fn.index, idof<ModuleResolve>());
17
19
  }
@@ -17,6 +17,7 @@ async function buildWithWARPO({ sources, output, userDefinedFlags, collectCovera
17
17
  "--debug",
18
18
  "--enable-feature",
19
19
  "tail-call",
20
+ "return-to-br",
20
21
  // instrumentation flags
21
22
  "--instrument",
22
23
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../../tools/test_runner/core/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,sBAAsB,CAAC;AAG9C,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,YAAgC;QAC1C,KAAK,CAAC,YAAY,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,KAAK,UAAU,cAAc,CAAC,EAC5B,OAAO,EACP,MAAM,EACN,gBAAgB,EAChB,eAAe,GACG;IAClB,IAAI,SAAS,GAAG;QACd,GAAG,OAAO;QACV,IAAI;QACJ,MAAM,CAAC,IAAI;QACX,eAAe;QACf,wDAAwD;QACxD,eAAe;QACf,mBAAmB;QACnB,SAAS;QACT,kBAAkB;QAClB,WAAW;QACX,wBAAwB;QACxB,cAAc;KACf,CAAC;IACF,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,SAAS,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,2CAA2C;QAC3C,iCAAiC;QACjC,8EAA8E;QAC9E,gDAAgD;QAChD,MAAM,IAAI,GAAG,gBAAgB;aAC1B,OAAO,CAAC,WAAW,EAAE,mBAAmB,CAAC;aACzC,OAAO,CAAC,uBAAuB,EAAE,EAAE,CAAC;aACpC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;aAC9B,KAAK,CAAC,GAAG,CAAC,CAAC;QACd,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;QACnC,IAAI,EAAE,SAAS;KAChB,CAAC,CAAC;IACH,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AASD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAyB;IACzD,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
1
+ {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../../tools/test_runner/core/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,sBAAsB,CAAC;AAG9C,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,YAAgC;QAC1C,KAAK,CAAC,YAAY,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,KAAK,UAAU,cAAc,CAAC,EAC5B,OAAO,EACP,MAAM,EACN,gBAAgB,EAChB,eAAe,GACG;IAClB,IAAI,SAAS,GAAG;QACd,GAAG,OAAO;QACV,IAAI;QACJ,MAAM,CAAC,IAAI;QACX,eAAe;QACf,wDAAwD;QACxD,eAAe;QACf,mBAAmB;QACnB,SAAS;QACT,kBAAkB;QAClB,WAAW;QACX,cAAc;QACd,wBAAwB;QACxB,cAAc;KACf,CAAC;IACF,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,SAAS,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,2CAA2C;QAC3C,iCAAiC;QACjC,8EAA8E;QAC9E,gDAAgD;QAChD,MAAM,IAAI,GAAG,gBAAgB;aAC1B,OAAO,CAAC,WAAW,EAAE,mBAAmB,CAAC;aACzC,OAAO,CAAC,uBAAuB,EAAE,EAAE,CAAC;aACpC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;aAC9B,KAAK,CAAC,GAAG,CAAC,CAAC;QACd,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;QACnC,IAAI,EAAE,SAAS;KAChB,CAAC,CAAC;IACH,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AASD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAyB;IACzD,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "warpo",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "next generation AssemblyScript compiler with optimizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,6 +8,10 @@ declare module "warpo/ffi" {
8
8
  }
9
9
  }
10
10
 
11
+ declare module "warpo/memory" {
12
+ function memory_expose<T>(): void;
13
+ }
14
+
11
15
  declare module "rt/index" {
12
16
  // allocate new object with size and rtid
13
17
  function __new(size: usize, id: u32): usize;
@@ -0,0 +1,6 @@
1
+ // Copyright (C) 2026 wasm-ecosystem
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // @ts-ignore: decorator
5
+ @builtin
6
+ export declare function memory_expose<T>(): void;