From: Richard Cornford on
Garrett Smith wrote:
> Richard Cornford wrote:
>> Garrett Smith wrote:
>> <snip>
>>> IIRC jquery uses window["eval"] in the source code. looking...
>>>
>>> hat rack:
>>> | // Get the JavaScript object, if JSON is used.
>>> | if ( type == "json" )
>>> | data = window["eval"]("(" + data + ")");
>>>
>>> Not sure why they chose that approach over:
>>> data = eval("(" + data + ")");
>>>
>>> That approach uses indirect eval. The calling context's scope is
>>> used, so is just as unsafe in that regard. Only difference is older
>>> implementations' thisArg is different.
>>>
>>> It indirect eval, for reasons I'm failing to comprehend.
>>>
>>> I believe I mentioned this very issue about a year or so ago on
>>> the jquery newsgroup.
>>
>> I have a recollection of discussions around the use of - eval -
>> relating the 'compressors' (YUI compressor, as I recall) that
>> modify (shorten) Identifier names. Much like the way such a
>> compressor cannot act on code within a - with - statement
>> because there is no means of determining whether an unqualified
>> Identifier in that context is a name of a property of the object
>> added to the scope chain or a reference to a
>> variable/function/parameter from a containing scope, such
>> compressors cannot act on unqualified Identifiers in the
>> lexically containing scopes when - eval - is used, because it
>> has no way of determining whether any string that is - eval-ed
>> will attempt to use them to refer to items from those containing
>> scopes.
>>
>> My impression was JQuery's authors were then going to do whatever
>> was necessary to fool the compressor in question into not noticing
>> the - eval - use (and making it unrecognisably indirect should
>> achieve that). Obviously the sane alternative, of using - new
>> Function - to create a minimal scope chain function that wrapped
>> the (now not indirect) eval call, did not occur to them.
>
> I see.
>
> That could be avoided by either Function constructor or a separate,
> globally-accssible method:
>
> jQuery.evalString = function(s) {
> return eval(s);
> };
>
> That function would not be compressed, but it would be so short
> that it wouldn't matter.

Recall that as it stands JQurey is wrapped in the inline execution of a
function expression, which provides a 'private' 'global' scope for just
the JQuery code. If your function was defined within that function
expression its obvious - eval - use would prevent any modification to
the Identifiers employed in the 'private' 'global' scope facilitated by
the function expression. That would be a bad thing from the point of
view of code minimisation. If your function were defined outside of the
function expression then the whole becomes less of a discreet single
unit. Opinions on the latter are likely to vary, but I can see
justification in arguing against doing that.

The advantage in the use of - new Function - to create the function is
that doing so will create a function with the minimum scope chain, and
can achieve that even from within the containing function expression. A
'smart' code compressor should be able to see that the lexically
containing scopes (except the global scope, which could never be
modified anyway because that would cost the external API (if you munge
the Identifier - JQuery - the whole library is dead)) could not be
influenced by the code within such a function.

> The eval call, as used there, has potential to modify variables
> in the calling context's scope chain. Using new Function or
> a call to eval in global context and scope avoids that. ES5
> would "fix" that:
>
> | 10.4.2: In Edition 5, indirect calls to the eval function use
> | the global environment as both the variable environment and
> | lexical environment for the eval code. In Edition 3, the
> | variable and lexical environments of the caller of an indirect
> | eval was used as the environments for the eval code.
>
> That would be a significant change, but one that would be of
> use to jQuery here.

It would be of use to anyone who wanted to dynamically import script
source text. It is a pity that much of the dynamic importing of script
source text that goes on at present is already largely inadvisable.

> Indirect eval does not throw EvalError in ES5.
>
> | 15.1.2.1. Implementations are no longer permitted to restrict the
> use
> | of eval in ways that are not a direct call. In addition, any
> | invocation of eval that is not a direct call uses the global
> | environment as its variable environment rather than the caller�s
> | variable environment.
>
> The jQuery code escapes any problem with indirect eval throwing
> EvalError.

Well, in few years when ES5 implementations have replaced ES3
implementations. In the meanwhile what it is doing is non-standard by
ES3, and one of the factors arbitrarily restricting its supported
browser set (as it can only be believed to work where it has been
observed to work).

Richard.

From: David Mark on
On Nov 16, 3:38 pm, Garrett Smith <dhtmlkitc...(a)gmail.com> wrote:
> Stefan Weiss wrote:
> > On 16/11/09 20:23, Garrett Smith wrote:
> >> Richard Cornford wrote:
>
> [...]
>
> >> jQuery.evalString = function(s) {
> >>    return eval(s);
> >> };
>
> >> That function would not be compressed, but it would be so short that it
> >> wouldn't matter.
>
> >> The eval call, as used there, has potential to modify variables in the
> >> calling context's scope chain. Using new Function or a call to eval
> >> in global context and scope avoids that.
>
> > Your evalString example cannot be used to create new global variables:
>
> No, obviously not; "How do I make a global variable from eval" was not a
> proposed problem nor was there any such solution discussed.
>
> If creating properties of the global object through a string is desired,
> then it can be done in the string, as:
> eval( 'window.x = "s"' );

But that's not really what that does (or what it can be rationally
expected to do). If you really want to do this in lieu of standard
declarations (would require script injection instead of eval), use
something like this:-

