porffor 0.22.8 → 0.22.10

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.
@@ -164,13 +164,31 @@ export default (funcs, globals, tags, pages, data, flags, noTreeshake = false) =
164
164
  // generate func lut data
165
165
  const bytes = [];
166
166
  for (let i = 0; i < funcs.length; i++) {
167
- const argc = Math.floor(funcs[i].params.length / 2);
167
+ const func = funcs[i];
168
+ let name = func.name;
169
+
170
+ const typedParams = !func.internal || func.typedParams;
171
+ let argc = Math.floor(typedParams ? func.params.length / 2 : func.params.length);
172
+
173
+ // hack: argc-- for prototype methods to remove _this hack from count
174
+ if (name.includes('_prototype_')) argc--;
175
+
168
176
  bytes.push(argc % 256, (argc / 256 | 0) % 256);
169
177
 
170
178
  let flags = 0b00000000; // 8 flag bits
171
- if (funcs[i].returnType != null) flags |= 0b1;
172
- if (funcs[i].constr) flags |= 0b10;
179
+ if (func.returnType != null) flags |= 0b1;
180
+ if (func.constr) flags |= 0b10;
173
181
  bytes.push(flags);
182
+
183
+ // eg: __String_prototype_toLowerCase -> toLowerCase
184
+ if (name.startsWith('__')) name = name.split('_').pop();
185
+
186
+ bytes.push(...new Uint8Array(new Int32Array([ name.length ]).buffer));
187
+
188
+ for (let i = 0; i < (128 - 3 - 4); i++) {
189
+ const c = name.charCodeAt(i);
190
+ bytes.push((c || 0) % 256);
191
+ }
174
192
  }
175
193
 
