What is joined object in ECMA-262

J

johnhax

Hi, all:

I'm sorry because I don't know whether here is the right place to
discuss it. We are talking about closure in
javascript, and we have some questions about ECMA-262 3rd edition:
What is joined function object(ecma-262 13.1)?

The spec have given a example:

function A() {
function B(x) { return x*x }
return B
}

var b1 = A();
var b2 = A();

Spec says: b1 and b2 can be joined, and implementation may make b1 and
b2 the same object because [[scope]] of them have no difference.

Two call of A() will produce a function from the same FunctionBody, so
they are equated and the result function objects can be joined, am I
right? What about this code:

function C(x) {
function D() { return x*x }
return D
}

var d1 = C(1);
var d2 = C(2);

Are these two call of A() also are equated uses of the same source?
And can d1 anb d2 be joined in this case even their [[scope]] is
different?

If they can be joined, and as the definition of joined object(13.1.2),
d1 === d2 should return true. That's so strange because d1 === d2, but
d1() != d2()

I've read spec several times, but still confused.

In fact, I tested many js engine and no implementation join b1 and b2
(and which will make b1 === b2 = true) as spec (except dmdscript, but
it not support closure at all!). I know joining them is optional, but
if there is any implementation which join them, then it and current
impl may get diff result from the same code. Example:

function A() {
return function () {
return arguments.callee.test;
}

}

var x = A();
var y = A();
x.test = 1;
y.test = 2;
print(x == y);
print(x());
print(y());

x and y can be joined, and their [[scope]] are equal, so impl can make
x and y the same object, so this code will print true, 2 and 2. But
current implements choose to not join them, print false, 1 and 2.

I believe optimization should never change the semantics of a program,
is joined object a mistake of the spec so that no impl support this
bug feature. Or, maybe I misunderstand the spec.

BTW, I know in spidermonkey b1.__proto__ == b2.__proto__, it some like
joined function, but they are not real joined object because b1 != b2.
 
R

Richard Cornford

Hi, all:

I'm sorry because I don't know whether here is the right
place to discuss it.

Which of the 4 groups you cross-posted to is 'here'?

netscape.public.mozilla.jseng is not the correct place to post anything
any more. The - jseng - group have moved to a Mozilla foundation hosted
news server and (as far as I know) the Netscape hosted Mozilla groups
are all dead now. (I have removed netscape.public.mozilla.jseng from the
groups list on this post, but I cannot remember the Mozilla hosted
equivalent's name and my ISP doesn't cover it on their new servers (they
don't react to change at all quickly, possibly do not see it happening
at all) so I cannot look it up right now).
We are talking about closure in
javascript, and we have some questions about ECMA-262 3rd
edition: What is joined function object(ecma-262 13.1)?

The spec have given a example:

function A() {
function B(x) { return x*x }
return B

}

var b1 = A();
var b2 = A();

Spec says: b1 and b2 can be joined, and implementation may
make b1 and b2 the same object because [[scope]] of them
have no difference.

The [[Scope]] of them does differ, b1 and b2 have distinct Variable
objects at the top of the scope chain assigned to their [[Scope]]
property. That does not matter in this context because their [[Scope]]
properties could not be observed to differ, as that Variable object is
not used here.
Two call of A() will produce a function from the same
FunctionBody, so they are equated and the result function
objects can be joined, am I right? What about this code:

That would be my interpretation, even taking into account the final
paragraph of section 13.2, which reads:-

| In practice it's likely to be productive to join two Function objects
| only in the cases where an implementation can prove that the
| differences between their [[Scope]] properties are not observable,
| so one object can be reused. By following this policy, an
| implementation will only encounter the vacuous case of an object being
| joined with itself.

function C(x) {
function D() { return x*x }
return D

}

var d1 = C(1);
var d2 = C(2);

Are these two call of A() also are equated uses of the same
source?
Yes.

And can d1 anb d2 be joined in this case even their [[scope]]
is different?
Yes.

If they can be joined, and as the definition of joined object(13.1.2),
d1 === d2 should return true. That's so strange because d1 === d2, but
d1() != d2()

Yes, but that final paragraph suggests that implementations are not
expected to join functions in cases where the [[Scope]] properties can
be observed to differ, such as in this case.

And it is important to note that I cannot find a single implementation
where either of the above sets of example code results in functions that
do equate (either by type-converting or strict comparison(not that the
type of comparison operator makes any difference when the items being
compared are objects)).
I've read spec several times, but still confused.

