@scriptc/compiler 0.1.5 → 0.1.6
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/dist/backend/c/exprs.js +36 -8
- package/dist/backend/c/exprs.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +28 -2
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +8 -0
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +4 -3
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +1 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/coverage/surface-manifest.js +6 -1
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/frontend/lowering/array-indexed-mutation.d.ts +8 -0
- package/dist/frontend/lowering/array-indexed-mutation.js +176 -0
- package/dist/frontend/lowering/array-indexed-mutation.js.map +1 -0
- package/dist/frontend/lowering/containers/string-and-regexp.d.ts +7 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js +190 -13
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/indexed-comparison.d.ts +6 -0
- package/dist/frontend/lowering/indexed-comparison.js +30 -0
- package/dist/frontend/lowering/indexed-comparison.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.d.ts +1 -1
- package/dist/frontend/lowering/lower-builtins.js +6 -6
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +266 -10
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +3 -27
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +8 -2
- package/dist/frontend/lowering/lower-containers.js +454 -104
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +18 -2
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +2 -0
- package/dist/frontend/lowering/surfaces.js +13 -1
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/ir/ir.d.ts +15 -2
- package/dist/ir/ir.js +2 -0
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/validate.js +43 -26
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +17 -0
- package/dist/ir/validate.test.js.map +1 -1
- package/package.json +17 -17
- package/surface-manifest.json +36 -4
|
@@ -10,12 +10,12 @@ import { isJsSourceFile, isNodeEsmFile, locOf } from "../program.js";
|
|
|
10
10
|
import { genResultRecord, isGenericCallableMemberType, typeKey } from "../type-mapper.js";
|
|
11
11
|
import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr, staticImportNamespaceType } from "./lowerer.js";
|
|
12
12
|
import { enforceLibBoundary } from "./lib-boundary.js";
|
|
13
|
-
import { NARROW_FIRST, STR_METHODS, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
|
|
13
|
+
import { NARROW_FIRST, STRING_INDEX_METHODS, STR_METHODS, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
|
|
14
14
|
import { ffiBindingDiag, ffiSignatureDiag, libCallbackDiag, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
|
|
15
15
|
import { mixinFnShapeOf } from "./lower-mixins.js";
|
|
16
|
-
import { dynStringReceiver, lowerArrayFromCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerTupleReadMethodCall } from "./lower-containers.js";
|
|
16
|
+
import { dynStringReceiver, lowerArrayConstructor, lowerArrayFromCall, lowerArrayOfCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerTupleReadMethodCall } from "./lower-containers.js";
|
|
17
17
|
import { bufEncoding } from "./containers/bytes.js";
|
|
18
|
-
import { lowerRegexMethodCall, lowerStringMethodCall, lowerStringPaddingCall } from "./containers/string-and-regexp.js";
|
|
18
|
+
import { lowerRegexMethodCall, lowerStringIndexCall, lowerStringMethodCall, lowerStringPaddingCall, lowerStringSplitCall } from "./containers/string-and-regexp.js";
|
|
19
19
|
import { lowerChildStreamMethodCall, lowerChildWriterMethodCall, lowerCreateRequireCall, lowerCryptoHashMethodCall, lowerDirentMethodCall, lowerFileHandleMethodCall, lowerImportMetaResolveCall, lowerNodeModuleCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerRequireResolveCall, lowerWatcherMethodCall } from "./lower-builtins.js";
|
|
20
20
|
import { lowerAbsenceProbe, lowerPromiseAllTupleCall, lowerPromiseRejectCall, stringWrapperToString, templateRawTextOf } from "./lower-exprs.js";
|
|
21
21
|
import { isSafeToDiscard } from "./expressions/evaluation-safety.js";
|
|
@@ -3581,6 +3581,9 @@ export function lowerCall(lowerer, expr) {
|
|
|
3581
3581
|
// supported form is fenced here per site. Members with no lowering at
|
|
3582
3582
|
// all (fs.watch, os.cpus, ...) fence with the module-qualified name.
|
|
3583
3583
|
if (ts.isIdentifier(expr.expression)) {
|
|
3584
|
+
if (expr.expression.text === "Array" && lowerer.isStdlibGlobal(expr.expression, "Array")) {
|
|
3585
|
+
return lowerArrayConstructor(lowerer, expr, expr.arguments);
|
|
3586
|
+
}
|
|
3584
3587
|
// A call through a compile-time util.promisify projection. execFile
|
|
3585
3588
|
// keeps its custom result helper; ordinary builtins route through
|
|
3586
3589
|
// their existing promise-module lowering.
|
|
@@ -4188,6 +4191,8 @@ export function lowerCall(lowerer, expr) {
|
|
|
4188
4191
|
// TestContext surface (t.test/t.skip/t.diagnostic), t.assert.*.
|
|
4189
4192
|
lowerer.lowerTestMethodCall(expr, expr.expression) ??
|
|
4190
4193
|
lowerer.lowerTimeoutMethodCall(expr, expr.expression) ??
|
|
4194
|
+
lowerObjectOwnPrototypeCall(lowerer, expr, expr.expression) ??
|
|
4195
|
+
lowerObjectPrototypeCall(lowerer, expr, expr.expression) ??
|
|
4191
4196
|
lowerStringPrototypeCall(lowerer, expr, expr.expression) ??
|
|
4192
4197
|
lowerStringMethodCallWithOptionalArgs(lowerer, expr, expr.expression) ??
|
|
4193
4198
|
// Typed-array/Buffer receivers and the Buffer statics — before the
|
|
@@ -4217,6 +4222,7 @@ export function lowerCall(lowerer, expr) {
|
|
|
4217
4222
|
// hasOwnProperty on a program class CONSTRUCTOR — own statics are
|
|
4218
4223
|
// compile-time-known, so a literal key folds to a constant.
|
|
4219
4224
|
lowerClassHasOwnPropertyCall(lowerer, expr, expr.expression) ??
|
|
4225
|
+
lowerObjectOwnMethodCall(lowerer, expr, expr.expression) ??
|
|
4220
4226
|
// Response constructor-object operations are unsupported in both
|
|
4221
4227
|
// tiers. Keep their SC2020 inventory contract ahead of the island
|
|
4222
4228
|
// and generic-call fallbacks (Response.json otherwise reports the
|
|
@@ -4257,6 +4263,7 @@ export function lowerCall(lowerer, expr) {
|
|
|
4257
4263
|
lowerObjectStaticCall(lowerer, expr, expr.expression) ??
|
|
4258
4264
|
lowerObjectFromEntriesCall(lowerer, expr, expr.expression) ??
|
|
4259
4265
|
lowerArrayFromCall(lowerer, expr, expr.expression) ??
|
|
4266
|
+
lowerArrayOfCall(lowerer, expr, expr.expression) ??
|
|
4260
4267
|
lowerer.lowerArrayMethodCall(expr, expr.expression) ??
|
|
4261
4268
|
// Read-only array methods (slice/map) on TUPLE receivers — the
|
|
4262
4269
|
// positions snapshot into a fresh array (the for-of stance).
|
|
@@ -4640,6 +4647,243 @@ function plainObjectCoercionReceiver(lowerer, node) {
|
|
|
4640
4647
|
return false;
|
|
4641
4648
|
return bindingNeverReassigned(lowerer, symbol, decl);
|
|
4642
4649
|
}
|
|
4650
|
+
function objectToStringTag(lowerer, type) {
|
|
4651
|
+
switch (type.kind) {
|
|
4652
|
+
case "undefinedT":
|
|
4653
|
+
case "void": return "Undefined";
|
|
4654
|
+
case "nullT": return "Null";
|
|
4655
|
+
case "bool": return "Boolean";
|
|
4656
|
+
case "f64": return "Number";
|
|
4657
|
+
case "string": return "String";
|
|
4658
|
+
case "bigint": return "BigInt";
|
|
4659
|
+
case "symbol": return "Symbol";
|
|
4660
|
+
case "array": return "Array";
|
|
4661
|
+
case "record": return "Object";
|
|
4662
|
+
case "func":
|
|
4663
|
+
case "classval": return "Function";
|
|
4664
|
+
case "date": return "Date";
|
|
4665
|
+
case "regex": return "RegExp";
|
|
4666
|
+
case "map": return "Map";
|
|
4667
|
+
case "set": return "Set";
|
|
4668
|
+
case "promise": return "Promise";
|
|
4669
|
+
case "bytes":
|
|
4670
|
+
return ({ u8: "Uint8Array", u32: "Uint32Array", i32: "Int32Array", f32: "Float32Array", f64: "Float64Array" })[type.elem];
|
|
4671
|
+
case "object":
|
|
4672
|
+
return type.className === "%Error" || lowerer.isSubclassOf(type.className, "%Error") ? "Error" : null;
|
|
4673
|
+
default: return null;
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
function immediateObjectTagReceiver(lowerer, node) {
|
|
4677
|
+
let value = node;
|
|
4678
|
+
while (ts.isParenthesizedExpression(value))
|
|
4679
|
+
value = value.expression;
|
|
4680
|
+
const loc = locOf(value);
|
|
4681
|
+
if (ts.isArrayLiteralExpression(value))
|
|
4682
|
+
return { tag: "Array", effect: lowerer.lowerExpr(value) };
|
|
4683
|
+
if ((ts.isCallExpression(value) || ts.isNewExpression(value)) && ts.isIdentifier(value.expression) &&
|
|
4684
|
+
(value.expression.text === "Array" || value.expression.text === "Error") &&
|
|
4685
|
+
(value.arguments?.length ?? 0) === 0 && lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
|
|
4686
|
+
return { tag: value.expression.text, effect: { kind: "numLit", value: 0, type: F64, loc } };
|
|
4687
|
+
}
|
|
4688
|
+
if (ts.isNewExpression(value) && ts.isIdentifier(value.expression) &&
|
|
4689
|
+
lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
|
|
4690
|
+
const name = value.expression.text;
|
|
4691
|
+
const args = value.arguments ?? [];
|
|
4692
|
+
if (args.length <= 1 && !args.some(ts.isSpreadElement)) {
|
|
4693
|
+
if (name === "String") {
|
|
4694
|
+
const effect = stringWrapperToString(lowerer, value);
|
|
4695
|
+
if (effect)
|
|
4696
|
+
return { tag: "String", effect };
|
|
4697
|
+
}
|
|
4698
|
+
if (name === "Number")
|
|
4699
|
+
return {
|
|
4700
|
+
tag: "Number",
|
|
4701
|
+
effect: args.length ? lowerNumberConstructorValue(lowerer, args[0], loc) : { kind: "numLit", value: 0, type: F64, loc },
|
|
4702
|
+
};
|
|
4703
|
+
if (name === "Boolean")
|
|
4704
|
+
return {
|
|
4705
|
+
tag: "Boolean",
|
|
4706
|
+
effect: args.length ? lowerer.lowerCondition(args[0]) : { kind: "boolLit", value: false, type: BOOL, loc },
|
|
4707
|
+
};
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
if ((ts.isCallExpression(value) || ts.isNewExpression(value)) &&
|
|
4711
|
+
ts.isIdentifier(value.expression) && value.expression.text === "Object" &&
|
|
4712
|
+
lowerer.isStdlibSymbol(lowerer.resolveValueSymbol(value.expression) ?? undefined)) {
|
|
4713
|
+
const args = value.arguments ?? [];
|
|
4714
|
+
if (args.some(ts.isSpreadElement))
|
|
4715
|
+
return null;
|
|
4716
|
+
const first = args[0];
|
|
4717
|
+
const inner = first && (immediateObjectTagReceiver(lowerer, first) ?? (() => {
|
|
4718
|
+
const effect = lowerer.lowerExpr(first);
|
|
4719
|
+
const tag = isUnitType(effect.type) || effect.type.kind === "void" ? "Object" : objectToStringTag(lowerer, effect.type);
|
|
4720
|
+
return tag === null ? null : { tag, effect };
|
|
4721
|
+
})());
|
|
4722
|
+
if (first && !inner)
|
|
4723
|
+
return null;
|
|
4724
|
+
const stmts = [];
|
|
4725
|
+
if (inner && !isSafeToDiscard(inner.effect))
|
|
4726
|
+
stmts.push({ kind: "exprStmt", expr: inner.effect, loc: inner.effect.loc });
|
|
4727
|
+
for (const extra of args.slice(1)) {
|
|
4728
|
+
const effect = lowerer.lowerExpr(extra);
|
|
4729
|
+
if (!isSafeToDiscard(effect))
|
|
4730
|
+
stmts.push({ kind: "exprStmt", expr: effect, loc: locOf(extra) });
|
|
4731
|
+
}
|
|
4732
|
+
return {
|
|
4733
|
+
tag: inner?.tag ?? "Object",
|
|
4734
|
+
effect: { kind: "seqExpr", stmts, result: { kind: "numLit", value: 0, type: F64, loc }, type: F64, loc },
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
return null;
|
|
4738
|
+
}
|
|
4739
|
+
function lowerObjectOwnPrototypeCall(lowerer, call, access) {
|
|
4740
|
+
if (lowerer.dynamic || access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
|
|
4741
|
+
return null;
|
|
4742
|
+
const method = access.expression;
|
|
4743
|
+
const enumerable = method.name.text === "propertyIsEnumerable";
|
|
4744
|
+
if (method.name.text !== "hasOwnProperty" && !enumerable)
|
|
4745
|
+
return null;
|
|
4746
|
+
const prototype = method.expression;
|
|
4747
|
+
if (!ts.isPropertyAccessExpression(prototype) || prototype.name.text !== "prototype" ||
|
|
4748
|
+
!lowerer.isStdlibGlobal(prototype.expression, "Object") || !lowerer.isStdlibMember(method) ||
|
|
4749
|
+
call.arguments.some(ts.isSpreadElement))
|
|
4750
|
+
return null;
|
|
4751
|
+
return lowerObjectOwnCall(lowerer, call, call.arguments[0], call.arguments[1], call.arguments.slice(2), method.name.text);
|
|
4752
|
+
}
|
|
4753
|
+
function lowerObjectOwnMethodCall(lowerer, call, access) {
|
|
4754
|
+
const method = access.name.text;
|
|
4755
|
+
if (lowerer.dynamic || call.questionDotToken || access.questionDotToken ||
|
|
4756
|
+
(method !== "hasOwnProperty" && method !== "propertyIsEnumerable") ||
|
|
4757
|
+
!lowerer.isStdlibMember(access) || call.arguments.some(ts.isSpreadElement))
|
|
4758
|
+
return null;
|
|
4759
|
+
return lowerObjectOwnCall(lowerer, call, access.expression, call.arguments[0], call.arguments.slice(1), method);
|
|
4760
|
+
}
|
|
4761
|
+
function lowerObjectOwnCall(lowerer, call, receiverNode, keyNode, extraNodes, method) {
|
|
4762
|
+
const enumerable = method === "propertyIsEnumerable";
|
|
4763
|
+
const loc = locOf(call);
|
|
4764
|
+
const stmts = [];
|
|
4765
|
+
const receiver = receiverNode ? lowerer.lowerExpr(receiverNode) : { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc };
|
|
4766
|
+
let receiverRef = receiver;
|
|
4767
|
+
if (isUnitType(receiver.type) || receiver.type.kind === "void") {
|
|
4768
|
+
if (!isSafeToDiscard(receiver))
|
|
4769
|
+
stmts.push({ kind: "exprStmt", expr: receiver, loc });
|
|
4770
|
+
}
|
|
4771
|
+
else {
|
|
4772
|
+
const local = lowerer.declareHiddenLocal("%ownReceiver", receiver.type);
|
|
4773
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: receiver, loc });
|
|
4774
|
+
receiverRef = varRef(local.id, receiver.type, loc);
|
|
4775
|
+
}
|
|
4776
|
+
const rawKey = keyNode ? lowerer.lowerExpr(keyNode) : null;
|
|
4777
|
+
let keyRef = rawKey;
|
|
4778
|
+
if (rawKey && (isUnitType(rawKey.type) || rawKey.type.kind === "void")) {
|
|
4779
|
+
if (!isSafeToDiscard(rawKey))
|
|
4780
|
+
stmts.push({ kind: "exprStmt", expr: rawKey, loc: rawKey.loc });
|
|
4781
|
+
}
|
|
4782
|
+
else if (rawKey) {
|
|
4783
|
+
const local = lowerer.declareHiddenLocal("%ownKey", rawKey.type);
|
|
4784
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: rawKey, loc: rawKey.loc });
|
|
4785
|
+
keyRef = varRef(local.id, rawKey.type, loc);
|
|
4786
|
+
}
|
|
4787
|
+
for (const extra of extraNodes) {
|
|
4788
|
+
const effect = lowerer.lowerExpr(extra);
|
|
4789
|
+
if (!isSafeToDiscard(effect))
|
|
4790
|
+
stmts.push({ kind: "exprStmt", expr: effect, loc: locOf(extra) });
|
|
4791
|
+
}
|
|
4792
|
+
let key = keyRef;
|
|
4793
|
+
if (!keyNode || rawKey?.type.kind === "undefinedT" || rawKey?.type.kind === "void") {
|
|
4794
|
+
key = { kind: "strLit", value: "undefined", type: STRING, loc };
|
|
4795
|
+
}
|
|
4796
|
+
else if (rawKey?.type.kind === "nullT") {
|
|
4797
|
+
key = { kind: "strLit", value: "null", type: STRING, loc };
|
|
4798
|
+
}
|
|
4799
|
+
else if (key && (key.type.kind === "f64" || key.type.kind === "bool" || key.type.kind === "bigint")) {
|
|
4800
|
+
key = { kind: "toString", operand: key, type: STRING, loc };
|
|
4801
|
+
}
|
|
4802
|
+
if (!key || key.type.kind !== "string") {
|
|
4803
|
+
lowerer.noLowering("Object.prototype." + method + " with this property key", keyNode ?? call);
|
|
4804
|
+
}
|
|
4805
|
+
let result;
|
|
4806
|
+
const type = receiver.type;
|
|
4807
|
+
if (isUnitType(type) || type.kind === "void") {
|
|
4808
|
+
result = nodeThrowExpr(1, "", "Cannot convert undefined or null to object", BOOL, loc);
|
|
4809
|
+
}
|
|
4810
|
+
else if (type.kind === "record") {
|
|
4811
|
+
const shape = lowerer.shapes.get(type.shapeId);
|
|
4812
|
+
if (!shape || shape.tuple || shape.indexValue || shapeHasAccessorSlots(shape)) {
|
|
4813
|
+
lowerer.noLowering("Object.prototype." + method + " over this record shape", receiverNode ?? call);
|
|
4814
|
+
}
|
|
4815
|
+
result = { kind: "call", callee: recordHasOwnHelper(lowerer, type.shapeId, loc), args: [receiverRef, key], type: BOOL, loc };
|
|
4816
|
+
}
|
|
4817
|
+
else if (type.kind === "dyn" && !enumerable) {
|
|
4818
|
+
result = { kind: "libCall", fn: "dyn.hasOwn", args: [receiverRef, key], type: BOOL, loc };
|
|
4819
|
+
}
|
|
4820
|
+
else if (type.kind === "string" || type.kind === "array") {
|
|
4821
|
+
const needleLocal = lowerer.declareHiddenLocal("%ownIndex", F64);
|
|
4822
|
+
stmts.push({ kind: "varDecl", localId: needleLocal.id, init: { kind: "libCall", fn: "num.fromString", args: [key], type: F64, loc }, loc });
|
|
4823
|
+
const needle = varRef(needleLocal.id, F64, loc);
|
|
4824
|
+
const length = type.kind === "string"
|
|
4825
|
+
? { kind: "strIntrinsic", method: "length", receiver: receiverRef, args: [], type: F64, loc }
|
|
4826
|
+
: { kind: "arrIntrinsic", method: "length", receiver: receiverRef, args: [], type: F64, loc };
|
|
4827
|
+
const inRange = { kind: "bin", op: "<", left: needle, right: length, type: BOOL, loc };
|
|
4828
|
+
const nonNegative = { kind: "bin", op: ">=", left: needle, right: { kind: "numLit", value: 0, type: F64, loc }, type: BOOL, loc };
|
|
4829
|
+
const integer = { kind: "bin", op: "===", left: needle,
|
|
4830
|
+
right: { kind: "libCall", fn: "math.floor", args: [needle], type: F64, loc }, type: BOOL, loc };
|
|
4831
|
+
const canonical = { kind: "strEq", negated: false, left: key, right: { kind: "toString", operand: needle, type: STRING, loc }, type: BOOL, loc };
|
|
4832
|
+
const present = type.kind === "string" ? { kind: "boolLit", value: true, type: BOOL, loc } :
|
|
4833
|
+
{ kind: "bin", op: "===", left: { kind: "arrIntrinsic", method: "nextPresent", receiver: receiverRef, args: [needle], type: F64, loc }, right: needle, type: BOOL, loc };
|
|
4834
|
+
const no = { kind: "boolLit", value: false, type: BOOL, loc };
|
|
4835
|
+
const indexOwn = [inRange, integer, canonical, present].reduce((left, right) => ({ kind: "logical", op: "&&", left, right, type: BOOL, loc }), nonNegative);
|
|
4836
|
+
const lengthOwn = !enumerable
|
|
4837
|
+
? { kind: "strEq", negated: false, left: key, right: { kind: "strLit", value: "length", type: STRING, loc }, type: BOOL, loc }
|
|
4838
|
+
: no;
|
|
4839
|
+
result = { kind: "logical", op: "||", left: lengthOwn, right: indexOwn, type: BOOL, loc };
|
|
4840
|
+
}
|
|
4841
|
+
else if (type.kind === "f64" || type.kind === "bool" || type.kind === "bigint" || type.kind === "symbol") {
|
|
4842
|
+
result = { kind: "boolLit", value: false, type: BOOL, loc };
|
|
4843
|
+
}
|
|
4844
|
+
else {
|
|
4845
|
+
lowerer.noLowering("Object.prototype." + method + " over a " + lowerer.fmt(type) + " receiver", receiverNode ?? call);
|
|
4846
|
+
}
|
|
4847
|
+
return { kind: "seqExpr", stmts, result, type: BOOL, loc };
|
|
4848
|
+
}
|
|
4849
|
+
function lowerObjectPrototypeCall(lowerer, call, access) {
|
|
4850
|
+
if (lowerer.dynamic || access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
|
|
4851
|
+
return null;
|
|
4852
|
+
const method = access.expression;
|
|
4853
|
+
const prototype = method.expression;
|
|
4854
|
+
if (method.name.text !== "toString" || !ts.isPropertyAccessExpression(prototype) ||
|
|
4855
|
+
prototype.name.text !== "prototype" || !lowerer.isStdlibGlobal(prototype.expression, "Object") ||
|
|
4856
|
+
!lowerer.isStdlibMember(method) || call.arguments.some(ts.isSpreadElement))
|
|
4857
|
+
return null;
|
|
4858
|
+
const loc = locOf(call);
|
|
4859
|
+
if (call.arguments.length === 0)
|
|
4860
|
+
return { kind: "strLit", value: "[object Undefined]", type: STRING, loc };
|
|
4861
|
+
const receiverNode = call.arguments[0];
|
|
4862
|
+
const immediate = immediateObjectTagReceiver(lowerer, receiverNode);
|
|
4863
|
+
const receiver = immediate?.effect ?? lowerer.lowerExpr(receiverNode);
|
|
4864
|
+
const tag = immediate?.tag ?? objectToStringTag(lowerer, receiver.type);
|
|
4865
|
+
if (tag === null && receiver.type.kind !== "dyn") {
|
|
4866
|
+
lowerer.noLowering("Object.prototype.toString.call over a " + lowerer.fmt(receiver.type) + " receiver", receiverNode);
|
|
4867
|
+
}
|
|
4868
|
+
const stmts = [];
|
|
4869
|
+
let ref = receiver;
|
|
4870
|
+
if (isUnitType(receiver.type) || receiver.type.kind === "void") {
|
|
4871
|
+
if (!isSafeToDiscard(receiver))
|
|
4872
|
+
stmts.push({ kind: "exprStmt", expr: receiver, loc });
|
|
4873
|
+
}
|
|
4874
|
+
else {
|
|
4875
|
+
const local = lowerer.declareHiddenLocal("%objectTagReceiver", receiver.type);
|
|
4876
|
+
stmts.push({ kind: "varDecl", localId: local.id, init: receiver, loc });
|
|
4877
|
+
ref = varRef(local.id, receiver.type, loc);
|
|
4878
|
+
}
|
|
4879
|
+
for (const extra of call.arguments.slice(1)) {
|
|
4880
|
+
stmts.push({ kind: "exprStmt", expr: lowerer.lowerExpr(extra), loc: locOf(extra) });
|
|
4881
|
+
}
|
|
4882
|
+
const result = tag === null
|
|
4883
|
+
? { kind: "libCall", fn: "dyn.objectTag", args: [ref], type: STRING, loc }
|
|
4884
|
+
: { kind: "strLit", value: "[object " + tag + "]", type: STRING, loc };
|
|
4885
|
+
return { kind: "seqExpr", stmts, result, type: STRING, loc };
|
|
4886
|
+
}
|
|
4643
4887
|
function lowerStringPrototypeCall(lowerer, call, access) {
|
|
4644
4888
|
if (access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression))
|
|
4645
4889
|
return null;
|
|
@@ -4647,21 +4891,26 @@ function lowerStringPrototypeCall(lowerer, call, access) {
|
|
|
4647
4891
|
const prototypeAccess = methodAccess.expression;
|
|
4648
4892
|
if (!ts.isPropertyAccessExpression(prototypeAccess) || prototypeAccess.name.text !== "prototype" ||
|
|
4649
4893
|
!ts.isIdentifier(prototypeAccess.expression) || prototypeAccess.expression.text !== "String" ||
|
|
4650
|
-
!Object.hasOwn(STR_METHODS, methodAccess.name.text)
|
|
4894
|
+
(!Object.hasOwn(STR_METHODS, methodAccess.name.text) && !STRING_INDEX_METHODS.has(methodAccess.name.text)) ||
|
|
4895
|
+
!lowerer.isStdlibMember(methodAccess))
|
|
4651
4896
|
return null;
|
|
4652
4897
|
if (call.arguments.some(ts.isSpreadElement))
|
|
4653
4898
|
return null;
|
|
4654
4899
|
const entry = STR_METHODS[methodAccess.name.text];
|
|
4900
|
+
const indexMethod = STRING_INDEX_METHODS.has(methodAccess.name.text) ? methodAccess.name.text : null;
|
|
4901
|
+
const resultType = indexMethod ? lowerer.withUndefinedArmOf(indexMethod === "at" ? STRING : F64) : entry?.result;
|
|
4902
|
+
if (!resultType)
|
|
4903
|
+
return null;
|
|
4655
4904
|
const method = methodAccess.name.text === "trimStart" ? "trimLeft" :
|
|
4656
4905
|
methodAccess.name.text === "trimEnd" ? "trimRight" : methodAccess.name.text;
|
|
4657
4906
|
const loc = locOf(call);
|
|
4658
|
-
const nullishError = nodeThrowExpr(1, "", `String.prototype.${method} called on null or undefined`,
|
|
4907
|
+
const nullishError = nodeThrowExpr(1, "", `String.prototype.${method} called on null or undefined`, resultType, loc);
|
|
4659
4908
|
const receiverNode = call.arguments[0];
|
|
4660
4909
|
if (!receiverNode)
|
|
4661
4910
|
return nullishError;
|
|
4662
4911
|
// Receiver coercion runs after .call has evaluated its arguments; keep
|
|
4663
4912
|
// contextual wrapper lowering on forms without later method arguments.
|
|
4664
|
-
const immediateWrapper = entry
|
|
4913
|
+
const immediateWrapper = entry?.maxArgs === 0 && call.arguments.length === 1
|
|
4665
4914
|
? immediatePrimitiveWrapperToString(lowerer, receiverNode) : null;
|
|
4666
4915
|
const receiverValue = immediateWrapper ?? lowerer.lowerExpr(receiverNode);
|
|
4667
4916
|
const receiverType = receiverValue.type;
|
|
@@ -4673,26 +4922,33 @@ function lowerStringPrototypeCall(lowerer, call, access) {
|
|
|
4673
4922
|
kind: "seqExpr",
|
|
4674
4923
|
stmts: values.filter((value) => !isSafeToDiscard(value)).map((value) => ({ kind: "exprStmt", expr: value, loc: value.loc })),
|
|
4675
4924
|
result: nullishError,
|
|
4676
|
-
type:
|
|
4925
|
+
type: resultType,
|
|
4677
4926
|
loc,
|
|
4678
4927
|
};
|
|
4679
4928
|
}
|
|
4680
|
-
const padding = entry
|
|
4929
|
+
const padding = entry?.method === "padStart" || entry?.method === "padEnd";
|
|
4681
4930
|
const scalar = receiverType.kind === "string" || receiverType.kind === "f64" ||
|
|
4682
4931
|
receiverType.kind === "bool" || receiverType.kind === "bigint" ||
|
|
4683
4932
|
(receiverType.kind === "union" && (lowerer.unions.get(receiverType.unionId)?.arms.every((arm) => arm.kind === "string" || arm.kind === "f64" || arm.kind === "bool" || arm.kind === "bigint") ?? false));
|
|
4684
4933
|
// Other methods convert object receivers before lowering their arguments;
|
|
4685
4934
|
// padding uses a helper that delays conversion until every argument is ready.
|
|
4686
|
-
const objectWithoutMethodArgs = entry
|
|
4935
|
+
const objectWithoutMethodArgs = entry?.maxArgs === 0 && call.arguments.length === 1 &&
|
|
4687
4936
|
(receiverType.kind === "record" || receiverType.kind === "array" || receiverType.kind === "object");
|
|
4688
|
-
const dynObjectWithoutMethodArgs = entry
|
|
4937
|
+
const dynObjectWithoutMethodArgs = entry?.maxArgs === 0 && call.arguments.length === 1 &&
|
|
4689
4938
|
receiverType.kind === "dyn" && plainObjectCoercionReceiver(lowerer, receiverNode);
|
|
4690
4939
|
if (!scalar && !objectWithoutMethodArgs && !dynObjectWithoutMethodArgs && !(padding && receiverType.kind === "dyn")) {
|
|
4691
4940
|
return lowerer.noLowering(`String.prototype.${methodAccess.name.text}.call with ${lowerer.fmt(receiverType)} receivers`, call);
|
|
4692
4941
|
}
|
|
4942
|
+
if (indexMethod)
|
|
4943
|
+
return lowerStringIndexCall(lowerer, call, indexMethod, receiverValue, receiverNode, call.arguments.slice(1));
|
|
4944
|
+
if (!entry)
|
|
4945
|
+
return null;
|
|
4693
4946
|
if (padding) {
|
|
4694
4947
|
return lowerStringPaddingCall(lowerer, call, entry.method, receiverValue, receiverNode, call.arguments.slice(1));
|
|
4695
4948
|
}
|
|
4949
|
+
if (entry.method === "split") {
|
|
4950
|
+
return lowerStringSplitCall(lowerer, call, receiverValue, receiverNode, call.arguments.slice(1));
|
|
4951
|
+
}
|
|
4696
4952
|
const receiver = dynObjectWithoutMethodArgs
|
|
4697
4953
|
? { kind: "libCall", fn: "dyn.toStringCoerce", args: [receiverValue], type: STRING, loc }
|
|
4698
4954
|
: lowerer.ensureString(receiverValue, receiverNode);
|