8000 Report JS tree in targetPureWasm=true in Analyzer by tanishiking · Pull Request #24 · scala-wasm/scala-wasm · GitHub
[go: up one dir, main page]

Skip to content

Report JS tree in targetPureWasm=true in Analyzer #24

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 2 commits into from
May 25, 2025

Conversation

tanishiking
Copy link
@tanishiking tanishiking commented Apr 16, 2025

cherry-picking scala-js@95d74bf

Previously, when targetPureWasm = true, JS-related trees
would lead to link failure at FunctionEmitter with an exception.
However, this report only indicated which location contained the JS interop, and not the call path leading to it.
This made it difficult to tell how the JS interop code was reached.

This commit introduces a new JSInteropInPureWasm error that specifically flags JS-related trees discovered within a reachable code path when targetPureWasm is enabled.

Now, if the Analyzer detects JS interop under these conditions, it reports not just the location but also the sequence of calls that led to that point, that improves the debugging experience for pure Wasm targets.

[error] Uses JS interop with targetPureWasm = true
[error]   dispatched from scala.collection.mutable.ArrayBuilder$.scala$collection$mutable$ArrayBuilder$$genericArrayBuilderResult(java.lang.Class,scala.scalajs.js.Array)java.lang.Object
[error]   called from scala.collection.mutable.ArrayBuilder$generic.result()java.lang.Object
[error]   dispatched from scala.collection.mutable.Builder.result()java.lang.Object
[error]   called from scala.collection.generic.GenericCompanion.apply(scala.collection.Seq)scala.collection.GenTraversable
[error]   dispatched from scala.collection.immutable.Set$.apply(scala.collection.Seq)scala.collection.GenTraversable
[error]   called from org.scalajs.testsuite.javalib.lang.ThrowablesTest.suppression()void
[error]   called from testSuiteWASI.JavalibLangTest$.run()void
[error]   called from testSuiteWASI.Run$.main([java.lang.String)void
[error]   called from static testSuiteWASI.Run.main([java.lang.String)void
[error]   called from core module module initializers
[error] involving instantiated classes:
[error]   scala.collection.mutable.ArrayBuilder$generic
[error]   scala.collection.mutable.HashSet$
[error]   scala.collection.Seq$
[error]   scala.collection.immutable.Set$
[error]   org.scalajs.testsuite.javalib.lang.ThrowablesTest
[error]   testSuiteWASI.JavalibLangTest$
[error]   testSuiteWASI.Run$

FloatingPointBits.longBitsToDouble(bits)
linkTimeIf(LinkingInfo.targetPureWasm) {
// TODO: implement for Wasm without intrinsic?
throw new AssertionError("Not implemented.")
Copy link
Collaborator

Choose a reason for hiding this comment

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

You could use (a copy of) the "polyfills" in FloatingPointBits for the 4 floating point bits conversions.

Comment on lines 227 to 228
// TODO: implement for Wasm without intrinsic?
throw new AssertionError("Not implemented.")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// TODO: implement for Wasm without intrinsic?
throw new AssertionError("Not implemented.")
(toUnsignedLong(dividend) / toUnsignedLong(divisor)).toInt

Comment on lines 237 to 238
// TODO: implement for Wasm without intrinsic?
throw new AssertionError("Not implemented.")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// TODO: implement for Wasm without intrinsic?
throw new AssertionError("Not implemented.")
(toUnsignedLong(dividend) % toUnsignedLong(divisor)).toInt

Comment on lines 294 to 295
// TODO: implement for Wasm without intrinsic?
throw new AssertionError("Not implemented.")
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can use the algorithm under "See Hacker's Delight" from clz32Dynamic just below.

Comment on lines 40 to 42
linkTimeIf(!LinkingInfo.targetPureWasm) {
if (writableStackTrace)
fillInStackTrace()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider moving this test inside fillInStackTrace() instead. User code can call fillInStackTrace; it should link IMO.

Copy link
Author

Choose a reason for hiding this comment

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

Comment on lines 54 to 56
def equals(x: Object, y: Object): Boolean =
if (scala.scalajs.LinkingInfo.targetPureWasm) {
linkTimeIf(LinkingInfo.targetPureWasm) {
equals2(x, y)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This one should be

if (x eq y) true
else equals2(x, y)

for pure Wasm.

Copy link
Author

Choose a reason for hiding this comment

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

@tanishiking tanishiking force-pushed the report-js-analyzer branch 2 times, most recently from 528f905 to ae95e4a Compare April 25, 2025 07:52
@tanishiking tanishiking force-pushed the report-js-analyzer branch from ae95e4a to 16522a3 Compare May 25, 2025 21:34
Previously, when `targetPureWasm = true`, JS-related trees
would lead to link failure at `FunctionEmitter` with an exception.
However, this report only indicated which location contained the JS interop, and not the call path leading to it.
This made it difficult to tell how the JS interop code was reached.

This commit introduces a new `JSInteropInPureWasm` error that specifically flags JS-related trees discovered within a reachable code path when `targetPureWasm` is enabled.

Now, if the Analyzer detects JS interop under these conditions, it reports not just the location but also the sequence of calls that led to that point, that improves the debugging experience for pure Wasm targets.

e.g.

```
[error] Uses JS interop with targetPureWasm = true
[error]   dispatched from scala.collection.mutable.ArrayBuilder$.scala$collection$mutable$ArrayBuilder$$genericArrayBuilderResult(java.lang.Class,scala.scalajs.js.Array)java.lang.Object
[error]   called from scala.collection.mutable.ArrayBuilder$generic.result()java.lang.Object
[error]   dispatched from scala.collection.mutable.Builder.result()java.lang.Object
[error]   called from scala.collection.generic.GenericCompanion.apply(scala.collection.Seq)scala.collection.GenTraversable
[error]   dispatched from scala.collection.immutable.Set$.apply(scala.collection.Seq)scala.collection.GenTraversable
[error]   called from org.scalajs.testsuite.javalib.lang.ThrowablesTest.suppression()void
[error]   called from testSuiteWASI.JavalibLangTest$.run()void
[error]   called from testSuiteWASI.Run$.main([java.lang.String)void
[error]   called from static testSuiteWASI.Run.main([java.lang.String)void
[error]   called from core module module initializers
[error] involving instantiated classes:
[error]   scala.collection.mutable.ArrayBuilder$generic
[error]   scala.collection.mutable.HashSet$
[error]   scala.collection.Seq$
[error]   scala.collection.immutable.Set$
[error]   org.scalajs.testsuite.javalib.lang.ThrowablesTest
[error]   testSuiteWASI.JavalibLangTest$
[error]   testSuiteWASI.Run$
```
@tanishiking tanishiking force-pushed the report-js-analyzer branch from 16522a3 to 6c8be06 Compare May 25, 2025 21:47
@inline def abs(a: scala.Double): scala.Double = js.Math.abs(a)
@inline def abs(a: scala.Float): scala.Float =
linkTimeIf(LinkingInfo.targetPureWasm) {
if (a == -0.0) 0.0f
Copy link
Collaborator

Choose a reason for hiding this comment

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

I've got a better one:

Float.intBitsToFloat(Float.floatToIntBits(a) & ~Int.MinValue)

😁

Copy link
Author

Choose a reason for hiding this comment

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

Aha right, masking all bits except the sign bit turns into abs, nice 😄
Is there any reason to use ~Int.MinValue rather than Int.MaxValue ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

MinValue is synonymous of SignBit. So it's clear that we're masking off the sign bit.

With MaxValue it's less obvious.

Copy link
Author

Choose a reason for hiding this comment

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

Makes sense 👍


@inline def abs(a: scala.Double): scala.Double =
linkTimeIf(LinkingInfo.targetPureWasm) {
if (a == -0.0) 0.0
Copy link
Collaborator

Choose a reason for hiding this comment

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

And likewise here:

Double.longBitsToDouble(Double.doubleToLongBits(a) & ~Long.MinValue)

To avoid analyzer reports the `JSInteropInPureWasm` error
@tanishiking tanishiking force-pushed the report-js-analyzer branch from 6c8be06 to eeb00ff Compare May 25, 2025 22:48
@tanishiking tanishiking merged commit fc9056d into scala-wasm May 25, 2025
8 checks passed
@tanishiking tanishiking deleted the report-js-analyzer branch May 25, 2025 23:35
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.

2 participants
0