dirac icon indicating copy to clipboard operation
dirac copied to clipboard

Local names incorrect

Open mtruyens opened this issue 7 years ago • 18 comments

Hi,

In the attached screenshot, you can see that the local "nodeleaf-id" is shown several times in the list at the right side. Only one of those instance is correct (the one having value 538) -- the other values correspond to other locals (e.g., :node-types/tree corresponds to local "rep-tree|fold?" and value null corresponds to "has-children?"). When I select the text of the local at the left side, the current value is correctly displayed in the popup window that appears.

Using Chrome 57.0.2984.0, Dirac 1.0.0, and I tried to start with a fresh Chrome folder with no extensions installed other than Dirac.

screen shot 2017-01-17 at 10 52 39

mtruyens avatar Jan 17 '17 09:01 mtruyens

Another example, this time with the function parameter "regy": screen shot 2017-01-17 at 10 59 38

mtruyens avatar Jan 17 '17 10:01 mtruyens

I can confirm this. It is caused by the way ClojureScript generates Javascript code and how default DevTools select local variables for display. I believe if you break the code at the same spot in internal DevTools (without Dirac installed), you will experience a similar thing.

I haven't tried to counter it yet because I don't fully understand the mechanism. The only thing Dirac does is that it tries to sort properties and put null and undefined values at the end of list. This way it mitigates the problem a bit without losing any information.

But I don't see this feature in your screenshots. Have you disabled "Enable clustered locals" in Dirac DevTools extension preferences?

darwin avatar Jan 17 '17 20:01 darwin

I think I indeed disabled "Enable clustered locals" when taking the screenshot. However, before doing so, I had deliberately played with this and the other preferences, such as "Enabled friendly locals", but without any positive result...

On 17 Jan 2017, at 21:25, Antonin Hildebrand [email protected] wrote:

I can confirm this. It is caused by the way ClojureScript generates Javascript code and how default DevTools select local variables for display. I believe if you break the code at the same spot in internal DevTools (without Dirac installed), you will experience a similar thing.

I haven't tried to counter it yet because I don't fully understand the mechanism. The only thing Dirac does is that it tries to sort properties and put non-null and non-undefined values first. This way it mitigates the problem a bit without losing any information.

But I don't see this feature in your screenshots. Have you disabled "Enable clustered locals" in Dirac DevTools extension preferences?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/binaryage/dirac/issues/53#issuecomment-273289086, or mute the thread https://github.com/notifications/unsubscribe-auth/ABztcbFHoeKpzWcAStytaxG_3TR_06svks5rTSPHgaJpZM4Llbuf.

mtruyens avatar Jan 17 '17 20:01 mtruyens

If I remember well, ClojureScript generated Javascript similar to this one:

if (condition) {
  var v = exp1;
  ...
  // breakpoint here
} else {
  var v = exp2;
  ...
}

There are two variables named "v", you stopped in true branch, but DevTools sees all branches. Of course this gets worse when nested.

I believe this will have to be solved on DevTools side, to display only locals from relevant code branches.

darwin avatar Jan 17 '17 20:01 darwin

Investigated this issue a bit today. Scratch my previous comment. Javascript scope variables work as expected.

The problem is in case with source maps. DevTools tries to map real variable names to original names. Unfortunately the implementation does not expect that multiple distinct variables could potentially map to the same name in original sources.

I was able to reproduce it on this minimal repro case:

(let [x 1]
  (let [x 2]
    (js-debugger)))

Which generates javascript similar to this:

var x = (1);
var x__$1 = (2);
debugger;

Both x and x__$1 map to original name x. If naive code wants to construct reverse mapping keyed by original name, it ends up with { "x": "x__$1" } (which overwrote previous { "x": "x" }).

I have identified multiple places where they use some intermediate data structure for reverse-mapping names and it does not expect multiple variables with the same name. This confuses lookups in code relying on those mappings.

https://github.com/binaryage/dirac/blob/9d14d09ad5cfda5829b93f1909ebb5d4d47f111b/front_end/sources/SourceMapNamesResolver.js#L112 https://github.com/binaryage/dirac/blob/9d14d09ad5cfda5829b93f1909ebb5d4d47f111b/front_end/sources/SourceMapNamesResolver.js#L137

@aslushnikov I believe commit decf4d4c088e8d107e16ac726101d72d809d570f needs a review.

Secondary problem is in the code producing source code decorations:

https://github.com/binaryage/dirac/blob/9d14d09ad5cfda5829b93f1909ebb5d4d47f111b/front_end/sources/JavaScriptSourceFrame.js#L619

@pfeldman 80774d69c3829680ecee3b07450263c36682bd5f

I think this will deserve a ticket upstream, because this needs to be fixed in general.