var GLOBAL = this;
eval('GLOBAL.x = "s"');
From: Garrett Smith on
Asen Bozhilov wrote:
> Garrett Smith wrote:
>
>>>> jQuery.evalString = function(s) {
>>>> return eval(s);
>>> };
>
> But `this' still associated with execution context in which been
> executed `eval'. With [[Construct]] and [[Call]] method of object who
> refer `Function' you create `object' who internal [[scope]] refer
> Global Object independent of execution context in who's been called
> Function.[[Construct]] or Function.[[Call]]. After that if you call
> this `object' without providing `this' value from caller, `this' in
> created execution context will be referrer to Global Object. I like
> much more that approach with Function construct and call method.
>

If the this value were undefined, then the global object would be used.

If that is undesirable, then it could be avoided with:

var f = Function("alert(this)");
f.call(f);

>> If creating properties of the global object through a string is desired,
>> then it can be done in the string, as:
>> eval( 'window.x = "s"' );
>
> What about this in Spider Monkey:
>
> function evalCode(code)
> {
> return eval.call(null, code);
> }
>
> evalCode('var a = 10;');
> window.alert('a' in this); //true
> window.alert(a); //10
>

Results:
elert "false"
elert "10"

In ES5-compliant engine, *any* indirect eval would have that result,
using the global execution context.

In non-strict mode, eval such as this:

data = window["eval"]("(" + data + ")");

- create properties of the global object, following ES5 s. 10.4.1.1.

This is explained in ES5 s. 10.4.2, 10.4.2.1, and 15.1.2.1.
--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/
From: Garrett Smith on
Richard Cornford wrote:
> Garrett Smith wrote:
>> Richard Cornford wrote:
>>> Garrett Smith wrote:
>>> <snip>
>>>> IIRC jquery uses window["eval"] in the source code. looking...
>>>>
[snip]
>>>
>>> My impression was JQuery's authors were then going to do whatever
>>> was necessary to fool the compressor in question into not noticing
>>> the - eval - use (and making it unrecognisably indirect should
>>> achieve that). Obviously the sane alternative, of using - new
>>> Function - to create a minimal scope chain function that wrapped
>>> the (now not indirect) eval call, did not occur to them.
>>
>> I see.
>>
>> That could be avoided by either Function constructor or a separate,
>> globally-accssible method:
>>
>> jQuery.evalString = function(s) {
>> return eval(s);
>> };
>>
>> That function would not be compressed, but it would be so short
>> that it wouldn't matter.
>
> Recall that as it stands JQurey is wrapped in the inline execution of a
> function expression, which provides a 'private' 'global' scope for just
> the JQuery code. If your function was defined within that function
> expression its obvious - eval - use would prevent any modification to
> the Identifiers employed in the 'private' 'global' scope facilitated by
> the function expression. That would be a bad thing from the point of
> view of code minimisation. If your function were defined outside of the
> function expression then the whole becomes less of a discreet single
> unit. Opinions on the latter are likely to vary, but I can see
> justification in arguing against doing that.
>

It would be separate, globally accessible. It is less "discreet" in that
there is a "pubic" |evalString| property whose only justification is
based on the need to avoid scope chain modification.

> The advantage in the use of - new Function - to create the function is
> that doing so will create a function with the minimum scope chain, and
> can achieve that even from within the containing function expression. A
> 'smart' code compressor should be able to see that the lexically
> containing scopes (except the global scope, which could never be
> modified anyway because that would cost the external API (if you munge
> the Identifier - JQuery - the whole library is dead)) could not be
> influenced by the code within such a function.
>

"The" advantage? I count three.
1) minimum scope chain
2) called from same context
3) minification works hack-free


>> The eval call, as used there, has potential to modify variables
>> in the calling context's scope chain. Using new Function or
>> a call to eval in global context and scope avoids that. ES5
>> would "fix" that:
>>
>> | 10.4.2: In Edition 5, indirect calls to the eval function use
>> | the global environment as both the variable environment and
>> | lexical environment for the eval code. In Edition 3, the
>> | variable and lexical environments of the caller of an indirect
>> | eval was used as the environments for the eval code.
>>
>> That would be a significant change, but one that would be of
>> use to jQuery here.
>
> It would be of use to anyone who wanted to dynamically import script
> source text. It is a pity that much of the dynamic importing of script
> source text that goes on at present is already largely inadvisable.

Importing script source text is possible by assigning a property to a
globally-accessible identifier. Given an |items| object:

"if(typeof items !== 'undefined') {"
+ "items.myNewItem = { name : 'a' };"
+"}";

What is inadvisable?
--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/
From: kangax on
Garrett Smith wrote:

[...]

> Perhaps future revisions of ECMAScript will include stronger wording for
> Host objects, so that implementations will have at least some
> conformance guidelines, though it seems too late for WebIDL, which
> also takes liberty to implement special behavior on Host object,
> defining a very large number of additional internal properties.

ES5 (currently draft) puts few restrictions on host objects that weren't
there in ES3. This makes for a somewhat saner state of affairs. `typeof`
should now return more consistent results: "function" for any callable
object � native *or host*; and anything but "undefined", boolean",
"number" or "string" for host object that's not callable (doesn't
implement [[Call]]).

[[Class]] values of host objects are now more or less under control. To
cite 8.6.2:

"The value of the [[Class]] internal property of a host object may be
any String value except one of "Arguments", "Array", "Boolean", "Date",
"Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and
"String"."

--
kangax