Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions crates/perry-hir/src/lower/expr_member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1866,12 +1866,26 @@ fn lower_member_inner(ctx: &mut LoweringContext, member: &ast::MemberExpr) -> Re
&& outer_static_member
.map(|member| matches!(member, "now" | "parse" | "UTC"))
.unwrap_or(false);
let outer_is_reified_number_static_value = !member_is_call_callee
&& property == "Number"
&& outer_static_member
.map(|member| matches!(member, "isInteger" | "isSafeInteger"))
.unwrap_or(false);
let outer_is_reified_string_static_value = !member_is_call_callee
&& property == "String"
&& outer_static_member
.map(|member| {
matches!(member, "fromCharCode" | "fromCodePoint" | "raw")
})
.unwrap_or(false);
if !outer_is_prototype_or_proto
&& !receiver_is_namespace_value
&& !outer_is_websocket_static
&& !outer_is_reified_object_static_value
&& !outer_is_reified_builtin_static_value
&& !outer_is_reified_date_static_value
&& !outer_is_reified_number_static_value
&& !outer_is_reified_string_static_value
&& !receiver_is_detached_console_read
{
object_expr = Expr::GlobalGet(0);
Expand Down
65 changes: 65 additions & 0 deletions crates/perry-runtime/src/object/global_this.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3049,6 +3049,45 @@ extern "C" fn number_is_integer_thunk(
crate::builtins::js_number_is_integer(value)
}

extern "C" fn string_from_char_code_thunk(
_closure: *const crate::closure::ClosureHeader,
rest: f64,
) -> f64 {
let ptr = crate::string::js_string_from_char_code_array(rest);
crate::value::js_nanbox_string(ptr as i64)
}

extern "C" fn string_from_code_point_thunk(
_closure: *const crate::closure::ClosureHeader,
rest: f64,
) -> f64 {
let values = global_this_rest_array_values(rest);
if values.is_empty() {
let empty = crate::string::js_string_from_bytes(b"".as_ptr(), 0);
return crate::value::js_nanbox_string(empty as i64);
}

let mut result = crate::string::js_string_from_code_point(values[0]);
for value in values.iter().skip(1) {
let left = crate::value::js_nanbox_string(result as i64);
let part = crate::string::js_string_from_code_point(*value);
let right = crate::value::js_nanbox_string(part as i64);
let joined = crate::string::js_string_concat_box(left, right);
result =
crate::value::js_get_string_pointer_unified(joined) as *mut crate::string::StringHeader;
}
crate::value::js_nanbox_string(result as i64)
}

extern "C" fn string_raw_thunk(
_closure: *const crate::closure::ClosureHeader,
call_site: f64,
substitutions: f64,
) -> f64 {
let ptr = crate::string::js_string_raw(call_site, substitutions);
crate::value::js_nanbox_string(ptr as i64)
}

/// Shared impl for `BigInt.asIntN`/`asUintN` (both the ctor-static thunks and
/// the `("bigint", ...)` native-module dispatch). Coerces `bits` via ToIndex
/// (RangeError on negative/non-integer), brand-checks `value` is a BigInt
Expand Down Expand Up @@ -3610,6 +3649,32 @@ fn install_builtin_constructor_statics(name: &str, ctor: *mut crate::closure::Cl
false,
);
}
"String" => {
install_constructor_static_with_call_arity(
ctor,
"fromCharCode",
string_from_char_code_thunk as *const u8,
1,
0,
true,
);
install_constructor_static_with_call_arity(
ctor,
"fromCodePoint",
string_from_code_point_thunk as *const u8,
1,
0,
true,
);
install_constructor_static_with_call_arity(
ctor,
"raw",
string_raw_thunk as *const u8,
1,
1,
true,
);
}
"BigInt" => {
// BigInt.asIntN(bits, bigint) / asUintN(bits, bigint) — spec length 2.
install_constructor_static(
Expand Down
42 changes: 42 additions & 0 deletions test-parity/node-suite/globals/number-string-static-values.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
function showStatic(label: string, fn: any) {
const nameDesc = Object.getOwnPropertyDescriptor(fn, "name");
const lengthDesc = Object.getOwnPropertyDescriptor(fn, "length");
console.log(label, "typeof:", typeof fn);
console.log(label, "name/length:", fn?.name, fn?.length);
console.log(label, "name desc:", JSON.stringify(nameDesc));
console.log(label, "length desc:", JSON.stringify(lengthDesc));
}

const numberIsInteger = Number.isInteger;
const numberIsSafeInteger = Number.isSafeInteger;
const stringFromCharCode = String.fromCharCode;
const stringFromCodePoint = String.fromCodePoint;
const stringRaw = String.raw;

showStatic("Number.isInteger", numberIsInteger);
showStatic("Number.isSafeInteger", numberIsSafeInteger);
showStatic("String.fromCharCode", stringFromCharCode);
showStatic("String.fromCodePoint", stringFromCodePoint);
showStatic("String.raw", stringRaw);

console.log(
"Direct calls:",
Number.isInteger(4),
String.fromCharCode(88, 89),
String.raw({ raw: ["x", "y"] }, "-"),
);
console.log(
"Number calls:",
numberIsInteger(3),
numberIsInteger(3.5),
numberIsSafeInteger(9007199254740991),
numberIsSafeInteger(9007199254740992),
);
console.log(
"String calls:",
stringFromCharCode(65, 66, 67),
stringFromCodePoint(65, 128512),
stringRaw({ raw: ["a", "b", "c"] }, 1, 2),
);
console.log("Number.parseInt identity:", Number.parseInt === parseInt);
console.log("Number.isNaN meta:", Number.isNaN.name, Number.isNaN.length);