Open EyeDeck opened 6 years ago
Some extra weirdness. Commenting out some of the previous calls to Debug.trace
results in what looks like proper output (I have not ran it)
Function testBoolStruct() Global
testStruct structA = new testStruct
testStruct structB = new testStruct
structA.b = false
structA.i = 0
structA.f = 0
structA.O = Game.GetForm(0x14) as ObjectReference ; PlayerRef
structB.b = true
structB.i = 1
structB.f = 1
structB.O = Game.GetForm(0x22B94) as ObjectReference ; some arbitrary rock somewhere
bool bFalse = false
bool bTrue = true
int i0 = 0
int i1 = 1
float f0 = 0
float f1 = 1
ObjectReference PlayerRef = Game.GetForm(0x14) as ObjectReference
ObjectReference Rock = Game.GetForm(0x22B94) as ObjectReference
Debug.Trace("##############################")
; these work properly
Debug.Trace("structA: " + structA.b)
Debug.Trace("structB: " + structB.b)
; Debug.Trace("structA.b || structB.b: " + (structA.b || structB.b))
; Debug.Trace("structB.b || structA.b: " + (structB.b || structA.b))
; Debug.Trace("false || (bTrue != bFalse): " + (false || (bTrue != bFalse)))
; Debug.Trace("false || (i0 != i1): " + (false || (i0 != i1)))
; Debug.Trace("false || (f0 != f1): " + (false || (f0 != f1)))
; Debug.Trace("false || (PlayerRef != Rock): " + (false || (PlayerRef != Rock)))
; Debug.Trace("false || (structA.i != structB.i): " + (false || (structA.i != structB.i)))
; Debug.Trace("false || (structA.f != structB.f): " + (false || (structA.f != structB.f)))
; Debug.Trace("false || (structA.O != structB.O): " + (false || (structA.O != structB.O)))
; this one doesn't, with Caprica
Debug.Trace("false || (structA.b != structB.b): " + (false || (structA.b != structB.b)))
Debug.Trace("##############################")
EndFunction
The assembly:
ASSIGN ::temp2 False
JUMPT ::temp2 label0
STRUCTGET ::temp2 structA b
STRUCTGET ::temp4 structB b
COMPAREEQ ::temp4 ::temp2 ::temp4
NOT ::temp4 ::temp4
ASSIGN ::temp2 ::temp4
label0:
CAST ::temp3 ::temp2
STRCAT ::temp3 "false || (structA.b != structB.b): " ::temp3
CALLSTATIC debug Trace ::nonevar ::temp3 0
The two STRUCTGET
instructions store to different temporaries.
Hmm, it appears to be related to reuse of earlier temp variables created for the same function.
scriptname boolTestC
struct testStruct
bool b
endStruct
Function TestBoolStruct()
testStruct structA = new testStruct
testStruct structB = new testStruct
structA.b = false
structB.b = true
Debug.Trace("##############################")
Debug.Trace(structA.b != structB.b)
Debug.Trace(structA.b != structB.b)
Debug.Trace("##############################")
EndFunction
compiles down to
.function TestBoolStruct
.userFlags 0 ; Flags: 0x00000000
.docString ""
.return None
.paramTable
.endParamTable
.localTable
.local structA booltestc#teststruct
.local ::temp0 booltestc#teststruct
.local structB booltestc#teststruct
.local ::nonevar None
.local ::temp1 Bool
.local ::temp2 Bool
.local ::temp3 String
.endLocalTable
.code
StructCreate ::temp0 ;@line 8
Assign structA ::temp0 ;@line 8
StructCreate ::temp0 ;@line 9
Assign structB ::temp0 ;@line 9
StructSet structA b False ;@line 11
StructSet structB b True ;@line 12
CallStatic debug Trace ::nonevar "##############################" 0 ;@line 14
StructGet ::temp1 structA b ;@line 15
StructGet ::temp2 structB b ;@line 15
CompareEQ ::temp2 ::temp1 ::temp2 ;@line 15
Not ::temp2 ::temp2 ;@line 15
Cast ::temp3 ::temp2 ;@line 15
CallStatic debug Trace ::nonevar ::temp3 0 ;@line 15
StructGet ::temp2 structA b ;@line 16
StructGet ::temp2 structB b ;@line 16
CompareEQ ::temp2 ::temp2 ::temp2 ;@line 16
Not ::temp2 ::temp2 ;@line 16
Cast ::temp3 ::temp2 ;@line 16
CallStatic debug Trace ::nonevar ::temp3 0 ;@line 16
CallStatic debug Trace ::nonevar "##############################" 0 ;@line 17
.endCode
.endFunction
I haven't tried running this variation either, but presumably the first line would correctly return true, and the second one, incorrectly, false.
So, I ran into a confusing thing earlier, where one line of code in one of my scripts wasn't working as expected. I investigated, and here's some test code that demonstrates the bug:
I compiled two separate scripts, identical except that one was compiled with the Bethesda compiler, and the other with Caprica (boolTestB and boolTestC respectively), and here's the log output, again in the same order:
Also, here's the assembly after running each through Bethesda's disassembler:
It appears that Caprica gets really confused and starts assigning everything to ::temp3 for some reason. This also makes Champollion fail miserably on the Caprica script, it just gives up when it gets to that section. This was compiled with the now 2-year-old v0.2.0 release binary from the Nexus, without the -O flag since I know it's borked in that version.
Also, here's a link to an archive with the sources, compiled scripts, Caprica .pas output (for boolTestC), and disassembled .pas outputs for both, in case it helps any.
This is easy enough to work around, but really confusing if you run into it like I did. Any ideas?