Skip to content

Commit

Permalink
Method boxing overloads + added private methods (#450)
Browse files Browse the repository at this point in the history
* feat: added validation step when boxing available

* fix: fallback to boxing case
  • Loading branch information
iusildra authored Jun 13, 2023
1 parent 280afd8 commit e8bc704
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ private[internal] class EvaluationProvider(
compilePrepare(expression, frame).orElse(Success(RuntimeExpression(tree)))
case Valid(tree) => Success(RuntimeExpression(tree))
case Fatal(e) => Failure(e)
case _: Invalid => compilePrepare(expression, frame)
case inv: Invalid =>
println(s"\u001b[31mInvalid expression: ${inv.exception}\u001b[0m")
compilePrepare(expression, frame)
}
else compilePrepare(expression, frame)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ private[evaluator] class RuntimeEvaluationHelpers(frame: JdiFrame) {
}
}

private def argsMatch(method: Method, args: Seq[Type]): Boolean =
method.argumentTypeNames().size() == args.size && areAssignableFrom(method, args)
private def argsMatch(method: Method, args: Seq[Type], boxing: Boolean): Boolean =
method.argumentTypeNames().size() == args.size && areAssignableFrom(method, args, boxing)

/**
* @see <a href="https://docs.oracle.com/javase/specs/jls/se20/html/jls-15.html#jls-15.12.2.5">JLS#15.12.2.5. Choosing the most specific method</a>
Expand Down Expand Up @@ -86,15 +86,20 @@ private[evaluator] class RuntimeEvaluationHelpers(frame: JdiFrame) {
args: Seq[Type],
encode: Boolean = true
): Validation[Method] = {
val candidates: Seq[Method] = ref
.methodsByName { if (encode) NameTransformer.encode(funName) else funName }
.asScalaSeq
.filter { method => !method.isPrivate && argsMatch(method, args) }
.toSeq
val candidates: List[Method] = ref.methodsByName {
if (encode) NameTransformer.encode(funName) else funName
}.asScalaList

val candidatesWithoutBoxing = candidates.filter { argsMatch(_, args, boxing = false) }

val candidatesWithBoxing = candidatesWithoutBoxing.size match {
case 0 => candidates.filter { argsMatch(_, args, boxing = true) }
case _ => candidatesWithoutBoxing
}

val withoutBridges = candidates.size match {
case 0 | 1 => candidates
case _ => candidates.filterNot(_.isBridge())
val withoutBridges = candidatesWithBoxing.size match {
case 0 | 1 => candidatesWithBoxing
case _ => candidatesWithBoxing.filterNot(_.isBridge())
}

val finalCandidates = withoutBridges.size match {
Expand Down Expand Up @@ -135,15 +140,17 @@ private[evaluator] class RuntimeEvaluationHelpers(frame: JdiFrame) {
}
}

def areAssignableFrom(method: Method, args: Seq[Type]): Boolean =
def areAssignableFrom(method: Method, args: Seq[Type], boxing: Boolean): Boolean =
if (method.argumentTypes().size() != args.size) false
else
method
.argumentTypes()
.asScalaSeq
.zip(args)
.forall { case (expected, got) =>
isAssignableFrom(got, expected)
.forall {
case (_: PrimitiveType, _: ReferenceType) | (_: ReferenceType, _: PrimitiveType) if !boxing => false
case (expected, got) =>
isAssignableFrom(got, expected)
}

/* -------------------------------------------------------------------------- */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,19 @@ object RuntimeEvaluatorEnvironments {
|
|case class Foo(x: Int)
|""".stripMargin

val boxingOverloads =
"""|package example
|
|object Main {
| def main(args: Array[String]): Unit = {
| println("ok")
| }
|
| def test(i: Int): String = "primitive int"
| def test(i: java.lang.Integer): String = "boxed int"
|}
|""".stripMargin
}

abstract class RuntimeEvaluatorTests(val scalaVersion: ScalaVersion) extends DebugTestSuite {
Expand All @@ -238,6 +251,8 @@ abstract class RuntimeEvaluatorTests(val scalaVersion: ScalaVersion) extends Deb
TestingDebuggee.mainClass(RuntimeEvaluatorEnvironments.hierarchyOverload, "example.Main", scalaVersion)
lazy val nested = TestingDebuggee.mainClass(RuntimeEvaluatorEnvironments.nested, "example.Main", scalaVersion)
lazy val cls = TestingDebuggee.mainClass(RuntimeEvaluatorEnvironments.cls, "example.Main", scalaVersion)
lazy val boxingOverloads =
TestingDebuggee.mainClass(RuntimeEvaluatorEnvironments.boxingOverloads, "example.Main", scalaVersion)

protected override def defaultConfig: DebugConfig =
super.defaultConfig.copy(evaluationMode = DebugConfig.RuntimeEvaluationOnly)
Expand Down Expand Up @@ -517,8 +532,21 @@ abstract class RuntimeEvaluatorTests(val scalaVersion: ScalaVersion) extends Deb
)
)
}

test("Should resolve overloads in 2 steps when boxing is available") {
implicit val debuggee = boxingOverloads
check(
Breakpoint(5),
Evaluation.success("test(1)", "primitive int"),
Evaluation.success("test(new Integer(1))", "boxed int")
)
}
}

/* -------------------------------------------------------------------------- */
/* Scala version specific tests */
/* -------------------------------------------------------------------------- */

class Scala212RuntimeEvaluatorTests extends RuntimeEvaluatorTests(ScalaVersion.`2.12`) {
test("Should access to wrapping 'object' methods") {
implicit val debuggee = nested
Expand Down

0 comments on commit e8bc704

Please sign in to comment.