Skip to content

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

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

    deferredDe-prioritized; not planned for active work (see tracking comment)enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions