Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Latest patch (Q4 2021 Patch 3) fails to load #5932

Closed
KlemenDEV opened this issue Feb 12, 2022 · 5 comments · Fixed by #5945
Closed

Latest patch (Q4 2021 Patch 3) fails to load #5932

KlemenDEV opened this issue Feb 12, 2022 · 5 comments · Fixed by #5945
Labels
issue: bug Describes why the code or behaviour is wrong

Comments

@KlemenDEV
Copy link

KlemenDEV commented Feb 12, 2022

Describe the bug

The latest patch (Q4 2021 Patch 3) fails to load

To Reproduce

Try to load Blockly as specified with examples.

Resolution

This seems to be fixed by changing line from

root.Blockly.blocks.all = factory(root.Blockly);

to

root.Blockly.blocks = factory(root.Blockly);

@KlemenDEV KlemenDEV added the issue: triage Issues awaiting triage by a Blockly team member label Feb 12, 2022
@Taikono-Himazin
Copy link

Thanks to this, it works.
thank you.

@alschmiedt
Copy link
Contributor

Thanks for reporting! We are currently working on a fix for this, but in the meantime we have reverted the patch so version 7.20211209.4 should work as expected.

@cpcallen
Copy link
Contributor

cpcallen commented Feb 15, 2022

O.K., I came up with a fix for this issue yesterday—for which I will submit a PR shortly—but I realised I was confused about why it should even have been necessary so in the mean time I advised @alschmiedt to submit #5938 to revert the release until I could be sure that my fix was actually correct.

Here's the deal. As noted in this (and other) bug reports, the error is indeed caused by the line

root.Blockly.blocks.all = factory(root.Blockly);

in blocks_compressed.js.

My first take was: of course this will fail, because while blockly_compressed.js, when loaded in a browser, creates a root.Blockly object that object won't have a .blocks property onto which could be tacked an .all. I prepared a fix based on this.

But then, while verifying my fix, I noticed / remembered that, because of how .declareLegacyNamespace works (specifically, in this case, how it is implemented by Closure Compiler), the factory function actually creates a .blocks property and initialises it with an empty object and then additionally creates the .blocks.all property and initialises that too! So root.Blockly.blocks.all = factory(...) should in fact be doing nothing at all. (If you look at the contents of blocks_compressed.js, note that root.Blockly will have previously been set to $.Blocky and factory returns $.Blockly.blocks.all.)

Unfortunately, while root.Blockly.blocks.all = factory(root.Blockly) ought to do nothing, it is actually throwing TypeError, because root.Blockly.blocks is evaluated before the call to factory which would create the .blocks property, and thus evaluates to undefined, and attempting to assign to (undefined).all is indeed a TypeError.

Here ends today's lesson on the order of evaluation of JavaScript expressions.

@cpcallen
Copy link
Contributor

I've also looked in to why this issue was not picked up in testing. It turns out that:

  • The playgrounds have been loading only blockly_uncompressed.js via <script>, then use goog.requireing to load the other modules they need.
  • The mocha tests only import blockly_compressed.js, not any of the other compiled chunks.
  • The node tests import all of dist/, but this exercises only the second clause of the UMD wrapper:
    ;(function(root, factory) {
      if (typeof define === 'function' && define.amd) { // AMD
        define(["./blockly_compressed.js"], factory);
      } else if (typeof exports === 'object') { // Node.js
        module.exports = factory(require("./blockly_compressed.js"));
      } else { // Browser
        root.Blockly.blocks.all = factory(root.Blockly);
      }
    }(this, function(Blockly) {
      /* ... */
    which does not trigger the bug in the third clause.

In PR #5835, @alschmiedt has somewhat rectified this by having the playgrounds load blocks_compressed.js via a <script> tag in certain instances, but this will still not exercise the relevant code in the most typical tests that we do during development, and I am not aware of us doing any testing, ever, that exercises the AMD clause at the top of the wrappers, so I have filed #5944 to address this deficiency.

cpcallen added a commit to cpcallen/blockly that referenced this issue Feb 15, 2022
A problem can occur when loading chunks in a browser: although
factory() will create the full exported path on $, and thus the
assignment

    root.<path> = factory(...) { ...; return $.<path> }

will normally be a do-nothing in every chunk except the first, if
the exported path (e.g. Blockly.blocks.all) is more than one level
deeper than any previously-existing path (e.g. Blockly), this will
fail because root.<path> is evaluated before calling factory(), and
so the left hand side will will evaluate to a undefined reference
(e.g. undefined.all) and TypeError will be thrown before the call
to factory is evaluated.

To fix this, call factory first and store the exports object in a
variable before assigning it to the exported path.

Fixes google#5932.
@cpcallen cpcallen linked a pull request Feb 15, 2022 that will close this issue
3 tasks
cpcallen added a commit that referenced this issue Feb 22, 2022
* fix(build): Correctly handle deep export paths

A problem can occur when loading chunks in a browser: although
factory() will create the full exported path on $, and thus the
assignment

    root.<path> = factory(...) { ...; return $.<path> }

will normally be a do-nothing in every chunk except the first, if
the exported path (e.g. Blockly.blocks.all) is more than one level
deeper than any previously-existing path (e.g. Blockly), this will
fail because root.<path> is evaluated before calling factory(), and
so the left hand side will will evaluate to a undefined reference
(e.g. undefined.all) and TypeError will be thrown before the call
to factory is evaluated.

To fix this, call factory first and store the exports object in a
variable before assigning it to the exported path.

Fixes #5932.
@BeksOmega BeksOmega added issue: bug Describes why the code or behaviour is wrong and removed issue: triage Issues awaiting triage by a Blockly team member labels Feb 23, 2022
@alschmiedt
Copy link
Contributor

Closing since the fix to this was releasing version 7.20211209.4 and the fix to the bug causing the patch to fail was fixed in #5953.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
issue: bug Describes why the code or behaviour is wrong
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants