mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 00:25:34 +08:00
Handle coerced positions in static string search calls (#424)
* Coerce static string search positions * Record TypeScript parity for string search fixture
This commit is contained in:
@@ -320,14 +320,15 @@ export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
|
||||
? dynReceiver()
|
||||
: lowerMethodReceiver(lowerer, access.expression, STRING, access.name.text);
|
||||
const loc = locOf(call);
|
||||
if ((entry.method === "startsWith" || entry.method === "endsWith") && call.arguments.length === 2) {
|
||||
if ((entry.method === "indexOf" || entry.method === "includes" ||
|
||||
entry.method === "startsWith" || entry.method === "endsWith") && call.arguments.length === 2) {
|
||||
const needle = lowerer.lowerExpr(call.arguments[0]!);
|
||||
const defaultPosition: IrExpr = entry.method === "startsWith"
|
||||
? numLit(0, loc)
|
||||
: { kind: "bin", op: "/", left: numLit(1, loc), right: numLit(0, loc), type: F64, loc };
|
||||
const defaultPosition: IrExpr = entry.method === "endsWith"
|
||||
? { kind: "bin", op: "/", left: numLit(1, loc), right: numLit(0, loc), type: F64, loc }
|
||||
: numLit(0, loc);
|
||||
const position = lowerStringPositionArgument(lowerer, call.arguments[1], defaultPosition);
|
||||
if (position.type.kind === "f64" || position.type.kind === "jsval") {
|
||||
return { kind: "strIntrinsic", method: entry.method, receiver, args: [needle, position], type: BOOL, loc };
|
||||
return { kind: "strIntrinsic", method: entry.method, receiver, args: [needle, position], type: entry.result, loc };
|
||||
}
|
||||
const key = `str.positions:${entry.method}:${typeKey(needle.type)}:${typeKey(position.type)}`;
|
||||
let helper = lowerer.widthHelpers.get(key);
|
||||
@@ -340,16 +341,16 @@ export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
|
||||
varRef("arg.1", needle.type, loc),
|
||||
stringPositionNumber(lowerer, varRef("arg.2", position.type, loc), defaultPosition, call.arguments[1]!),
|
||||
],
|
||||
type: BOOL, loc,
|
||||
type: entry.result, loc,
|
||||
};
|
||||
lowerer.widthHelpers.set(key, helper);
|
||||
lowerer.liftedFns.push({
|
||||
name: helper, params, returnType: BOOL,
|
||||
name: helper, params, returnType: entry.result,
|
||||
locals: params.map(param => ({ id: param.localId, name: param.name, type: param.type, mutable: false })),
|
||||
body: [{ kind: "return", value: result, loc }], loc,
|
||||
});
|
||||
}
|
||||
return { kind: "call", callee: helper, args: [receiver, needle, position], type: BOOL, loc };
|
||||
return { kind: "call", callee: helper, args: [receiver, needle, position], type: entry.result, loc };
|
||||
}
|
||||
if (entry.method === "charAt" || entry.method === "charCodeAt" || entry.method === "slice" || entry.method === "substring") {
|
||||
const defaults: IrExpr[] = [numLit(0, loc)];
|
||||
|
||||
@@ -6919,6 +6919,12 @@
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/3025-string-search-position-coercion.js": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/3025-string-search-position-coercion.js"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/303-break-continue.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/303-break-continue.ts"
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
let trace = "";
|
||||
function receiver() { trace += "r"; return "abcde"; }
|
||||
function needle() { trace += "n"; return "bc"; }
|
||||
function position() { trace += "p"; return "1.9"; }
|
||||
console.log(receiver().indexOf(needle(), position()), trace);
|
||||
trace = "";
|
||||
console.log(receiver().includes(needle(), position()), trace);
|
||||
trace = "";
|
||||
console.log(receiver().indexOf(needle(), void position()), trace);
|
||||
trace = "";
|
||||
console.log(receiver().includes(needle(), void position()), trace);
|
||||
|
||||
/** @type {(number | string | boolean | null | undefined)[]} */
|
||||
const positions = [undefined, null, false, true, "", "2.9", "bad", NaN, -Infinity, -3, 0, 1, 3, Infinity, 99];
|
||||
for (const value of positions) {
|
||||
const text = "a😀bc😀d";
|
||||
console.log(text.indexOf("😀", value), text.indexOf("", value));
|
||||
console.log(text.includes("bc", value), text.includes("", value));
|
||||
}
|
||||
@@ -51,6 +51,9 @@
|
||||
"test/built-ins/String/prototype/endsWith/return-true-if-searchstring-is-empty.js",
|
||||
"test/built-ins/String/prototype/endsWith/searchstring-found-with-position.js",
|
||||
"test/built-ins/String/prototype/endsWith/searchstring-not-found-with-position.js",
|
||||
"test/built-ins/String/prototype/includes/coerced-values-of-position.js",
|
||||
"test/built-ins/String/prototype/includes/return-false-with-out-of-bounds-position.js",
|
||||
"test/built-ins/String/prototype/includes/return-true-if-searchstring-is-empty.js",
|
||||
"test/built-ins/String/prototype/lastIndexOf/not-a-substring.js",
|
||||
"test/built-ins/String/prototype/slice/S15.5.4.13_A1_T4.js",
|
||||
"test/built-ins/String/prototype/startsWith/out-of-bounds-position.js",
|
||||
@@ -129,6 +132,9 @@
|
||||
"test/built-ins/String/prototype/endsWith/return-true-if-searchstring-is-empty.js": "708b505a6dc830c907bb483b99c124582de700a9f2c0251985ace72361afaa72",
|
||||
"test/built-ins/String/prototype/endsWith/searchstring-found-with-position.js": "532eb90be62d4faa2faa230ee91180ea260dc330b4427b9a08a696ac7417a3a4",
|
||||
"test/built-ins/String/prototype/endsWith/searchstring-not-found-with-position.js": "5995922f82d645d02eabbbd75ec1b5e2d250f93870a3edd20ee491691a953743",
|
||||
"test/built-ins/String/prototype/includes/coerced-values-of-position.js": "292b6f29f5f223027deb54dafe62726790e2a61ddf2eb5e5036688b7088f105f",
|
||||
"test/built-ins/String/prototype/includes/return-false-with-out-of-bounds-position.js": "4e65326e88ae9b915771fcc70c52fd7e4aa1f7a6aa8ca047010806555b57d668",
|
||||
"test/built-ins/String/prototype/includes/return-true-if-searchstring-is-empty.js": "c5552ee08afae1dd6147d388e987a1083571d2faf18ace56b0324204d1ea50b2",
|
||||
"test/built-ins/String/prototype/lastIndexOf/not-a-substring.js": "1fa0e88ffe6a342e6204c925918ae6fc29b003d47007210fc377c24bc56bd41d",
|
||||
"test/built-ins/String/prototype/slice/S15.5.4.13_A1_T4.js": "1f67b7089a1e53f850b6ceb962193f49d8a2c114fc37a0377ac6553108c83ac0",
|
||||
"test/built-ins/String/prototype/startsWith/out-of-bounds-position.js": "2c27bbb2505d166a6a0fbab388ca9660780c6b396e0634e87a3e855c11865cb3",
|
||||
|
||||
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2015 the V8 project authors. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
/*---
|
||||
es6id: 21.1.3.7
|
||||
description: >
|
||||
Returns based on coerced values of position.
|
||||
info: |
|
||||
21.1.3.7 String.prototype.includes ( searchString [ , position ] )
|
||||
|
||||
...
|
||||
9. Let pos be ToInteger(position). (If position is undefined, this step
|
||||
produces the value 0).
|
||||
10. ReturnIfAbrupt(pos).
|
||||
11. Let len be the number of elements in S.
|
||||
12. Let start be min(max(pos, 0), len).
|
||||
13. Let searchLen be the number of elements in searchStr.
|
||||
14. If there exists any integer k not smaller than start such that k +
|
||||
searchLen is not greater than len, and for all nonnegative integers j less
|
||||
than searchLen, the code unit at index k+j of S is the same as the code unit
|
||||
at index j of searchStr, return true; but if there is no such integer k,
|
||||
return false.
|
||||
...
|
||||
features: [String.prototype.includes]
|
||||
---*/
|
||||
|
||||
var str = 'The future is cool!';
|
||||
|
||||
assert(str.includes('The future', NaN), 'NaN coerced to 0');
|
||||
assert(str.includes('The future', null), 'null coerced to 0');
|
||||
assert(str.includes('The future', false), 'false coerced to 0');
|
||||
assert(str.includes('The future', ''), '"" coerced to 0');
|
||||
assert(str.includes('The future', '0'), '"0" coerced to 0');
|
||||
assert(str.includes('The future', undefined), 'undefined coerced to 0');
|
||||
assert(str.includes('The future', 0.4), '0.4 coerced to 0');
|
||||
|
||||
assert(str.includes('The future', -1));
|
||||
|
||||
assert.sameValue(str.includes('The future', true), false, 'true coerced to 1');
|
||||
assert.sameValue(str.includes('The future', '1'), false, '"1" coerced to 1');
|
||||
assert.sameValue(str.includes('The future', 1.4), false, '1.4 coerced to 1');
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2015 the V8 project authors. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
/*---
|
||||
es6id: 21.1.3.7
|
||||
description: >
|
||||
Returns false if position is >= this.length and searchString.length > 0.
|
||||
info: |
|
||||
21.1.3.7 String.prototype.includes ( searchString [ , position ] )
|
||||
|
||||
...
|
||||
11. Let len be the number of elements in S.
|
||||
12. Let start be min(max(pos, 0), len).
|
||||
13. Let searchLen be the number of elements in searchStr.
|
||||
14. If there exists any integer k not smaller than start such that k +
|
||||
searchLen is not greater than len, and for all nonnegative integers j less
|
||||
than searchLen, the code unit at index k+j of S is the same as the code unit
|
||||
at index j of searchStr, return true; but if there is no such integer k,
|
||||
return false.
|
||||
...
|
||||
features: [String.prototype.includes]
|
||||
---*/
|
||||
|
||||
var str = 'The future is cool!';
|
||||
|
||||
assert.sameValue(
|
||||
str.includes('!', str.length + 1), false,
|
||||
'str.includes("!", str.length + 1) returns false'
|
||||
);
|
||||
|
||||
assert.sameValue(
|
||||
str.includes('!', 100), false,
|
||||
'str.includes("!", 100) returns false'
|
||||
);
|
||||
|
||||
assert.sameValue(
|
||||
str.includes('!', Infinity), false,
|
||||
'str.includes("!", Infinity) returns false'
|
||||
);
|
||||
|
||||
assert.sameValue(
|
||||
str.includes('!', str.length), false,
|
||||
'str.includes("!", str.length) returns false'
|
||||
);
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2015 the V8 project authors. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
/*---
|
||||
es6id: 21.1.3.7
|
||||
description: >
|
||||
Returns true if searchString.length == 0.
|
||||
info: |
|
||||
21.1.3.7 String.prototype.includes ( searchString [ , position ] )
|
||||
|
||||
...
|
||||
11. Let len be the number of elements in S.
|
||||
12. Let start be min(max(pos, 0), len).
|
||||
13. Let searchLen be the number of elements in searchStr.
|
||||
14. If there exists any integer k not smaller than start such that k +
|
||||
searchLen is not greater than len, and for all nonnegative integers j less
|
||||
than searchLen, the code unit at index k+j of S is the same as the code unit
|
||||
at index j of searchStr, return true; but if there is no such integer k,
|
||||
return false.
|
||||
...
|
||||
features: [String.prototype.includes]
|
||||
---*/
|
||||
|
||||
var str = 'The future is cool!';
|
||||
|
||||
assert(
|
||||
str.includes('', str.length),
|
||||
'str.includes("", str.length) returns true'
|
||||
);
|
||||
|
||||
assert(
|
||||
str.includes(''),
|
||||
'str.includes("") returns true'
|
||||
);
|
||||
|
||||
assert(
|
||||
str.includes('', Infinity),
|
||||
'str.includes("", Infinity) returns true'
|
||||
);
|
||||
Reference in New Issue
Block a user