diff --git a/src/ChibiRuby.Serializer/Resolvers/GeneratedResolver.cs b/src/ChibiRuby.Serializer/Resolvers/GeneratedResolver.cs index a23bb8d2..5da7d254 100644 --- a/src/ChibiRuby.Serializer/Resolvers/GeneratedResolver.cs +++ b/src/ChibiRuby.Serializer/Resolvers/GeneratedResolver.cs @@ -26,8 +26,9 @@ static Cache() static bool TryInvokeRegisterFormatter(Type type) { - if (type.GetCustomAttribute() == null) return false; - + // Do not gate on [MRubyObject] here: Unity 6000.5+'s linker strips instances of + // PreserveAttribute-derived attributes from player builds, so the attribute can be + // absent at runtime even for generated types. The generated method is the reliable signal. var m = type.GetMethod("__RegisterMRubyValueFormatter", BindingFlags.Public | BindingFlags.NonPublic | diff --git a/tests/ChibiRuby.Serializer.Tests/Classes.cs b/tests/ChibiRuby.Serializer.Tests/Classes.cs index 91ec4e64..f29b9524 100644 --- a/tests/ChibiRuby.Serializer.Tests/Classes.cs +++ b/tests/ChibiRuby.Serializer.Tests/Classes.cs @@ -25,3 +25,24 @@ partial struct Struct1 { public long Id { get; set; } } + +// Simulates a source-generated [MRubyObject] type after Unity 6000.5's linker has stripped the +// attribute instance (#199): the generated registration method exists, but the attribute does not. +class AttributeStrippedObject +{ + public int Value { get; set; } + + public static void __RegisterMRubyValueFormatter() + { + GeneratedResolver.Register(new AttributeStrippedObjectFormatter()); + } + + class AttributeStrippedObjectFormatter : IMRubyValueFormatter + { + public MRubyValue Serialize(AttributeStrippedObject? value, MRubyState mrb, MRubyValueSerializerOptions options) + => value is null ? default : new MRubyValue(value.Value); + + public AttributeStrippedObject? Deserialize(MRubyValue value, MRubyState mrb, MRubyValueSerializerOptions options) + => new() { Value = checked((int)value.IntegerValue) }; + } +} diff --git a/tests/ChibiRuby.Serializer.Tests/GeneratedFormatterTest.cs b/tests/ChibiRuby.Serializer.Tests/GeneratedFormatterTest.cs index 56376185..237e317d 100644 --- a/tests/ChibiRuby.Serializer.Tests/GeneratedFormatterTest.cs +++ b/tests/ChibiRuby.Serializer.Tests/GeneratedFormatterTest.cs @@ -95,4 +95,16 @@ public void DeserializeWithCtor() Assert.That(result.Y, Is.EqualTo(456)); Assert.That(result.Hoge, Is.EqualTo("hello hello")); } + + [Test] + public void RegisterFormatterWithoutAttributeInstance() + { + // The [MRubyObject] attribute instance may be stripped by Unity 6000.5+'s linker (#199). + // Registration must work based on the generated method alone. + var result = MRubyValueSerializer.Deserialize(new MRubyValue(42), state)!; + Assert.That(result.Value, Is.EqualTo(42)); + + var serialized = MRubyValueSerializer.Serialize(new AttributeStrippedObject { Value = 43 }, state); + Assert.That(serialized, Is.EqualTo(new MRubyValue(43))); + } }