
Is eval evil? Just-in-time compiling - mwanago
https://wanago.io/2018/11/19/how-does-eval-work-and-how-is-it-evil-javascript-eval/
======
matthewaveryusa
Here's a really good use of eval:

[https://jsfiddle.net/matthewaveryusa/e6EKV/6/](https://jsfiddle.net/matthewaveryusa/e6EKV/6/)

I could have done this without an eval if I decided to write an interpreter.
Instead I whitelisted a subset of javascript and evaled it -- I believe the
code is simpler with the eval than if I were to write a parser + interpreter.

I challenge anyone to escape my whitelisting :)

~~~
robocat
Generally I always use Function() instead of eval, because Function doesn't
capture the current scope (and also it won't interfere with JIT optimisation,
and avoids possible memory bloat of variables in closure if you keep
references to the functions).

~~~
sbr464
I saw a trick where you just do:

    
    
      const eval2 = eval
    
      eval2(...)
    

That put’s it back in global scope, even when assigned within a closure with
access to a local variable. The local variable will be undefined, rather than
it’s actual value, even when called next to the regular eval(). Odd, I know.

Here’s a quote:

"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."
ECMAScript 5 standard,

------
chrisseaton
Contrary to what the article says, there is no reason whatsoever that code in
an eval, even if it uses the calling environment, cannot be optimised.

I work on a Ruby implementation that optimises through eval just fine.

~~~
zamalek
Exactly. It's just like SQL: different queries end up as different entries in
the query cache; although SQL is beneficially fuzzy in this case, if only the
constants differ you should get a cache hit.

The "safe" and fast way to use eval for JIT is to get it to return a function,
with all user inputs as parameters (like like the safe and fast way to do SQL
is with parameters):

    
    
        var f = eval("(function (a, b) { return a + b })");
        f("a", "b");

~~~
wefarrell
Why not just do:

var f = new Function('a', 'b', 'return a + b')

~~~
zamalek
I had no idea that was possible. The concept still applies though.

------
pizlonator
Eval doesn't _have_ to be bad for JITing. Someone oughta write a JS JIT that
interoperates with the parser to look for patterns like:

    
    
        eval("some code { " + stuff + "} more code")
    

and runs the parser enough to observe that `stuff` fills in a scope about
which it is possible to reason even without knowing about what `stuff` is. In
some cases, this will get really lucky:

    
    
        eval("some code blah blah object." + field + " more things")
    

and you can imagine this being turned into object[field] anytime `field` in
this expression is an identifier.

Basically, in this world, you'd be able to distill out the long-lived parts of
an eval string, and the JIT(s) can focus on optimizing that.

~~~
kccqzy
What if stuff is `} //`?

Basically you are trying to say we want to discover some kind of semantic
structure from just a prefix of the textual form of the code. We can do a
partial parse and see what's possible right after the string literal; and if
there's only one possibility, we JIT it. But without full code, we can't
really do much JIT at all.

~~~
pizlonator
You just speculate that this doesn’t happen, and bail if it does.

------
ronilan
Last winter I wrote BlockLike.js, an educational library that allows one to
use Block programming concepts in JavaScript. This required supporting pacing
and waiting while still allowing the user to write simple (and valid)
JavaScript.

When I started I had this voice in my head whispering again and again "eval is
evil, eval is evil". Then I just got over it...

I ended up with one-pass, event triggered, construct-and-run (essentially
"eval") solution. The user writes simple code that is event triggered. The
code is converted into a string, rewritten in realtime and "enhanced". An
Asynchronous Function Constructor then converts it back to a function and it
gets invoked by the triggering event.

It works great and though there is, obviously, a performance penalty for the
realtime rewriting, it is never the limiting performance factor for
implementations (multiple DOM element animation is).

Examples:

