Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

Fix #12: Implement the behavior of hijacked classes. #13

Merged
merged 10 commits into from
Mar 13, 2024

Conversation

sjrd
Copy link
Collaborator

@sjrd sjrd commented Mar 9, 2024

So I got excited and carried away, and I implemented the proposed design for handling hijacked classes.

Based on #11.

Copy link
Owner

@tanishiking tanishiking left a comment

Choose a reason for hiding this comment

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

@sjrd Thank you very much for this great work!

First of all, I read through the proposed design in #12 and I think preserving the Scala.js semantics and delegating the string implementation to JS is the best option for now (at least for targeting browser Wasm).
I'll leave some comments/questions on the original issue, but I believe this design looks good to me 👍

For this change itself, I left some questions and comments, but it looks great to me 👍
While it would be great to add some more explanation or link into the code comment (as I commented in the code), but I think we can merge this PR anytime soon :)

wasm/src/main/scala/Compiler.scala Show resolved Hide resolved
wasm/src/main/scala/wasm4s/Instructions.scala Show resolved Hide resolved
@@ -396,15 +396,15 @@ class WasmBuilder {
)(implicit ctx: WasmContext): WasmFunction = {
val receiver = WasmLocal(
Names.WasmLocalName.receiver,
// Receiver type for non-constructor methods needs to be Object type because params are invariant
// Receiver type for non-constructor methods needs to be `(ref any)` because params are invariant
Copy link
Owner

Choose a reason for hiding this comment

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

👍

Comment on lines 201 to 215
case IRTypes.CharType =>
genBox(IRTypes.CharType, SpecialNames.CharBoxClass)
case IRTypes.LongType =>
genBox(IRTypes.LongType, SpecialNames.LongBoxClass)
Copy link
Owner

Choose a reason for hiding this comment

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

[note]

Representation of char and long
These are opaque to JS in the Scala.js semantics. We can implement them with real Wasm classes following the correct vtable. Upcasting will wrap a primitive into the corresponding class, and downcasting will extract the primitive from the field. This would not leave Wasm code.
#12

* block (ref any) $done
* block anyref $notOurObject
* load receiver
* br_on_cast_fail anyref (ref $targetRealClass) $notOurObject
Copy link
Owner

Choose a reason for hiding this comment

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

[question]

Suggested change
* br_on_cast_fail anyref (ref $targetRealClass) $notOurObject
* br_on_cast_fail anyref (ref $java.lang.Object) $notOurObject

?

and, this cast gonna success only for long and char, am I right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It will succeed for long and char, as well as for any real class. We are in a situation where the receiver's static type is an ancestor of a hijacked class (e.g., java.lang.CharSequence, or even any). So at run-time we can have either primitives (possibly boxed for long and char) or real class instances.

loader.mjs Outdated
Comment on lines 68 to 81
// Hash code, because it is overridden in all hijacked classes
jsValueHashCode: (x) => {
if (typeof x === 'number')
return x | 0; // TODO make this compliant for floats
if (typeof x === 'string')
return stringHashCode(x);
if (typeof x === 'boolean')
return x ? 1231 : 1237;
if (typeof x === 'undefined')
return 0;
return 42; // for any JS object
},
Copy link
Owner

Choose a reason for hiding this comment

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

[question]
I'm curious how they are calculated, is there a spec I can refer to?

Comment on lines 13 to 14
val charValueField = FieldName("value")
val longValueField = FieldName("value")
Copy link
Owner

Choose a reason for hiding this comment

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

unused?

Suggested change
val charValueField = FieldName("value")
val longValueField = FieldName("value")

@sjrd
Copy link
Collaborator Author

sjrd commented Mar 12, 2024

Thanks for the review. I'll integrate all your comments in the code.

sjrd added 10 commits March 12, 2024 15:58
This will allow us to add helper JS functions that the Wasm code
can import.
…`string`.

For `int`s, we use JavaScript helpers.
Using a JavaScript helper in all cases for now.
This correctly implements the interoperability semantics of strings,
which are now seen by JavaScript as primitive strings.

It also allows us to correctly and efficiently implement the various
conversions to string.

However, it makes primitive operations worse. Hopefully the
`stringref` proposal will come to save us one day.
Always returning the same value is technically in-spec for objects,
although that defeats its purpose, of course.
In actual classes that we automatically derive from the
corresponding hijacked classes.
When the type of the receiver of an `Apply` is an ancestor of any
hijacked class, it might contain a JS value instead of one of our
own objects. In that case, we must perform a more elaborate
dispatch that tests whether we have a true object, or whether we
must dispatch to a hijacked class method.

This change also surfaces that the methods of `j.l.Object` can
receive an arbitrary non-null value, i.e., a `(ref any)`. This has
to be reflected in vtables and itables, unfortunately.
Including with dispatch on potentially hijacked classes, and all
the weird null handling corner cases.
@sjrd sjrd force-pushed the upcast-downcast-hijacked-classes branch from 5149c84 to 5acf283 Compare March 12, 2024 16:31
@sjrd
Copy link
Collaborator Author

sjrd commented Mar 12, 2024

I have now integrated all your questions and notes as additional comments in the code.

@tanishiking
Copy link
Owner

Thank you very much @sjrd for adding a lot of comments, that helps me a lot 😍

@tanishiking tanishiking merged commit 334446d into tanishiking:main Mar 13, 2024
1 check passed
@sjrd sjrd deleted the upcast-downcast-hijacked-classes branch March 13, 2024 02:38
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants