mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 00:25:34 +08:00
Handle scalar search values in positioned string methods (#436)
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { BOOL, F64, IrExpr, IrFunction, IrStmt, IrType, STRING, SrcLoc, arrayOf, isUnitType, typeEquals, typeKey } from "../../../ir/ir.js";
|
||||
import { numLit, varRef } from "../../../ir/build.js";
|
||||
import { numLit, strLit, varRef } from "../../../ir/build.js";
|
||||
import { locOf } from "../../program.js";
|
||||
import type { Lowerer } from "../lowerer.js";
|
||||
import { own } from "../lowerer.js";
|
||||
import { isRequireMainFilename } from "../expressions/optional-chains.js";
|
||||
import { STR_METHODS } from "../surfaces.js";
|
||||
import { defaultAfterUndefined, lowerOptionalArgument, lowerPositionArgument, lowerStaticallyUndefinedArgument, lowerStringSearchArgument, positionNumber } from "../optional-arguments.js";
|
||||
import { coerceStringSearchValue, defaultAfterUndefined, lowerOptionalArgument, lowerPositionArgument, lowerStaticallyUndefinedArgument, lowerStringSearchArgument, positionNumber } from "../optional-arguments.js";
|
||||
|
||||
function lowerSplitLimitArg(lowerer: Lowerer, node: ts.Expression | undefined, loc: SrcLoc): IrExpr {
|
||||
const defaultValue: IrExpr = { kind: "numLit", value: 4294967295, type: F64, loc };
|
||||
@@ -278,9 +278,17 @@ export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
|
||||
return { kind: "strIntrinsic", method: entry.method, receiver, args: [needle], type: entry.result, loc };
|
||||
}
|
||||
if (searchMethod && call.arguments.length === 2) {
|
||||
const needle = lowerer.lowerExpr(call.arguments[0]!);
|
||||
const optionalNeedle = needle.type.kind === "union" && lowerer.runtimeOptionalWidening(needle.type, STRING) !== null;
|
||||
if (needle.type.kind !== "string" && needle.type.kind !== "dyn" && !optionalNeedle) {
|
||||
const needleNode = call.arguments[0]!;
|
||||
const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, needleNode);
|
||||
let needle = undefinedArg
|
||||
? defaultAfterUndefined(undefinedArg, strLit("undefined", loc))
|
||||
: lowerer.lowerExpr(needleNode);
|
||||
if (isUnitType(needle.type)) needle = coerceStringSearchValue(lowerer, needle, needleNode, loc);
|
||||
const scalarUnion = needle.type.kind === "union" &&
|
||||
(lowerer.unions.get(needle.type.unionId)?.arms.every((arm) =>
|
||||
arm.kind === "string" || arm.kind === "f64" || arm.kind === "bool" || arm.kind === "bigint" || isUnitType(arm)) ?? false);
|
||||
const scalarNeedle = needle.type.kind === "f64" || needle.type.kind === "bool" || needle.type.kind === "bigint" || scalarUnion;
|
||||
if (needle.type.kind !== "string" && needle.type.kind !== "dyn" && !scalarNeedle) {
|
||||
lowerer.noLowering(`.${entry.method} with '${lowerer.fmt(needle.type)}' search values`, call);
|
||||
}
|
||||
const defaultPosition: IrExpr = entry.method === "endsWith"
|
||||
@@ -311,7 +319,7 @@ export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
|
||||
const value = varRef("arg.1", needle.type, loc);
|
||||
const init: IrExpr = needle.type.kind === "dyn"
|
||||
? { kind: "libCall", fn: "dyn.toStringCoerce", args: [value], type: STRING, loc }
|
||||
: lowerer.ensureString(value, call.arguments[0]!);
|
||||
: coerceStringSearchValue(lowerer, value, needleNode, loc);
|
||||
locals.push({ id: "search.0", name: "search", type: STRING, mutable: false });
|
||||
body.push({ kind: "varDecl", localId: "search.0", init, loc });
|
||||
}
|
||||
|
||||
@@ -49,7 +49,11 @@ export function lowerStringSearchArgument(lowerer: Lowerer, node: ts.Expression
|
||||
if (!node) return absent;
|
||||
const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, node);
|
||||
if (undefinedArg) return defaultAfterUndefined(undefinedArg, absent);
|
||||
const value = lowerer.lowerExpr(node);
|
||||
return coerceStringSearchValue(lowerer, lowerer.lowerExpr(node), node, loc);
|
||||
}
|
||||
|
||||
/** Convert a lowered string-search value while preserving unit-value effects. */
|
||||
export function coerceStringSearchValue(lowerer: Lowerer, value: IrExpr, node: ts.Expression, loc: SrcLoc): IrExpr {
|
||||
if (isUnitType(value.type)) {
|
||||
return defaultAfterUndefined(value, strLit(value.type.kind === "nullT" ? "null" : "undefined", loc));
|
||||
}
|
||||
|
||||
@@ -6985,6 +6985,12 @@
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/3035-string-positioned-search-value.js": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/3035-string-positioned-search-value.js"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/304-compound-incdec.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/304-compound-incdec.ts"
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
console.log("xundefinedy".indexOf(undefined, 1), "xundefinedy".includes(undefined, 1));
|
||||
console.log("xundefinedy".startsWith(undefined, 1), "xundefinedy".endsWith(undefined, 10));
|
||||
console.log("xnullnull".indexOf(null, 2), "xnullnull".includes(null, 5));
|
||||
console.log("xnull".startsWith(null, 1), "xnull".endsWith(null, 5));
|
||||
console.log("xtruefalse".indexOf(true, 1), "xtruefalse".includes(false, 5));
|
||||
console.log("xtruefalse".startsWith(true, 1), "xtruefalse".endsWith(false, 10));
|
||||
console.log("x42".indexOf(42, 1), "x42".includes(42, 1), "x42".startsWith(42, 1), "x42".endsWith(42, 3));
|
||||
console.log("x1".indexOf(1n, 1), "x1".includes(1n, 1), "x1".startsWith(1n, 1), "x1".endsWith(1n, 2));
|
||||
console.log("x0NaNInfinity".indexOf(-0, 1), "x0NaNInfinity".includes(NaN, 2));
|
||||
console.log("xInfinity".startsWith(Infinity, 1), "xInfinity".endsWith(Infinity, 9));
|
||||
|
||||
/** @param {string | number | boolean | null | undefined} needle */
|
||||
function search(needle) {
|
||||
const text = "xundefined-null-1-true";
|
||||
console.log(text.indexOf(needle, 1), text.includes(needle, 1), text.startsWith(needle, 1), text.endsWith(needle, 11));
|
||||
}
|
||||
search(undefined);
|
||||
search(null);
|
||||
search(1);
|
||||
search(true);
|
||||
search("undefined");
|
||||
|
||||
let trace = "";
|
||||
function receiver() { trace += "r"; return "xundefinedy"; }
|
||||
function needleEffect() { trace += "n"; return 1; }
|
||||
function position() { trace += "p"; return "1"; }
|
||||
console.log(receiver().indexOf(void needleEffect(), position()), trace);
|
||||
trace = "";
|
||||
console.log(receiver().includes(void needleEffect(), position()), trace);
|
||||
trace = "";
|
||||
console.log(receiver().startsWith(void needleEffect(), position()), trace);
|
||||
trace = "";
|
||||
console.log(receiver().endsWith(void needleEffect(), position()), trace);
|
||||
|
||||
function nullEffect() { trace += "n"; return null; }
|
||||
trace = "";
|
||||
console.log(receiver().indexOf(nullEffect(), position()), trace);
|
||||
Reference in New Issue
Block a user