176
194
  data.push({
@@ -62,8 +62,7 @@ export const __Porffor_object_checkAllFlags = (_this: object, dataAnd: i32, acce
62
62
  return true;
63
63
  };
64
64
 
65
-
66
- export const __Porffor_object_packAccessor = (get: i32, set: i32): f64 => {
65
+ export const __Porffor_object_packAccessor = (get: any, set: any): f64 => {
67
66
  // pack i32s get & set into a single f64 (i64)
68
67
  Porffor.wasm`
69
68
  local.get ${set}
@@ -81,11 +80,23 @@ return`;
81
80
 
82
81
  export const __Porffor_object_accessorGet = (entryPtr: i32): Function => {
83
82
  const out: Function = Porffor.wasm.i32.load(entryPtr, 0, 4);
83
+
84
+ // no getter, return undefined
85
+ if (Porffor.wasm`local.get ${out}` == 0) {
86
+ return undefined;
87
+ }
88
+
84
89
  return out;
85
90
  };
86
91
 
87
92
  export const __Porffor_object_accessorSet = (entryPtr: i32): Function => {
88
93
  const out: Function = Porffor.wasm.i32.load(entryPtr, 0, 8);
94
+
95
+ // no setter, return undefined
96
+ if (Porffor.wasm`local.get ${out}` == 0) {
97
+ return undefined;
98
+ }
99
+
89
100
  return out;
90
101
  };
91
102
 
@@ -134,7 +145,37 @@ export const __Porffor_object_lookup = (_this: object, target: any): i32 => {
134
145
  return -1;
135
146
  };
136
147
 
137
- export const __Porffor_object_get = (_this: object, key: any): any => {
148
+ export const __Porffor_object_get = (_this: any, key: any): any => {
149
+ if (Porffor.wasm`local.get ${_this+1}` == Porffor.TYPES.function) {
150
+ let tmp: bytestring = '';
151
+ tmp = 'name';
152
+ if (key == tmp) {
153
+ const o: bytestring = __Porffor_funcLut_name(_this);
154
+ const t: i32 = Porffor.TYPES.bytestring;
155
+ Porffor.wasm`
156
+ local.get ${o}
157
+ f64.convert_i32_u
158
+ local.get ${t}
159
+ return`;
160
+ }
161
+
162
+ tmp = 'length';
163
+ if (key == tmp) {
164
+ const o: i32 = __Porffor_funcLut_length(_this);
165
+ Porffor.wasm`
166
+ local.get ${o}
167
+ f64.convert_i32_u
168
+ i32.const 1
169
+ return`;
170
+ }
171
+
172
+ // undefined
173
+ Porffor.wasm`
174
+ f64.const 0
175
+ i32.const 128
176
+ return`;
177
+ }
178
+
138
179
  const entryPtr: i32 = __Porffor_object_lookup(_this, key);
139
180
  if (entryPtr == -1) {
140
181
  Porffor.wasm`
@@ -148,6 +189,15 @@ return`;
148
189
  if (tail & 0b0001) {
149
190
  // accessor descriptor
150
191
  const get: Function = __Porffor_object_accessorGet(entryPtr);
192
+
193
+ // no getter, return undefined
194
+ if (Porffor.wasm`local.get ${get}` == 0) {
195
+ Porffor.wasm`
196
+ f64.const 0
197
+ i32.const 128
198
+ return`;
199
+ }
200
+
151
201
  Porffor.wasm`
152
202
  local.get ${get}
153
203
  call_indirect 0 0
@@ -205,7 +255,14 @@ export const __Porffor_object_set = (_this: object, key: any, value: any): any =
205
255
  if (tail & 0b0001) {
206
256
  // accessor descriptor
207
257
  const set: Function = __Porffor_object_accessorSet(entryPtr);
208
- Porffor.wasm`
258
+
259
+ // no setter, return early
260
+ if (Porffor.wasm`local.get ${set}` == 0) {
261
+ // todo: throw in strict mode?
262
+ return value;
263
+ }
264
+
265
+ Porffor.wasm`
209
266
  local.get ${value}
210
267
  local.get ${value+1}
211
268
  local.get ${set}
@@ -141,8 +141,23 @@ export const __Porffor_print = (arg: any, colors: boolean = true) => {
141
141
 
142
142
  case Porffor.TYPES.object:
143
143
  if (arg) {
144
- if (colors) printStatic('\x1b[34m'); // blue
145
- printStatic('[Object]');
144
+ printStatic('{\n');
145
+
146
+ const keys = Object.keys(arg);
147
+ const len = keys.length - 1;
148
+ for (let i: i32 = 0; i <= len; i++) {
149
+ const x = keys[i];
150
+
151
+ printStatic(' ');
152
+ __Porffor_printString(x);
153
+
154
+ printStatic(': ');
155
+ __Porffor_print(arg[x]);
156
+
157
+ if (i != len) printStatic(',\n');
158
+ }
159
+
160
+ printStatic('\n}');
146
161
  } else {
147
162
  if (colors) printStatic('\x1b[1m'); // bold
148
163
  printStatic('null');
@@ -170,6 +170,18 @@ export const __Object_prototype_hasOwnProperty = (_this: any, prop: any) => {
170
170
  return Porffor.object.lookup(_this, p) != -1;
171
171
  }
172
172
 
173
+ if (t == Porffor.TYPES.function) {
174
+ let tmp: bytestring = '';
175
+
176
+ tmp = 'name';
177
+ if (p == tmp) return true;
178
+
179
+ tmp = 'length';
180
+ if (p == tmp) return true;
181
+
182
+ return false;
183
+ }
184
+
173
185
  const keys: any[] = __Object_keys(_this);
174
186
  return __Array_prototype_includes(keys, p);
175
187
  };
@@ -397,12 +409,29 @@ export const __Object_isSealed = (obj: any): any => {
397
409
 
398
410
 
399
411
  export const __Object_getOwnPropertyDescriptor = (obj: any, prop: any): any => {
412
+ const p: any = ecma262.ToPropertyKey(prop);
413
+
414
+ const objType: i32 = Porffor.rawType(obj);
415
+ if (objType == Porffor.TYPES.function) {
416
+ // hack: function .name and .length
417
+ const out: object = {};
418
+
419
+ out.writable = false;
420
+ out.enumerable = false;
421
+ out.configurable = true;
422
+
423
+ const v = obj[p];
424
+ if (v != null) {
425
+ out.value = v;
426
+ return out;
427
+ }
428
+ }
429
+
400
430
  // todo: support non-pure-objects
401
- if (Porffor.rawType(obj) != Porffor.TYPES.object) {
431
+ if (objType != Porffor.TYPES.object) {
402
432
  return undefined;
403
433
  }
404
434
 
405
- const p: any = ecma262.ToPropertyKey(prop);
406
435
  const entryPtr: i32 = Porffor.object.lookup(obj, p);
407
436
  if (entryPtr == -1) return undefined;
408
437
 
@@ -1,8 +1,10 @@
1
- import * as GeneratedBuiltins from './generated_builtins.js';
1
+ import * as PrecompiledBuiltins from './builtins_precompiled.js';
2
+ import ObjectBuiltins from './builtins_objects.js';
2
3
  import { Blocktype, Opcodes, Valtype, ValtypeSize } from './wasmSpec.js';
3
4
  import { number } from './embedding.js';
4
5
  import { TYPES, TYPE_NAMES } from './types.js';
5
6
  import Prefs from './prefs.js';
7
+ import { unsignedLEB128 } from './encoding.js';
6
8
 
7
9
  export const importedFuncs = [
8
10
  {
@@ -81,11 +83,10 @@ const printStaticStr = str => {
81
83
  return out;
82
84
  };
83
85
 
84
- // todo: somehow diff between these (undefined != null) while remaining falsey in wasm as a number value
85
86
  export const UNDEFINED = 0;
86
87
  export const NULL = 0;
87
88
 
88
- export const BuiltinVars = function() {
89
+ export const BuiltinVars = function(ctx) {
89
90
  this.undefined = number(UNDEFINED);
90
91
  this.undefined.type = TYPES.undefined;
91
92
 
@@ -114,7 +115,6 @@ export const BuiltinVars = function() {
114
115
 
115
116
  this.__Number_MAX_SAFE_INTEGER = this.__Number_MAX_VALUE;
116
117
  this.__Number_MIN_SAFE_INTEGER = this.__Number_MIN_VALUE;
117
-
118
118
  break;
119
119
 
120
120
  case 'i64':
@@ -124,7 +124,6 @@ export const BuiltinVars = function() {
124
124
 
125
125
  this.__Number_MAX_SAFE_INTEGER = this.__Number_MAX_VALUE;
126
126
  this.__Number_MIN_SAFE_INTEGER = this.__Number_MIN_VALUE;
127
-
128
127
  break;
129
128
 
130
129
  case 'f64':
@@ -135,30 +134,9 @@ export const BuiltinVars = function() {
135
134
  this.__Number_MIN_SAFE_INTEGER = number(-9007199254740991);
136
135
 
137
136
  this.__Number_EPSILON = number(2.220446049250313e-16);
138
-
139
- this.__Math_E = number(Math.E);
140
- this.__Math_LN10 = number(Math.LN10);
141
- this.__Math_LN2 = number(Math.LN2);
142
- this.__Math_LOG10E = number(Math.LOG10E);
143
- this.__Math_LOG2E = number(Math.LOG2E);
144
- this.__Math_PI = number(Math.PI);
145
- this.__Math_SQRT1_2 = number(Math.SQRT1_2);
146
- this.__Math_SQRT2 = number(Math.SQRT2);
147
-
148
- // https://github.com/rwaldron/proposal-math-extensions/issues/10
149
- this.__Math_RAD_PER_DEG = number(Math.PI / 180);
150
- this.__Math_DEG_PER_RAD = number(180 / Math.PI);
151
-
152
137
  break;
153
138
  }
154
139
 
155
- // stubs just so that parent objects exist
156
- this.Math = number(1);
157
-
158
- // wintercg(tm)
159
- this.__navigator_userAgent = (scope, { makeString }) => makeString(scope, `Porffor/${globalThis.version ?? '0.17.0'}`, false, '__navigator_userAgent');
160
- this.__navigator_userAgent.type = Prefs.bytestring ? TYPES.bytestring : TYPES.string;
161
-
162
140
  for (const x in TYPES) {
163
141
  this['__Porffor_TYPES_' + x] = number(TYPES[x]);
164
142
  }
@@ -176,6 +154,8 @@ export const BuiltinVars = function() {
176
154
  this.__Int32Array_BYTES_PER_ELEMENT = number(4);
177
155
  this.__Float32Array_BYTES_PER_ELEMENT = number(4);
178
156
  this.__Float64Array_BYTES_PER_ELEMENT = number(8);
157
+
158
+ ObjectBuiltins.call(this, ctx, Prefs);
179
159
  };
180
160
 
181
161
  export const BuiltinFuncs = function() {
@@ -1089,5 +1069,34 @@ export const BuiltinFuncs = function() {
1089
1069
  ]
1090
1070
  };
1091
1071
 
1092
- GeneratedBuiltins.BuiltinFuncs.call(this);
1072
+ this.__Porffor_funcLut_length = {
1073
+ params: [ Valtype.i32 ],
1074
+ returns: [ Valtype.i32 ],
1075
+ returnType: TYPES.number,
1076
+ wasm: (scope, { allocPage }) => [
1077
+ [ Opcodes.local_get, 0 ],
1078
+ ...number(128, Valtype.i32),
1079
+ [ Opcodes.i32_mul ],
1080
+ [ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut') * pageSize) ]
1081
+ ],
1082
+ table: true
1083
+ };
1084
+
1085
+ this.__Porffor_funcLut_name = {
1086
+ params: [ Valtype.i32 ],
1087
+ returns: [ Valtype.i32 ],
1088
+ returnType: TYPES.bytestring,
1089
+ wasm: (scope, { allocPage }) => [
1090
+ [ Opcodes.local_get, 0 ],
1091
+ ...number(128, Valtype.i32),
1092
+ [ Opcodes.i32_mul ],
1093
+ ...number(3, Valtype.i32),
1094
+ [ Opcodes.i32_add ],
1095
+ ...number(allocPage(scope, 'func lut') * pageSize, Valtype.i32),
1096
+ [ Opcodes.i32_add ]
1097
+ ],
1098
+ table: true
1099
+ };
1100
+
1101
+ PrecompiledBuiltins.BuiltinFuncs.call(this);
1093
1102
  };
@@ -0,0 +1,181 @@
1
+ import { Opcodes, PageSize, Valtype } from './wasmSpec.js';
2
+ import { TYPES } from './types.js';
3
+ import { number } from './embedding.js';
4
+
5
+ export default function({ builtinFuncs }, Prefs) {
6
+ const done = new Set();
7
+ const object = (name, props) => {
8
+ done.add(name);
9
+
10
+ let cached;
11
+ this[name] = (scope, { allocPage, makeString, generateIdent, getNodeType, builtin }) => {
12
+ if (cached) {
13
+ return number(cached);
14
+ }
15
+
16
+ // todo: precompute bytes here instead of calling real funcs if we really care about perf later
17
+
18
+ const page = allocPage(scope, `builtin object: ${name}`);
19
+ const ptr = page === 0 ? 4 : page * PageSize;
20
+ cached = ptr;
21
+
22
+ const out = [];
23
+
24
+ for (const x in props) {
25
+ const value = {
26
+ type: 'Identifier',
27
+ name: '__' + name + '_' + x
28
+ };
29
+
30
+ let flags = 0b0000;
31
+
32
+ const d = props[x];
33
+ if (d.configurable) flags |= 0b0010;
34
+ if (d.enumerable) flags |= 0b0100;
35
+ if (d.writable) flags |= 0b1000;
36
+
37
+ out.push(
38
+ ...number(ptr, Valtype.i32),
39
+ ...number(TYPES.object, Valtype.i32),
40
+
41
+ ...makeString(scope, x, false, `#builtin_object_${name}_${x}`),
42
+ Opcodes.i32_to_u,
43
+ ...number(TYPES.bytestring, Valtype.i32),
44
+
45
+ ...generateIdent(scope, value),
46
+ ...getNodeType(scope, value),
47
+
48
+ ...number(flags, Valtype.i32),
49
+ ...number(TYPES.number, Valtype.i32),
50
+
51
+ [ Opcodes.call, ...builtin('__Porffor_object_define') ],
52
+ [ Opcodes.drop ],
53
+ [ Opcodes.drop ]
54
+ );
55
+ }
56
+
57
+ out.push(...number(ptr));
58
+ return out;
59
+ };
60
+ this[name].type = TYPES.object;
61
+
62
+ for (const x in props) {
63
+ const d = props[x];
64
+
65
+ if (d.value) {
66
+ const k = '__' + name + '_' + x;
67
+
68
+ if (typeof d.value === 'number') {
69
+ this[k] = number(d.value);
70
+ this[k].type = TYPES.number;
71
+ continue;
72
+ }
73
+
74
+ if (typeof d.value === 'string') {
75
+ this[k] = (scope, { makeString }) => makeString(scope, d.value, false, k);
76
+ this[k].type = TYPES.bytestring;
77
+ continue;
78
+ }
79
+
80
+ throw new Error(`unsupported value type (${typeof d.value})`);
81
+ }
82
+ }
83
+ };
84
+
85
+ const props = (base, vals) => {
86
+ const out = {};
87
+
88
+ if (Array.isArray(vals)) {
89
+ // array of keys with no value
90
+ for (const x of vals) {
91
+ out[x] = {
92
+ ...base
93
+ };
94
+ }
95
+ } else for (const x in vals) {
96
+ // object of key values
97
+ out[x] = {
98
+ ...base,
99
+ value: vals[x]
100
+ };
101
+ }
102
+
103
+ return out;
104
+ };
105
+
106
+ const builtinFuncKeys = Object.keys(builtinFuncs);
107
+ const autoFuncs = name => builtinFuncKeys.filter(x => x.startsWith('__' + name + '_')).map(x => x.slice(name.length + 3));
108
+
109
+ object('Math', {
110
+ ...props({
111
+ writable: false,
112
+ enumerable: false,
113
+ configurable: false
114
+ }, {
115
+ E: Math.E,
116
+ LN10: Math.LN10,
117
+ LN2: Math.LN2,
118
+ LOG10E: Math.LOG10E,
119
+ LOG2E: Math.LOG2E,
120
+ PI: Math.PI,
121
+ SQRT1_2: Math.SQRT1_2,
122
+ SQRT2: Math.SQRT2,
123
+
124
+ // https://github.com/rwaldron/proposal-math-extensions/issues/10
125
+ RAD_PER_DEG: Math.PI / 180,
126
+ DEG_PER_RAD: 180 / Math.PI
127
+ }),
128
+
129
+ ...props({
130
+ writable: true,
131
+ enumerable: false,
132
+ configurable: true
133
+ }, autoFuncs('Math'))
134
+ });
135
+
136
+
137
+ // todo: support when existing func
138
+ // object('Number', {
139
+ // NaN: NaN,
140
+ // POSITIVE_INFINITY: Infinity,
141
+ // NEGATIVE_INFINITY: -Infinity,
142
+
143
+ // MAX_VALUE: valtype === 'i32' ? 2147483647 : 1.7976931348623157e+308,
144
+ // MIN_VALUE: valtype === 'i32' ? -2147483648 : 5e-324,
145
+
146
+ // MAX_SAFE_INTEGER: valtype === 'i32' ? 2147483647 : 9007199254740991,
147
+ // MIN_SAFE_INTEGER: valtype === 'i32' ? -2147483648 : -9007199254740991,
148
+
149
+ // EPSILON: 2.220446049250313e-16
150
+ // });
151
+
152
+
153
+ // technically not spec compliant as it should be a navigator class but bleh
154
+ object('navigator', {
155
+ ...props({
156
+ writable: false,
157
+ enumerable: true,
158
+ configurable: false
159
+ }, {
160
+ userAgent: `Porffor/${globalThis.version}`
161
+ })
162
+ });
163
+
164
+ if (Prefs.logMissingObjects) for (const x of Object.keys(builtinFuncs).concat(Object.keys(this))) {
165
+ if (!x.startsWith('__')) continue;
166
+
167
+ const name = x.split('_').slice(2, -1).join('_').replaceAll('_', '.');
168
+
169
+ let t = globalThis;
170
+ for (const x of name.split('.')) {
171
+ t = t[x];
172
+ if (!t) break;
173
+ }
174
+ if (!t) continue;
175
+
176
+ if (!done.has(name)) {
177
+ console.log(name);
178
+ done.add(name);
179
+ }
180
+ }
181
+ };