porffor 0.34.21 → 0.35.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_string.ts +74 -0
- package/compiler/builtins/_internal_string.ts +21 -0
- package/compiler/builtins/string.ts +28 -18
- package/compiler/builtins_precompiled.js +291 -281
- package/compiler/codegen.js +53 -223
- package/package.json +1 -1
- package/runner/index.js +1 -1
package/compiler/codegen.js
CHANGED
@@ -499,126 +499,19 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
499
499
|
];
|
500
500
|
};
|
501
501
|
|
502
|
-
const concatStrings = (scope, left, right, leftType, rightType
|
503
|
-
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
504
|
-
// todo: convert left and right to strings if not
|
505
|
-
// todo: optimize by looking up names in arrays and using that if exists?
|
506
|
-
// todo: optimize this if using literals/known lengths?
|
507
|
-
|
508
|
-
const rightPointer = localTmp(scope, 'concat_right_pointer', Valtype.i32);
|
509
|
-
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
510
|
-
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
511
|
-
|
512
|
-
const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
|
513
|
-
|
514
|
-
// alloc/assign array
|
515
|
-
const out = localTmp(scope, 'concat_out_pointer', Valtype.i32);
|
516
|
-
|
517
|
-
if (!skipTypeCheck && !allBytestrings) includeBuiltin(scope, '__Porffor_bytestringToString');
|
502
|
+
const concatStrings = (scope, left, right, leftType, rightType) => {
|
518
503
|
return [
|
519
|
-
|
520
|
-
...(
|
521
|
-
|
522
|
-
[ Opcodes.local_set, rightPointer ],
|
523
|
-
|
524
|
-
Opcodes.i32_to_u,
|
525
|
-
[ Opcodes.local_set, leftPointer ],
|
526
|
-
] : [
|
527
|
-
...left,
|
528
|
-
Opcodes.i32_to_u,
|
529
|
-
[ Opcodes.local_set, leftPointer ],
|
530
|
-
|
531
|
-
...right,
|
532
|
-
Opcodes.i32_to_u,
|
533
|
-
[ Opcodes.local_set, rightPointer ],
|
534
|
-
]),
|
535
|
-
|
536
|
-
// setup out
|
537
|
-
[ Opcodes.i32_const, 1 ],
|
538
|
-
[ Opcodes.memory_grow, 0 ],
|
539
|
-
[ Opcodes.i32_const, ...signedLEB128(65536) ],
|
540
|
-
[ Opcodes.i32_mul ],
|
541
|
-
[ Opcodes.local_tee, out ],
|
542
|
-
|
543
|
-
// calculate length
|
544
|
-
[ Opcodes.local_get, leftPointer ],
|
545
|
-
[ Opcodes.i32_load, 0, 0 ],
|
546
|
-
[ Opcodes.local_tee, leftLength ],
|
547
|
-
|
548
|
-
[ Opcodes.local_get, rightPointer ],
|
549
|
-
[ Opcodes.i32_load, 0, 0 ],
|
550
|
-
[ Opcodes.local_tee, rightLength ],
|
551
|
-
|
552
|
-
[ Opcodes.i32_add ],
|
553
|
-
|
554
|
-
// store length
|
555
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
556
|
-
|
557
|
-
...(skipTypeCheck || allBytestrings ? [] : [
|
558
|
-
...leftType,
|
559
|
-
...number(TYPES.bytestring, Valtype.i32),
|
560
|
-
[ Opcodes.i32_eq ],
|
561
|
-
[ Opcodes.if, Blocktype.void ],
|
562
|
-
[ Opcodes.local_get, leftPointer ],
|
563
|
-
[ Opcodes.local_get, leftLength ],
|
564
|
-
[ Opcodes.call, funcIndex.__Porffor_bytestringToString ],
|
565
|
-
[ Opcodes.local_set, leftPointer ],
|
566
|
-
[ Opcodes.end ],
|
567
|
-
|
568
|
-
...rightType,
|
569
|
-
...number(TYPES.bytestring, Valtype.i32),
|
570
|
-
[ Opcodes.i32_eq ],
|
571
|
-
[ Opcodes.if, Blocktype.void ],
|
572
|
-
[ Opcodes.local_get, rightPointer ],
|
573
|
-
[ Opcodes.local_get, rightLength ],
|
574
|
-
[ Opcodes.call, funcIndex.__Porffor_bytestringToString ],
|
575
|
-
[ Opcodes.local_set, rightPointer ],
|
576
|
-
[ Opcodes.end ]
|
577
|
-
]),
|
578
|
-
|
579
|
-
// copy left
|
580
|
-
// dst = out pointer + length size
|
581
|
-
[ Opcodes.local_get, out ],
|
582
|
-
...number(ValtypeSize.i32, Valtype.i32),
|
583
|
-
[ Opcodes.i32_add ],
|
584
|
-
|
585
|
-
// src = left pointer + length size
|
586
|
-
[ Opcodes.local_get, leftPointer ],
|
587
|
-
...number(ValtypeSize.i32, Valtype.i32),
|
588
|
-
[ Opcodes.i32_add ],
|
589
|
-
|
590
|
-
// size = PageSize - length size. we do not need to calculate length as init value
|
591
|
-
...number(pageSize - ValtypeSize.i32, Valtype.i32),
|
592
|
-
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
593
|
-
|
594
|
-
// copy right
|
595
|
-
// dst = out pointer + length size + left length * sizeof valtype
|
596
|
-
[ Opcodes.local_get, out ],
|
597
|
-
...number(ValtypeSize.i32, Valtype.i32),
|
598
|
-
[ Opcodes.i32_add ],
|
599
|
-
|
600
|
-
[ Opcodes.local_get, leftLength ],
|
601
|
-
...number(allBytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
602
|
-
[ Opcodes.i32_mul ],
|
603
|
-
[ Opcodes.i32_add ],
|
604
|
-
|
605
|
-
// src = right pointer + length size
|
606
|
-
[ Opcodes.local_get, rightPointer ],
|
607
|
-
...number(ValtypeSize.i32, Valtype.i32),
|
608
|
-
[ Opcodes.i32_add ],
|
609
|
-
|
610
|
-
// size = right length * sizeof valtype
|
611
|
-
[ Opcodes.local_get, rightLength ],
|
612
|
-
...number(allBytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
613
|
-
[ Opcodes.i32_mul ],
|
614
|
-
|
615
|
-
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
504
|
+
...left,
|
505
|
+
...(valtypeBinary === Valtype.i32 ? [ [ Opcodes.f64_convert_i32_s ] ] : []),
|
506
|
+
...leftType,
|
616
507
|
|
617
|
-
...
|
508
|
+
...right,
|
509
|
+
...(valtypeBinary === Valtype.i32 ? [ [ Opcodes.f64_convert_i32_s ] ] : []),
|
510
|
+
...rightType,
|
618
511
|
|
619
|
-
|
620
|
-
|
621
|
-
Opcodes.
|
512
|
+
[ Opcodes.call, includeBuiltin(scope, '__Porffor_concatStrings').index ],
|
513
|
+
...setLastType(scope),
|
514
|
+
...(valtypeBinary === Valtype.i32 ? [ Opcodes.i32_trunc_sat_f64_u ] : []),
|
622
515
|
];
|
623
516
|
};
|
624
517
|
|
@@ -860,23 +753,16 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
860
753
|
// todo: if equality op and an operand is undefined, return false
|
861
754
|
// todo: niche null hell with 0
|
862
755
|
|
863
|
-
if (knownLeft === TYPES.string || knownRight === TYPES.string)
|
756
|
+
if ((knownLeft === TYPES.string || knownRight === TYPES.string) ||
|
757
|
+
(knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring)) {
|
864
758
|
if (op === '+') {
|
865
759
|
// string concat (a + b)
|
866
|
-
return
|
867
|
-
...left,
|
868
|
-
...right,
|
869
|
-
...concatStrings(scope, [], [], leftType, rightType, false, knownLeft === TYPES.string && knownRight === TYPES.string)
|
870
|
-
];
|
760
|
+
return concatStrings(scope, left, right, leftType, rightType);
|
871
761
|
}
|
872
762
|
|
873
763
|
// not an equality op, NaN
|
874
764
|
if (!eqOp) return number(NaN);
|
875
765
|
|
876
|
-
// else leave bool ops
|
877
|
-
// todo: convert string to number if string and number/bool
|
878
|
-
// todo: string (>|>=|<|<=) string
|
879
|
-
|
880
766
|
// string comparison
|
881
767
|
if (op === '===' || op === '==' || op === '!==' || op === '!=') {
|
882
768
|
return [
|
@@ -884,32 +770,8 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
884
770
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : [])
|
885
771
|
];
|
886
772
|
}
|
887
|
-
}
|
888
|
-
|
889
|
-
if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) {
|
890
|
-
if (op === '+') {
|
891
|
-
// string concat (a + b)
|
892
|
-
return [
|
893
|
-
...left,
|
894
|
-
...right,
|
895
|
-
...concatStrings(scope, [], [], leftType, rightType, knownLeft === TYPES.bytestring && knownRight === TYPES.bytestring)
|
896
|
-
];
|
897
|
-
}
|
898
773
|
|
899
|
-
//
|
900
|
-
if (!eqOp) return number(NaN);
|
901
|
-
|
902
|
-
// else leave bool ops
|
903
|
-
// todo: convert string to number if string and number/bool
|
904
|
-
// todo: string (>|>=|<|<=) string
|
905
|
-
|
906
|
-
// string comparison
|
907
|
-
if (op === '===' || op === '==' || op === '!==' || op === '!=') {
|
908
|
-
return [
|
909
|
-
...compareStrings(scope, left, right, leftType, rightType),
|
910
|
-
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : [])
|
911
|
-
];
|
912
|
-
}
|
774
|
+
// todo: proper >|>=|<|<=
|
913
775
|
}
|
914
776
|
|
915
777
|
let ops = operatorOpcode[valtype][op];
|
@@ -943,39 +805,19 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
943
805
|
tmpRight = localTmp(scope, '__tmpop_right');
|
944
806
|
|
945
807
|
ops.unshift(...stringOnly([
|
946
|
-
// if left
|
947
|
-
...leftType,
|
948
|
-
...number(TYPES.bytestring, Valtype.i32),
|
949
|
-
[ Opcodes.i32_eq ],
|
950
|
-
|
951
|
-
// if right is bytestring
|
952
|
-
...rightType,
|
953
|
-
...number(TYPES.bytestring, Valtype.i32),
|
954
|
-
[ Opcodes.i32_eq ],
|
955
|
-
|
956
|
-
// if both are true
|
957
|
-
[ Opcodes.i32_and ],
|
958
|
-
[ Opcodes.if, Blocktype.void ],
|
959
|
-
...concatStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], leftType, rightType, true),
|
960
|
-
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
961
|
-
[ Opcodes.br, 1 ],
|
962
|
-
[ Opcodes.end ],
|
963
|
-
|
964
|
-
// if left is string or bytestring
|
808
|
+
// if left or right are string or bytestring
|
965
809
|
...leftType,
|
966
810
|
...number(TYPE_FLAGS.parity, Valtype.i32),
|
967
811
|
[ Opcodes.i32_or ],
|
968
812
|
...number(TYPES.bytestring, Valtype.i32),
|
969
813
|
[ Opcodes.i32_eq ],
|
970
814
|
|
971
|
-
// if right is string or bytestring
|
972
815
|
...rightType,
|
973
816
|
...number(TYPE_FLAGS.parity, Valtype.i32),
|
974
817
|
[ Opcodes.i32_or ],
|
975
818
|
...number(TYPES.bytestring, Valtype.i32),
|
976
819
|
[ Opcodes.i32_eq ],
|
977
820
|
|
978
|
-
// if either
|
979
821
|
[ Opcodes.i32_or ],
|
980
822
|
[ Opcodes.if, Blocktype.void ],
|
981
823
|
...concatStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], leftType, rightType),
|
@@ -1066,9 +908,6 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
1066
908
|
}
|
1067
909
|
|
1068
910
|
if (decl.operator === 'in') {
|
1069
|
-
// hack: a in b -> Object.hasOwn(b, a)
|
1070
|
-
// todo: not spec compliant, in should check prototype chain too (once we have it)
|
1071
|
-
|
1072
911
|
return generate(scope, {
|
1073
912
|
type: 'CallExpression',
|
1074
913
|
callee: {
|
@@ -1112,6 +951,7 @@ const asmFuncToAsm = (scope, func) => {
|
|
1112
951
|
builtin: (n, offset = false) => {
|
1113
952
|
let idx = funcIndex[n] ?? importedFuncs[n];
|
1114
953
|
if (idx == null && builtinFuncs[n]) {
|
954
|
+
// console.log(scope.name, '->', n);
|
1115
955
|
includeBuiltin(scope, n);
|
1116
956
|
idx = funcIndex[n];
|
1117
957
|
}
|
@@ -1478,6 +1318,7 @@ const getNodeType = (scope, node) => {
|
|
1478
1318
|
if (node.type === 'AssignmentExpression') {
|
1479
1319
|
const op = node.operator.slice(0, -1) || '=';
|
1480
1320
|
if (op === '=') return getNodeType(scope, node.right);
|
1321
|
+
// if (op === '=') return getNodeType(scope, node.left);
|
1481
1322
|
|
1482
1323
|
return getNodeType(scope, {
|
1483
1324
|
type: ['||', '&&', '??'].includes(op) ? 'LogicalExpression' : 'BinaryExpression',
|
@@ -1500,11 +1341,14 @@ const getNodeType = (scope, node) => {
|
|
1500
1341
|
const knownLeft = knownType(scope, leftType);
|
1501
1342
|
const knownRight = knownType(scope, rightType);
|
1502
1343
|
|
1503
|
-
if (knownLeft
|
1504
|
-
|
1505
|
-
|
1344
|
+
if ((knownLeft != null || knownRight != null) && !(
|
1345
|
+
(knownLeft === TYPES.string || knownRight === TYPES.string) ||
|
1346
|
+
(knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring)
|
1347
|
+
)) return TYPES.number;
|
1506
1348
|
|
1507
|
-
if (knownLeft
|
1349
|
+
// if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1350
|
+
// if (knownLeft === TYPES.bytestring && knownRight === TYPES.bytestring) return TYPES.bytestring;
|
1351
|
+
// if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) return TYPES.string;
|
1508
1352
|
|
1509
1353
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1510
1354
|
|
@@ -2234,6 +2078,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2234
2078
|
|
2235
2079
|
// value
|
2236
2080
|
i32_const: { imms: 1, args: [], returns: 0 },
|
2081
|
+
|
2082
|
+
// dst, src, size, _, _
|
2083
|
+
memory_copy: { imms: 2, args: [ true, true, true ], returns: 0 }
|
2237
2084
|
};
|
2238
2085
|
|
2239
2086
|
const opName = name.slice('__Porffor_wasm_'.length);
|
@@ -2256,9 +2103,12 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2256
2103
|
// literals only
|
2257
2104
|
const imms = decl.arguments.slice(op.args.length).map(x => x.value);
|
2258
2105
|
|
2106
|
+
let opcode = Opcodes[opName];
|
2107
|
+
if (!Array.isArray(opcode)) opcode = [ opcode ];
|
2108
|
+
|
2259
2109
|
return [
|
2260
2110
|
...argOut,
|
2261
|
-
[
|
2111
|
+
[ ...opcode, ...imms ],
|
2262
2112
|
...(new Array(op.returns).fill(Opcodes.i32_from))
|
2263
2113
|
];
|
2264
2114
|
}
|
@@ -2977,8 +2827,10 @@ const setLocalWithType = (scope, name, isGlobal, decl, tee = false, overrideType
|
|
2977
2827
|
// todo: detect last type then i32 conversion op
|
2978
2828
|
const lastOp = out.at(-1);
|
2979
2829
|
if (lastOp[0] === Opcodes.local_set && lastOp[1] === scope.locals['#last_type']?.idx) {
|
2980
|
-
|
2830
|
+
// set last type -> tee last type
|
2831
|
+
lastOp[0] = Opcodes.local_tee;
|
2981
2832
|
|
2833
|
+
// still set last type due to side effects or type of decl gotten later
|
2982
2834
|
const setOut = setType(scope, name, []);
|
2983
2835
|
out.push(
|
2984
2836
|
// drop if setType is empty
|
@@ -3173,7 +3025,7 @@ const generateVarDstr = (scope, kind, pattern, init, defaultValue, global) => {
|
|
3173
3025
|
const usedProps = [];
|
3174
3026
|
for (const prop of properties) {
|
3175
3027
|
if (prop.type == 'Property') { // let { foo } = {}
|
3176
|
-
usedProps.push(
|
3028
|
+
usedProps.push(getProperty(prop));
|
3177
3029
|
|
3178
3030
|
if (prop.value.type === 'AssignmentPattern') { // let { foo = defaultValue } = {}
|
3179
3031
|
decls.push(
|
@@ -3257,13 +3109,22 @@ const generateVar = (scope, decl) => {
|
|
3257
3109
|
return out;
|
3258
3110
|
};
|
3259
3111
|
|
3260
|
-
const
|
3261
|
-
|
3112
|
+
const privateIDName = name => '.#.' + name;
|
3113
|
+
const privateIdentifierToIdentifier = decl => ({
|
3114
|
+
type: 'Identifier',
|
3115
|
+
name: privateIDName(decl.name)
|
3116
|
+
});
|
3117
|
+
|
3118
|
+
const getProperty = decl => {
|
3119
|
+
const prop = decl.property ?? decl.key;
|
3120
|
+
if (decl.computed) return prop;
|
3262
3121
|
|
3263
|
-
return {
|
3122
|
+
if (prop.name) return {
|
3264
3123
|
type: 'Literal',
|
3265
|
-
value:
|
3124
|
+
value: prop.type === 'PrivateIdentifier' ? privateIDName(prop.name) : prop.name,
|
3266
3125
|
};
|
3126
|
+
|
3127
|
+
return prop;
|
3267
3128
|
};
|
3268
3129
|
|
3269
3130
|
// todo: optimize this func for valueUnused
|
@@ -3348,7 +3209,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
3348
3209
|
const pointerTmp = localTmp(scope, '#member_setter_ptr_tmp', Valtype.i32);
|
3349
3210
|
|
3350
3211
|
const object = decl.left.object;
|
3351
|
-
const property =
|
3212
|
+
const property = getProperty(decl.left);
|
3352
3213
|
|
3353
3214
|
// todo/perf: use i32 object (and prop?) locals
|
3354
3215
|
const objectWasm = [ [ Opcodes.local_get, localTmp(scope, '#member_obj') ] ];
|
@@ -3751,7 +3612,7 @@ const generateUnary = (scope, decl) => {
|
|
3751
3612
|
case 'delete': {
|
3752
3613
|
if (decl.argument.type === 'MemberExpression') {
|
3753
3614
|
const object = decl.argument.object;
|
3754
|
-
const property =
|
3615
|
+
const property = getProperty(decl.argument);
|
3755
3616
|
|
3756
3617
|
if (property.value === 'length' || property.value === 'name') scope.noFastFuncMembers = true;
|
3757
3618
|
|
@@ -5183,12 +5044,7 @@ const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
|
5183
5044
|
continue;
|
5184
5045
|
}
|
5185
5046
|
|
5186
|
-
|
5187
|
-
if (!computed && key.type !== 'Literal') k = {
|
5188
|
-
type: 'Literal',
|
5189
|
-
value: key.name
|
5190
|
-
};
|
5191
|
-
|
5047
|
+
const k = getProperty(x);
|
5192
5048
|
if (isFuncType(value.type)) {
|
5193
5049
|
let id = value.id;
|
5194
5050
|
|
@@ -5287,7 +5143,7 @@ const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) =>
|
|
5287
5143
|
}
|
5288
5144
|
|
5289
5145
|
const object = decl.object;
|
5290
|
-
const property =
|
5146
|
+
const property = getProperty(decl);
|
5291
5147
|
|
5292
5148
|
// generate now so type is gotten correctly later (it gets cached)
|
5293
5149
|
generate(scope, object);
|
@@ -5721,28 +5577,6 @@ const generateClass = (scope, decl) => {
|
|
5721
5577
|
|
5722
5578
|
if (decl.superClass) {
|
5723
5579
|
out.push(
|
5724
|
-
// ...generateCall(scope, {
|
5725
|
-
// type: 'CallExpression',
|
5726
|
-
// callee: {
|
5727
|
-
// type: 'Identifier',
|
5728
|
-
// name: '__Porffor_object_assignAll'
|
5729
|
-
// },
|
5730
|
-
// arguments: [
|
5731
|
-
// proto,
|
5732
|
-
// {
|
5733
|
-
// type: 'MemberExpression',
|
5734
|
-
// object: decl.superClass,
|
5735
|
-
// property: {
|
5736
|
-
// type: 'Identifier',
|
5737
|
-
// name: 'prototype'
|
5738
|
-
// },
|
5739
|
-
// computed: false,
|
5740
|
-
// optional: false
|
5741
|
-
// }
|
5742
|
-
// ]
|
5743
|
-
// }),
|
5744
|
-
// [ Opcodes.drop ]
|
5745
|
-
|
5746
5580
|
// class Foo {}
|
5747
5581
|
// class Bar extends Foo {}
|
5748
5582
|
// Bar.__proto__ = Foo
|
@@ -5760,11 +5594,7 @@ const generateClass = (scope, decl) => {
|
|
5760
5594
|
|
5761
5595
|
let object = _static ? root : proto;
|
5762
5596
|
|
5763
|
-
|
5764
|
-
if (!computed && key.type !== 'Literal') k = {
|
5765
|
-
type: 'Literal',
|
5766
|
-
value: key.name
|
5767
|
-
};
|
5597
|
+
const k = getProperty(x);
|
5768
5598
|
|
5769
5599
|
let initKind = 'init';
|
5770
5600
|
if (kind === 'get' || kind === 'set') initKind = kind;
|
package/package.json
CHANGED