Skip to content

Test262 #7: o.bar(foo()) — argument evaluation order vs callability check (~30 tests) #106

@nickna

Description

@nickna

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.jso.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:

  1. The spec rule is concrete and small; the fix doesn't need broad type-checker changes.
  2. 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:

  1. Type checker (subset of #100): allow o.bar(...) on o: {} to type-check (as any returning any), so the runtime even gets to fire.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions