I'd like to be able to do something like the following:
js> (function(){ eval("var t = 5;"); })();
js> t
5
However it appears as if performing the following always throws an
exception (even though it is supposed to only throw exceptions under
strict mode - as far as I can tell):
js> eval.call(this, "var t=5;");
js: "<stdin>", line 22: uncaught JavaScript runtime exception:
EvalError: Function "eval" must be called directly, and not by way of
a function of another name.
As that is the only avenue for evaluating a piece of code within the
global context - are there any alternatives?
I found this previous thread but it requires adding additional classes
or methods to external Java files - is there a way to achieve the
above while remaining in "JavaScript-land"?
http://groups.google.com/group/mozilla.dev.tech.js-engine.rhino/browse_thread/thread/d35cf92bbccc5efe
--John
--John
> above while remaining in "JavaScript-land"?http://groups.google.com/group/mozilla.dev.tech.js-engine.rhino/brows...
>
> --John
(just got a copy of your Pro JavaScript Techniques today from Amazon
-- I'll be making its first 75 pages (we're into server side JS) a
required reading for all devs in my organization!)
Anyway... back to your question.
The ECMAScript standard makes these constraints on eval in section
15.1.2.1, notably
<quote>
I f val ue of t he eval pr opert y i s used i n any way ot her t han a
di rect cal l ( t hat i s, ot her t han by t he
expl i ci t use of i t s name as an Ident i f i er whi ch i s t he
MemberExpressi on i n a Cal l Expressi on), or i f t he
eval pr oper t y i s assi gned t o, an Eval Error except i on may be t
hr own.
</quote>
(Sorry for crappy copy/paste, that's how this is in the ECMA-published
PDF. Also, I'm sure you were aware of this already, I'm just putting
it here for the benefit of others reading this thread).
It does say an EvalError "may" be thrown", not "must be thrown" so
indeed we might allow a relaxation on it when in non-strict mode. Now,
Rhino actually does have three "FEATURE_STRICT_*" constants already:
FEATURE_STRICT_VARS, FEATURE_STRICT_EVAL, and FEATURE_STRICT_MODE.
FEATURE_STRICT_EVAL seems like it's what you're looking for, but
actually it has different semantics, its docs say:
* When the feature is on Rhino reports runtime errors if non-
string
* argument is passed to the eval function. When the feature is off
* eval simply return non-string argument as is without
performing any
* evaluation as required by ECMA 262.
I'm wondering if it would make sense to expand its semantics to also
lift 15.1.2.1 restrictions on Global.eval, or maybe we introduce yet
another feature constant (apparently, you can never have enough of
them :-) ) [*]
What do others think?
Attila.
[*] I'm also getting the feeling we'd want a mechanism where the Shell
class can read the required features for a script in a specially
formatted comment at the beginning of the file...
> (just got a copy of your Pro JavaScript Techniques today from Amazon -- I'll
> be making its first 75 pages (we're into server side JS) a required reading
> for all devs in my organization!)
Awesome! I just finished the first 1/3 of my new JavaScript book
(Secrets of the JavaScript Ninja) covering the advanced concepts of
JavaScript - it should be a good successor to my other book.
> It does say an EvalError "may" be thrown", not "must be thrown" so indeed we
> might allow a relaxation on it when in non-strict mode.
As far as I can tell Rhino is the only engine that makes it "must"
instead of "may" - which is quite confusing and frustrating.
> Now, Rhino actually
> does have three "FEATURE_STRICT_*" constants already: FEATURE_STRICT_VARS,
> FEATURE_STRICT_EVAL, and FEATURE_STRICT_MODE. FEATURE_STRICT_EVAL seems like
> it's what you're looking for, but actually it has different semantics, its
> docs say:
>
> * When the feature is on Rhino reports runtime errors if non-string
> * argument is passed to the eval function. When the feature is off
> * eval simply return non-string argument as is without performing any
> * evaluation as required by ECMA 262.
>
> I'm wondering if it would make sense to expand its semantics to also lift
> 15.1.2.1 restrictions on Global.eval, or maybe we introduce yet another
> feature constant (apparently, you can never have enough of them :-) ) [*]
>
> What do others think?
Anything that can be done would be appreciated - I'm kind of stuck
right now, since globally evaluating the code doesn't seem to be an
option. Hmm...
--John
I should explain why limitation on calling eval by another name is in
the spec. It turns out it is due to Rhino...
One of the biggest sources of efficiency in compiling JS to bytecode
on the JVM is to avoid creating an activation object. (An activation
object is an object representing the function level-scope; all
variables of the function are represented as properties of the
activation object.) Certain language features in JavaScript can't be
implemented without an activation object, and eval is one of them.
Consider the following:
js> function f() {
> eval("var x = 5;");
> return x;
> }
js> f()
5
In function f(), the variable x is only defined as a side effect of
the call to eval. We can't determine at compile time what variables
are present in a function, so we can't allocate JVM registers to them.
When compiling a function, Rhino looks for the constructs like eval
that require an activation object. If it doesn't see them, it won't
create an activation object. Great! Now we have the eval feature and
we have fast code when we don't use it. But consider the following:
js> function f() {
> g("var x = 5;");
> return x;
> }
js> var g = eval
When compiling f(), Rhino *has no way to know that g could be eval*.
So if we supported calling eval by a function of another name, Rhino
would never be able to avoid creating the activation object for any
function that calls another function. So Rhino handles calls to "eval"
differently than calls to other function names. We were building Rhino
at the time the ECMA spec was being finalized, and asked that this
special wording be put into the spec specifically so implementations
like Rhino could compile more efficiently.
So what do we do in this case? It seems like we could extend Rhino to
handle eval.call specially, much like eval is handled specially. That
seems like a reasonable approach; would it solve your problem?
--N
> I should explain why limitation on calling eval by another name is in
> the spec. It turns out it is due to Rhino...
Wow :-)
> One of the biggest sources of efficiency in compiling JS to bytecode
> on the JVM is to avoid creating an activation object.
Right, 'cause then you can just use method local variable slots for
them.
> (An activation
> object is an object representing the function level-scope; all
> variables of the function are represented as properties of the
> activation object.) Certain language features in JavaScript can't be
> implemented without an activation object, and eval is one of them.
Yep.
> But consider the following:
>
> js> function f() {
>> g("var x = 5;");
>> return x;
>> }
> js> var g = eval
>
> When compiling f(), Rhino *has no way to know that g could be eval*.
> So if we supported calling eval by a function of another name, Rhino
> would never be able to avoid creating the activation object for any
> function that calls another function. So Rhino handles calls to "eval"
> differently than calls to other function names. We were building Rhino
> at the time the ECMA spec was being finalized, and asked that this
> special wording be put into the spec specifically so implementations
> like Rhino could compile more efficiently.
Right place, right time, eh? I was beginning to wonder, actually, when
John said only Rhino implements this optional restriction :-)
> So what do we do in this case? It seems like we could extend Rhino to
> handle eval.call specially, much like eval is handled specially. That
> seems like a reasonable approach; would it solve your problem?
Don't want to speak in John's name, but I think it would. eval.call
and eval.apply would help him achieve what he wanted, and we could
still keep the optimization. It's aliasing the eval that'd make the
optimization impossible...
Attila.
We have a bug report (in the HtmlUnit project) where someone is
complaining about not being able to alias eval (because all of the
native browsers allow this). So for us, this partial fix wouldn't
work. Any chance we can allow eval aliasing in interpreted mode
(possibly subject to some FEATURE_XXX config)?
I tried this on a recent SpiderMonkey build:
js> eval.call(this, "var x = 3;")
js> x
3
js> var obj = {}
js> eval.call(obj, "var x = 7")
typein:6: EvalError: function eval must be called directly, and not by
way of a
function of another name
js> function f() {
eval.call(this, "var x = 8")
}
js> f()
js> x
8
So eval.call appears only to work on the global scope. Seems like an
odd distinction. This would work for John's case, but not for the more
general HtmlUnit case (although I get similar behavior in Firefox 3 as
you'd expect, so perhaps it's not an issue for HtmlUnit).
--N
I submitted a change to allow indirect calls to eval as long as the
execution scope is the global scope. A build is available at
ftp://ftp.mozilla.org/pub/mozilla.org/js/rhino1_7R2pre.zip.
--N
Thanks for making this happen.
As to the distinction of only making eval.call() work for the global object: This occurred because too many script and libraries were depending on this behavior - and removing it would cause a serious regression. However taking out the eval.call(object, ...) behavior was relatively safe.
I really appreciate this!
--John
--N
_______________________________________________
dev-tech-js-engine-rhino mailing list
dev-tech-js-...@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-tech-js-engine-rhino