[https://www.blocklike.org/example/12-advanced/recursion_tree...](https://www.blocklike.org/example/12-advanced/recursion_tree.html)

[https://www.blocklike.org/example/12-advanced/color_bubbles_...](https://www.blocklike.org/example/12-advanced/color_bubbles_sprite_factory.html)

[https://www.blocklike.org/example/14-games/avoid.html](https://www.blocklike.org/example/14-games/avoid.html)

Repo:

[https://github.com/ronilan/BlockLike](https://github.com/ronilan/BlockLike)

------
nine_k
I suppose the biggest problem with `eval` is not getting uncontrolled user
data into it.

A kind of filter shown in the matthewaveryusa's reply may be secure, but it's
pretty limiting, and may happen to be too limiting for a particular purpose.

It would be great if the AST of the expression to be evaluated could be
checked. That is, `eval` could be two pieces: "compile" and "execute" phases;
this is how it works in Python.

Another tool could be a type system that marks user input as "tainted" and
helps you track where you're using user input in any capacity. But languages
featuring `eval` usually lack such a type system.

------
sifoobar
I'm still holding off adding eval() to Snigl [0]. Not because of JIT clashes,
since it is a straight interpreter; but because of the can of worms it opens.
The thing is that I have to compile code in some kind of context, and
depending on what the code says, it may or may not be safe to throw the
compiled code away once done. Which means that I'd have to deal with a growing
mountain of compiled code or complicate the design to support a feature that's
mostly the wrong answer.

[https://gitlab.com/sifoo/snigl](https://gitlab.com/sifoo/snigl)

------
tombert
This is tangential, but I think that the way that the way Lisp does eval with
its macros is incredibly elegant. There was a post on HN a few weeks ago [1]
about doing symbolic derivatives in Lisp via quotations and eval, and while
that was probably old news to most of the HN audience, it was downright
_magic_ to me, and the use of eval seemed appropriate.

[1]
[https://news.ycombinator.com/item?id=18343652](https://news.ycombinator.com/item?id=18343652)

------
calibas
Just my personal experience, but I used eval way more often when I didn't
really know what I was doing. As I've learned Javascript, I've realized that
in the vast majority of cases it's simply not needed.

If you're an experienced programmer who understands the risks of eval and
there's no other good options, then by all means use it, but for other cases
"eval is evil".

~~~
Twisol
Eval is a lot like goto in that way. In the vast majority of cases, there's a
better way to do things -- but in a very, very small number of scenarios, it's
the right tool for the job.

------
tobr
Can eval ever increase performance? I’m thinking that for some types of
abstractions you can end up with highly nested, indirect calls, and if you can
instead generate new code as needed, maybe it could speed things up? Are there
any mainstream examples of this?

~~~
Veedrac
Python uses/used eval for namedtuple at least in part for performance.

~~~
xapata
Used. Raymond revised the implementation.

------
rogual
A while back I used eval in a matrix multiplication routine, to make it accept
matrices of arbitrary size and generate code to multiply them:

[https://github.com/rogual/mxd/blob/master/Matrix.js#L50](https://github.com/rogual/mxd/blob/master/Matrix.js#L50)

It benchmarked pretty well back when I wrote it, though I haven't checked
against current JS engines to see if it's still a performance win.

------
w0mbat
Eval has been evil since medireview times.

[https://revealingerrors.com/medireview](https://revealingerrors.com/medireview)

------
jancsika
The answer to the question (consistent with all headline questions) is "no."

The article even mentions JSON.parse which itself relied on eval.

Does JSON.parse still rely on eval under the hood?

Regardless... a wildly popular method used to propagate the most popular data
format relied on eval. At least for a time. Nothing evil happened as a result.

------
cremp
The thing is, it isn't just javascript eval; any eval is bad.

In another life I did PHP shells. PHP (which has a weird relationship with
JIT) had so many ways to use eval, without actually typing eval. One of the
weird ways that was only removed in PHP 7 was the preg_replace function, with
the _e_ flag; which evals any php expression, outside the scope of the
function.

My point is that if there is eval used in code; it either is meant for
temporary use (because devs are lazy,) or the code needs to be done
differently to support the desired outcome.

~~~
ArchTypical
> any eval is bad

Saying eval is just confusing the concept with the implementation. There's no
evidence that metaprogramming is "bad" unless you are ready to define "bad".
Might as well say pointers are "bad" in the same vein.

