You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reflect.construct(target, argumentsList, newTarget?) currently loses required Reflect semantics in lowering and runtime:
the HIR node stores only target and args, so newTarget is ignored,
a special-case fold turns known-class Reflect.construct(ClassName, [args...]) into direct new ClassName(...), also ignoring newTarget,
generic codegen calls js_proxy_construct(target, args, undefined), even for non-proxy targets,
argumentsList is treated like Perry's array helper input rather than Node's array-like CreateListFromArrayLike,
constructor/non-constructor validation and proxy construct trap result validation are missing.
Node behavior
Checked with Node v25.9.0:
functionTarget(a){this.a=a;}functionNewTarget(){}NewTarget.prototype.kind='custom';constobj=Reflect.construct(Target,['x'],NewTarget);obj.a;// 'x'Object.getPrototypeOf(obj)===NewTarget.prototype;// trueobj.kind;// 'custom'constarrayLikeArgs=Reflect.construct(function(a,b){this.sum=a+b;},{0: 2,1: 3,length: 2},);arrayLikeArgs.sum;// 5constproxy=newProxy(function(a){this.a=a;},{construct(target,args,newTarget){return{arg: args[0],isNewTarget: newTarget===proxy};},});Reflect.construct(proxy,['p']);// { arg: 'p', isNewTarget: true }constbadReturn=newProxy(function(){},{construct(){return1;},});Reflect.construct(badReturn,[]);// TypeError: 'construct' on proxy: trap returned non-object ('1')Reflect.construct(1,[]);// TypeError: 1 is not a constructorReflect.construct(function(){},null);// TypeError: CreateListFromArrayLike called on non-objectReflect.construct(function(){},1);// TypeError: CreateListFromArrayLike called on non-objectReflect.construct(function(){},[],1);// TypeError: 1 is not a constructor
Perry implementation
Lowering loses newTarget and has a direct-new shortcut:
crates/perry-runtime/src/proxy.rs:500 returns undefined when lookup(proxy_boxed) fails, so generic non-proxy Reflect.construct does not construct or throw through this path.
crates/perry-runtime/src/proxy.rs:518 invokes a construct trap when present.
crates/perry-runtime/src/proxy.rs:520 returns the trap result directly, without checking that the trap returned an object.
Summary
Reflect.construct(target, argumentsList, newTarget?)currently loses required Reflect semantics in lowering and runtime:targetandargs, sonewTargetis ignored,Reflect.construct(ClassName, [args...])into directnew ClassName(...), also ignoringnewTarget,js_proxy_construct(target, args, undefined), even for non-proxy targets,argumentsListis treated like Perry's array helper input rather than Node's array-likeCreateListFromArrayLike,Node behavior
Checked with Node v25.9.0:
Perry implementation
Lowering loses
newTargetand has a direct-new shortcut:crates/perry-hir/src/lower/expr_call/native_module.rs:969handlesReflect.construct.crates/perry-hir/src/lower/expr_call/native_module.rs:970special-casesReflect.construct(ClassName, [args...])and lowers it directly toExpr::New.crates/perry-hir/src/lower/expr_call/native_module.rs:995otherwise reads onlytargetandargs_arr.crates/perry-hir/src/lower/expr_call/native_module.rs:998returnsExpr::ReflectConstruct { target, args }; there is nonewTargetfield.Generic codegen hard-codes
undefinedfornewTarget:crates/perry-codegen/src/expr/proxy_reflect.rs:177lowersExpr::ReflectConstruct.crates/perry-codegen/src/expr/proxy_reflect.rs:180creates anundefinedvalue.crates/perry-codegen/src/expr/proxy_reflect.rs:183callsjs_proxy_construct(target, args, undefined).The runtime helper is proxy-specific and not a full Reflect.construct implementation:
crates/perry-runtime/src/proxy.rs:499implementsjs_proxy_construct(proxy_boxed, args_array, _new_target).crates/perry-runtime/src/proxy.rs:500returnsundefinedwhenlookup(proxy_boxed)fails, so generic non-proxy Reflect.construct does not construct or throw through this path.crates/perry-runtime/src/proxy.rs:518invokes aconstructtrap when present.crates/perry-runtime/src/proxy.rs:520returns the trap result directly, without checking that the trap returned an object.crates/perry-runtime/src/proxy.rs:525falls back tocall_with_args_array(target, args_array), which shares the array-only / no validation behavior noted in runtime: make Reflect.apply use thisArg, array-like args, and TypeErrors #2767 for Reflect.apply.Expected fix direction
Reflect.constructshould have a real Reflect path that:newTargetthrough HIR and codegen,newTargettotargetwhen omitted,targetandnewTargetare constructors,argumentsList,constructtraps with(target, args, newTarget),Duplicate search
Searched existing issues/PRs for:
Reflect.constructReflect.construct newTargetReflect.construct array-likeproxy construct trapconstruct trap returned non-objectReflect.construct OR proxy constructNo existing issue covered this Reflect.construct behavior.