Summary
In compiled mode, calling a class instance or static method (or a constructor) directly with fewer arguments than parameters pads a missing trailing optional, no-default parameter (x?: T) with CLR null instead of the undefined sentinel. Observable through typeof, === undefined, === null. The interpreter is correct, and free functions are correct.
This is the direct-call analogue of #640 (which fixed the value-call / $TSFunction.Invoke path) and #703 (class methods invoked as values). The direct-call path for class methods was not covered.
Repro
class C {
m(x?: any): string { return typeof x; }
static s(x?: any): string { return typeof x; }
}
console.log(new C().m()); // interp: "undefined" compiled: "object" (BUG)
console.log(C.s()); // interp: "undefined" compiled: "object" (BUG)
typeof null === "object", so the missing optional reads as null. Free functions are fine (function h(x?: any) { return typeof x; } h() → "undefined" in both modes). Class expressions are also fine (they use all-object param slots).
Cause / fix sketch
The direct method-call emitter pads missing trailing args with the slot's CLR default (null for object slots) rather than the $Undefined singleton. For object-typed params it should pad with the sentinel (cf. the private-method call padding added in #705, ILEmitter.Properties.Private.cs EmitPrivateCallArgsPadded, which pads with $Undefined). Mirror that for the instance/static method and constructor direct-call sites. Note this is distinct from default-parameter firing — these params have no default; the value should simply be undefined.
Discovered
While implementing #703/#705.
Summary
In compiled mode, calling a class instance or static method (or a constructor) directly with fewer arguments than parameters pads a missing trailing optional, no-default parameter (
x?: T) with CLRnullinstead of theundefinedsentinel. Observable throughtypeof,=== undefined,=== null. The interpreter is correct, and free functions are correct.This is the direct-call analogue of #640 (which fixed the value-call /
$TSFunction.Invokepath) and #703 (class methods invoked as values). The direct-call path for class methods was not covered.Repro
typeof null === "object", so the missing optional reads asnull. Free functions are fine (function h(x?: any) { return typeof x; } h()→"undefined"in both modes). Class expressions are also fine (they use all-objectparam slots).Cause / fix sketch
The direct method-call emitter pads missing trailing args with the slot's CLR default (null for object slots) rather than the
$Undefinedsingleton. For object-typed params it should pad with the sentinel (cf. the private-method call padding added in #705,ILEmitter.Properties.Private.csEmitPrivateCallArgsPadded, which pads with$Undefined). Mirror that for the instance/static method and constructor direct-call sites. Note this is distinct from default-parameter firing — these params have no default; the value should simply beundefined.Discovered
While implementing #703/#705.