[[Call]] pass through result completion to callee

# Yusuke Suzuki (13 years ago)

Hello all,

Because of 8.3.19.1 step 17, [[Call]] returns result completion even if result.type isn't return. As the result, [[Call]] may return NormalCompletion(empty), is it expected behavior? I think we should change empty to undefined.

For example,

function test() { var i; } var value = test(); // value is empty...

# Allen Wirfs-Brock (13 years ago)

(Note that this is probably the sort of issue that would be better to report to bugs.ecmascript.org)

Yes, that looks like an issue. It appears to me that the best fix is in the evaluation semantics of FunctionBody. It should take care of Return completions and also make sure that normal completions yield undefined and not empty.

Letting Return completions escape from functions was needed when we were considering supporting block lambdas but isn't need now.

# Yusuke Suzuki (13 years ago)

Thanks.

I've just filed it to bugs.ecmascript.org, ecmascript#1119

It appears to me that the best fix is in the evaluation semantics of

FunctionBody.

Looks nice to me too.

# Brendan Eich (13 years ago)

In ES1-5 this was done in 13.2.1 [[Call]] on a function object. See also 13.2.2 [[Construct]], which layers on [[Call]], so [[Call]] is the lowest layer single algorithm that has to deal with completions.

Pushing down into FunctionBody is ok if there are no other uses of FunctionBody that do not want this common processing, i.e., where evaluation does not funnel through [[Call]]. Are there no other such uses?

ES1-5 all handle the cases via something like

  1. If result.type is throw then throw result.value.
  2. If result.type is return then return result.value.
  3. Otherwise result.type must be normal. Return undefined.

in 13.2.1 [[Call]].

Conserving this code under ES6's relocated [[Call]] internal method seems best, all else equal.

# Allen Wirfs-Brock (13 years ago)

On Dec 4, 2012, at 11:32 AM, Brendan Eich wrote:

In ES1-5 this was done in 13.2.1 [[Call]] on a function object. See also 13.2.2 [[Construct]], which layers on [[Call]], so [[Call]] is the lowest layer single algorithm that has to deal with completions.

Pushing down into FunctionBody is ok if there are no other uses of FunctionBody that do not want this common processing, i.e., where evaluation does not funnel through [[Call]]. Are there no other such uses?

ES1-5 all handle the cases via something like

  1. If result.type is throw then throw result.value.
  2. If result.type is return then return result.value.
  3. Otherwise result.type must be normal. Return undefined.

in 13.2.1 [[Call]].

Conserving this code under ES6's relocated [[Call]] internal method seems best, all else equal.

I don't really think so. First we not have multiple implementations of [[Call]] (always could have, but it's now more obvious) and trap handlers implementing [[Call]] don't have direct access to Completion Records so they can't exactly emulate ordinary function [[Call]] does.

Also, [[Call]] is mostly about defining with the invocation mechanism, not about the statement level semantics of a function body. Determination of a function's result (including handling of internal returns and throws) seems like something that should be part of the specified semantics of a function body rather than of the [[Call]] MOP entry point.

As to the other point, nobody evaluates FunctionBody that doesn't want this semantics so it still is at one place and IMO the best place.

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

On Dec 4, 2012, at 11:32 AM, Brendan Eich wrote:

In ES1-5 this was done in 13.2.1 [[Call]] on a function object. See also 13.2.2 [[Construct]], which layers on [[Call]], so [[Call]] is the lowest layer single algorithm that has to deal with completions.

Pushing down into FunctionBody is ok if there are no other uses of FunctionBody that do not want this common processing, i.e., where evaluation does not funnel through [[Call]]. Are there no other such uses?

ES1-5 all handle the cases via something like

  1. If result.type is throw then throw result.value.
  2. If result.type is return then return result.value.
  3. Otherwise result.type must be normal. Return undefined.

in 13.2.1 [[Call]].

Conserving this code under ES6's relocated [[Call]] internal method seems best, all else equal.

I don't really think so. First we not have multiple implementations of [[Call]] (always could have, but it's now more obvious) and trap handlers implementing [[Call]] don't have direct access to Completion Records so they can't exactly emulate ordinary function [[Call]] does.

This is a good reason!

Also, [[Call]] is mostly about defining with the invocation mechanism, not about the statement level semantics of a function body. Determination of a function's result (including handling of internal returns and throws) seems like something that should be part of the specified semantics of a function body rather than of the [[Call]] MOP entry point.

It should be one of the two, but for ES1-5 it was [[Call]], which was not a MOP entry point. That change makes the good reason you gave above.

As to the other point, nobody evaluates FunctionBody that doesn't want this semantics so it still is at one place and IMO the best place.

Good to know, thanks.