Declarations

# Michael O'Brien (17 years ago)

Question about declarations:

Consider:

print(Shape.x) public class Shape { public static var x = 1; } print(Shape.x)

fun() function fun() { print("fun"); }

In the RI this prints:

undefined 1 fun

In ASC this prints: 1 1 fun

What happens in the RI is the class declaration seems to occur where
it is coded. It is not being hoisted to the top of the enclosing var block. Whereas the function
declaration is and thus can be called before its declaration.

Is this an RI bug or just a divergence from AS3?

Michael

# Michael O'Brien (17 years ago)

As a follow up, the following somewhat surprised me.

{ let b = 1 { function fun() { print("CALL: " + b) } } } fun()

gives:

ERROR EvalError: uncaught exception: ReferenceError: unresolved
lexical reference {multiname: [ns public '']::b [ns internal '']::b
[ns public 'ES4']::b } (near /Users/dherman/es4/src/builtins/ Error.es:86:55-86.55)

ie. the function is hoisted to the outer var block, but does not seem
to capture the scope chain of where it was declared and b is out of
scope.

Is this correct or an RI bug?

# Jon Zeppieri (17 years ago)

On Tue, Apr 15, 2008 at 1:56 PM, Michael O'Brien <mob at mbedthis.com> wrote:

As a follow up, the following somewhat surprised me.

{ let b = 1 { function fun() { print("CALL: " + b) } } } fun()

gives:

ERROR EvalError: uncaught exception: ReferenceError: unresolved lexical reference {multiname: [ns public '']::b [ns internal '']::b [ns public 'ES4']::b } (near /Users/dherman/es4/src/builtins/ Error.es:86:55-86.55)

ie. the function is hoisted to the outer var block, but does not seem to capture the scope chain of where it was declared and b is out of scope.

Is this correct or an RI bug?

RI bug.

bugs.ecmascript.org/ticket/243

# Michael O'Brien (17 years ago)

Jon, thanks. Any thoughts on the original post?

# Jeff Dyer (17 years ago)

This is an RI bug too (my fault). AS3 initializes programs in a single pass, before evaluating the top level code. The RI does both in one pass. I just filed a bug (bugs.ecmascript.org/ticket/382)

# Michael O'Brien (17 years ago)

Jeff,

Can I tease out a bit more detail. I'm not sure I agree with what the
correct answer should be.

Lars raised the issue with "var" a while back and I think, if my
memory serves me, the conclusion was that the variable should be hoisted to the top of the var block,
but the initialization statement should execute in place.

ie.

=> declaration for y hoisted to here

print(y) // Y should exist here but be null x = 1 var y = x // Assigment from x remains here

If the above is true for variables, should we not have the same rules
for functions and for classes? ie. shouldn't class static initialization code remain inline
where the class is defined? I'd recommend that we make var,function and class all behave the same
way.

Does any of the above change if enclosed in a package block? I presume
the class declaration should be hoisted to the top of the package block. But still, the
initialization remain inline where the class was declared.

Michael

# Lars Hansen (17 years ago)

-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Michael O'Brien Sent: 15. april 2008 15:06 To: Jeff Dyer Cc: es4-discuss Discuss; Jon Zeppieri Subject: Re: Declarations

Jeff,

Can I tease out a bit more detail. I'm not sure I agree with what the correct answer should be.

Lars raised the issue with "var" a while back and I think, if my memory serves me, the conclusion was that the variable should be hoisted to the top of the var block, but the initialization statement should execute in place.

ie.

=> declaration for y hoisted to here print(y) // Y should exist here but be null x = 1 var y = x // Assigment from x remains here

If the above is true for variables, should we not have the same rules
for functions and for classes? ie. shouldn't class static initialization code remain inline
where the class is defined? I'd recommend that we make var,function and class all behave the same
way.

They don't work the same way, in ES3.

IMO method init needs to be hoisted in classes like function init is in ES3. I think this is important for implementations with vtables.

Classes are compile-time entities (with types and namespaces) and may need to be privileged that way, in initialization terms.

