Avoid uninitialized let in nested for-loop headers for ES5 compat#18
Conversation
|
Seems reasonable. Note that in general this project doesn't guarantee ES5 compatibility (nor more generally compatibility with unmaintained browsers), and since there's a big focus on small code size, it's likely there will be other issues with converting it. I've pushed a couple of commits to update the statistics files (these are auto-generated when running the tests), and made a couple of small "golfing" changes to reduce the code size (with those, this actually saves a byte on the uncompressed size, and is approximately the same size compressed). I'll merge and cut a new release. I'd recommend raising the var-hoisting issue to Babel - when they hoist an uninitialised variable, they clearly need to retrofit an |
Live Example: https://gh.ovo.re/lean-qr-verify/
Two for-loop headers declare variables without an initializer:
Under ES6 this is fine.
let prevevaluates toundefinedon each outer iteration, sosize !== undefinedis true and the loop enters correctly.The problem appears when Babel transpiles to ES5.
@babel/plugin-transform-block-scopingconvertslettovar. Since neither loop body references the variable in a closure, no per-iteration_loopwrapper is generated. The hoistedvarkeeps its value from the previous outer iteration instead of resetting toundefined.In
errorCorrection.mjs, this causes the interleave loop to skip the second data group. Inscore.mjs, it skews the penalty score. The result is broken QR codes with chessboard or stripe artifacts.This patch gives the variables explicit initial values:
errorCorrection.mjs:let prevbecomeslet prev = -1.sizestarts at 0 and only increases, sosize !== -1is always true on first pass, same assize !== undefined.score.mjs:lastbecomeslast = 0.consecalready starts at 0, so whether the firstcur !== lastcheck triggers the reset or not makes no difference.Both changes are one character, behavior-preserving under ES6, and produce correct output through Babel ES5.