JDK-8168373 : "Bad local variable type" in ES6 Nashorn when reassigning a `let` within a `try`
  • Type: Bug
  • Component: core-libs
  • Sub-Component: jdk.nashorn
  • Affected Version: 8,9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2016-10-19
  • Updated: 2017-11-29
  • Resolved: 2016-11-11
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u152Fixed 9 b145Fixed
Description
FULL PRODUCT VERSION :
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Linux ______ 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:33:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
Nashorn crashes when running valid ES6 JavaScript code.  Beyond that, this is a difficult error to describe with words.  For a better understanding of the problem, please see the "Steps to Reproduce" and the "Actual Result" below.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I save the following into a file called 'bug.js':

  try { // Must be within a `try`
    let x = 0; // Must use `let` instead of `var`; our variable must be defined before some possible `throw`, and it must be given some initial value
    if (false || false) { throw {}; } // We need `false || false` so this block doesn't get optimized away, since we need some apparent possibility of a `throw`
    x = 0.0; // It seems necessary that the internal type of `x` changes; if `x` is set to `1`, it will be fine, but setting it to a string or double triggers this error
    x; // `x` must be referenced again after being set
  } catch (e) {}

  print("Everything is okay") // Just kidding; it's not, since we don't get here before the JVM explodes

Then, I run this from my terminal: jjs --language=es6 bug.js

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
That it prints out "Everything is okay"
ACTUAL -
It produces an error, whose output is listed in the section below

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.VerifyError: Bad local variable type
Exception Details:
  Location:
    jdk/nashorn/internal/scripts/Script$bug.:program(Ljdk/nashorn/internal/runtime/ScriptFunction;Ljava/lang/Object;)Ljava/lang/Object; @67: iload
  Reason:
    Type top (current frame, locals[7]) is not assignable to integer
  Current Frame:
    bci: @67
    flags: { }
    locals: { 'jdk/nashorn/internal/runtime/ScriptFunction', 'java/lang/Object', 'jdk/nashorn/internal/runtime/ScriptObject', top, top, 'jdk/nashorn/internal/runtime/Undefined', 'jdk/nashorn/internal/scripts/JO0P0', top, top, 'jdk/nashorn/internal/scripts/JO4' }
    stack: { 'jdk/nashorn/internal/runtime/ECMAException' }
  Bytecode:
    0x0000000: 2ab6 0014 4dbb 0016 5903 b800 1a2c b700
    0x0000010: 1e3a 0619 06b8 0024 4db2 0028 3a05 0357
    0x0000020: 039a 0007 0399 0029 bb00 2a59 04b8 001a
    0x0000030: b700 2d3a 0919 0959 b800 3312 3406 1018
    0x0000040: b800 3a15 0787 3907 b200 283a 05bf 0e5c
    0x0000050: 3907 5cb8 0040 3a05 4a18 075c b800 403a
    0x0000060: 054a 29b8 0040 3a05 a700 393a 06bb 0048
    0x0000070: 5905 b800 1a2c b700 493a 0719 074d 2c19
    0x0000080: 0659 c100 3699 0009 c000 36b4 004c ba00
    0x0000090: 5800 002c b600 5b4d a700 092c b600 5b4d
    0x00000a0: bf2c ba00 6100 00b2 0028 1263 ba00 6700
    0x00000b0: 003a 0519 05b0                         
  Exception Handler Table:
    bci [30, 104] => handler: 107
    bci [126, 147] => handler: 155
  Stackmap Table:
    full_frame(@40,{Object[#16],Object[#110],Object[#47],Top,Top,Object[#112],Object[#22]},{})
    same_frame(@78)
    full_frame(@107,{Object[#16],Object[#110],Object[#47],Top,Top,Object[#110],Object[#22]},{Object[#70]})
    full_frame(@142,{Object[#16],Object[#110],Object[#72],Top,Top,Object[#110],Object[#70],Object[#72]},{Object[#72],Object[#110]})
    same_locals_1_stack_item_frame(@155,Object[#70])
    full_frame(@161,{Object[#16],Object[#110],Object[#47],Top,Top,Object[#110],Object[#110]},{})

	at java.lang.Class.getDeclaredFields0(Native Method)
	at java.lang.Class.privateGetDeclaredFields(Class.java:2583)
	at java.lang.Class.getDeclaredField(Class.java:2068)
	at jdk.nashorn.internal.runtime.Context$ContextCodeInstaller$1.run(Context.java:209)
	at jdk.nashorn.internal.runtime.Context$ContextCodeInstaller$1.run(Context.java:204)
	at java.security.AccessController.doPrivileged(Native Method)
	at jdk.nashorn.internal.runtime.Context$ContextCodeInstaller.initialize(Context.java:204)
	at jdk.nashorn.internal.codegen.CompilationPhase$InstallPhase.transform(CompilationPhase.java:508)
	at jdk.nashorn.internal.codegen.CompilationPhase.apply(CompilationPhase.java:624)
	at jdk.nashorn.internal.codegen.Compiler.compile(Compiler.java:654)
	at jdk.nashorn.internal.runtime.Context.compile(Context.java:1319)
	at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:1253)
	at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:625)
	at jdk.nashorn.tools.Shell.runScripts(Shell.java:316)
	at jdk.nashorn.tools.Shell.run(Shell.java:171)
	at jdk.nashorn.tools.Shell.main(Shell.java:135)
	at jdk.nashorn.tools.Shell.main(Shell.java:111)

REPRODUCIBILITY :
This bug can be reproduced always.


Comments
Thanks for helping with this, Attila. I was sniffing around the catch handling code in LocalVariableTypesCalculator but didn't quite understand what went wrong. This helps me to better understand that code.
07-11-2016

LocalVariableTypeCalculator creates bogus local variable type conversions for symbols that got invalidated because they were out of their lexical scope. Before ES6 let/const came along the only lexically scoped symbols were those for catch variables, and they were handled specially (see "exceptionSymbol" handling in LocalVariableTypeCalculator.enterTryNode) and some internal symbols (e.g. for iterator variables in for-in, evaluated switch expression for non-integer cases etc.) that couldn't be observed by the program anyway, so the problem didn't really surface; vars are function-wide and thus lexically valid within the whole function. I have now implemented proper handling for this; lexically no longer valid symbols are tracked, and local variables will never have a type conversion generated for them at a join point that is outside their lexical scope.
06-11-2016

Nashorn will optimize initial "let x = 0" to ICONST_0;POP: as it's never read, it will not be stored in a local variable, but then the code emitted for the "throw" will try to widen the (never stored) int value: ILOAD 2; I2D; DSTORE 2. That is wrong. Interestingly, if we use "var" instead of "let", this incorrect int-to-double widening code is not emitted.
06-11-2016

To reproduce the issue, run the following from command line: jjs --language=es6 bug.js where bug.js is the file attached to the bug report. Following are the test results: JDK 8u102 - Fail JDK 8u122ea - Fail JDK 9ea + 137 - Fail Following is the output: Exception in thread "main" java.lang.VerifyError: Bad local variable type Exception Details: Location: jdk/nashorn/internal/scripts/Script$bug.:program(Ljdk/nashorn/internal/runtime/ScriptFunction;Ljava/lang/Object;)Ljava/lang/Object; @72: iload Reason: Type top (current frame, locals[7]) is not assignable to integer Current Frame: bci: @72 flags: { } locals: { 'jdk/nashorn/internal/runtime/ScriptFunction', 'java/lang/Object', 'jdk/nashorn/internal/runtime/ScriptObject', top, top, 'jdk/nashorn/internal/runtime/Undefined', 'jdk/nashorn/internal/scripts/JO0P0', top, top, 'jdk/nashorn/internal/scripts/JO4' } stack: { 'jdk/nashorn/internal/runtime/ECMAException' } Bytecode: 0x0000000: 2ab6 0014 4dbb 0016 5903 b800 1a2c b700 0x0000010: 1e3a 0619 06b8 0024 4db2 0028 3a05 0357 0x0000020: 039a 0007 0399 0029 bb00 2a59 04b8 001a 0x0000030: b700 2d3a 0919 0959 b800 3312 3406 101a 0x0000040: b800 3ab2 0028 3a05 1507 8739 07bf 0e5c 0x0000050: 3907 5cb8 0040 3a05 4a18 075c b800 403a 0x0000060: 054a 29b8 0040 3a05 a700 393a 06bb 0048 0x0000070: 5905 b800 1a2c b700 493a 0719 074d 2c19 0x0000080: 0659 c100 3699 0009 c000 36b4 004c ba00 0x0000090: 5800 002c b600 5b4d a700 092c b600 5b4d 0x00000a0: bf2c ba00 6100 00b2 0028 1263 ba00 6700 0x00000b0: 003a 0519 05b0 Exception Handler Table: bci [30, 104] => handler: 107 bci [126, 147] => handler: 155 Stackmap Table: full_frame(@40,{Object[#16],Object[#110],Object[#47],Top,Top,Object[#112],Object[#22]},{}) same_frame(@78) full_frame(@107,{Object[#16],Object[#110],Object[#47],Top,Top,Object[#110],Object[#22]},{Object[#70]}) full_frame(@142,{Object[#16],Object[#110],Object[#72],Top,Top,Object[#110],Object[#70],Object[#72]},{Object[#72],Object[#110]}) same_locals_1_stack_item_frame(@155,Object[#70]) full_frame(@161,{Object[#16],Object[#110],Object[#47],Top,Top,Object[#110],Object[#110]},{}) at java.lang.Class.getDeclaredFields0(Native Method) at java.lang.Class.privateGetDeclaredFields(Class.java:2583) at java.lang.Class.getDeclaredField(Class.java:2068) at jdk.nashorn.internal.runtime.Context$ContextCodeInstaller$1.run(Context.java:209) at jdk.nashorn.internal.runtime.Context$ContextCodeInstaller$1.run(Context.java:204) at java.security.AccessController.doPrivileged(Native Method) at jdk.nashorn.internal.runtime.Context$ContextCodeInstaller.initialize(Context.java:204) at jdk.nashorn.internal.codegen.CompilationPhase$InstallPhase.transform(CompilationPhase.java:508) at jdk.nashorn.internal.codegen.CompilationPhase.apply(CompilationPhase.java:624) at jdk.nashorn.internal.codegen.Compiler.compile(Compiler.java:655) at jdk.nashorn.internal.runtime.Context.compile(Context.java:1317) at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:1251) at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:627) at jdk.nashorn.tools.Shell.runScripts(Shell.java:394) at jdk.nashorn.tools.Shell.run(Shell.java:179) at jdk.nashorn.tools.Shell.main(Shell.java:143) at jdk.nashorn.tools.Shell.main(Shell.java:119)
20-10-2016