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.
- package/compiler/assemble.js +21 -3
- package/compiler/builtins/_internal_object.ts +61 -4
- package/compiler/builtins/console.ts +17 -2
- package/compiler/builtins/object.ts +31 -2
- package/compiler/builtins.js +36 -27
- package/compiler/builtins_objects.js +181 -0
- package/compiler/builtins_precompiled.js +4603 -0
- package/compiler/codegen.js +88 -79
- package/compiler/havoc.js +0 -25
- package/compiler/precompile.js +1 -1
- package/package.json +1 -1
- package/runner/index.js +1 -1
- package/compiler/generated_builtins.js +0 -4603
package/compiler/assemble.js
CHANGED
@@ -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
|
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 (
|
172
|
-
if (
|
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:
|
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
|
-
|
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
|
-
|
145
|
-
|
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 (
|
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
|
|
package/compiler/builtins.js
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
import * as
|
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
|
-
|
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
|
+
};
|