Skip to content

Fix start of local var visibility in classfiles #8754

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

Merged
merged 1 commit into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
val Local(tk, _, idx, isSynth) = loc
if (rhs == EmptyTree) { emitZeroOf(tk) }
else { genLoad(rhs, tk) }
val localVarStart = currProgramPoint()
bc.store(idx, tk)
val localVarStart = currProgramPoint()
if (!isSynth) { // there are case <synthetic> ValDef's emitted by patmat
varsInScope ::= (sym -> localVarStart)
}
Expand Down
38 changes: 37 additions & 1 deletion compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import asm.tree._

import scala.tools.asm.Opcodes
import scala.jdk.CollectionConverters._

import Opcodes._

class TestBCode extends DottyBytecodeTest {
import ASMConverters._
Expand Down Expand Up @@ -786,6 +786,42 @@ class TestBCode extends DottyBytecodeTest {
}
}


@Test // wrong local variable table for methods containing while loops
def t9179(): Unit = {
val code =
"""class C {
| def t(): Unit = {
| var x = ""
| while (x != null) {
| foo()
| x = null
| }
| bar()
| }
| def foo(): Unit = ()
| def bar(): Unit = ()
|}
""".stripMargin
checkBCode(code) { dir =>
val c = loadClassNode(dir.lookupName("C.class", directory = false).input, skipDebugInfo = false)
val t = getMethod(c, "t")
val instructions = instructionsFromMethod(t)
val isFrameLine = (x: Instruction) => x.isInstanceOf[FrameEntry] || x.isInstanceOf[LineNumber]
// TODO: The same test in scalac uses different labels because their LineNumberTable isn't the same as ours,
// this should be investigated.
assertSameCode(instructions.filterNot(isFrameLine), List(
Label(0), Ldc(LDC, ""), VarOp(ASTORE, 1),
Label(5), VarOp(ALOAD, 1), Jump(IFNULL, Label(19)),
Label(10), VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "C", "foo", "()V", false), Label(14), Op(ACONST_NULL), VarOp(ASTORE, 1), Jump(GOTO, Label(5)),
Label(19), VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "C", "bar", "()V", false), Label(24), Op(RETURN), Label(26)))
val labels = instructions collect { case l: Label => l }
val x = convertMethod(t).localVars.find(_.name == "x").get
assertEquals(x.start, labels(1))
assertEquals(x.end, labels(5))
}
}

@Test
def invocationReceivers(): Unit = {
import Opcodes._
Expand Down