22

As a byproduct of code optimization done by modern browsers, while debugging, you can't "see" all variables which "factually" are in scope. This is well known and has been addressed in a previous question here on SO. This feature, while most certainly useful in production is annoying me a lot during development, it slows me down (that should be obvious.)

Now my question is, is there any way to turn off this behavior? Can I edit some configuration file, or is there a browser plugin, or maybe there is a "special build version for developers" of the browser executable? I love typing my code into the console right away when I'm writing new code, so this is really bugging me.

visualSummaryIffalseConsoleLog

UPDATE / EDIT

Here is a partial solution, credit to Paul1365972.

You have to start the chrome browser from the command line, with special options, like so:

  1. Close Chrome completely
  2. Run Chrome from console with "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe" --js-flags="--allow-natives-syntax" that's for windows other OS similar.
  3. Open developer console and execute "%GetHeapUsage()". If you properly started Chrome with the option, a number will be logged to the console, otherwise you'll get a syntax error.

With this command line flag, you can 'talk to the V8' engine with commands starting with %, which are syntax errors in plain JavaScript. A list to available V8 commands of this kind was given in Paul's answer.

There is %NeverOptimizeFunction() on that list, which is something which looked like the thing I'd just have to call and be done with it. Unfortunately, that function does not do what I hoped for, as demonstrated in next screenshot.

lorem still not defined

(((The other link from Paul's answer (v8-natives node module) is of no importance to us here in this context. All it does is it wraps one-liners around the "%" function calls so the code doesn't crash browsers which are not v8.)))

