porffor 0.56.8 → 0.57.1
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/builtins/_internal_object.ts +4 -4
- package/compiler/builtins/array.ts +1 -1
- package/compiler/builtins/base64.ts +2 -2
- package/compiler/builtins/console.ts +9 -12
- package/compiler/builtins/date.ts +1 -1
- package/compiler/builtins/function.ts +1 -1
- package/compiler/builtins/number.ts +1 -1
- package/compiler/builtins/object.ts +4 -4
- package/compiler/builtins/promise.ts +3 -3
- package/compiler/builtins_precompiled.js +663 -1392
- package/compiler/codegen.js +39 -12
- package/compiler/precompile.js +2 -4
- package/package.json +1 -1
- package/runner/index.js +8 -6
- package/optional-chaining-tests.js +0 -335
- package/r.cjs +0 -71
package/compiler/codegen.js
CHANGED
@@ -1322,7 +1322,7 @@ const asmFuncToAsm = (scope, func, extra) => func(scope, {
|
|
1322
1322
|
allocPage: (scope, name) => allocPage({ scope, pages }, name)
|
1323
1323
|
}, extra);
|
1324
1324
|
|
1325
|
-
const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTypes = [], globals: globalTypes = [], globalInits = [], returns = [], returnType, localNames = [], globalNames = [], table = false, constr = false, hasRestArgument = false, usesTag = false, usesImports = false,
|
1325
|
+
const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTypes = [], globals: globalTypes = [], globalInits = [], returns = [], returnType, localNames = [], globalNames = [], table = false, constr = false, hasRestArgument = false, usesTag = false, usesImports = false, returnTypes } = {}) => {
|
1326
1326
|
if (wasm == null) { // called with no built-in
|
1327
1327
|
log.warning('codegen', `${name} has no built-in!`);
|
1328
1328
|
wasm = [];
|
@@ -1379,7 +1379,12 @@ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTy
|
|
1379
1379
|
|
1380
1380
|
if (table) funcs.table = true;
|
1381
1381
|
if (usesTag) ensureTag();
|
1382
|
-
|
1382
|
+
|
1383
|
+
if (returnTypes) {
|
1384
|
+
for (const x of returnTypes) typeUsed(func, x);
|
1385
|
+
} else if (returnType != null) {
|
1386
|
+
typeUsed(func, returnType);
|
1387
|
+
}
|
1383
1388
|
|
1384
1389
|
func.wasm = wasm;
|
1385
1390
|
return func;
|
@@ -1752,7 +1757,7 @@ const getNodeType = (scope, node) => {
|
|
1752
1757
|
const out = typeof ret === 'number' ? [ number(ret, Valtype.i32) ] : ret;
|
1753
1758
|
if (guess != null) out.guess = typeof guess === 'number' ? [ number(guess, Valtype.i32) ] : guess;
|
1754
1759
|
|
1755
|
-
typeUsed(scope, knownType(scope, out));
|
1760
|
+
if (!node._doNotMarkTypeUsed) typeUsed(scope, knownType(scope, out));
|
1756
1761
|
return out;
|
1757
1762
|
};
|
1758
1763
|
|
@@ -2654,7 +2659,8 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2654
2659
|
args = args.slice(0, paramCount - 1);
|
2655
2660
|
args.push({
|
2656
2661
|
type: 'ArrayExpression',
|
2657
|
-
elements: restArgs
|
2662
|
+
elements: restArgs,
|
2663
|
+
_doNotMarkTypeUsed: true
|
2658
2664
|
});
|
2659
2665
|
}
|
2660
2666
|
}
|
@@ -3015,10 +3021,14 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary, fallthrough = fals
|
|
3015
3021
|
};
|
3016
3022
|
|
3017
3023
|
if (globalThis.precompile) {
|
3018
|
-
|
3019
|
-
|
3020
|
-
|
3021
|
-
|
3024
|
+
if (scope.usedTypes && types.some(x => scope.usedTypes.has(x))) {
|
3025
|
+
add();
|
3026
|
+
} else {
|
3027
|
+
// just magic precompile things™
|
3028
|
+
out.push([ null, 'typeswitch case start', types ]);
|
3029
|
+
add();
|
3030
|
+
out.push([ null, 'typeswitch case end' ]);
|
3031
|
+
}
|
3022
3032
|
} else {
|
3023
3033
|
if (types.some(x => usedTypes.has(x))) {
|
3024
3034
|
// type already used, just add it now
|
@@ -3135,7 +3145,7 @@ const extractTypeAnnotation = decl => {
|
|
3135
3145
|
let a = decl;
|
3136
3146
|
while (a.typeAnnotation) a = a.typeAnnotation;
|
3137
3147
|
|
3138
|
-
let type = null, elementType = null;
|
3148
|
+
let types = null, type = null, elementType = null;
|
3139
3149
|
if (a.typeName) {
|
3140
3150
|
type = a.typeName.name;
|
3141
3151
|
} else if (a.type.endsWith('Keyword')) {
|
@@ -3144,12 +3154,16 @@ const extractTypeAnnotation = decl => {
|
|
3144
3154
|
} else if (a.type === 'TSArrayType') {
|
3145
3155
|
type = 'array';
|
3146
3156
|
elementType = extractTypeAnnotation(a.elementType).type;
|
3157
|
+
} else if (a.type === 'TSUnionType') {
|
3158
|
+
types = a.types.map(x => extractTypeAnnotation(x).type);
|
3147
3159
|
}
|
3148
3160
|
|
3149
3161
|
const typeName = type;
|
3150
3162
|
type = typeAnnoToPorfType(type);
|
3151
3163
|
|
3152
|
-
|
3164
|
+
if (!types && type != null) types = [ type ];
|
3165
|
+
|
3166
|
+
return { type, types, typeName, elementType };
|
3153
3167
|
};
|
3154
3168
|
|
3155
3169
|
const setLocalWithType = (scope, name, isGlobal, decl, tee = false, overrideType = undefined) => {
|
@@ -5099,6 +5113,10 @@ const generateSwitch = (scope, decl) => {
|
|
5099
5113
|
if (canTypeCheck) {
|
5100
5114
|
depth.push('switch_typeswitch');
|
5101
5115
|
|
5116
|
+
// temporarily stub scope used types to have none always included for these cases
|
5117
|
+
const usedTypes = scope.usedTypes;
|
5118
|
+
scope.usedTypes = { add: () => {}, has: () => false };
|
5119
|
+
|
5102
5120
|
const out = typeSwitch(scope, getNodeType(scope, decl.discriminant.arguments[0]), () => {
|
5103
5121
|
const bc = [];
|
5104
5122
|
let types = [];
|
@@ -5114,6 +5132,8 @@ const generateSwitch = (scope, decl) => {
|
|
5114
5132
|
return bc;
|
5115
5133
|
}, Blocktype.void, true);
|
5116
5134
|
|
5135
|
+
scope.usedTypes = usedTypes;
|
5136
|
+
|
5117
5137
|
depth.pop();
|
5118
5138
|
out.push(number(UNDEFINED));
|
5119
5139
|
return out;
|
@@ -6602,6 +6622,8 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
|
|
6602
6622
|
const typeAnno = extractTypeAnnotation(type);
|
6603
6623
|
addVarMetadata(func, name, false, typeAnno);
|
6604
6624
|
|
6625
|
+
if (typeAnno.types) for (const x of typeAnno.types) typeUsed(func, x);
|
6626
|
+
|
6605
6627
|
// automatically add throws if unexpected this type to builtins
|
6606
6628
|
if (globalThis.precompile && i === 0 && func.name.includes('_prototype_') && !func.name.startsWith('__Porffor_')) {
|
6607
6629
|
if (typeAnno.type === TYPES.array) {
|
@@ -6707,6 +6729,7 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
|
|
6707
6729
|
// make out generator local
|
6708
6730
|
allocVar(func, '#generator_out', false, false);
|
6709
6731
|
typeUsed(func, func.async ? TYPES.__porffor_asyncgenerator : TYPES.__porffor_generator);
|
6732
|
+
if (func.async) typeUsed(func, TYPES.promise);
|
6710
6733
|
}
|
6711
6734
|
|
6712
6735
|
if (func.async && !func.generator) {
|
@@ -6788,11 +6811,15 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
|
|
6788
6811
|
funcs.push(func);
|
6789
6812
|
|
6790
6813
|
if (typedInput && decl.returnType) {
|
6791
|
-
const { type } = extractTypeAnnotation(decl.returnType);
|
6814
|
+
const { type, types } = extractTypeAnnotation(decl.returnType);
|
6815
|
+
|
6792
6816
|
if (type != null) {
|
6793
6817
|
typeUsed(func, type);
|
6794
6818
|
func.returnType = type;
|
6795
6819
|
func.returns = func.returnType === TYPES.undefined && !func.async && !func.generator ? [] : [ valtypeBinary ];
|
6820
|
+
} else if (types != null) {
|
6821
|
+
func.returnTypes = types;
|
6822
|
+
for (const x of types) typeUsed(func, x);
|
6796
6823
|
}
|
6797
6824
|
}
|
6798
6825
|
|
@@ -6836,7 +6863,7 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
|
|
6836
6863
|
break;
|
6837
6864
|
}
|
6838
6865
|
|
6839
|
-
args.push({ name, def, destr, type: typedInput &&
|
6866
|
+
args.push({ name, def, destr, type: typedInput && x.typeAnnotation });
|
6840
6867
|
}
|
6841
6868
|
|
6842
6869
|
func.params = new Array((params.length + (func.constr ? 2 : (func.method ? 1 : 0))) * 2).fill(0).map((_, i) => i % 2 ? Valtype.i32 : valtypeBinary);
|
package/compiler/precompile.js
CHANGED
@@ -260,13 +260,11 @@ ${funcs.map(x => {
|
|
260
260
|
// todo: check for other identifier unsafe characters
|
261
261
|
const name = x.name.includes('#') ? `['${x.name}']` : `.${x.name}`;
|
262
262
|
|
263
|
-
const
|
264
|
-
|
263
|
+
const returnTypes = [...(x.returnTypes ?? [])].filter(x => ![ TYPES.empty, TYPES.undefined, TYPES.number, TYPES.boolean, TYPES.function ].includes(x));
|
265
264
|
return `this${name} = {
|
266
265
|
wasm:${rewriteWasm(x.wasm)},
|
267
|
-
params:${JSON.stringify(x.params)},typedParams:1,returns:${JSON.stringify(x.returns)},${x.returnType != null ? `returnType:${JSON.stringify(x.returnType)},` : ''}
|
266
|
+
params:${JSON.stringify(x.params)},typedParams:1,returns:${JSON.stringify(x.returns)},${x.returnType != null ? `returnType:${JSON.stringify(x.returnType)},` : ''}${returnTypes.length > 0 ? `returnTypes:${JSON.stringify(returnTypes)},` : ''}
|
268
267
|
locals:${JSON.stringify(locals.slice(x.params.length).map(x => x[1].type))},localNames:${JSON.stringify(locals.map(x => x[0]))},
|
269
|
-
${usedTypes.length > 0 ? `usedTypes:${JSON.stringify(usedTypes)},` : ''}
|
270
268
|
${x.globalInits ? `globalInits:{${Object.keys(x.globalInits).map(y => `${y}:${rewriteWasm(x.globalInits[y])}`).join(',')}},` : ''}${x.data && Object.keys(x.data).length > 0 ? `data:${JSON.stringify(x.data)},` : ''}
|
271
269
|
${x.table ? `table:1,` : ''}${x.constr ? `constr:1,` : ''}${x.hasRestArgument ? `hasRestArgument:1,` : ''}${x.usesTag ? `usesTag:1,` : ''}${x.usesImports ? `usesImports:1,` : ''}
|
272
270
|
}`.replaceAll('\n\n', '\n').replaceAll('\n\n', '\n').replaceAll('\n\n', '\n');
|
package/package.json
CHANGED
package/runner/index.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
2
|
import fs from 'node:fs';
|
3
|
-
globalThis.version = '0.
|
3
|
+
globalThis.version = '0.57.1';
|
4
4
|
|
5
5
|
// deno compat
|
6
6
|
if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
|
@@ -46,9 +46,11 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
46
46
|
// flags
|
47
47
|
console.log(`\n\x1B[1m\x1B[4mFlags\x1B[0m`);
|
48
48
|
for (let [ flag, desc ] of Object.entries({
|
49
|
+
'On': 'Optimization level, use -O(0|\x1B[1m1\x1B[0m|2|3)',
|
50
|
+
t: 'Force parsing input as TypeScript',
|
51
|
+
d: 'Debug mode (include names in Wasm and debug logs)',
|
49
52
|
module: 'Parse input as a module',
|
50
|
-
|
51
|
-
d: 'Debug mode (include names in Wasm and debug logs)'
|
53
|
+
secure: 'Secure mode (error on unsafe Porffor features eg FFI)'
|
52
54
|
})) {
|
53
55
|
flag = '-' + flag;
|
54
56
|
if (flag.length > 3) flag = '-' + flag;
|
@@ -57,10 +59,10 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
57
59
|
}
|
58
60
|
|
59
61
|
// niche flags
|
60
|
-
if (process.argv.includes('
|
62
|
+
if (process.argv.includes('all')) {
|
61
63
|
for (let [ flag, desc ] of Object.entries({
|
62
64
|
f: 'Print disassembled Wasm generated from user functions',
|
63
|
-
pgo: 'Enable
|
65
|
+
pgo: 'Enable profile-guided optimization',
|
64
66
|
valtype: 'Valtype to use, not well supported (i32|i64|\x1B[1mf64\x1B[0m)',
|
65
67
|
'no-coctc': 'Disable COCTC (cross-object compile-time cache)',
|
66
68
|
cyclone: 'Enable experimental Cyclone optimizer',
|
@@ -77,7 +79,7 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
77
79
|
console.log(` \x1B[1m${flag}\x1B[0m${' '.repeat(36 - flag.length)}${desc}`);
|
78
80
|
}
|
79
81
|
} else {
|
80
|
-
console.log(` \x1B[
|
82
|
+
console.log(` \x1B[90m(To view all flags use --help all)\x1B[0m`);
|
81
83
|
}
|
82
84
|
|
83
85
|
console.log();
|
@@ -1,335 +0,0 @@
|
|
1
|
-
// Optional Chaining Tests
|
2
|
-
// This file tests all major features of the optional chaining operator (?.)
|
3
|
-
|
4
|
-
// Test utility function
|
5
|
-
function assert(condition, message) {
|
6
|
-
if (!condition) {
|
7
|
-
throw new Error('Assertion failed: ' + message);
|
8
|
-
}
|
9
|
-
return true;
|
10
|
-
}
|
11
|
-
|
12
|
-
// BASIC OBJECT PROPERTY ACCESS TESTS
|
13
|
-
console.log('\n=== Basic Object Property Access ===');
|
14
|
-
|
15
|
-
// Test 1: Basic property access with existing property
|
16
|
-
(function() {
|
17
|
-
const obj = { a: 1 };
|
18
|
-
assert(obj?.a === 1, 'Basic property access failed');
|
19
|
-
console.log('✓ Basic property access with existing property');
|
20
|
-
})();
|
21
|
-
|
22
|
-
// Test 2: Basic property access with undefined object
|
23
|
-
(function() {
|
24
|
-
const obj = undefined;
|
25
|
-
assert(obj?.a === undefined, 'Optional chaining with undefined object failed');
|
26
|
-
console.log('✓ Basic property access with undefined object');
|
27
|
-
})();
|
28
|
-
|
29
|
-
// Test 3: Basic property access with null object
|
30
|
-
(function() {
|
31
|
-
const obj = null;
|
32
|
-
assert(obj?.a === undefined, 'Optional chaining with null object failed');
|
33
|
-
console.log('✓ Basic property access with null object');
|
34
|
-
})();
|
35
|
-
|
36
|
-
// NESTED PROPERTY ACCESS TESTS
|
37
|
-
console.log('\n=== Nested Property Access ===');
|
38
|
-
|
39
|
-
// Test 4: Nested property access with all properties existing
|
40
|
-
(function() {
|
41
|
-
const obj = { a: { b: { c: 42 } } };
|
42
|
-
assert(obj?.a?.b?.c === 42, 'Nested property access failed');
|
43
|
-
console.log('✓ Nested property access with all properties existing');
|
44
|
-
})();
|
45
|
-
|
46
|
-
// Test 5: Nested property access with missing intermediate property
|
47
|
-
(function() {
|
48
|
-
const obj = { a: {} };
|
49
|
-
assert(obj?.a?.b?.c === undefined, 'Nested property access with missing intermediate property failed');
|
50
|
-
console.log('✓ Nested property access with missing intermediate property');
|
51
|
-
})();
|
52
|
-
|
53
|
-
// Test 6: Nested property access with null intermediate property
|
54
|
-
(function() {
|
55
|
-
const obj = { a: { b: null } };
|
56
|
-
assert(obj?.a?.b?.c === undefined, 'Nested property access with null intermediate property failed');
|
57
|
-
console.log('✓ Nested property access with null intermediate property');
|
58
|
-
})();
|
59
|
-
|
60
|
-
// METHOD CALL TESTS
|
61
|
-
console.log('\n=== Method Call Tests ===');
|
62
|
-
|
63
|
-
// Test 7: Method call on existing method
|
64
|
-
(function() {
|
65
|
-
const obj = {
|
66
|
-
method: function() { return 'result'; }
|
67
|
-
};
|
68
|
-
assert(obj?.method?.() === 'result', 'Method call on existing method failed');
|
69
|
-
console.log('✓ Method call on existing method');
|
70
|
-
})();
|
71
|
-
|
72
|
-
// Test 8: Method call on undefined object
|
73
|
-
(function() {
|
74
|
-
const obj = undefined;
|
75
|
-
assert(obj?.method?.() === undefined, 'Method call on undefined object failed');
|
76
|
-
console.log('✓ Method call on undefined object');
|
77
|
-
})();
|
78
|
-
|
79
|
-
// Test 9: Method call on null method
|
80
|
-
(function() {
|
81
|
-
const obj = { method: null };
|
82
|
-
assert(obj?.method?.() === undefined, 'Method call on null method failed');
|
83
|
-
console.log('✓ Method call on null method');
|
84
|
-
})();
|
85
|
-
|
86
|
-
// Test 10: Method call on undefined method
|
87
|
-
(function() {
|
88
|
-
const obj = {};
|
89
|
-
assert(obj?.method?.() === undefined, 'Method call on undefined method failed');
|
90
|
-
console.log('✓ Method call on undefined method');
|
91
|
-
})();
|
92
|
-
|
93
|
-
// ARRAY ELEMENT ACCESS TESTS
|
94
|
-
console.log('\n=== Array Element Access ===');
|
95
|
-
|
96
|
-
// Test 11: Array element access with valid index
|
97
|
-
(function() {
|
98
|
-
const arr = [1, 2, 3];
|
99
|
-
assert(arr?.[1] === 2, 'Array element access with valid index failed');
|
100
|
-
console.log('✓ Array element access with valid index');
|
101
|
-
})();
|
102
|
-
|
103
|
-
// Test 12: Array element access with undefined array
|
104
|
-
(function() {
|
105
|
-
const arr = undefined;
|
106
|
-
assert(arr?.[1] === undefined, 'Array element access with undefined array failed');
|
107
|
-
console.log('✓ Array element access with undefined array');
|
108
|
-
})();
|
109
|
-
|
110
|
-
// Test 13: Array element access with null array
|
111
|
-
(function() {
|
112
|
-
const arr = null;
|
113
|
-
assert(arr?.[1] === undefined, 'Array element access with null array failed');
|
114
|
-
console.log('✓ Array element access with null array');
|
115
|
-
})();
|
116
|
-
|
117
|
-
// Test 14: Access array method with optional chaining
|
118
|
-
(function() {
|
119
|
-
const arr = [1, 2, 3];
|
120
|
-
assert(arr?.map(x => x * 2)?.[0] === 2, 'Access array method with optional chaining failed');
|
121
|
-
console.log('✓ Access array method with optional chaining');
|
122
|
-
})();
|
123
|
-
|
124
|
-
// MIXED SYNTAX TESTS
|
125
|
-
console.log('\n=== Mixed Syntax Tests ===');
|
126
|
-
|
127
|
-
// Test 15: Mix of array and object property access
|
128
|
-
(function() {
|
129
|
-
const obj = { arr: [{ val: 42 }] };
|
130
|
-
assert(obj?.arr?.[0]?.val === 42, 'Mix of array and object property access failed');
|
131
|
-
console.log('✓ Mix of array and object property access');
|
132
|
-
})();
|
133
|
-
|
134
|
-
// Test 16: Method returning object with property
|
135
|
-
(function() {
|
136
|
-
const obj = {
|
137
|
-
getObject: function() { return { prop: 'value' }; }
|
138
|
-
};
|
139
|
-
assert(obj?.getObject?.()?.prop === 'value', 'Method returning object with property failed');
|
140
|
-
console.log('✓ Method returning object with property');
|
141
|
-
})();
|
142
|
-
|
143
|
-
// Test 17: Object with method returning array
|
144
|
-
(function() {
|
145
|
-
const obj = {
|
146
|
-
getArray: function() { return [1, 2, 3]; }
|
147
|
-
};
|
148
|
-
assert(obj?.getArray?.()?.[1] === 2, 'Object with method returning array failed');
|
149
|
-
console.log('✓ Object with method returning array');
|
150
|
-
})();
|
151
|
-
|
152
|
-
// COMPLEX EXPRESSIONS AND EDGE CASES
|
153
|
-
console.log('\n=== Complex Expressions and Edge Cases ===');
|
154
|
-
|
155
|
-
// Test 18: Optional chaining with function arguments
|
156
|
-
(function() {
|
157
|
-
const obj = {
|
158
|
-
method: function(a, b) { return a + b; }
|
159
|
-
};
|
160
|
-
assert(obj?.method?.(1, 2) === 3, 'Optional chaining with function arguments failed');
|
161
|
-
console.log('✓ Optional chaining with function arguments');
|
162
|
-
})();
|
163
|
-
|
164
|
-
// Test 19: Optional chaining in expressions
|
165
|
-
(function() {
|
166
|
-
const obj = { a: 5 };
|
167
|
-
assert((obj?.a + 5) === 10, 'Optional chaining in expressions failed');
|
168
|
-
console.log('✓ Optional chaining in expressions');
|
169
|
-
})();
|
170
|
-
|
171
|
-
// Test 20: Optional chaining with property names containing special characters
|
172
|
-
(function() {
|
173
|
-
const obj = { 'special-prop': 42 };
|
174
|
-
assert(obj?.['special-prop'] === 42, 'Optional chaining with property names containing special characters failed');
|
175
|
-
console.log('✓ Optional chaining with property names containing special characters');
|
176
|
-
})();
|
177
|
-
|
178
|
-
// Test 21: Optional chaining with computed property names
|
179
|
-
(function() {
|
180
|
-
const key = 'dynamicKey';
|
181
|
-
const obj = { dynamicKey: 'dynamic value' };
|
182
|
-
assert(obj?.[key] === 'dynamic value', 'Optional chaining with computed property names failed');
|
183
|
-
console.log('✓ Optional chaining with computed property names');
|
184
|
-
})();
|
185
|
-
|
186
|
-
// Test 22: Optional chaining with Symbol properties
|
187
|
-
(function() {
|
188
|
-
const sym = Symbol('test');
|
189
|
-
const obj = {};
|
190
|
-
obj[sym] = 'symbol value';
|
191
|
-
assert(obj?.[sym] === 'symbol value', 'Optional chaining with Symbol properties failed');
|
192
|
-
console.log('✓ Optional chaining with Symbol properties');
|
193
|
-
})();
|
194
|
-
|
195
|
-
// Test 23: Optional chaining with object returned from function
|
196
|
-
(function() {
|
197
|
-
function getObject(returnValue) {
|
198
|
-
return returnValue ? { prop: 'exists' } : null;
|
199
|
-
}
|
200
|
-
assert(getObject(true)?.prop === 'exists', 'Optional chaining with object returned from function (true case) failed');
|
201
|
-
assert(getObject(false)?.prop === undefined, 'Optional chaining with object returned from function (false case) failed');
|
202
|
-
console.log('✓ Optional chaining with object returned from function');
|
203
|
-
})();
|
204
|
-
|
205
|
-
// Test 24: Optional chaining with prototype chain
|
206
|
-
(function() {
|
207
|
-
const proto = { inheritedProp: 'inherited' };
|
208
|
-
const obj = Object.create(proto);
|
209
|
-
assert(obj?.inheritedProp === 'inherited', 'Optional chaining with prototype chain failed');
|
210
|
-
console.log('✓ Optional chaining with prototype chain');
|
211
|
-
})();
|
212
|
-
|
213
|
-
// Test 25: Optional chaining with getters
|
214
|
-
(function() {
|
215
|
-
const obj = {
|
216
|
-
get prop() { return 'getter value'; }
|
217
|
-
};
|
218
|
-
assert(obj?.prop === 'getter value', 'Optional chaining with getters failed');
|
219
|
-
console.log('✓ Optional chaining with getters');
|
220
|
-
})();
|
221
|
-
|
222
|
-
// SHORT-CIRCUIT EVALUATION TESTS
|
223
|
-
console.log('\n=== Short-circuit Evaluation Tests ===');
|
224
|
-
|
225
|
-
// Test 26: Short-circuit evaluation - should not call function after undefined
|
226
|
-
(function() {
|
227
|
-
let functionCalled = false;
|
228
|
-
const obj = undefined;
|
229
|
-
|
230
|
-
function sideEffect() {
|
231
|
-
functionCalled = true;
|
232
|
-
return 'side effect';
|
233
|
-
}
|
234
|
-
|
235
|
-
obj?.prop?.[sideEffect()];
|
236
|
-
assert(functionCalled === false, 'Short-circuit evaluation failed - function called when it should not have been');
|
237
|
-
console.log('✓ Short-circuit evaluation - function not called after undefined');
|
238
|
-
})();
|
239
|
-
|
240
|
-
// Test 27: Non-short-circuit evaluation - should call function when property exists
|
241
|
-
(function() {
|
242
|
-
let functionCalled = false;
|
243
|
-
const obj = { prop: {} };
|
244
|
-
|
245
|
-
function sideEffect() {
|
246
|
-
functionCalled = true;
|
247
|
-
return 'key';
|
248
|
-
}
|
249
|
-
|
250
|
-
obj?.prop?.[sideEffect()];
|
251
|
-
assert(functionCalled === true, 'Non-short-circuit evaluation failed - function not called when it should have been');
|
252
|
-
console.log('✓ Non-short-circuit evaluation - function called when property exists');
|
253
|
-
})();
|
254
|
-
|
255
|
-
// ASSIGNMENT AND DESTRUCTURING TESTS
|
256
|
-
console.log('\n=== Assignment and Destructuring Tests ===');
|
257
|
-
|
258
|
-
// Test 28: Optional chaining in destructuring
|
259
|
-
(function() {
|
260
|
-
const obj = { nested: { value: 42 } };
|
261
|
-
const { nested: { value } = {} } = obj ?? {};
|
262
|
-
assert(value === 42, 'Optional chaining in destructuring failed');
|
263
|
-
console.log('✓ Optional chaining in destructuring');
|
264
|
-
})();
|
265
|
-
|
266
|
-
// Test 29: Optional chaining with the nullish coalescing operator
|
267
|
-
(function() {
|
268
|
-
const obj = null;
|
269
|
-
const value = obj?.prop ?? 'default';
|
270
|
-
assert(value === 'default', 'Optional chaining with nullish coalescing operator failed');
|
271
|
-
console.log('✓ Optional chaining with nullish coalescing operator');
|
272
|
-
})();
|
273
|
-
|
274
|
-
// Test 30: Optional chaining in logical expressions
|
275
|
-
(function() {
|
276
|
-
const obj = { prop: true };
|
277
|
-
const nullObj = null;
|
278
|
-
|
279
|
-
assert((obj?.prop && 'yes') === 'yes', 'Optional chaining in logical AND expression failed');
|
280
|
-
assert((nullObj?.prop && 'yes') === undefined, 'Optional chaining in logical AND expression with null failed');
|
281
|
-
assert((obj?.prop || 'fallback') === true, 'Optional chaining in logical OR expression failed');
|
282
|
-
assert((nullObj?.prop || 'fallback') === 'fallback', 'Optional chaining in logical OR expression with null failed');
|
283
|
-
|
284
|
-
console.log('✓ Optional chaining in logical expressions');
|
285
|
-
})();
|
286
|
-
|
287
|
-
// PERFORMANCE EDGE CASES
|
288
|
-
console.log('\n=== Performance Edge Cases ===');
|
289
|
-
|
290
|
-
// Test 31: Deep nesting - many levels
|
291
|
-
(function() {
|
292
|
-
const obj = { a: { b: { c: { d: { e: { f: { g: { h: { i: { j: 'deep' } } } } } } } } } };
|
293
|
-
assert(obj?.a?.b?.c?.d?.e?.f?.g?.h?.i?.j === 'deep', 'Deep nesting optional chaining failed');
|
294
|
-
console.log('✓ Deep nesting - many levels');
|
295
|
-
})();
|
296
|
-
|
297
|
-
// Test 32: Complex real-world scenario
|
298
|
-
(function() {
|
299
|
-
const data = {
|
300
|
-
user: {
|
301
|
-
profile: {
|
302
|
-
name: 'John',
|
303
|
-
address: {
|
304
|
-
city: 'New York',
|
305
|
-
zipcode: '10001'
|
306
|
-
}
|
307
|
-
},
|
308
|
-
preferences: {
|
309
|
-
theme: 'dark',
|
310
|
-
notifications: {
|
311
|
-
email: true,
|
312
|
-
sms: false
|
313
|
-
}
|
314
|
-
},
|
315
|
-
friends: [
|
316
|
-
{ id: 1, name: 'Alice' },
|
317
|
-
{ id: 2, name: 'Bob' }
|
318
|
-
]
|
319
|
-
}
|
320
|
-
};
|
321
|
-
|
322
|
-
// Multiple complex optional chaining expressions
|
323
|
-
assert(data?.user?.profile?.name === 'John', 'Complex scenario - user name test failed');
|
324
|
-
assert(data?.user?.profile?.address?.city === 'New York', 'Complex scenario - address city test failed');
|
325
|
-
assert(data?.user?.preferences?.notifications?.email === true, 'Complex scenario - notifications email test failed');
|
326
|
-
assert(data?.user?.friends?.[1]?.name === 'Bob', 'Complex scenario - friends name test failed');
|
327
|
-
assert(data?.user?.nonexistent?.something === undefined, 'Complex scenario - nonexistent property test failed');
|
328
|
-
|
329
|
-
console.log('✓ Complex real-world scenario');
|
330
|
-
})();
|
331
|
-
|
332
|
-
// SUMMARY
|
333
|
-
console.log('\n=== Test Summary ===');
|
334
|
-
console.log('All tests completed successfully!');
|
335
|
-
console.log('Total tests: 32');
|
package/r.cjs
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
|
2
|
-
// class C {
|
3
|
-
// // static #method = () => 1;
|
4
|
-
// static #method() {
|
5
|
-
// return 'Test262';
|
6
|
-
// }
|
7
|
-
|
8
|
-
// static getPrivateMethod() {
|
9
|
-
// this.#method = () => 2;
|
10
|
-
// return this.#method();
|
11
|
-
// }
|
12
|
-
// }
|
13
|
-
|
14
|
-
// console.log(C.getPrivateMethod())
|
15
|
-
|
16
|
-
// var __assert_throws = (expectedErrorConstructor, func) => {
|
17
|
-
// if (typeof func !== 'function') {
|
18
|
-
// throw new Test262Error('assert.throws invoked with a non-function value');
|
19
|
-
// }
|
20
|
-
|
21
|
-
// try {
|
22
|
-
// func();
|
23
|
-
// } catch {
|
24
|
-
// return;
|
25
|
-
// }
|
26
|
-
|
27
|
-
// throw new Test262Error('assert.throws failed');
|
28
|
-
// };
|
29
|
-
|
30
|
-
// var __assert__isSameValue = (a, b) => {
|
31
|
-
// if (a === b) {
|
32
|
-
// // Handle +/-0 vs. -/+0
|
33
|
-
// return a !== 0 || 1 / a === 1 / b;
|
34
|
-
// }
|
35
|
-
|
36
|
-
// // Handle NaN vs. NaN
|
37
|
-
// return a !== a && b !== b;
|
38
|
-
// };
|
39
|
-
|
40
|
-
// var __assert_sameValue = (actual, expected) => {
|
41
|
-
// if (__assert__isSameValue(actual, expected)) {
|
42
|
-
// return;
|
43
|
-
// }
|
44
|
-
|
45
|
-
// throw new Test262Error('assert.sameValue failed');
|
46
|
-
// };
|
47
|
-
|
48
|
-
// (function() {
|
49
|
-
// eval(
|
50
|
-
// '__assert_throws(ReferenceError, function() {\
|
51
|
-
// f;\
|
52
|
-
// }, "An initialized binding is not created prior to evaluation");\
|
53
|
-
// __assert_sameValue(\
|
54
|
-
// typeof f,\
|
55
|
-
// "undefined",\
|
56
|
-
// "An uninitialized binding is not created prior to evaluation"\
|
57
|
-
// );\
|
58
|
-
// \
|
59
|
-
// {\
|
60
|
-
// let f = 123;if (false) ; else function f() { }}\
|
61
|
-
// \
|
62
|
-
// __assert_throws(ReferenceError, function() {\
|
63
|
-
// f;\
|
64
|
-
// }, "An initialized binding is not created following evaluation");\
|
65
|
-
// __assert_sameValue(\
|
66
|
-
// typeof f,\
|
67
|
-
// "undefined",\
|
68
|
-
// "An uninitialized binding is not created following evaluation"\
|
69
|
-
// );'
|
70
|
-
// );
|
71
|
-
// }());
|