Lower static String prototype call forms (#448)

This commit is contained in:
Chris Tate
2026-09-25 18:38:10 -05:00
committed by GitHub
parent 2bd50ba0ee
commit d51aa96290
19 changed files with 298 additions and 17 deletions
@@ -237,7 +237,9 @@ export function lowerRegexMethodCall(lowerer: Lowerer, call: ts.CallExpression,
* Complete position defaults and conversions here for both backends. */
export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
access: ts.PropertyAccessExpression,
dynReceiver?: () => IrExpr,): IrExpr | null {
dynReceiver?: () => IrExpr,
argumentNodes: readonly ts.Expression[] = call.arguments,
): IrExpr | null {
if (lowerer.chainBlocked(access, call)) return null;
if (dynReceiver === undefined && access.name.text === "localeCompare") return lowerLocaleCompareCall(lowerer, call, access);
const entry = own(STR_METHODS, access.name.text);
@@ -261,9 +263,9 @@ export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
}
// The lib declares optional parameters beyond some lowered forms; fence
// those arities instead of passing arguments the runtime doesn't take.
if (call.arguments.length < entry.minArgs || call.arguments.length > entry.maxArgs) {
if (argumentNodes.length < entry.minArgs || argumentNodes.length > entry.maxArgs) {
lowerer.noLowering(
`.${access.name.text} with ${call.arguments.length} argument${call.arguments.length === 1 ? "" : "s"} on strings`,
`.${access.name.text} with ${argumentNodes.length} argument${argumentNodes.length === 1 ? "" : "s"} on strings`,
call,
);
}
@@ -273,12 +275,12 @@ export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
const loc = locOf(call);
const searchMethod = entry.method === "indexOf" || entry.method === "includes" ||
entry.method === "startsWith" || entry.method === "endsWith";
if (searchMethod && call.arguments.length < 2) {
const needle = lowerStringSearchArgument(lowerer, call.arguments[0], loc);
if (searchMethod && argumentNodes.length < 2) {
const needle = lowerStringSearchArgument(lowerer, argumentNodes[0], loc);
return { kind: "strIntrinsic", method: entry.method, receiver, args: [needle], type: entry.result, loc };
}
if (searchMethod && call.arguments.length === 2) {
const needleNode = call.arguments[0]!;
if (searchMethod && argumentNodes.length === 2) {
const needleNode = argumentNodes[0]!;
const undefinedArg = lowerStaticallyUndefinedArgument(lowerer, needleNode);
let needle = undefinedArg
? defaultAfterUndefined(undefinedArg, strLit("undefined", loc))
@@ -294,7 +296,7 @@ export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
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 = lowerPositionArgument(lowerer, call.arguments[1], defaultPosition);
const position = lowerPositionArgument(lowerer, argumentNodes[1], defaultPosition);
if (needle.type.kind === "string" && (position.type.kind === "f64" || position.type.kind === "jsval")) {
return { kind: "strIntrinsic", method: entry.method, receiver, args: [needle, position], type: entry.result, loc };
}
@@ -309,7 +311,7 @@ export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
kind: "strIntrinsic", method: entry.method, receiver: varRef("arg.0", STRING, loc),
args: [
search,
positionNumber(lowerer, varRef("arg.2", position.type, loc), defaultPosition, call.arguments[1]!, "string position"),
positionNumber(lowerer, varRef("arg.2", position.type, loc), defaultPosition, argumentNodes[1]!, "string position"),
],
type: entry.result, loc,
};
@@ -337,7 +339,7 @@ export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
if (entry.method === "slice" || entry.method === "substring") {
defaults.push({ kind: "bin", op: "/", left: numLit(1, loc), right: numLit(0, loc), type: F64, loc });
}
const args = defaults.map((value, index) => lowerPositionArgument(lowerer, call.arguments[index], value));
const args = defaults.map((value, index) => lowerPositionArgument(lowerer, argumentNodes[index], value));
const subject = entry.method === "repeat" ? "string repeat count" : "string position";
// Keep ordinary numeric calls on the direct intrinsic path, including the
// existing boundary validation for island values in numeric slots.
@@ -354,7 +356,7 @@ export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
const params = [receiver, ...args].map((arg, index) => ({ localId: `arg.${index}`, name: `arg${index}`, type: arg.type }));
const result: IrExpr = {
kind: "strIntrinsic", method: entry.method, receiver: varRef("arg.0", STRING, loc),
args: args.map((arg, index) => positionNumber(lowerer, varRef(`arg.${index + 1}`, arg.type, loc), defaults[index]!, call.arguments[index] ?? call, subject)),
args: args.map((arg, index) => positionNumber(lowerer, varRef(`arg.${index + 1}`, arg.type, loc), defaults[index]!, argumentNodes[index] ?? call, subject)),
type: entry.result, loc,
};
lowerer.widthHelpers.set(key, helper);
@@ -367,15 +369,15 @@ export function lowerStringMethodCall(lowerer: Lowerer, call: ts.CallExpression,
return { kind: "call", callee: helper, args: [receiver, ...args], type: entry.result, loc };
}
const args = entry.method === "split"
? [lowerer.lowerExpr(call.arguments[0]!), lowerSplitLimitArg(lowerer, call.arguments[1], locOf(call))]
: call.arguments.map((a) => lowerer.lowerExpr(a));
? [lowerer.lowerExpr(argumentNodes[0]!), lowerSplitLimitArg(lowerer, argumentNodes[1], locOf(call))]
: argumentNodes.map((a) => lowerer.lowerExpr(a));
// split's separator must BE a string here (a regex argument was
// claimed by lowerRegexMethodCall before this path) — the lib's
// `string | RegExp` union has no lowering as a VALUE.
if (entry.method === "split" && args[0]!.type.kind !== "string") {
lowerer.unsupported(
"SC1090",
call.arguments[0]!,
argumentNodes[0]!,
`'.split()' on a '${lowerer.fmt(args[0]!.type)}' separator (pass a string, or a regex literal)`,
);
}
@@ -12,7 +12,7 @@ import { isJsSourceFile, isNodeEsmFile, locOf } from "../program.js";
import { genResultRecord, isGenericCallableMemberType, typeKey } from "../type-mapper.js";
import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr, staticImportNamespaceType } from "./lowerer.js";
import { enforceLibBoundary } from "./lib-boundary.js";
import { NARROW_FIRST, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
import { NARROW_FIRST, STR_METHODS, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
import { ffiBindingDiag, ffiSignatureDiag, libCallbackDiag, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
import type { ScrDiagnostic } from "../../diagnostics/diagnostic.js";
import { mixinFnShapeOf } from "./lower-mixins.js";
@@ -4659,6 +4659,7 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr {
// TestContext surface (t.test/t.skip/t.diagnostic), t.assert.*.
lowerer.lowerTestMethodCall(expr, expr.expression) ??
lowerer.lowerTimeoutMethodCall(expr, expr.expression) ??
lowerStringPrototypeCall(lowerer, expr, expr.expression) ??
lowerStringMethodCallWithOptionalArgs(lowerer, expr, expr.expression) ??
// Typed-array/Buffer receivers and the Buffer statics — before the
// island path (bytes never cross the boundary).
@@ -5032,8 +5033,10 @@ function lowerStringMethodCallWithOptionalArgs(
lowerer: Lowerer,
call: ts.CallExpression,
access: ts.PropertyAccessExpression,
receiver?: () => IrExpr,
argumentNodes: readonly ts.Expression[] = call.arguments,
): IrExpr | null {
const lowered = lowerer.lowerStringMethodCall(call, access);
const lowered = lowerStringMethodCall(lowerer, call, access, receiver, argumentNodes);
if (lowered?.kind !== "strIntrinsic") return lowered;
const args = [...lowered.args];
let changed = false;
@@ -5061,12 +5064,60 @@ function lowerStringMethodCallWithOptionalArgs(
args[0]?.type.kind === "union" &&
lowerer.runtimeOptionalWidening(args[0].type, STRING) !== null
) {
args[0] = lowerer.ensureString(args[0], call.arguments[0]!);
args[0] = lowerer.ensureString(args[0], argumentNodes[0]!);
changed = true;
}
return changed ? { ...lowered, args } : lowered;
}
function lowerStringPrototypeCall(
lowerer: Lowerer,
call: ts.CallExpression,
access: ts.PropertyAccessExpression,
): IrExpr | null {
if (access.name.text !== "call" || !ts.isPropertyAccessExpression(access.expression)) return null;
const methodAccess = access.expression;
const prototypeAccess = methodAccess.expression;
if (!ts.isPropertyAccessExpression(prototypeAccess) || prototypeAccess.name.text !== "prototype" ||
!ts.isIdentifier(prototypeAccess.expression) || prototypeAccess.expression.text !== "String" ||
!Object.hasOwn(STR_METHODS, methodAccess.name.text) || !lowerer.isStdlibMember(methodAccess)) return null;
if (call.arguments.some(ts.isSpreadElement)) return null;
const entry = STR_METHODS[methodAccess.name.text]!;
const method = methodAccess.name.text === "trimStart" ? "trimLeft" :
methodAccess.name.text === "trimEnd" ? "trimRight" : methodAccess.name.text;
const loc = locOf(call);
const nullishError = nodeThrowExpr(1, "", `String.prototype.${method} called on null or undefined`, entry.result, loc);
const receiverNode = call.arguments[0];
if (!receiverNode) return nullishError;
const receiverValue = lowerer.lowerExpr(receiverNode);
const receiverType = receiverValue.type;
const nullish = isUnitType(receiverType) || receiverType.kind === "void" ||
(receiverType.kind === "union" && (lowerer.unions.get(receiverType.unionId)?.arms.every((arm) =>
isUnitType(arm) || arm.kind === "void") ?? false));
if (nullish) {
const values = [receiverValue, ...call.arguments.slice(1).map((arg) => lowerer.lowerExpr(arg))];
return {
kind: "seqExpr",
stmts: values.filter((value) => !isSafeToDiscard(value)).map((value) => ({ kind: "exprStmt", expr: value, loc: value.loc })),
result: nullishError,
type: entry.result,
loc,
};
}
const scalar = receiverType.kind === "string" || receiverType.kind === "f64" ||
receiverType.kind === "bool" || receiverType.kind === "bigint" ||
(receiverType.kind === "union" && (lowerer.unions.get(receiverType.unionId)?.arms.every((arm) =>
arm.kind === "string" || arm.kind === "f64" || arm.kind === "bool" || arm.kind === "bigint") ?? false));
// Object conversion may run user code, so no later method arguments can remain to evaluate.
const objectWithoutMethodArgs = entry.maxArgs === 0 && call.arguments.length === 1 &&
(receiverType.kind === "record" || receiverType.kind === "array" || receiverType.kind === "object");
if (!scalar && !objectWithoutMethodArgs) {
return lowerer.noLowering(`String.prototype.${methodAccess.name.text}.call with ${lowerer.fmt(receiverType)} receivers`, call);
}
const receiver = lowerer.ensureString(receiverValue, receiverNode);
return lowerStringMethodCallWithOptionalArgs(lowerer, call, methodAccess, () => receiver, call.arguments.slice(1));
}
function lowerScalarUnionNumber(lowerer: Lowerer, arg: IrExpr, node: ts.Expression, loc: SrcLoc): IrExpr | null {
if (arg.type.kind !== "union" || !lowerer.unions.get(arg.type.unionId)?.arms.every(
(arm) => arm.kind === "f64" || arm.kind === "string" || arm.kind === "bool" || isUnitType(arm)
@@ -7021,6 +7021,12 @@
],
"diags": []
},
"<repo>/tests/corpus/3040-string-prototype-call.js": {
"order": [
"<repo>/tests/corpus/3040-string-prototype-call.js"
],
"diags": []
},
"<repo>/tests/corpus/305-truthiness.ts": {
"order": [
"<repo>/tests/corpus/305-truthiness.ts"
@@ -0,0 +1,46 @@
console.log(String.prototype.trim.call(" \u00a0 x \n"), String.prototype.trim.call(false), String.prototype.trim.call(-0));
console.log(String.prototype.trimStart.call(12), String.prototype.trimEnd.call(true));
console.log(String.prototype.trimLeft.call(" x"), String.prototype.trimRight.call("x "));
console.log(String.prototype.charAt.call(12345, 2), String.prototype.charCodeAt.call("AB", 1));
console.log(String.prototype.indexOf.call(12345, "3"), String.prototype.includes.call(12345, 34));
console.log(String.prototype.startsWith.call("abc", "b", 1), String.prototype.endsWith.call("abc", "b", 2));
console.log(String.prototype.slice.call(true, 1), String.prototype.substring.call("abcdef", "2", "4"));
console.log(String.prototype.repeat.call(12, "2"), String.prototype.padStart.call(42, 4, "0"), String.prototype.padEnd.call(42, 4, "0"));
console.log(String.prototype.split.call("a,b", ",").join("|"), String.prototype.toLowerCase.call("ABC"), String.prototype.toUpperCase.call("abc"));
console.log(String.prototype.trim.call({}), String.prototype.trim.call([1, 2]), String.prototype.trim.call(new Error("test")));
const scalar = Math.floor(1.9) === 1 ? 42 : " abc ";
console.log(String.prototype.trim.call(scalar));
let trace = "";
function receiver() { trace += "r"; return " ab "; }
function search() { trace += "s"; return "a"; }
function position() { trace += "p"; return "2"; }
console.log(String.prototype.indexOf.call(receiver(), search(), position()), trace);
let nullishTrace = "";
function nullReceiver() { nullishTrace += "r"; return null; }
function searched() { nullishTrace += "s"; return "x"; }
try {
String.prototype.indexOf.call(nullReceiver(), searched());
} catch (error) {
console.log(nullishTrace, error.name);
}
try {
String.prototype.trim.call(undefined);
} catch (error) {
console.log(error.name);
}
try {
String.prototype.split.call();
} catch (error) {
console.log(error.name);
}
let extraTrace = "";
function extra() { extraTrace += "e"; throw new Error("argument"); }
try {
String.prototype.trim.call(null, extra());
} catch (error) {
console.log(extraTrace, error.message);
}
+28
View File
@@ -83,6 +83,20 @@
"test/built-ins/String/prototype/startsWith/searchstring-found-with-position.js",
"test/built-ins/String/prototype/startsWith/searchstring-not-found-with-position.js",
"test/built-ins/String/prototype/substring/S15.5.4.15_A1_T4.js",
"test/built-ins/String/prototype/trim/15.5.4.20-1-3.js",
"test/built-ins/String/prototype/trim/15.5.4.20-1-4.js",
"test/built-ins/String/prototype/trim/15.5.4.20-1-5.js",
"test/built-ins/String/prototype/trim/15.5.4.20-1-7.js",
"test/built-ins/String/prototype/trim/15.5.4.20-2-1.js",
"test/built-ins/String/prototype/trim/15.5.4.20-2-10.js",
"test/built-ins/String/prototype/trim/15.5.4.20-2-15.js",
"test/built-ins/String/prototype/trim/15.5.4.20-2-16.js",
"test/built-ins/String/prototype/trim/15.5.4.20-2-18.js",
"test/built-ins/String/prototype/trim/15.5.4.20-2-19.js",
"test/built-ins/String/prototype/trim/15.5.4.20-2-27.js",
"test/built-ins/String/prototype/trim/15.5.4.20-2-47.js",
"test/built-ins/String/prototype/trim/15.5.4.20-2-50.js",
"test/built-ins/String/prototype/trim/15.5.4.20-2-6.js",
"test/language/expressions/addition/S11.6.1_A4_T1.js",
"test/language/expressions/addition/S11.6.1_A4_T2.js",
"test/language/expressions/addition/S11.6.1_A4_T3.js",
@@ -186,6 +200,20 @@
"test/built-ins/String/prototype/startsWith/searchstring-found-with-position.js": "b7bb997f562d672d919e0f4e9c994ff42a7f1b5bf2858f33c0bdc3b689ecde4f",
"test/built-ins/String/prototype/startsWith/searchstring-not-found-with-position.js": "333fe4703bef1a9d7e1819153216aeaefa48a97eb546bc680dbeb2cf0cd77af6",
"test/built-ins/String/prototype/substring/S15.5.4.15_A1_T4.js": "9bf9c2c32206d9b3c6af0ca156e8b87d7d3978153a803ffaaedaac7e9a04e5bb",
"test/built-ins/String/prototype/trim/15.5.4.20-1-3.js": "a4b02a4343ceaa0252801ddc4f14e4bea6d0865fa091e35d0208760cbfe75b3b",
"test/built-ins/String/prototype/trim/15.5.4.20-1-4.js": "74abb7d23585d6a8be9e8e4d76a7671208896d39fd6834782daa6fd77d1ac85f",
"test/built-ins/String/prototype/trim/15.5.4.20-1-5.js": "635953ee4b01a953143839adf17defb653a7eddab6d58b6aa125e2206dfeae52",
"test/built-ins/String/prototype/trim/15.5.4.20-1-7.js": "2733b093914828ae18279e52c76cf8dacd44a45f668b048b9bcdfa0c862f10a0",
"test/built-ins/String/prototype/trim/15.5.4.20-2-1.js": "f2434055c69276e47fa9dcd3ad011a9d94d184037dfda4eed672e69078a6bc80",
"test/built-ins/String/prototype/trim/15.5.4.20-2-10.js": "690f384f84a3be7feb199e634c8f7d16c95e0b5eb040fd5b2a5fb2b9c67076a8",
"test/built-ins/String/prototype/trim/15.5.4.20-2-15.js": "3d6c790488a9ce532695663818146f32cc311e49f972244f644ca4e9ee01e258",
"test/built-ins/String/prototype/trim/15.5.4.20-2-16.js": "fccc1fb180d4800816370483e85e1be542f095ce5acfa482ca2cf4e81322d6ac",
"test/built-ins/String/prototype/trim/15.5.4.20-2-18.js": "9119005f9642477fdcb512da167b0af7f7c0111eddae69d2e50660f65490d93d",
"test/built-ins/String/prototype/trim/15.5.4.20-2-19.js": "dca675e1f8269bc9f3b4e03b9522d1d6c7c93dd5bff1187d98b5a51ae3fdb214",
"test/built-ins/String/prototype/trim/15.5.4.20-2-27.js": "810993546eab06216c6d03a9590bf747281e5ffaaf739bfe238fee581340967c",
"test/built-ins/String/prototype/trim/15.5.4.20-2-47.js": "3e1a745af465177a96a29601e56299afc044060dbc2ac3061a59032be451cfde",
"test/built-ins/String/prototype/trim/15.5.4.20-2-50.js": "38b84997faa99e422408917eeac7f3575bd6064b429ea390a9d041fd77fce532",
"test/built-ins/String/prototype/trim/15.5.4.20-2-6.js": "3d5857f24117f65f07a70edd0d254dfa07f593c76effed94500847ccf8564c06",
"test/language/expressions/addition/S11.6.1_A4_T1.js": "12928eb319dcf0cc785e399580effb0b7495709296dce9c0a58e8408616a959f",
"test/language/expressions/addition/S11.6.1_A4_T2.js": "aee914241b607f56a98de04492f2d580e7bc69cca232af679d0c02b7025cc06b",
"test/language/expressions/addition/S11.6.1_A4_T3.js": "987b1c3cf66b6bbb2f6d8aaf6d1435cb1238b19132fa861a91aa2c35085d229d",
@@ -0,0 +1,9 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-1-3
description: String.prototype.trim works for primitive type boolean
---*/
assert.sameValue(String.prototype.trim.call(true), "true", 'String.prototype.trim.call(true)');
@@ -0,0 +1,9 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-1-4
description: String.prototype.trim works for primitive type number
---*/
assert.sameValue(String.prototype.trim.call(0), "0", 'String.prototype.trim.call(0)');
@@ -0,0 +1,9 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-1-5
description: String.prototype.trim works for an Object
---*/
assert.sameValue(String.prototype.trim.call({}), "[object Object]", 'String.prototype.trim.call({})');
@@ -0,0 +1,9 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-1-7
description: String.prototype.trim works for a primitive string
---*/
assert.sameValue(String.prototype.trim.call("abc"), "abc", 'String.prototype.trim.call("abc")');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-2-1
description: >
String.prototype.trim - argument 'this' is a boolean whose value
is false
---*/
assert.sameValue(String.prototype.trim.call(false), "false", 'String.prototype.trim.call(false)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-2-10
description: >
String.prototype.trim - argument 'this' is a number that converts
to a string (value is +Infinity)
---*/
assert.sameValue(String.prototype.trim.call(+Infinity), "Infinity", 'String.prototype.trim.call(+Infinity)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-2-15
description: >
String.prototype.trim - argument 'this' is a number that converts
to a string (value is 1e+20)
---*/
assert.sameValue(String.prototype.trim.call(1e+20), "100000000000000000000", 'String.prototype.trim.call(1e+20)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-2-16
description: >
String.prototype.trim - argument 'this' is a number that converts
to string (value is 1e+21)
---*/
assert.sameValue(String.prototype.trim.call(1e+21), "1e+21", 'String.prototype.trim.call(1e+21)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-2-18
description: >
String.prototype.trim - argument 'this' is a number that converts
to a string (value is 0.000001)
---*/
assert.sameValue(String.prototype.trim.call(0.000001), "0.000001", 'String.prototype.trim.call(0.000001)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-2-19
description: >
String.prototype.trim - argument argument 'this' is a number that
converts to a string (value is 0.0000001)
---*/
assert.sameValue(String.prototype.trim.call(0.0000001), "1e-7", 'String.prototype.trim.call(0.0000001)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-2-27
description: >
String.prototype.trim - argument 'this' is a number that converts
to a string (value is 123.1234567)
---*/
assert.sameValue(String.prototype.trim.call(123.1234567), "123.1234567", 'String.prototype.trim.call(123.1234567)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-2-47
description: >
String.prototype.trim - 'this' is a object Object that converts to
a string
---*/
assert.sameValue(String.prototype.trim.call({}), "[object Object]", 'String.prototype.trim.call({})');
@@ -0,0 +1,13 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-2-50
description: >
String.prototype.trim - 'this' is a Error Object that converts to
a string
---*/
var errObj = new Error("test");
assert.sameValue(String.prototype.trim.call(errObj), "Error: test", 'String.prototype.trim.call(errObj)');
@@ -0,0 +1,11 @@
// Copyright (c) 2012 Ecma International. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es5id: 15.5.4.20-2-6
description: >
String.prototype.trim - argument 'this' is a number that converts
to a string (value is -0)
---*/
assert.sameValue(String.prototype.trim.call(-0), "0", 'String.prototype.trim.call(-0)');