watr 4.5.0 → 4.5.3

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.
package/src/compile.js CHANGED
@@ -308,7 +308,7 @@ function normalize(nodes, ctx) {
308
308
  else if ((node.startsWith('memory.') || node.endsWith('load') || node.endsWith('store')) && isIdx(nodes[0])) out.push(nodes.shift())
309
309
  }
310
310
  else if (Array.isArray(node)) {
311
- const op = node[0]
311
+ let op = node[0]
312
312
  node.loc != null && (err.loc = node.loc) // track position for errors
313
313
 
314
314
  // code metadata annotations - pass through as marker with metadata type and data
@@ -348,6 +348,13 @@ function normalize(nodes, ctx) {
348
348
  }
349
349
  out.push(...normalize(parts, ctx), 'end')
350
350
  }
351
+ else if (op === 'ref.test' || op === 'ref.cast') {
352
+ const type = parts[0]
353
+ const isNullable = !Array.isArray(type) || type[1] === 'null' || type[0] !== 'ref'
354
+ if (isNullable) op += '_null'
355
+ out.push(...normalize(parts.slice(1), ctx), op, type)
356
+ nodes.unshift(...out.splice(out.length - 2))
357
+ }
351
358
  else {
352
359
  const imm = []
353
360
  // Collect immediate operands (non-arrays or special forms like type/param/result/ref)
@@ -965,7 +972,7 @@ const instr = (nodes, ctx) => {
965
972
  // select: becomes typed select (opcode+1) if next node is an array with result types
966
973
  if (op === 'select' && nodes[0]?.length) bytes[0]++
967
974
  // ref.type|cast: opcode+1 if type is nullable: (ref null $t) or (funcref, anyref, etc.)
968
- else if (HANDLER[op] === IMM.reftype && (nodes[0][1] === 'null' || nodes[0][0] !== 'ref')) {
975
+ else if (HANDLER[op] === IMM.reftype && !op.endsWith('_null') && (nodes[0][1] === 'null' || nodes[0][0] !== 'ref')) {
969
976
  bytes[bytes.length - 1]++
970
977
  }
971
978
  bytes.push(...HANDLER[op](nodes, ctx, op))
package/src/const.js CHANGED
@@ -59,7 +59,7 @@ export const INSTR = [
59
59
  'struct.new typeidx', 'struct.new_default typeidx', 'struct.get typeidx_field', 'struct.get_s typeidx_field', 'struct.get_u typeidx_field', 'struct.set typeidx_field',
60
60
  'array.new typeidx', 'array.new_default typeidx', 'array.new_fixed typeidx_multi', 'array.new_data typeidx_dataidx', 'array.new_elem typeidx_elemidx',
61
61
  'array.get typeidx', 'array.get_s typeidx', 'array.get_u typeidx', 'array.set typeidx', 'array.len', 'array.fill typeidx', 'array.copy typeidx_typeidx',
62
- 'array.init_data typeidx_dataidx', 'array.init_elem typeidx_elemidx', 'ref.test reftype', '', 'ref.cast reftype', '', 'br_on_cast reftype2', 'br_on_cast_fail reftype2',
62
+ 'array.init_data typeidx_dataidx', 'array.init_elem typeidx_elemidx', 'ref.test reftype', 'ref.test_null reftype', 'ref.cast reftype', 'ref.cast_null reftype', 'br_on_cast reftype2', 'br_on_cast_fail reftype2',
63
63
  'any.convert_extern', 'extern.convert_any', 'ref.i31', 'i31.get_s', 'i31.get_u',
64
64
  // custom descriptors (Phase 3): 0xFB 0x20-0x26
65
65
  , 'struct.new_desc typeidx', 'struct.new_default_desc typeidx', 'ref.get_desc typeidx', 'ref.cast_desc_eq reftype', , 'br_on_cast_desc_eq reftype2', 'br_on_cast_desc_eq_fail reftype2',
@@ -182,7 +182,7 @@ export const TYPE = {
182
182
  // Value types
183
183
  i8: 0x78, i16: 0x77, i32: 0x7f, i64: 0x7e, f32: 0x7d, f64: 0x7c, void: 0x40, v128: 0x7B,
184
184
  // Heap types
185
- exn: 0x69, noexn: 0x74, nofunc: 0x73, noextern: 0x72, none: 0x71, func: 0x70, extern: 0x6F, any: 0x6E, eq: 0x6D, i31: 0x6C, struct: 0x6B, array: 0x6A,
185
+ exn: 0x69, noexn: 0x74, nofunc: 0x73, noextern: 0x72, none: 0x71, func: 0x70, extern: 0x6F, any: 0x6E, eq: 0x6D, i31: 0x6C, struct: 0x6B, array: 0x6A, data: 0x6B,
186
186
  cont: 0x68, nocont: 0x75, // stack switching (Phase 3)
187
187
  string: 0x67, stringview_wtf8: 0x66, stringview_wtf16: 0x60, stringview_iter: 0x61, // stringref
188
188
  // Reference type abbreviations (absheaptype abbrs)
package/src/encode.js CHANGED
@@ -128,16 +128,30 @@ const _u8 = new Uint8Array(_buf), _i32 = new Int32Array(_buf), _f32 = new Float3
128
128
 
129
129
  i64.parse = n => {
130
130
  n = cleanInt(n)
131
- n = n[0] === '-' ? -BigInt(n.slice(1)) : BigInt(n) // can be -0x123
132
- if (n < -0x8000000000000000n || n > 0xffffffffffffffffn) err(`i64 constant out of range`)
133
- _i64[0] = n
131
+ const neg = n[0] === '-'
132
+ const body = neg ? n.slice(1) : n
133
+ // Range check on the literal string before BigInt conversion (lexicographic compare on clean digits).
134
+ let max
135
+ if (body[0] === '0' && (body[1] === 'x' || body[1] === 'X')) {
136
+ const hex = body.slice(2).replace(/^0+/, '') || '0'
137
+ max = neg ? '8000000000000000' : 'ffffffffffffffff'
138
+ if (hex.length > 16 || (hex.length === 16 && hex.toLowerCase() > max)) err(`i64 constant out of range`)
139
+ } else {
140
+ const dec = body.replace(/^0+/, '') || '0'
141
+ max = neg ? '9223372036854775808' : '18446744073709551615'
142
+ if (dec.length > max.length || (dec.length === max.length && dec > max)) err(`i64 constant out of range`)
143
+ }
144
+ let bi = BigInt(body)
145
+ if (neg) bi = 0n - bi
146
+ _i64[0] = bi
134
147
  return _i64[0]
135
148
  }
136
149
 
137
- const F32_SIGN = 0x80000000, F32_NAN = 0x7f800000
150
+ const F32_SIGN = 0x80000000, F32_NAN = 0x7f800000, F32_QUIET = 0x400000
138
151
  export function f32(input, value, idx) {
139
- if (typeof input === 'string' && ~(idx = input.indexOf('nan:'))) {
140
- value = i32.parse(input.slice(idx + 4))
152
+ // Plain `nan` / `-nan` (with optional `:0xPAYLOAD`) set the bit pattern explicitly.
153
+ if (typeof input === 'string' && (idx = input.indexOf('nan')) >= 0) {
154
+ value = input[idx + 3] === ':' ? i32.parse(input.slice(idx + 4)) : F32_QUIET
141
155
  value |= F32_NAN
142
156
  if (input[0] === '-') value |= F32_SIGN
143
157
  _i32[0] = value
@@ -150,10 +164,11 @@ export function f32(input, value, idx) {
150
164
  return [_u8[0], _u8[1], _u8[2], _u8[3]]
151
165
  }
152
166
 
153
- const F64_SIGN = 0x8000000000000000n, F64_NAN = 0x7ff0000000000000n
167
+ const F64_SIGN = 0x8000000000000000n, F64_NAN = 0x7ff0000000000000n, F64_QUIET = 0x8000000000000n
154
168
  export function f64(input, value, idx) {
155
- if (typeof input === 'string' && ~(idx = input.indexOf('nan:'))) {
156
- value = i64.parse(input.slice(idx + 4))
169
+ // Plain `nan` / `-nan` (with optional `:0xPAYLOAD`) set the bit pattern explicitly.
170
+ if (typeof input === 'string' && (idx = input.indexOf('nan')) >= 0) {
171
+ value = input[idx + 3] === ':' ? i64.parse(input.slice(idx + 4)) : F64_QUIET
157
172
  value |= F64_NAN
158
173
  if (input[0] === '-') value |= F64_SIGN
159
174
  _i64[0] = value
@@ -179,9 +194,15 @@ f64.parse = (input, max=Number.MAX_VALUE) => {
179
194
  let [int, fract=''] = sig.split('.'); // integer and fractional parts
180
195
  let flen = fract.length ?? 0;
181
196
 
182
- // Parse integer part
183
- let intVal = parseInt(int); // 0x is included in int
184
- isNaN(intVal) && err()
197
+ // Parse integer part — accumulate from least-significant digit to preserve precision.
198
+ // parseInt loses low bits for values > 2^53 because left-to-right
199
+ // accumulation rounds at each step; right-to-left keeps intermediates
200
+ // small so the final large+small addition rounds correctly.
201
+ let intVal = 0;
202
+ for (let i = int.length - 1; i >= 2; i--) {
203
+ let digit = parseInt(int[i], 16);
204
+ intVal += digit * (16 ** (int.length - 1 - i));
205
+ }
185
206
 
186
207
  // 0x10a.fbc = 0x10afbc * 16⁻³ = 266.9833984375
187
208
  // Parse fractional part: fract / 16^flen