From 911204b5e225aee6251818fcf7b48666c29fe4cd Mon Sep 17 00:00:00 2001 From: AutisticLulu Date: Thu, 13 Aug 2026 15:36:05 +0200 Subject: [PATCH 1/3] Give a cppia member field declared Bool its boolean storage CppiaVar::linkVarTypes has two forms. The static form asks fieldStorageFromType, which answers fsBool for Bool. The member form switches on exprType instead, and TypeData::link has already mapped Bool onto etInt by the time it runs, so the field is laid out as fsInt and reads back through reflection as 1 rather than true. The member form now asks fieldStorageFromType as well. The slot stays the size of an int, leaving the layout and the AlignOffset call above it unchanged. The cppia test suite covers it. --- src/hx/cppia/CppiaVars.cpp | 17 ++++++++++------- test/cppia/Client.hx | 9 +++++++++ test/cppia/cases/TestCommon.hx | 12 ++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/hx/cppia/CppiaVars.cpp b/src/hx/cppia/CppiaVars.cpp index ad98ca47e..37ed9a83c 100644 --- a/src/hx/cppia/CppiaVars.cpp +++ b/src/hx/cppia/CppiaVars.cpp @@ -180,14 +180,17 @@ void CppiaVar::linkVarTypes(CppiaModule &cppia, int &ioOffset) AlignOffset(exprType, ioOffset); offset = ioOffset; - switch(exprType) + storeType = typeId==0 ? fsObject : fieldStorageFromType(type); + + switch(storeType) { - case etInt: ioOffset += sizeof(int); storeType=fsInt; break; - case etFloat: ioOffset += sizeof(Float);storeType=fsFloat; break; - case etString: ioOffset += sizeof(String);storeType=fsString; break; - case etObject: ioOffset += sizeof(hx::Object *);storeType=fsObject; break; - case etVoid: - case etNull: + case fsBool: ioOffset += sizeof(int); break; + case fsByte: ioOffset += sizeof(int); break; + case fsInt: ioOffset += sizeof(int); break; + case fsFloat: ioOffset += sizeof(Float); break; + case fsString: ioOffset += sizeof(String); break; + case fsObject: ioOffset += sizeof(hx::Object *); break; + case fsUnknown: break; } } diff --git a/test/cppia/Client.hx b/test/cppia/Client.hx index 710373f4b..41d951021 100644 --- a/test/cppia/Client.hx +++ b/test/cppia/Client.hx @@ -14,6 +14,15 @@ class ClientFoo implements IFoo { } } +class ClientBoolField { + + public var flag:Bool = true; + + public static var staticFlag:Bool = true; + + public function new() {} +} + class Client { public static var clientBool0 = true; diff --git a/test/cppia/cases/TestCommon.hx b/test/cppia/cases/TestCommon.hx index eaacb5a76..770a48f9e 100644 --- a/test/cppia/cases/TestCommon.hx +++ b/test/cppia/cases/TestCommon.hx @@ -59,6 +59,18 @@ class TestCommon extends Test { Assert.equals(2, Common.callbackSet, 'Bad cppia closure'); } + @:depends(testStatus) + function testBoolMemberStorage() { + final cls = Type.resolveClass('ClientBoolField'); + + if (Assert.notNull(cls, 'Unable to resolve ClientBoolField')) { + final obj = Type.createInstance(cls, []); + + Assert.equals('true', Std.string(Reflect.field(obj, 'flag')), 'Member Bool did not read back as a boolean'); + Assert.equals('true', Std.string(Reflect.field(cls, 'staticFlag')), 'Static Bool did not read back as a boolean'); + } + } + @:depends(testStatus) function testInterfaceCalling() { final obj : IFoo = Type.createInstance(Type.resolveClass('ClientFoo'), []); From 11ead717d4b7dc848a3d5ed9a1c91a115bb55603 Mon Sep 17 00:00:00 2001 From: AutisticLulu Date: Sun, 16 Aug 2026 13:49:05 +0200 Subject: [PATCH 2/3] Cover the script and write paths for a Bool member Reflect.field only reaches CppiaVar::getValue, which became correct the moment storeType changed. It never touches MemReference or the jtByte path the JIT uses for the field, which is where the risk in this change actually sits. ClientBoolField gains a false-valued member and methods that read the fields from script, stringify one, and branch on both. The new cases add a script-side read, a script write read back through reflection, and a reflection write read back through script. Reverting CppiaVars.cpp fails all three cases in jit and interp. Direct reads and branches pass either way since nonzero is truthy, so it is the stringify and the read back that catch it. --- test/cppia/Client.hx | 13 +++++++++++ test/cppia/cases/TestCommon.hx | 41 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/test/cppia/Client.hx b/test/cppia/Client.hx index 41d951021..7cd44d361 100644 --- a/test/cppia/Client.hx +++ b/test/cppia/Client.hx @@ -17,10 +17,23 @@ class ClientFoo implements IFoo { class ClientBoolField { public var flag:Bool = true; + public var offFlag:Bool = false; public static var staticFlag:Bool = true; public function new() {} + + public function readFlag():Bool return flag; + + public function readOffFlag():Bool return offFlag; + + public function flagToString():String return "" + flag; + + public function branchOnFlag():Int return flag ? 10 : 20; + + public function branchOnOffFlag():Int return offFlag ? 10 : 20; + + public function clearFlag():Bool { flag = false; return flag; } } class Client diff --git a/test/cppia/cases/TestCommon.hx b/test/cppia/cases/TestCommon.hx index 770a48f9e..19f8b0a7c 100644 --- a/test/cppia/cases/TestCommon.hx +++ b/test/cppia/cases/TestCommon.hx @@ -71,6 +71,47 @@ class TestCommon extends Test { } } + @:depends(testStatus) + function testBoolMemberFromScript() { + final cls = Type.resolveClass('ClientBoolField'); + + if (Assert.notNull(cls, 'Unable to resolve ClientBoolField')) { + final obj = Type.createInstance(cls, []); + + Assert.equals(true, Reflect.callMethod(obj, Reflect.field(obj, 'readFlag'), []), + 'Script read of a true Bool member failed'); + Assert.equals(false, Reflect.callMethod(obj, Reflect.field(obj, 'readOffFlag'), []), + 'Script read of a false Bool member failed'); + Assert.equals('true', Reflect.callMethod(obj, Reflect.field(obj, 'flagToString'), []), + 'Bool member did not stringify as a boolean'); + Assert.equals(10, Reflect.callMethod(obj, Reflect.field(obj, 'branchOnFlag'), []), + 'Branch on a true Bool member took the wrong arm'); + Assert.equals(20, Reflect.callMethod(obj, Reflect.field(obj, 'branchOnOffFlag'), []), + 'Branch on a false Bool member took the wrong arm'); + } + } + + @:depends(testStatus) + function testBoolMemberWrite() { + final cls = Type.resolveClass('ClientBoolField'); + + if (Assert.notNull(cls, 'Unable to resolve ClientBoolField')) { + final obj = Type.createInstance(cls, []); + + Assert.equals(false, Reflect.callMethod(obj, Reflect.field(obj, 'clearFlag'), []), + 'Script write of a Bool member did not stick'); + Assert.equals('false', Std.string(Reflect.field(obj, 'flag')), + 'Reflection did not see the script write'); + + Reflect.setField(obj, 'flag', true); + + Assert.equals(true, Reflect.callMethod(obj, Reflect.field(obj, 'readFlag'), []), + 'Script did not see the reflection write'); + Assert.equals(10, Reflect.callMethod(obj, Reflect.field(obj, 'branchOnFlag'), []), + 'Branch did not see the reflection write'); + } + } + @:depends(testStatus) function testInterfaceCalling() { final obj : IFoo = Type.createInstance(Type.resolveClass('ClientFoo'), []); From 6920a42055602514ab98c425f7015e591a6e8391 Mon Sep 17 00:00:00 2001 From: AutisticLulu Date: Tue, 18 Aug 2026 22:17:31 +0200 Subject: [PATCH 3/3] Use sTypeSize for the member offset advance Every storage fieldStorageFromType returns maps onto an exprType with the same size, so the switch collapses to a table lookup and the offsets stay byte-identical. --- src/hx/cppia/CppiaVars.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/hx/cppia/CppiaVars.cpp b/src/hx/cppia/CppiaVars.cpp index 37ed9a83c..e97e82344 100644 --- a/src/hx/cppia/CppiaVars.cpp +++ b/src/hx/cppia/CppiaVars.cpp @@ -182,17 +182,7 @@ void CppiaVar::linkVarTypes(CppiaModule &cppia, int &ioOffset) storeType = typeId==0 ? fsObject : fieldStorageFromType(type); - switch(storeType) - { - case fsBool: ioOffset += sizeof(int); break; - case fsByte: ioOffset += sizeof(int); break; - case fsInt: ioOffset += sizeof(int); break; - case fsFloat: ioOffset += sizeof(Float); break; - case fsString: ioOffset += sizeof(String); break; - case fsObject: ioOffset += sizeof(hx::Object *); break; - case fsUnknown: - break; - } + ioOffset += sTypeSize[exprType]; } }