Skip to content
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

Repl truncation copes with null #17336

Merged
merged 3 commits into from
Mar 1, 2024
Merged

Conversation

som-snytt
Copy link
Contributor

Fixes #17333

initially:
run("val tpolecat = new Object { override def toString(): String = null }")
.andThen:
assertEquals("val tpolecat: Object = null // non-null reference has null-valued toString", lines().head)
Copy link
Member

@bishabosha bishabosha Apr 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is easier to understand, what do you think?

Suggested change
assertEquals("val tpolecat: Object = null // non-null reference has null-valued toString", lines().head)
assertEquals("val tpolecat: Object = null // .toString() was null", lines().head)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I wondered even, return value of .toString() was null after the NPE message

scala> X.toString.toString
java.lang.NullPointerException: Cannot invoke "Object.toString()" because the return value of "X$.toString()" is null

and now looking at it, I wonder if it is more useful and less redundant to use the default toString

scala> X
val res5: X.type = X$@23ba2c1e // return value of "X$.toString()" is null

or similar.

@@ -95,8 +98,9 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None):
"replStringOf should only be called on values creating using `classLoader()`, but `classLoader()` has not been called so far")
val maxPrintElements = ctx.settings.VreplMaxPrintElements.valueIn(ctx.settingsState)
val maxPrintCharacters = ctx.settings.VreplMaxPrintCharacters.valueIn(ctx.settingsState)
val res = myReplStringOf(value, maxPrintElements, maxPrintCharacters)
if res == null then "null // non-null reference has null-valued toString" else res
Option(value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if value is already expected to be non-null

Suggested change
Option(value)
Some(value)

Copy link
Contributor

@Kordyjan Kordyjan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution. I like the idea of the comment explaining that the reference was not null even though .toString is null. Nevertheless, it seems that it is also shown for null references. That causes fail in one of test cases run by the ScriptedTests.

@som-snytt
Copy link
Contributor Author

Thanks for reviews. I will follow up expeditiously.

@som-snytt som-snytt marked this pull request as draft April 27, 2023 17:27
@som-snytt
Copy link
Contributor Author

TIL toIdentityString is jdk 19. It seems so long ago. probably because of the "ea" program giving it a long runway.

@som-snytt som-snytt marked this pull request as ready for review April 28, 2023 11:07
@som-snytt
Copy link
Contributor Author

The update corrects null handling and "improves" the message.

Someone somewhere asked why is Scala 3 REPL relying on Scala 2 runtime to "render a thing". After this tweak, and realizing that the Scala 2 hasn't been "upgraded", I have the same question. Usually I prefer "code sharing", but probably Scala 3 should just ingest the stringOf code. That is for printing arrays and other things.

There is an issue about rendering infinite things; I haven't tested whether the code which tests the lengths of renderings actually works in that case.

Also refactor the stringOf reflection to lookup method once.
Add worst-case fallback to use String.valueOf.
Re-enable some tests which appear not to crash.
@bishabosha
Copy link
Member

I think it might be best to just do null // "res0.toString" was null, because otherwise you have inconsistency when objects are nested

scala> class Foo { override def toString = null }
// defined class Foo

scala> Foo()
val res0: Foo = Foo@4f7150bd // return value of "res0.toString" is null

scala> List(Foo())
val res1: List[Foo] = List(null)

scala>

@bishabosha bishabosha merged commit 18504b9 into scala:main Mar 1, 2024
18 checks passed
@som-snytt som-snytt deleted the issue/17333-repl-null branch March 1, 2024 21:04
@Kordyjan Kordyjan added this to the 3.4.2 milestone Mar 28, 2024
WojciechMazur added a commit that referenced this pull request Jul 3, 2024
Backports #17336 to the LTS branch.

PR submitted by the release tooling.
[skip ci]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

REPL crash when toString returns null
3 participants