Closed Bug 1305333 Opened 5 years ago Closed 5 years ago

Uninitialized lexial is cleared to undefined in BaselineFrame::trace, when GC happens before entering the lexixal scope.

Categories

(Core :: JavaScript Engine: JIT, defect)

defect
Not set
normal

Tracking

()

RESOLVED DUPLICATE of bug 1304649

People

(Reporter: arai, Unassigned)

References

Details

Attachments

(2 files)

While rebasing bug 1185106 patches, I hit the following strange behavior:

with the attached patch applied, the following test fails:

base revision:
  m-c 29beaebdfacc

environment:
  OSX 10.11.6

configure flags:
  --disable-optimize --enable-debug --enable-stdcxx-compat --enable-ctypes --disable-shared-js --enable-nspr-build

environment variable:
  JS_GC_ZEAL=14

runtime flags:
  --baseline-eager

code (modified version of js/src/jit-test/tests/basic/bug1091757.js):
  function f() {
      try {
          (function() {
              let a = 3;
              let XY = XY;
              return function() { return a; };
          })();
          assertEq(0, 1);
      } catch(e) {
          assertEq(e instanceof ReferenceError, true);
          assertEq(e.message.includes("XY"), true);
      }
  }

  for (let i = 0; i < 10; i++) {
      f();
  }

expected result:
  it runs successfully

actual result:
  test.js:10:11 Error: Assertion failed: got false, expected true

  but if I change the test code to show the exception, the issue disappears and just passes.


or run jit-test js/src/jit-test/tests/basic/bug1091757.js with --baseline-eager.


the attached code is an almost minimal testcase, reduced from bug 1185106's patch, that adds unused self-hosted functions and self-hosting intrinsics.
updated code:
  function f() {
      try {
          (function() {
              let a = 3;
              let XY = XY;
              return function() { return a; };
          })();
          assertEq(0, 1);
      } catch(e) {
          print("message:", e.message);
          assertEq(e instanceof ReferenceError, true);
          assertEq(e.message.includes("XY"), true);
      }
  }

  for (let i = 0; i < 10; i++) {
      f();
  }

it prints:
  message: can't access lexical declaration `XY' before initialization
  message: Assertion failed: got 0, expected 1
  /tmp/test.js:11:11 Error: Assertion failed: got false, expected true
  Stack:
    f@/tmp/test.js:11:11
    @/tmp/test.js:17:7

so looks like |let XY = XY| doesn't throw on 2nd call.
Summary: Strange test failure when I add unused self-hosted functions and self-hosting intrinsics. → Uninitialized lexical error isn't thrown in --baseline-eager when I add unused self-hosted functions and self-hosting intrinsics.
I see the stack value of XY is changed from MagicValue(JS_UNINITIALIZED_LEXICAL) to undefined here:

https://dxr.mozilla.org/mozilla-central/rev/29beaebdfaccbdaeb4c1ee5a43a9795ab015ef49/js/src/jit/BaselineFrame.cpp#80
>         // Clear dead block-scoped locals.
>         while (nfixed > nlivefixed)
>             unaliasedLocal(--nfixed).setUndefined();

and the stack value is not changed back to MagicValue(JS_UNINITIALIZED_LEXICAL).

therefore JSOP_CHECKLEXICAL doesn't throw.
https://dxr.mozilla.org/mozilla-central/rev/29beaebdfaccbdaeb4c1ee5a43a9795ab015ef49/js/src/jit/BaselineFrame.cpp#71
>     if (nfixed == nlivefixed) {
>         // All locals are live.
>         MarkLocals(this, trc, 0, numValueSlots());
>     } else {

if I change the condition to |if (true)|, the issue disappears.
This happens after bug 1263355.

when I apply the patch to 181336fdda66, the issue doesn't happen.
when I apply the patch to dbf7b0e7dc66, the issue happens.
Blocks: 1263355
Summary: Uninitialized lexical error isn't thrown in --baseline-eager when I add unused self-hosted functions and self-hosting intrinsics. → Uninitialized lexial is cleared to undefined in BaselineFrame::trace.
here's the bytecode for the function in testcase.
  00000:  uninitialized
  00001:  initlexical 0
  00005:  pop
  00006:  pushlexicalenv lexical {a: env slot 2, XY: frame slot 0}
  00011:  int8 3
  00013:  initaliasedlexical "a" (hops = 0, slot = 2)
  00018:  pop
  00019:  checklexical 0
  00023:  getlocal 0
  00027:  initlexical 0
  00031:  pop
  00032:  lambda (function () { return a; })
  00037:  setrval
  00038:  poplexicalenv
  00039:  retrval
  00040:  poplexicalenv
  00041:  retrval

GC happens in JSOP_PUSHLEXICALENV in pc=00006, and |lookupScope(pc)| in JSScript::calculateLiveFixed returns null, because it's not yet inside the lexical scope (lexical scope is available between pc=00011 and pc=00040).
|nlivefixed| in BaselineFrame::trace becomes 0, and uninitialized magic value created by JSOP_UNINITIALIZED and JSOP_INITLEXICAL (pc=00000 and pc=00001) is set to undefined.
Summary: Uninitialized lexial is cleared to undefined in BaselineFrame::trace. → Uninitialized lexial is cleared to undefined in BaselineFrame::trace, when GC happens before entering the lexixal scope.
shu, can you take a look?
Flags: needinfo?(shu)
talked in IRC, and this should be a dupe of bug 1304649.

confirmed the fix with the bug 1304649 patch applied, both for the testcase in this bug and bug 1185106.
Status: NEW → RESOLVED
Closed: 5 years ago
Flags: needinfo?(shu)
Resolution: --- → DUPLICATE
Duplicate of bug: 1304649
You need to log in before you can comment on or make changes to this bug.