More later.

# Michael O'Brien (17 years ago)

Here is a confusing example that bears it out.

var x = 1 print("x: " + x)

var y = Shape.z print("y: " + y)

class Shape { public static var z = x print("z: " + z) }

Currently es4 (and ejs) print: x: 1 y: undefined z: 1

ASC prints: z: undefined x: 1 y: undefined

NOTE: both values and order of execution are different.

I prefer the current es4 behavior because static class initialization
code can predictably use lexically scoped values. And it behaves like var declarations.

Michael O'Brien

# Waldemar Horwat (17 years ago)

Michael O'Brien wrote:

As a follow up, the following somewhat surprised me.

{ let b = 1 { function fun() { print("CALL: " + b) } } } fun()

gives:

ERROR EvalError: uncaught exception: ReferenceError: unresolved
lexical reference {multiname: [ns public '']::b [ns internal '']::b
[ns public 'ES4']::b } (near /Users/dherman/es4/src/builtins/ Error.es:86:55-86.55)

ie. the function is hoisted to the outer var block, but does not seem
to capture the scope chain of where it was declared and b is out of
scope.

Is this correct or an RI bug?

This is incorrect and an RI bug, but for a different reason: fun should be undefined in the outer scope, since it should not get hoisted out of the inner block, as we agreed at the last face-to-face meeting.

Waldemar
# liorean (17 years ago)

Michael O'Brien wrote:

As a follow up, the following somewhat surprised me.

{ let b = 1 { function fun() { print("CALL: " + b) } } } fun()

gives:

ERROR EvalError: uncaught exception: ReferenceError: unresolved lexical reference {multiname: [ns public '']::b [ns internal '']::b [ns public 'ES4']::b } (near /Users/dherman/es4/src/builtins/ Error.es:86:55-86.55)

ie. the function is hoisted to the outer var block, but does not seem to capture the scope chain of where it was declared and b is out of scope.

Is this correct or an RI bug?

On 16/04/2008, Waldemar Horwat <waldemar at google.com> wrote:

This is incorrect and an RI bug, but for a different reason: fun should be undefined in the outer scope, since it should not get hoisted out of the inner block, as we agreed at the last face-to-face meeting.

While ES3 has that case as a SyntaxError, implementations of ES3 allow it. SpiderMonkey (tested in JS1.7 mode) is the only browser hosted implementation that I could find which does not hoist the function declaration and initialisation to the top of the current function scope at compile time. What SpiderMonkey does is that it hoists the function to the current function scope at run time though. Which means that:

alert(foo);
{
    function foo(){}
    alert(foo);
}

is a reference error at the first line, while:

{
    function foo(){}
    alert(foo);
}
alert(foo);

is not an error.

So the choice to not hoist a function declaration to function scope at least at run time seems like it would break code written for any of our current browser hosted implementations. I would be much more comfortable with hoisting the declaration at compile time and doing the initialisation at run time to preserve the relation with control flow. (As I discussed in [1].)

Isn't it better to do what SpiderMonkey does for regular function declarations in statements, as a lowest-common-denominator for how widely the function declaration is accessible, and only make a qualified block scoped function:

{
    let function(){}
}

to be bound in block scope?

Also another point to make about this: You are effectively adding a function-declaration-within-statement production, something ES3 left undefined. Browser implementations vary in how these constructs are treated with relation to control flow and compile time vs. run time initialisation (see [2] for a breakdown of behaviour in various engines). If you allow it at all, you should be explicit about what the rules for declaration hoisting and initialisation timing really are - current implementations are not interoperable.

I personally prefer preserving control flow versus preserving use-before-definition mechanics (this is more useful of the two absolutes, and doing something like the inconsistency of JavaScriptCore is madness), and preserving the scoping to function level since all browser hosted implementations currently guarantee use even after the block has exited.

[1] uri:https://mail.mozilla.org/pipermail/es4-discuss/2007-March/000483.html [2] uri:https://mail.mozilla.org/pipermail/es4-discuss/2007-March/000495.html