Comments (15)
specifically, I encountered this issue when I tried to add headers to each struct I allocate into the heap. Java objects have metadata (such as to which class they belong to) and I was trying to emulate this in WASM by having every object begin with a (field i32)
(which would be initialized as a pointer to a data structure in memory). Every struct began with said i32 field; they also specified that they had a super-type which was nothing but (struct (field i32))
. When I tried to pass arbitrary references to structs (which began with the i32 field but contained other fields) into a function which expected a reference to just a (struct (field i32)), I got a cast error in the debugger. The cast error went away when I manually added the definition for the sub-type into the module inside of a recursive type definition. This isn't feasible to do automatically as I am not aware when generating a class which classes will extend it or what their field layouts would be, and things are therefore put into separate modules
from gc.
In the example should public class ClassB {
be public class ClassB extends ClassA {
?
from gc.
Can you share the text format of the modules/types you are generating? That will make it easier to spot the problem.
from gc.
In the example should
public class ClassB {
bepublic class ClassB extends ClassA {
?
good catch, i've just updated it
from gc.
Can you share the text format of the modules/types you are generating? That will make it easier to spot the problem.
sure, i'll grab them
from gc.
When defining the representation type for A you don't need to know what types extend it later. But when you define the representation type for B you of course need to know which types it extends. This is no different from Java itself.
If I understand your question correctly, all representations share a common ancestor, which is your struct with just i32. So what is the problem with declaring all types subtypes of this one?
Note that type definitions do not have to be within the same recursion group to be subtypes. You can do this:
(rec
(type $obj (sub (struct (field i32))))
)
(rec
(type $A (sub $obj (struct (field i32 i32))))
)
(rec
(type $B (sub $A (struct (field i32 i32 i32))))
)
Furthermore, if these definitions occur in multiple modules they will still be compatible, even when some of these modules do not contain $A or $B.
from gc.
When defining the representation type for A you don't need to know what types extend it later. But when you define the representation type for B you of course need to know which types it extends. This is no different from Java itself.
If I understand your question correctly, all representations share a common ancestor, which is your struct with just i32. So what is the problem with declaring all types subtypes of this one?
Note that type definitions do not have to be within the same recursion group to be subtypes. You can do this:
(rec (type $obj (sub (struct (field i32)))) ) (rec (type $A (sub $obj (struct (field i32 i32)))) ) (rec (type $B (sub $A (struct (field i32 i32 i32)))) )
Furthermore, if these definitions occur in multiple modules they will still be compatible, even when some of these modules do not contain $A or $B.
I already am defining all the classes as ancestors of the (struct (field i32)), or, at least, I'm pretty sure I am; here's the helper module I generate when the JVM first loads (the array types can be ignored, I've disabled their usage atm):
(module
(type $type0 (struct (field $field0 (mut i32))))
(type $type1 (array (field (mut structref))))
(type $type2 (array (field (mut i8))))
(type $type3 (array (field (mut i16))))
(type $type4 (array (field (mut i32))))
(type $type5 (array (field (mut i64))))
(type $type6 (array (field (mut f32))))
(type $type7 (array (field (mut f64))))
(table $jvm.objects (;0;) (import "jvm" "objects") 0 anyref)
(func $jvm.alloc_ref (;0;) (import "jvm" "alloc_ref") (result i32))
(func $obj_class (;1;) (export "obj_class") (param $var0 i32) (result i32)
local.get $var0
table.get $jvm.objects
ref.cast null $type0
struct.get $type0 $field0
)
)
Here's the shortened version of the WASM module that created the struct that's passed in. I've attached the full version as a file
(module
(type $type0 (struct (field $field0 (mut i32))))
(type $type1 (struct_subtype (field $field0 (mut i32)) $type0))
(type $type2 (struct_subtype (field $field0 (mut i32)) (field $field1 (mut i32)) $type1))
(type $type3 (struct_subtype
(field $field0 (mut i32))
(field $field1 (mut i32))
(field $field2 (mut i32))
$type2))
)
Here's the error I get in Edge's debugger
from gc.
You may be declaring your base type as final, which does not allow subtyping. Try to use struct_subtype
on the base type and not just struct
. Or, in the final text format, this:
(module
(type $type0 (sub (struct (field $field0 (mut i32)))))
(type $type1 (sub $type0 (struct (field $field0 (mut i32)))))
(type $type2 (sub $type1 (struct (field $field0 (mut i32)) (field $field1 (mut i32)))))
(type $type3 (sub $type2 (struct
(field $field0 (mut i32))
(field $field1 (mut i32))
(field $field2 (mut i32))
)))
)
Note the use of (sub ..)
for $type0
even though it has no declared supertype, without which it will error on attempts to subtype it.
from gc.
I'm using the wasm_encoder crate for generating the WASM modules. after checking my code, I can confirm that nowhere do I have it set to final for any type. The base type is also already declared as a sub-type, specifically I use this https://docs.rs/wasm-encoder/latest/wasm_encoder/struct.TypeSection.html#method.rec and all of the types in the hierarchy are in the provided iterator
from gc.
What tool are you using to generate the text here? I ask because parts of this text use older pre-standard syntax (e.g. struct_subtype
and ref.cast null
) and using an out-of-date disassembler may be hiding the real issue.
(FWIW it looks like Edge's disassembler is also out-of-date)
Even with the older text format, it looks like none of your structs are actually declaring a supertype. Could that be the issue?
from gc.
What tool are you using to generate the text here? I ask because parts of this text use older pre-standard syntax (e.g.
struct_subtype
andref.cast null
) and using an out-of-date disassembler may be hiding the real issue.(FWIW it looks like Edge's disassembler is also out-of-date)
Even with the older text format, it looks like none of your structs are actually declaring a supertype. Could that be the issue?
yeah it looks like Edge is printing it out improperly. here's it debugged with wasmprinter
(module
(rec
(type (;0;) (sub (struct (field (mut i32)))))
(type (;1;) (sub 0 (struct (field (mut i32)))))
(type (;2;) (sub 1 (struct (field (mut i32)) (field (mut i32)))))
(type (;3;) (sub 2 (struct (field (mut i32)) (field (mut i32)) (field (mut i32)))))
)
)
from gc.
and the helper class:
(module
(rec
(type (;0;) (sub (struct (field (mut i32)))))
)
(type (;1;) (array (mut structref)))
(type (;2;) (array (mut i8)))
(type (;3;) (array (mut i16)))
(type (;4;) (array (mut i32)))
(type (;5;) (array (mut i64)))
(type (;6;) (array (mut f32)))
(type (;7;) (array (mut f64)))
(type (;8;) (func (result i32)))
(type (;9;) (func (param i32) (result i32)))
(type (;10;) (func (param i32 i32) (result i32)))
(type (;11;) (func (param i32 i32 i32)))
(import "jvm" "objects" (table (;0;) 0 anyref))
(import "jvm" "alloc_ref" (func (;0;) (type 8)))
(func (;1;) (type 9) (param i32) (result i32)
local.get 0
table.get 0
ref.cast (ref null 0)
struct.get 0 0
)
(export "obj_class" (func 1))
)
from gc.
Alright, great, I think I know what the issue is now.
In the one module, your top Object type ((type (;0;) (sub (struct (field (mut i32)))))
) is declared in the same rec group as a bunch of other things and in the other module it is in its own rec group.
Since type identity is determined by index into a rec group and these Object types are defined in rec groups with different structures, you're actually defining two different Object types rather than the same Object type in two places. That's why the cast is failing; the Object types are incompatible with each other.
The fix is to make sure the rec groups are the same in every module, probably by making them as small as possible, as @rossberg suggested.
from gc.
I think I understand, I'll try it out and report back. tysm!
from gc.
making all of the struct types be in their own recursive group fixed my issue. thank you for being so helpful!
from gc.
Related Issues (20)
- Type matching with recursive types HOT 8
- In the latest standard, ref.extern is gone, but its use still exists in test. HOT 3
- Type equivalence in modifiend spec HOT 1
- [spec] array.init_elem: bounds check correct? HOT 1
- Request for clarification: is this Wasm valid or not? HOT 2
- validation rule of `ref.is_null`, `ref.as_non_null` and `ref.cast` HOT 7
- Dynamically initializing a non mutable array HOT 4
- Does the combo of subtyping and unreachable code require actual type inference? HOT 41
- [js-api] Throw LinkError consistently
- How to reinterpret `(array f32)` to `(array u8)`? HOT 1
- [Question] About GC heap area and linear memory HOT 1
- Working with wit component HOT 4
- Inconsistency in validating global HOT 2
- Array copy specification potential incorrect mutable assertion HOT 4
- Bounds check is possible wrong on array.copy/array.fill HOT 1
- Type of active element segments HOT 10
- Effects of optimized out instructions HOT 5
- Portability hazards of i31ref with non-native engines HOT 1
- Questions about multiple inheritance HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from gc.