darwin avatar Jan 30 '17 23:01 darwin

@darwin thank you for the investigation. Would you mind filing bugs on the crbug.com (and posting links here so that they're triaged quickly)?

If you have any time for the CL to address the issue, I'll be happy to review

aslushnikov avatar Jan 31 '17 00:01 aslushnikov

@aslushnikov sure, will do (probably at the end of this week).

darwin avatar Jan 31 '17 00:01 darwin

I was able to implement a fix but it will need deeper changes to resolve all remaining issues.

New observations:

  1. SDK.TextSourceMap.findEntry method was wrong in some cases
  2. the problem is not only mapping of compiled names to duplicit original names. I found a case where even compiled names can be duplicit in the context of a given scope.

Original ClojureScript:

(defn breakpoint-demo [count]
  (let [x 1]
    (let [y 2]
      (let [x 3
            z #(println x)]
        (js-debugger))))

Produces:

var x = (1);
var y = (2);
var x__$1 = (3);
var z = ((function (x__$1,y,x){
return (function (){
return cljs.core.println.call(null,x__$1);
});})(x__$1,y,x))

Compiled variable names x,y and x__$1 are present twice. Second time as args to the anonymous function.

Mapping from compiled to original names should be:

x -> x
y -> y
x__$1 -> x
x__$1 -> null (second)
y -> null (second)
x -> null (second)
z -> z

This means that we have to care about general case of duplicit compiled names pointing arbitrarily to (potentially) duplicit original names. Any code using plain names to uniquely identify something is potentially wrong, both when mapping compiled names to original ones and reverse.

Implementation notes:

  1. https://github.com/binaryage/dirac/commit/2f1268bd32b848d88ad6521ebf9f4e044cb2a061 SDK.TextSourceMap.findEntry method was returning false positives in some cases, the culprit was !first test which was preventing the whole condition in most cases
  2. https://github.com/binaryage/dirac/commit/65c926461a373da92887ee37b35ed4f152b47297 I was able to fix SourceMapNamesResolver to be aware of duplicit names, but I wasn't able to properly fix Sources.SourceMapNamesResolver.RemoteObject becasue I cannot modify its API design (that would lead to avalanche of more changes). The methods getAllProperties and setPropertyValue expect string names to identify properties. But names are not enough, we need some kind of id-system for property names so we can distinguish them even if they have duplicit names.
  3. https://github.com/binaryage/dirac/commit/0ed1b3baff72be9b48a99a0a2b99ef30994bbe3b I was able to fix JavaScriptSourceFrame to properly render decoration widgets even exposed to duplicit names. The trick was to invent string-based id-system which is used to do proper mapping between properties and widgets (see getLocationId)

darwin avatar Feb 01 '17 18:02 darwin

@mtruyens please test it again with v1.1.2 release and report back.

darwin avatar Feb 01 '17 22:02 darwin

The changes in 1.1.2 release were not enough to fix everything :-(

Found pretty nasty bug in adba5772a7f9969ba70f3707c89afc624a52da24.

darwin avatar Feb 02 '17 00:02 darwin

Reported upstream as ticket 687772: https://bugs.chromium.org/p/chromium/issues/detail?id=687772

darwin avatar Feb 02 '17 00:02 darwin

A very big thank you for this investigation (and for your product in general!)

mtruyens avatar Feb 07 '17 18:02 mtruyens

Here's another example of this which is slightly different:

(ns source-map-test2.core)

(defn test-fn
  [one-one]
  (let [two-two 2]
    ((fn rebind [] one-one))
    (js-debugger)))

(test-fn 1)
screenshot of intellij idea 14-03-18 11-59-17 am screenshot of google chrome 14-03-18 11-58-03 am

danielcompton avatar Mar 13 '18 23:03 danielcompton

@danielcompton Could you confirm this in Dirac? Dirac has a patch for this issue.

darwin avatar Mar 13 '18 23:03 darwin

Dirac maps them correctly, but uses the generated JS name (one_one, rather than the original ClojureScript name (one-one):

screenshot of google chrome 14-03-18 12-50-41 pm

danielcompton avatar Mar 13 '18 23:03 danielcompton

This is fishy. Are you sure you really ran the source-maps-enabled case under Dirac?

darwin avatar Mar 13 '18 23:03 darwin

I force updated the Chrome extension to the latest version, and I don't see this anymore, everything looks correct, sorry for the false alarm:

screenshot of google chrome 14-03-18 12-59-27 pm

danielcompton avatar Mar 14 '18 00:03 danielcompton

Please note that this feature is broken in latest ClojureScript 1.10.439 due to CLJS-2993.

You might want to stay on 1.10.339 which is last good version.

darwin avatar Nov 28 '18 14:11 darwin