@@ -131,6 +131,7 @@ impl<'a> PeepholeOptimizations {
131131
132132 pub fn substitute_call_expression ( expr : & mut CallExpression < ' a > , ctx : & mut Ctx < ' a , ' _ > ) {
133133 Self :: try_flatten_arguments ( & mut expr. arguments , ctx) ;
134+ Self :: try_rewrite_object_callee_indirect_call ( expr, ctx) ;
134135 }
135136
136137 pub fn substitute_new_expression ( expr : & mut NewExpression < ' a > , ctx : & mut Ctx < ' a , ' _ > ) {
@@ -885,6 +886,49 @@ impl<'a> PeepholeOptimizations {
885886 ctx. state . changed = true ;
886887 }
887888
889+ /// `Object(expr)(args)` -> `(0, expr)(args)`
890+ ///
891+ /// If `expr` is `null` or `undefined`, both before and after throws an TypeError ("something is not a function").
892+ /// It is because `Object(expr)` returns `{}`.
893+ ///
894+ /// If `expr` is other primitive values, both before and after throws an TypeError ("something is not a function").
895+ /// It is because `Object(expr)` returns the Object wrapped values (e.g. `new Boolean()`).
896+ ///
897+ /// If `expr` is an object / function, `Object(expr)` returns `expr` as-is.
898+ /// Note that we need to wrap `expr` as `(0, expr)` so that the `this` value is preserved.
899+ ///
900+ /// <https://tc39.es/ecma262/2025/multipage/fundamental-objects.html#sec-object-value>
901+ fn try_rewrite_object_callee_indirect_call (
902+ expr : & mut CallExpression < ' a > ,
903+ ctx : & mut Ctx < ' a , ' _ > ,
904+ ) {
905+ let Expression :: CallExpression ( inner_call) = & mut expr. callee else { return } ;
906+ if inner_call. optional || inner_call. arguments . len ( ) != 1 {
907+ return ;
908+ }
909+ let Expression :: Identifier ( callee) = & inner_call. callee else {
910+ return ;
911+ } ;
912+ if callee. name != "Object" || !ctx. is_global_reference ( callee) {
913+ return ;
914+ }
915+
916+ let span = inner_call. span ;
917+ let Some ( arg_expr) = inner_call. arguments [ 0 ] . as_expression_mut ( ) else {
918+ return ;
919+ } ;
920+
921+ let new_callee = ctx. ast . expression_sequence (
922+ span,
923+ ctx. ast . vec_from_array ( [
924+ ctx. ast . expression_numeric_literal ( span, 0.0 , None , NumberBase :: Decimal ) ,
925+ arg_expr. take_in ( ctx. ast ) ,
926+ ] ) ,
927+ ) ;
928+ expr. callee = new_callee;
929+ ctx. state . changed = true ;
930+ }
931+
888932 /// Remove name from function expressions if it is not used.
889933 ///
890934 /// e.g. `var a = function f() {}` -> `var a = function () {}`
@@ -1832,4 +1876,14 @@ mod test {
18321876 test ( "var {y: z, 'z': y} = x" , "var {y: z, z: y} = x" ) ;
18331877 test ( "var {y: y, 'z': z} = x" , "var {y, z} = x" ) ;
18341878 }
1879+
1880+ #[ test]
1881+ fn test_object_callee_indirect_call ( ) {
1882+ test ( "Object(f)(1,2)" , "f(1, 2)" ) ;
1883+ test ( "(Object(g))(a)" , "g(a)" ) ;
1884+ test ( "Object(a.b)(x)" , "(0, a.b)(x)" ) ;
1885+ test_same ( "Object?.(f)(1)" ) ;
1886+ test_same ( "function Object(x){return x} Object(f)(1)" ) ;
1887+ test_same ( "Object(...a)(1)" ) ;
1888+ }
18351889}
0 commit comments