porffor 0.36.1 → 0.36.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.
@@ -326,7 +326,6 @@ export const __Porffor_object_set = (obj: any, key: any, value: any): any => {
326
326
 
327
327
  // no setter, return early
328
328
  if (Porffor.wasm`local.get ${set}` == 0) {
329
- // todo: throw in strict mode?
330
329
  return value;
331
330
  }
332
331
 
@@ -375,6 +374,92 @@ export const __Porffor_object_set = (obj: any, key: any, value: any): any => {
375
374
  return value;
376
375
  };
377
376
 
377
+ export const __Porffor_object_setStrict = (obj: any, key: any, value: any): any => {
378
+ if (Porffor.wasm`local.get ${obj}` == 0) throw new TypeError('Cannot set property of null');
379
+
380
+ if (Porffor.wasm`local.get ${obj+1}` != Porffor.TYPES.object) {
381
+ obj = __Porffor_object_getObject(obj);
382
+ if (Porffor.wasm`local.get ${obj+1}` != Porffor.TYPES.object) return value;
383
+ }
384
+
385
+ let entryPtr: i32 = __Porffor_object_lookup(obj, key);
386
+ let flags: i32;
387
+ if (entryPtr == -1) {
388
+ // add new entry
389
+ // check if object is inextensible
390
+ if (__Porffor_object_isInextensible(obj)) {
391
+ throw new TypeError('Cannot add property to inextensible object');
392
+ }
393
+
394
+ // bump size +1
395
+ const size: i32 = Porffor.wasm.i32.load(obj, 0, 0);
396
+ Porffor.wasm.i32.store(obj, size + 1, 0, 0);
397
+
398
+ // entryPtr = current end of object
399
+ entryPtr = Porffor.wasm`local.get ${obj}` + 5 + size * 14;
400
+
401
+ __Porffor_object_writeKey(entryPtr, key);
402
+
403
+ // flags = writable, enumerable, configurable, not accessor
404
+ flags = 0b1110;
405
+ } else {
406
+ // existing entry, modify it
407
+ const tail: i32 = Porffor.wasm.i32.load16_u(entryPtr, 0, 12);
408
+
409
+ if (tail & 0b0001) {
410
+ // accessor descriptor
411
+ const set: Function = __Porffor_object_accessorSet(entryPtr);
412
+
413
+ // no setter, return early
414
+ if (Porffor.wasm`local.get ${set}` == 0) {
415
+ throw new TypeError('Cannot set property with no setter of object');
416
+ }
417
+
418
+ const funcFlags: i32 = __Porffor_funcLut_flags(set);
419
+ if (funcFlags & 0b10) {
420
+ // constructor func, add new.target, this args
421
+ Porffor.wasm`
422
+ f64.const 0
423
+ i32.const 0
424
+ local.get ${obj}
425
+ f64.convert_i32_u
426
+ i32.const 7
427
+ local.get ${value}
428
+ local.get ${value+1}
429
+ local.get ${set}
430
+ call_indirect 3 0`;
431
+ } else {
432
+ Porffor.wasm`
433
+ local.get ${value}
434
+ local.get ${value+1}
435
+ local.get ${set}
436
+ call_indirect 1 0`;
437
+ }
438
+
439
+ return value;
440
+ }
441
+
442
+ // data descriptor
443
+ if (!(tail & 0b1000)) {
444
+ // not writable, return now
445
+ throw new TypeError('Cannot modify read-only property of object');
446
+ }
447
+
448
+ // flags = same flags as before
449
+ flags = tail & 0xff;
450
+ }
451
+
452
+ // write new value value (lol)
453
+ Porffor.wasm.f64.store(entryPtr, value, 0, 4);
454
+
455
+ // write new tail (value type + flags)
456
+ Porffor.wasm.i32.store16(entryPtr,
457
+ flags + (Porffor.wasm`local.get ${value+1}` << 8),
458
+ 0, 12);
459
+
460
+ return value;
461
+ };
462
+
378
463
  export const __Porffor_object_define = (obj: any, key: any, value: any, flags: i32): void => {
379
464
  if (Porffor.wasm`local.get ${obj+1}` != Porffor.TYPES.object) {
380
465
  obj = __Porffor_object_getObject(obj);
@@ -477,7 +562,6 @@ export const __Porffor_object_delete = (obj: any, key: any): boolean => {
477
562
  const tail: i32 = Porffor.wasm.i32.load16_u(entryPtr, 0, 12);
478
563
  if (!(tail & 0b0010)) {
479
564
  // not configurable
480
- // todo: throw in strict mode
481
565
  return false;
482
566
  }
483
567
 
@@ -511,6 +595,71 @@ memory.copy 0 0`;
511
595
  return true;
512
596
  };
513
597
 
598
+ export const __Porffor_object_deleteStrict = (obj: any, key: any): boolean => {
599
+ if (Porffor.wasm`local.get ${obj}` == 0) throw new TypeError('Cannot delete property of null');
600
+
601
+ if (Porffor.wasm`local.get ${obj+1}` == Porffor.TYPES.function) {
602
+ const tmp1: bytestring = 'name';
603
+ if (key == tmp1) {
604
+ __Porffor_funcLut_deleteName(obj);
605
+ return true;
606
+ }
607
+
608
+ const tmp2: bytestring = 'length';
609
+ if (key == tmp2) {
610
+ __Porffor_funcLut_deleteLength(obj);
611
+ return true;
612
+ }
613
+ }
614
+
615
+ if (Porffor.wasm`local.get ${obj+1}` != Porffor.TYPES.object) obj = __Porffor_object_getObject(obj);
616
+ if (Porffor.rawType(obj) != Porffor.TYPES.object) {
617
+ // todo: support non-pure objects
618
+ return true;
619
+ }
620
+
621
+ const entryPtr: i32 = __Porffor_object_lookup(obj, key);
622
+ if (entryPtr == -1) {
623
+ // not found, stop
624
+ return true;
625
+ }
626
+
627
+ const tail: i32 = Porffor.wasm.i32.load16_u(entryPtr, 0, 12);
628
+ if (!(tail & 0b0010)) {
629
+ // not configurable
630
+ throw new TypeError('Cannot delete non-configurable property of object');
631
+ }
632
+
633
+ const ind: i32 = (entryPtr - Porffor.wasm`local.get ${obj}`) / 14;
634
+
635
+ // decrement size
636
+ let size: i32 = Porffor.wasm.i32.load(obj, 0, 0);
637
+ Porffor.wasm.i32.store(obj, --size, 0, 0);
638
+
639
+ if (size > ind) {
640
+ // offset all elements after by -1 ind
641
+ Porffor.wasm`
642
+ ;; dst = entryPtr
643
+ local.get ${entryPtr}
644
+
645
+ ;; src = entryPtr + 14 (+ 1 entry)
646
+ local.get ${entryPtr}
647
+ i32.const 14
648
+ i32.add
649
+
650
+ ;; size = (size - ind) * 14
651
+ local.get ${size}
652
+ local.get ${ind}
653
+ i32.sub
654
+ i32.const 14
655
+ i32.mul
656
+
657
+ memory.copy 0 0`;
658
+ }
659
+
660
+ return true;
661
+ };
662
+
514
663
 
515
664
  export const __Porffor_object_isEnumerable = (entryPtr: i32): boolean => {
516
665
  const out: boolean = Porffor.wasm.i32.load8_u(entryPtr, 0, 12) & 0b0100;
@@ -595,13 +595,11 @@ export const __Object_defineProperty = (target: any, prop: any, descriptor: any)
595
595
 
596
596
  let flags: i32 = 0b0000;
597
597
  if (accessor) flags |= 0b0001;
598
- if (configurable) flags |= 0b0010;
599
- if (enumerable) flags |= 0b0100;
600
- if (writable) flags |= 0b1000;
598
+ if (!!configurable) flags |= 0b0010;
599
+ if (!!enumerable) flags |= 0b0100;
600
+ if (!!writable) flags |= 0b1000;
601
601
 
602
- if (accessor) {
603
- value = Porffor.object.packAccessor(get, set);
604
- }
602
+ if (accessor) value = Porffor.object.packAccessor(get, set);
605
603
 
606
604
  Porffor.object.define(target, p, value, flags);
607
605
  return target;
@@ -680,6 +678,12 @@ export const __Object_prototype_isPrototypeOf = (_this: any, obj: any) => {
680
678
 
681
679
 
682
680
  export const __Object_prototype_toString = (_this: any) => {
681
+ if (Porffor.rawType(_this) == Porffor.TYPES.object) {
682
+ const obj: object = _this;
683
+ const ovr: any = obj.toString;
684
+ if (ovr != null && ovr !== __Object_prototype_toString) return ovr.call(_this);
685
+ }
686
+
683
687
  let out: bytestring = Porffor.allocate();
684
688
 
685
689
  // 1. If the this value is undefined, return "[object Undefined]".
@@ -704,10 +708,16 @@ export const __Object_prototype_toString = (_this: any) => {
704
708
  return out = '[object Object]';
705
709
  };
706
710
 
707
- export const __Object_prototype_toLocaleString = (_this: any) => __Object_prototype_toLocaleString(_this);
711
+ export const __Object_prototype_toLocaleString = (_this: any) => __Object_prototype_toString(_this);
708
712
 
709
713
  export const __Object_prototype_valueOf = (_this: any) => {
710
714
  // todo: ToObject
715
+ if (Porffor.rawType(_this) == Porffor.TYPES.object) {
716
+ const obj: object = _this;
717
+ const ovr: any = obj.valueOf;
718
+ if (ovr != null && ovr !== __Object_prototype_valueOf) return ovr.call(_this);
719
+ }
720
+
711
721
  return _this;
712
722
  };
713
723
 
@@ -1,6 +1,30 @@
1
1
  // general widely used ecma262/spec functions
2
2
  import type {} from './porffor.d.ts';
3
3
 
4
+ export const __ecma262_ToPrimitive_Number = (input: any): any => {
5
+ // todo: %Symbol.toPrimitive%
6
+
7
+ let value: any = input.valueOf?.();
8
+ if (value != null && !Porffor.object.isObjectOrNull(value)) return value;
9
+
10
+ value = input.toString?.();
11
+ if (value != null && !Porffor.object.isObjectOrNull(value)) return value;
12
+
13
+ throw new TypeError('Cannot convert an object to primitive');
14
+ };
15
+
16
+ export const __ecma262_ToPrimitive_String = (input: any): any => {
17
+ // todo: %Symbol.toPrimitive%
18
+
19
+ let value: any = input.toString?.();
20
+ if (value != null && !Porffor.object.isObjectOrNull(value)) return value;
21
+
22
+ value = input.valueOf?.();
23
+ if (value != null && !Porffor.object.isObjectOrNull(value)) return value;
24
+
25
+ throw new TypeError('Cannot convert an object to primitive');
26
+ };
27
+
4
28
  // 7.1.4 ToNumber (argument)
5
29
  // https://tc39.es/ecma262/#sec-tonumber
6
30
  export const __ecma262_ToNumber = (argument: unknown): number => {
@@ -35,13 +59,11 @@ export const __ecma262_ToNumber = (argument: unknown): number => {
35
59
 
36
60
  // 7. Assert: argument is an Object.
37
61
  // 8. Let primValue be ? ToPrimitive(argument, number).
62
+ const primValue: any = __ecma262_ToPrimitive_Number(argument);
63
+
38
64
  // 9. Assert: primValue is not an Object.
39
65
  // 10. Return ? ToNumber(primValue).
40
-
41
- // // todo: I doubt this is spec-compliant
42
- // return __ecma262_ToNumber(argument.valueOf());
43
-
44
- return NaN;
66
+ return __ecma262_ToNumber(primValue);
45
67
  };
46
68
 
47
69
 
@@ -128,26 +150,35 @@ export const __ecma262_ToString = (argument: unknown): any => {
128
150
  }
129
151
 
130
152
  // 7. If argument is a Number, return Number::toString(argument, 10).
153
+ if (type == Porffor.TYPES.number) return __Number_prototype_toString(argument, 10);
154
+
131
155
  // 8. If argument is a BigInt, return BigInt::toString(argument, 10).
156
+
132
157
  // 9. Assert: argument is an Object.
133
158
  // 10. Let primValue be ? ToPrimitive(argument, string).
159
+ const primValue: any = __ecma262_ToPrimitive_String(argument);
160
+
134
161
  // 11. Assert: primValue is not an Object.
135
162
  // 12. Return ? ToString(primValue).
136
- return argument.toString();
163
+ return __ecma262_ToString(primValue);
137
164
  };
138
165
 
139
166
  // 7.1.19 ToPropertyKey (argument)
140
167
  // https://tc39.es/ecma262/#sec-topropertykey
141
168
  export const __ecma262_ToPropertyKey = (argument: any): any => {
142
169
  // 1. Let key be ? ToPrimitive(argument, string).
143
- // argument = key
170
+ let key: any = argument;
171
+
172
+ // only run ToPrimitive if pure object for perf
173
+ if (Porffor.rawType(argument) == Porffor.TYPES.object && Porffor.wasm`local.get ${argument}` != 0)
174
+ key = __ecma262_ToPrimitive_String(argument);
144
175
 
145
176
  // 2. If key is a Symbol, then
146
- if (Porffor.rawType(argument) == Porffor.TYPES.symbol) {
177
+ if (Porffor.rawType(key) == Porffor.TYPES.symbol) {
147
178
  // a. Return key.
148
- return argument;
179
+ return key;
149
180
  }
150
181
 
151
182
  // 3. Return ! ToString(key).
152
- return __ecma262_ToString(argument);
183
+ return __ecma262_ToString(key);
153
184
  };
@@ -16,11 +16,9 @@ export default function({ builtinFuncs }, Prefs) {
16
16
  builtinFuncs['#get_' + name] = {
17
17
  params: [],
18
18
  locals: [],
19
- globals: [ Valtype.i32 ],
20
- globalNames: [ '#getptr_' + name ],
21
19
  returns: [ Valtype.i32 ],
22
20
  returnType: TYPES.object,
23
- wasm: (scope, { allocPage, makeString, generate, getNodeType, builtin }) => {
21
+ wasm: (scope, { allocPage, makeString, generate, getNodeType, builtin, glbl }) => {
24
22
  if (globalThis.precompile) return [ [ 'get object', name ] ];
25
23
 
26
24
  // todo/perf: precompute bytes here instead of calling real funcs if we really care about perf later
@@ -32,17 +30,18 @@ export default function({ builtinFuncs }, Prefs) {
32
30
  ptr = allocPage(scope, `builtin object: ${name}`);
33
31
  }
34
32
 
33
+ const getPtr = glbl(Opcodes.global_get, `getptr_${name}`, Valtype.i32)[0];
35
34
  const out = [
36
35
  // check if already made/cached
37
- [ Opcodes.global_get, 0 ],
36
+ getPtr,
38
37
  [ Opcodes.if, Blocktype.void ],
39
- [ Opcodes.global_get, 0 ],
38
+ getPtr,
40
39
  [ Opcodes.return ],
41
40
  [ Opcodes.end ],
42
41
 
43
42
  // set cache & ptr for use
44
43
  ...number(ptr, Valtype.i32),
45
- [ Opcodes.global_set, 0 ],
44
+ glbl(Opcodes.global_set, `getptr_${name}`, Valtype.i32)[0]
46
45
  ];
47
46
 
48
47
  for (const x in props) {
@@ -62,7 +61,7 @@ export default function({ builtinFuncs }, Prefs) {
62
61
  if (this[prefix + x]?.type === TYPES.object && this[prefix + x] !== this.null) value = { type: 'ObjectExpression', properties: [] };
63
62
 
64
63
  out.push(
65
- [ Opcodes.global_get, 0 ],
64
+ getPtr,
66
65
  ...number(existingFunc ? TYPES.function : TYPES.object, Valtype.i32),
67
66
 
68
67
  ...makeString(scope, x, false, `#builtin_object_${name}_${x}`),
@@ -75,16 +74,14 @@ export default function({ builtinFuncs }, Prefs) {
75
74
  ...number(flags, Valtype.i32),
76
75
  ...number(TYPES.number, Valtype.i32),
77
76
 
78
- [ Opcodes.call, builtin('__Porffor_object_define') ],
77
+ [ Opcodes.call, builtin('__Porffor_object_expr_initWithFlags') ],
79
78
  [ Opcodes.drop ],
80
79
  [ Opcodes.drop ]
81
80
  );
82
81
  }
83
82
 
84
- out.push(
85
- // return ptr
86
- [ Opcodes.global_get, 0 ]
87
- );
83
+ // return ptr
84
+ out.push(getPtr);
88
85
  return out;
89
86
  }
90
87
  };