Motivation
Surfaced during the #69 expressions/call folder rollout (commit 7c1542a). ECMA-262 §13.3.6.1 mandates that call arguments are evaluated before the callee's callability is checked. SharpTS's type checker rejects the test setup at type-check, so we never get to observe the spec-mandated runtime behavior.
The spec rule under test
EvaluateCall(func, ref, arguments, tailPosition):
1. Let thisValue be ...
2. Let argList be ? ArgumentListEvaluation(arguments). ← side-effects fire here
3. If Type(func) is not Object, throw a TypeError exception.
4. If IsCallable(func) is false, throw a TypeError exception.
Step 2 fires before step 3/4. So in o.bar(foo()) where o.bar is undefined, foo() must still execute (and its side effects must be observable) before the TypeError propagates.
Sample failing tests
test/language/expressions/call/11.2.3-3_1.js:
var fooCalled = false;
function foo(){ fooCalled = true; }
var o = { };
assert.throws(TypeError, function() {
o.bar( foo() ); // TS rejects o.bar; fooCalled never set
});
assert.sameValue(fooCalled, true, 'fooCalled');
test/language/expressions/call/11.2.3-3_2.js — identical with FunctionExpression for foo.
test/language/expressions/call/11.2.3-3_3.js — o.bar.gar(foo()); expectation flips: foo should NOT be called because o.bar is undefined and the error fires before arg eval (note this is testing the opposite edge — error during MemberExpression resolution).
Impact
~30 tests in language/expressions/call. This subset of the broader #100 cluster is worth calling out separately because:
- The spec rule is concrete and small; the fix doesn't need broad type-checker changes.
- Even after #100, the runtime must implement evaluate-args-then-check-callable in the right order. This is the runtime side of the same issue.
Suggested approach
Two parts:
- Type checker (subset of #100): allow
o.bar(...) on o: {} to type-check (as any returning any), so the runtime even gets to fire.
- Runtime / IL emitter: audit
Interpreter.EvaluateCall and ILEmitter.Calls.cs — the order must be:
- Evaluate the callee reference.
- Evaluate every argument.
- Then check
IsCallable(callee) and throw TypeError if not.
Likely the interp already does this correctly (the test bucket is TypeCheckError, not Fail). Verify, then focus on (1).
Acceptance
- The ~30 argument-evaluation-order tests in
expressions/call flip from TypeCheckError to Pass.
- After-the-fix audit:
(undefined)(sideEffect()) and (null).x(sideEffect()) produce the spec-correct order of operations in both modes.
Related
Part of #69. Subset of #100; breaking out so the runtime side gets independently verified.
Motivation
Surfaced during the #69 expressions/call folder rollout (commit 7c1542a). ECMA-262 §13.3.6.1 mandates that call arguments are evaluated before the callee's callability is checked. SharpTS's type checker rejects the test setup at type-check, so we never get to observe the spec-mandated runtime behavior.
The spec rule under test
Step 2 fires before step 3/4. So in
o.bar(foo())whereo.baris undefined,foo()must still execute (and its side effects must be observable) before theTypeErrorpropagates.Sample failing tests
test/language/expressions/call/11.2.3-3_1.js:test/language/expressions/call/11.2.3-3_2.js— identical with FunctionExpression forfoo.test/language/expressions/call/11.2.3-3_3.js—o.bar.gar(foo()); expectation flips:fooshould NOT be called becauseo.baris undefined and the error fires before arg eval (note this is testing the opposite edge — error during MemberExpression resolution).Impact
~30 tests in
language/expressions/call. This subset of the broader #100 cluster is worth calling out separately because:Suggested approach
Two parts:
o.bar(...)ono: {}to type-check (asanyreturningany), so the runtime even gets to fire.Interpreter.EvaluateCallandILEmitter.Calls.cs— the order must be:IsCallable(callee)and throwTypeErrorif not.Likely the interp already does this correctly (the test bucket is
TypeCheckError, notFail). Verify, then focus on (1).Acceptance
expressions/callflip fromTypeCheckErrortoPass.(undefined)(sideEffect())and(null).x(sideEffect())produce the spec-correct order of operations in both modes.Related
Part of #69. Subset of #100; breaking out so the runtime side gets independently verified.