Closed Bug 320887 Opened 19 years ago Closed 9 years ago

|var x| can throw a ReferenceError

Categories

(Core :: JavaScript Engine, defect)

defect
Not set
normal

Tracking

()

RESOLVED DUPLICATE of bug 1217099

People

(Reporter: mrbkap, Unassigned)

References

()

Details

Attachments

(1 file, 2 obsolete files)

STEPS TO REPRODUCE:
1. Try the testcase in the URL field.
2. Note that there is a JavaScript exception.

Shutdown pointed out in bug 320172 comment 13 that this is incorrect behavior, per ECMA. We're failing because we emit a JSOP_NAME bytecode for the decompiler, and the lookup fails.
Checking in regress-320887.js;
/cvsroot/mozilla/js/tests/js1_6/Array/regress-320887.js,v  <--  regress-320887.js
initial revision: 1.1
Flags: testcase+
note to self: this crashes the 1.8 branch with top 'o stack:
Variables(JSContext * 0x03a4a818, JSTokenStream * 0x03df3378, JSTreeContext * 0x0012e81c) line 2164 + 3 bytes

Fixed by Bug 320172
Flags: blocking1.9?
Obscure enough to not block on.
Flags: blocking1.9? → blocking1.9-
A fix for bug 442333 hides this bug, we need a better testcase.
Flags: in-testsuite+ → in-testsuite?
bug 446026 on tracemonkey unhides this bug.
Here's a test case.

Object.defineProperty(this, "x", {get: function () { FAIL; }});
eval("var x;");  // throws FAIL

Clearly against ES5. Waldo says we emit the erroneous JSOP_GET* opcode here for the decompiler's benefit. That being the case this will be a bit of a pain to fix.
Blocks: es5
This is ugly, somebody should have a look at this.
The decompiler has gone the way of the dodo, so maybe this is easier to fix now.  Anyone want to try?
Attached patch quick fix attempt (obsolete) — Splinter Review
Here's a quick attempt at a fix. The removal of the statement decompiler did not remove unnecessary source notes, in this case for decompiling var x, y=42, z; and the like. Jeff, could you please take this bug and further simplify the code if possible, and do whatever else is needed for the given testcase (jorendorff's).

/be
Wish I could, but I'm swamped with other stuff at the moment.  :-)
Evilpies, how about you? Free patch if you call from work or your parents house :-P.

/be
I started looking at the code and your patch. But variable names like pn3 are annoying to work with.
(In reply to Tom Schuster [:evilpie] from comment #14)
> I started looking at the code and your patch. But variable names like pn3
> are annoying to work with.

(a) That was already there, I just moved the declaration.

(b) Our job is to make the engine better in minimum viable patches that don't get stalled by stop-energy complaints about variable names. Either fix it in a patch revision, or at least mention it _en passant_ while adding greater value than your comment does. :-(

(c) It's *this bug's patch*, not "your patch" or "my patch".

How about another look?

/be
From my point of view the patch is okay, but I already asked Jason about this bug yesterday, and he agreed to review this.
Comment on attachment 714290 [details] [diff] [review]
must satisfy the full PNX_POPVAR contract (all combos)

Review of attachment 714290 [details] [diff] [review]:
-----------------------------------------------------------------

I just noticed that I forgot to submit this review. ;)

::: js/src/frontend/BytecodeEmitter.cpp
@@ +3055,5 @@
>  {
>      JS_ASSERT(pn->isArity(PN_LIST));
>      JS_ASSERT(isLet == (emitOption == PushInitialValues));
>  
> +    ParseNode *pn3 = NULL;

Initializer maybe?

@@ +3232,5 @@
>      if (pn->pn_xflags & PNX_POPVAR) {
> +        if (pn3 && Emit1(cx, bce, JSOP_POP) < 0)
> +            return false;
> +    } else {
> +        if (!pn3 && Emit1(cx, bce, JSOP_UNDEFINED) < 0)

This or the whole if {} block need a comment about what it's doing.
This !pn3 check seems to encode that the last variable had an initialize, I am wondering if we could do this a little bit cleaner and move pn3 inside the loop.
Comment on attachment 714290 [details] [diff] [review]
must satisfy the full PNX_POPVAR contract (all combos)

Jason said he would review this patch.
Attachment #714290 - Flags: review?(jorendorff)
Comment on attachment 714290 [details] [diff] [review]
must satisfy the full PNX_POPVAR contract (all combos)

Review of attachment 714290 [details] [diff] [review]:
-----------------------------------------------------------------

Clearing review flag.

::: js/src/frontend/BytecodeEmitter.cpp
@@ +3055,5 @@
>  {
>      JS_ASSERT(pn->isArity(PN_LIST));
>      JS_ASSERT(isLet == (emitOption == PushInitialValues));
>  
> +    ParseNode *pn3 = NULL;

I agree with Tom about calling this lastInit or something. Your call.

@@ -3233,5 @@
>          if (!next)
>              break;
> -        off = tmp;
> -        noteIndex = NewSrcNote2(cx, bce, SRC_PCDELTA, 0);
> -        if (noteIndex < 0 || Emit1(cx, bce, JSOP_POP) < 0)

Thanks for removing this. Filed follow-up bug 853178 to remove other unused PCDELTA notes.

@@ +3229,1 @@
>              return false;

All this has gotten messy over the past few patches that touched it; would you mind recasting lines 3207-3229 as:

    if (pn3 && emitOption == InitializeVars) {
        JS_ASSERT_IF(pn2->isDefn(), pn3 == pn2->pn_expr);
        if (!pn2->pn_cookie.isFree()) {
            if (!EmitVarOp(cx, pn2, op, bce))
                return false;
        } else {
            if (!EmitIndexOp(cx, op, atomIndex, bce))
                return false;
        }

#if JS_HAS_DESTRUCTURING
      emit_note_pop:
#endif
        if (next) {
            if (Emit1(cx, bce, JSOP_POP) < 0)
                return false;
        }
    }

This eliminates all the break or continue statements, but you would also have to add a condition in the loop head:

>-    for (ParseNode *pn2 = pn->pn_head; ; pn2 = next) {
>+    for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = next) {

I can do this in a follow-up bug if you just want to get this landed.

@@ +3229,5 @@
>              return false;
>      }
>  
>      if (pn->pn_xflags & PNX_POPVAR) {
> +        if (pn3 && Emit1(cx, bce, JSOP_POP) < 0)

I think something must be not quite right here, because the condition here does not match what happens in the loop.

And a lot of tests do fail. For example, this test case asserts:

  for (let c in Array) {}
  dis();

with the following bytecode:

main:
00000:  undefined
00001:  undefined
00002:  getgname "Array"
00007:  iter 1
00009:  enterlet1 depth 1 {c: 0}
00014:  goto 25 (+11)
00019:  loophead
00020:  iternext
00021:  setlocal 1
00024:  pop
00025:  loopentry
00026:  moreiter
00027:  ifne 19 (-8)
00032:  leaveforletin
00033:  enditer
00034:  popn 1
...
Attachment #714290 - Flags: review?(jorendorff)
Assignee: general → nobody
Duping forward to the bug that finally fixes this.
Status: NEW → RESOLVED
Closed: 9 years ago
Resolution: --- → DUPLICATE
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: