What is spec's purpose to Return some kind of Completion Record in the algorithms?

The spec in Implicit Normal Completion says,

In algorithms within abstract operations which are declared to return a Completion Record, within the Evaluation syntax-directed operation, and within all built-in functions, the returned value is first passed to NormalCompletion, and the result is used instead.

Why the algorithms implicitly return some kind Completion Record when the implementations(browsers or nodejs) don't implement it?

I'm I missing some part of the spec that says like that the implementations must return the [[value]] field of the Completion Record returned by a spec algorithm of JavaScript built-in functions/methods?

Completion Records are how the spec models exception handling (as well as other kinds of abrupt exits like return and break). These are unwrapped before passing to code (with the ubiquitous ? macro, generally), so code will never see an actual Completion Record, but they're necessary internally to model control flow.

1 Like

Thanks for your answer. But I'm still not clear. Could you please tell me, where do the spec makes it clear that implementations must use the unwraped return value. I understand that the ? shorthand is used to unwrap values inside algorithm steps like macros in C. But Implicit Normal Completion always ensures the return value is a Completion Record. My confusion starts from there. For example in Array.prototype.includes:

  • How does spec ensures that returns like "Return false"(step 11) of JavaScript built-in functions should return [[value]] field of the Completion Record?

I've noticed in a prior spec version there is a clear explanation as below:

Any reference to a Completion Record value that is in a context that does not explicitly require a complete Completion Record value is equivalent to an explicit reference to the [[value]] field of the Completion Record value unless the Completion Record is an abrupt completion.

But in latest spec I don't see any such clarification. Is it said in a different way somewhere else in the spec? Or they decided to leave the decision to the implementers?

I think the important thing to understand is that, when you call a built-in function, the algorithm that appears in the spec doesn't directly supply the value that your code receives.

For example, for the assignment x = isNaN(1), here's what happens (I think):

  • In the algorithm for isNaN, step 3 specifies return *false*.
  • That value is passed to NormalCompletion, according to the Implicit Normal Completion rules.
  • The resulting Completion Record is bound to _result_ in step 10 of the [[Call]] internal method for built-in functions.
  • A couple steps later, [[Call]] returns that value...
  • ... to the Call abstract operation, which immediately returns it ...
  • ... to the EvaluateCall abstract operation, which immediately returns it ...
  • ... to the Evaluation semantics for CallExpression, which immediately returns it ...
  • ... to step 1.d.i in the Evaluation semantics for AssignmentExpression, where it's bound to _rref_.
  • On the next step, that's passed to GetValue (as the _V_ parameter).
  • In step 1 of GetValue, _V_ is passed to ReturnIfAbrupt, which is sort-of a macro that 'unwraps' the Completion Record, and binds its [[Value]] (*false*) back to _V_.
  • On step 2, since _V_ is a Boolean, not a Reference Record, it's returned ...
  • ... back to the Evaluation semantics, where it gets bound to _rval_.
  • And on the next step (1.e), _rval_ is passed to the PutValue operation, which has the effect of setting your x variable to false.

So, roughly speaking, it's the call to ReturnIfAbrupt in GetValue that tells implementations to return the [[Value]] field of the Completion Record returned by the spec algorithm of a built-in function.

2 Likes

Thank you very much for the detailed explanation. I was trying to understand it the whole yesterday. After reading your answer, finally I understand what's happening! Hurray!!!

2 Likes