R
Rasmus Kromann-Larsen
The With Conundrum
I'm currently writing a master thesis on (preparations for) static
analysis of JavaScript, and after investigating the with statement, it
only even more evident to me that the with statement is indeed bad.
My initial thoughts on with were based on:
http://yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/
I built some examples to investigate - and later try to "eliminate"
the with statement from the code I'm analysing. All examples besides
the first one were evaluated in Rhino.
-----------------------------------------------------
Example 1 (pseudo). Readability.
with(x) {
a = b
}
Could be evaluated as:
e.a = e.b
e.a = b
a = e.b
a = b
(And this only gets worse if you add more variables - exponentially
worse actually
-----------------------------------------------------
Example 2 (rhino): Assignments.
with(x) {
var z = 42; // Or just z = 42 (without var)
}
Where does z end up? If x.z already exists, it will be overwritten, if
not, it will be added to the global scope.
-----------------------------------------------------
Example 3 (rhino): Assignments continued (functions).
--- Example 3.1 ---
var obj = { x:42 };
with(obj) {
function x() { print(x); }
}
(x || obj.x)(); // Hack to make it call existing function.
--- Example 3.2 ---
var obj = { x:42 };
with(obj) {
var x = function() { print(x); }
}
(x || obj.x)(); // Hack to make it call existing function.
-------------------
Example 3.1 will create a global function x with obj first in it's
scope chain, thus printing: "42"
Example 3.2 will create the method obj.x (since it overwrites 42),
with obj first in it's scope chain, thus printing: "function x()
{ print(x); }"
-----------------------------------------------------
Since I'm interested in doing static analysis on JavaScript code --
and I don't want to deal with all these abnormalities, I decided to
try and normalize my way out of it. That is, take any given code
containing a with statement and automatically output valid JavaScript
with the exact same semantics, but without the with statement.
My first wild attempt was to try and introduce temporary variables, so
that:
-----------------------------------------------------
with(x) {
a = b;
}
-----------------------------------------------------
would become 3 blocks:
-----------------------------------------------------
// With start. (save all variables)
var evaled_x = x;
var temp_a = (evaled_x.a || a);
var temp_b = (evaled_x.b || b);
// With block. (do replaced evaluation)
temp_a = temp_b;
// With end. (restore variables to newly calculated)
if(evaled_x.a)
evaled_x.a = temp_a;
else
a = temp_a;
if(evaled_x.b)
evaled_x.b = temp_b;
else
b = temp_b;
-----------------------------------------------------
But as far as I can figure, this won't do. I reached the conclusion
that you'd have to introduce temporary variables for each statement,
since each statement could potentially either directly or by side-
effects change the presence of properties on the evaluated object
(delete or assignments).
Also, more factors might even apply that I havn't even begun to
consider yet.
So, I was wondering, does anyone have a better solution (or more
insights) in the glorious quest of trying to eliminate a with
statement?
- Rasmus.
I'm currently writing a master thesis on (preparations for) static
analysis of JavaScript, and after investigating the with statement, it
only even more evident to me that the with statement is indeed bad.
My initial thoughts on with were based on:
http://yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/
I built some examples to investigate - and later try to "eliminate"
the with statement from the code I'm analysing. All examples besides
the first one were evaluated in Rhino.
-----------------------------------------------------
Example 1 (pseudo). Readability.
with(x) {
a = b
}
Could be evaluated as:
e.a = e.b
e.a = b
a = e.b
a = b
(And this only gets worse if you add more variables - exponentially
worse actually
-----------------------------------------------------
Example 2 (rhino): Assignments.
with(x) {
var z = 42; // Or just z = 42 (without var)
}
Where does z end up? If x.z already exists, it will be overwritten, if
not, it will be added to the global scope.
-----------------------------------------------------
Example 3 (rhino): Assignments continued (functions).
--- Example 3.1 ---
var obj = { x:42 };
with(obj) {
function x() { print(x); }
}
(x || obj.x)(); // Hack to make it call existing function.
--- Example 3.2 ---
var obj = { x:42 };
with(obj) {
var x = function() { print(x); }
}
(x || obj.x)(); // Hack to make it call existing function.
-------------------
Example 3.1 will create a global function x with obj first in it's
scope chain, thus printing: "42"
Example 3.2 will create the method obj.x (since it overwrites 42),
with obj first in it's scope chain, thus printing: "function x()
{ print(x); }"
-----------------------------------------------------
Since I'm interested in doing static analysis on JavaScript code --
and I don't want to deal with all these abnormalities, I decided to
try and normalize my way out of it. That is, take any given code
containing a with statement and automatically output valid JavaScript
with the exact same semantics, but without the with statement.
My first wild attempt was to try and introduce temporary variables, so
that:
-----------------------------------------------------
with(x) {
a = b;
}
-----------------------------------------------------
would become 3 blocks:
-----------------------------------------------------
// With start. (save all variables)
var evaled_x = x;
var temp_a = (evaled_x.a || a);
var temp_b = (evaled_x.b || b);
// With block. (do replaced evaluation)
temp_a = temp_b;
// With end. (restore variables to newly calculated)
if(evaled_x.a)
evaled_x.a = temp_a;
else
a = temp_a;
if(evaled_x.b)
evaled_x.b = temp_b;
else
b = temp_b;
-----------------------------------------------------
But as far as I can figure, this won't do. I reached the conclusion
that you'd have to introduce temporary variables for each statement,
since each statement could potentially either directly or by side-
effects change the presence of properties on the evaluated object
(delete or assignments).
Also, more factors might even apply that I havn't even begun to
consider yet.
So, I was wondering, does anyone have a better solution (or more
insights) in the glorious quest of trying to eliminate a with
statement?
- Rasmus.