(((I remember a time when this worked (when this optimization wasn't invented/implemented yet). I don't know how long ago. Ten years? 15 years? Something like that. What was the last Chrome version (if any) and what was the last firefox version (more sure here that it exists) where you could do? It won't get you the bounty, but it will get you an upvote, if you happen to know and post it as an answer.)))

THE SOLUTION

THANK YOU PETR SRNICEK

hacky fix

NEW QUESTION

While Petr's solution helps a lot, it is not perfect. This question is getting too long, so I posted a new question on how Petr's solution can be improved. (I could of course edit this question here, but that would feel "unhistorical", if you know what I mean.)

mathheadinclouds
  • 3,507
  • 2
  • 27
  • 37
  • unintended consequences, chapter tenthousandone. this optimization has a negative effect on my coding style. I find myself using the old fashioned for loop (instead of .map, .forEach, .reduce) more than I otherwise would, just so I avoid running into this issue. – mathheadinclouds Nov 18 '19 at 19:19
  • The `v8-natives` library just wraps the important % calls in code in a simple library that should be `noops` in a browser or node which was not started in the special --allow-natives-syntax flag.. – Nathanael Nov 27 '19 at 00:26
  • I ran some tests, the 'bodyOnLoad' function isn't optimized anyways; so using the internals commands to try and force it to de-optimize doesn't do anything. – Nathanael Nov 27 '19 at 04:04
  • @Nathanael: The important call is `%NeverOptimizeFunction(foo)` I just called it also for bodyOnload, "just because", thinking "well, it won't hurt". The issue is that `foo` is NOT deoptimized in the way I was hoping for. Variable `lorem` is invisible. Let's say I want to write the some code which is to go into function foo. Instead of typing it into my text editor, I type it into the dev console (while the debugger is sitting at foo), see if it does what I want, and then copy/paste it from console to my text editor. That is how I love to work. And can't. Because of the optimization. Get it? – mathheadinclouds Nov 27 '19 at 05:15
  • I'm not sure it is an optimization, so much as a developer tool assumption. I actually killed the optimization on both the outer and inner function. :) Didn't change the access. I think the issue is actually caused by scoping; the dev tools attempt to grab the values from the current scope and because inner() is a new scope; the dev tools lose access to the outer scope. I wonder if their is a way to make the dev tools access the outer scope... – Nathanael Nov 27 '19 at 06:01
  • @Nathanael, Sure, you can just go to the 'call stack' panel, on the right hand side of the dev tools, and go one step up there. Then I have access to the variables one step up. But, first, still no access to the vars from 2 steps up, and, second, I just lost acces to the local variables in function foo. Which I wanted to write. Thus, the call stack panel is not helpful in what I'm trying to achieve. – mathheadinclouds Nov 27 '19 at 06:13
  • @Nathanael; well, it's both an optimization and a dev tool weakness. if the source code of `foo` were different from as it actually is, and `lorem` were used there, then it would work; everybody who knows JavaScript knows that. But the code is as it is, and in order to run it, the computer has to do something. And what it/v8 does is 'compile' foo in such a way that, so to speak "`foo` has no idea about `lorem`". Which makes the code run faster which is good. But the purpose of dev tools is helping assist in finding out what would happen if the code were different. It's hard workwritingdevtools – mathheadinclouds Nov 27 '19 at 17:36
  • 1
    I spent several ours experimenting with various `--js-flags` (including several [TurboFan](https://v8.dev/docs/turbofan)-related ones) as well as with several V8 native commands before Paul1365972 posted his answer but I was not able to achieve the desired behaviour. I believe that this approach might be a dead end. It might be worthwhile to add a `[v8]` tag to this question. Somebody with a deep understanding of the inner workings of V8 might be able to clarify whether this is the way to go or perhaps point you in the correct direction. – Petr Srníček Nov 27 '19 at 17:39
  • thx petr, I added the tag, makes perfect sense. – mathheadinclouds Nov 27 '19 at 17:55
  • @mathheadinclouds You can get access to all variables by wrapping the debugger statement in an eval like this: `eval("debugger;");`. This hacky solution adds another anonymous function to the call stack though and it is obviously of no use for breakpoints that are set manually in DevTools. – Petr Srníček Nov 27 '19 at 17:55
  • THAT'S IT. eval("debugger"). Please post as answer. I'll give you the 200 points bounty. I don't care (much) that I can't set it as a breakpoint in the dev tools, but have to change the source code. As long as I can type my code in the console, see if it works and paste it in my texteditor, I'm happy. – mathheadinclouds Nov 27 '19 at 18:05
  • @PetrSrníček: to be more precise: I'll accept your answer, and give you the bounty in 5 days. Just in case maybe someone has an even better idea, which I doubt. – mathheadinclouds Nov 27 '19 at 18:13
  • @PetrSrníček: I already added a suitable screen shot to the question you just need to put 'eval("debugger")' into the answer. – mathheadinclouds Nov 27 '19 at 18:16

3 Answers3

11

You can get access to all variables by wrapping the debugger statement in an eval like this: eval("debugger;");. This hacky solution adds another anonymous function to the call stack though and it is obviously of no use for breakpoints that are set manually in DevTools.

This does not seem to be a very good solution, but since it is the only one that achieves the intended behaviour so far, I am posting it as an answer.

Petr Srníček
  • 2,296
  • 10
  • 22
  • it kind of surprises me that this works. You know, I tried typing eval("lorem"), and that gave the same "lorem is not defined" error. It doesn't make much sense to me that typing eval("lorem") on the console (while on debugger statement in function foo) should do anything different from what eval("debugger") is doing - expect, print "ipsum" to the console. But they are very different. Strange. – mathheadinclouds Nov 27 '19 at 19:23
  • That is an interesting work around. It is a bit hacky, as you can't step out of the debugger statement (you just have to manually switch to the source file) otherwise you lose the entire stack and return to the function that called the eval; leaving you the reduced stack missing the other context. – Nathanael Nov 28 '19 at 21:13
  • one can modify this trick thus: instead of eval("debugger"), put just eval("") - but many of them, distributed all over the code, everywhere where you think you might want an 'extended breakpoint'. You can then set a breakpoint (with the dev tools) where one of those eval('') statements is, and once you stop there, you do "step into". I'm considering to write a little transpiler (big word for a small thing I'm doing) putting those statements at the start of each function. https://stackoverflow.com/questions/59159996/how-do-i-go-into-evaldebugger-mode-when-already-stopped-at-regular-debugge – mathheadinclouds Dec 04 '19 at 16:48
  • I just tried replacing eval("debugger") by eval(); debugger, and got different results, depending on usage of firefox or chrome. https://i.stack.imgur.com/wy5WT.png – mathheadinclouds Dec 05 '19 at 11:44
6

Google Chrome uses the V8 JS-Engine, you can enable native calls to it with the --allow-natives-syntax flag, this exposes many useful debugging functions (full list here) like the one you are looking for: %NeverOptimizeFunction(). Without this flag, these calls would be illegal syntax so be careful when deploying (or use the v8-Natives library).

To enable this feature just open chrome with --js-flags="--allow-natives-syntax" (only use this for debugging of trusted websites, as this can give untrusted js code access to things you really do not want it to have access to).

Paul1365972
  • 169
  • 3
  • thank you. Please read my updated question. It seems quite likely you've got the solution here, but I need some more clarification to get it going. If this works, you sure deserve the bounty. – mathheadinclouds Nov 24 '19 at 01:55
  • tl;dr use --js-flags="--allow-natives-syntax" instead. To enable the feature the V8 JS-Engine needs to be started with the --allow-natives-syntax flag, however you can not start it directly, this is chromes job. So you have to tell chrome to start the engine with the flag, how do you do that? Just pass the mentioned engine flag via --js-flags= to chrome. – Paul1365972 Nov 24 '19 at 12:28
  • nope. Just tried that one more time, to be one the safe side. I have tried both `--allow-natives-syntax` and `--js-flags="--allow-natives-syntax"` multiple times as "the thing I type after 'chrome' in the operating system console". If I hadn't tried this all myself, me too I'd think a typo is the most likely explanation. I made another screenshot. https://i.stack.imgur.com/7cpPP.png See a typo? Please give me an honest answer: have you just "read and understood the article" (to be clear, nothing wrong with that, if you don't claim more), or have you actually tried all that on your machine? thx – mathheadinclouds Nov 24 '19 at 14:49
  • just a guess: might it be that I don't *start the chrome executable* with that option, but that I *compile the chrome C++(or whatever) sources* with that option? – mathheadinclouds Nov 24 '19 at 22:22
  • 1
    Thats strange I just tested it and it worked fine, no compiling needed. I will just write exactly what I did so there is no misunderstanding. 1. Close Chrome completely 2. Run Chrome from console with "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe" --js-flags="--allow-natives-syntax" 3. Open developer console and execute "%GetHeapUsage()" to test if everything is working – Paul1365972 Nov 25 '19 at 18:12
  • Here is a screenshot of it working on the default version of chrome https://imgur.com/zWTGhYa – Paul1365972 Nov 25 '19 at 18:20
  • now we're getting somewhere. Thank you for your continued efforts. I'm going to put my/our findings into an answer now. Not what I want yet, but definitely a lot closer. – mathheadinclouds Nov 25 '19 at 20:45
  • Very very probably, if there is a way to do it, then it's via starting chrome in allow-natives-syntax mode, and doing some suitable calls into the exposed natives api. Looks promising. It's just that %NeverOptimizeFunction doesn't do the trick. You have been very helpful. I'm hesitating to award the bounty, because I don't want to give the impression that the question has been answered satisfactorily. I don't know if can award the bounty without accepting the answer.... that's what I'd wish to do, since my bounty points are gone anyway. hmmm. – mathheadinclouds Nov 25 '19 at 21:03
  • ah great, giving bounty without accepting answer works. – mathheadinclouds Nov 25 '19 at 21:06
  • This is not a solution to the OP's problem, because the behavior under discussion there is unrelated to functions getting optimized. So it's true that `NeverOptimizeFunction(foo)` turns off optimization for `foo`, but that has no effect whatsoever on whether variables (in `foo` or elsewhere) get context- or stack-allocated. **Big +1 on the warning not to use `--allow-natives-syntax` for regular browsing, it is dangerous!** – jmrk Dec 04 '19 at 10:27
1

I really hope this question has a REAL answer. What follows isn't a real answer, it's a makeshift. I wrote a helper tool with which you can create stupid helper code of the form if (false) { console.log(variables, from, closures); } (see screen shot in question) using static analysis - you paste in your code, the stupid statement is created, you can copy it, then you don't need to type it. I don't know if that helps a lot, since all this copying and pasting also takes time, but that's what I got.

screenshot

fiddle

mathheadinclouds
  • 3,507
  • 2
  • 27
  • 37