In fact, I tested many js engine and no implementation join b1
and b2 (and which will make b1 === b2 = true) as spec (except
dmdscript, but it not support closure at all!).

So dmdscript is probably not a 3rd edition implementation.
I know joining them is optional,

It is, and it appears to be the exception rather than the rule.
but if there is any implementation which join them, then
it and current impl may get diff result from the same code.
Example:

function A() {
return function () {
return arguments.callee.test;
}

}

var x = A();
var y = A();
x.test = 1;
y.test = 2;
print(x == y);
print(x());
print(y());

x and y can be joined, and their [[scope]] are equal, so impl
can make x and y the same object, so this code will print true,
2 and 2. But current implements choose to not join them, print
false, 1 and 2.

As I said, their [[Scope]] properties do differ, but not in an
observable way (so they could be joined).

But as to seeing this happen, try this code in a recent JavaScript(tm)
release (so Firefox/Mozilla/ect.):-

var f = [];
var c, len = 2
for(c = 0;c < len;++c){
f[c] = (function(){
return;
});
}
f[0].x = 'test x'

document.write('(f[0] == f[1]) = '+(f[0] == f[1])+'<br>')
document.write('(f[0].x = '+f[0].x+' f[1].x = '+f[1].x+'<br>')

- where the results are:-

(f[0] == f[1]) = true
(f[0].x = test x f[1].x = test x

- while on IE and Opera the resutls are:-

(f[0] == f[1]) = false
(f[0].x = test x f[1].x = undefined

- And that leaves JavaScript(tm) apparently joining the function objects
resulting form the evaluation of a function ejxpresison in the global
execution context, or it is re-using the same function object (those two
possibilities cannot be discriminated between).
I believe optimization should never change the semantics of a program,

If a program is written pre-supposing that an optional aspect of a
specification will always be a particular way then the fault is in the
program (or the programmer). You look at the options, and their
consequences, and you write for the worst-case (which is joining). That
is unless you are writing for specific, known implementation(s) rather
than to the standard.
is joined object a mistake of the spec

I think it is a mistake and adds nothing useful to javascirpt. If it had
been jumped upon by implementers to create real optimizations then we
would have become used to the idea. But in reality you have to try
pretty hard to find any evidence of joining happening at all, so it
could very easily be dropped from the spec, and would result in more
(formally) predictable handling of function objects.

In the event that ECMA 262 4th edition would be an attempt to sort out
the ambiguities and faults in the 3rd Ed. (rather than the foolish foray
into attempts to imitate Java that it appears to be) then all talk of
'joining' could be removed.
so that no impl support this bug feature.

Well, JavaScritp(tm) does appear to support joining (or re-using
function objects, which cannot be distinguished from joining), but it
does so under such specific conditions that it does not matter much that
it is doing so. The rules for JavaScript(tm) are not even that the
[[Scope]] properties must be indistinguishable because in this version
of the previous code:-

var g = [];
var c, len = 2
with({}){
for(c = 0;c < len;++c){
g[c] = (function(b){
return;
});
}
}
g[0].x = 'test x'

document.write('(g[0] == g[1]) = '+(g[0] == g[1])+'<br>')
document.write('(g[0].x = '+g[0].x+' g[1].x = '+g[1].x+'<br>')

- JavaScript(tm) does not now join the two functions, yet they both
have _exactly_ the same [[Scope]] values (which then must be
indistinguishable).

This makes it look like the JavaScript(tm) criteria is that it will join
functions only if their [[Scope]] properties consist of chains that are
precisely one item long (only include the global object). (Obviously
this is pure speculation, you have to ask the people responsible if you
want to know for sure)

That is a reasonable position to take, given joining at all. One of the
main problems with optimisation in javascript implementations is that
the code is interrupted and complied each time it is run. It takes time
to look for where to apply optimisations, and that time comes out of the
user's experience (slow compiling equates to a non-responsive web-site
as it is loading). It could be very time consuming to look at functions
in enough detail to only join them when the differences in their
[[Scope]] properties were unobservable, so it would be easier not to
bother and just create a new function object for each. On the other
hand, JavaScript(tm)'s observing that the scope chain is only one item
long, and only then considering joining, should be very quick and cheep
to detect.
Or, maybe I misunderstand the spec.

BTW, I know in spidermonkey

And Rhino.
b1.__proto__ == b2.__proto__, it some like
joined function,
but they are not real joined object because b1 != b2.

The __proto__ properties expose the specified internal [[Prototype]]
property.
All function objects share the same [[Prototype]] property.

Richard.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top