Semantics of Multiple Routine Replacement (I6)

The compiler accepts both of the following I6 programs, although OtherOriginalMain in the second remains a zero constant. Are they supposed to be valid?

(I ask because I finally got around to writing the debug information patch, but it doesn’t handle corner cases like these, so I need to know if and how I should fix that.)

Replace Main OriginalMain; [ Main; print "Original."; ]; [ Main; print "Replacement."; OriginalMain(); ]; [ Main; print "Another Replacement."; OriginalMain(); ];Replace Main OriginalMain; Replace Main OtherOriginalMain; [ Main; print "Original."; ]; [ Main; print "Replacement."; OriginalMain(); OtherOriginalMain(); ];

Whoops, forgot about this post from a few days ago.

I need to run some tests. (Yes, on the code I wrote myself…) The first case has always been acceptable using the old “Replace Main;” form; I don’t remember whether the first replacement or the last replacement sticks, but it should be the same with the new “Replace Main Orig” form.

So, the first thing is that replacing Main() is iffy. It’s a special case – it’s supposed to be the first compiled function. I have a feeling that replacing it like that may potentially break Z-code compilation, by changing Main() to an address beyond $FFFF.

Ignoring that, and pretending that you’re replacing arbitrary functions…

The first example is valid code. It should print “Another replacement.^Original.^” The middle function will not be assigned to any symbol; it will wind up generating code, but it won’t be callable.

The second example should be valid, and both OriginalMain and OtherOriginalMain should wind up with the “Original” function. This currently doesn’t work, but I’m willing to file it as a bug and fix it.

Alternatively, we could decide that this is invalid, on the grounds that if two different pieces of code (two different libraries, let’s say) try to override a function, the result is not going to be what either of them want. The most likely way this happens is:

! Library 1: dancing
Replace Foo OrigFoo1;
[ Foo;
! perform dancing
OrigFoo1();
];

! Library 2: singing
Replace Foo OrigFoo2;
[ Foo;
! perform singing
OrigFoo2();
];

Result: library 1’s code is completly skipped, as per previous case. This is bad.

The third alternative is to try to chain them (so library 2 winds up calling library 1’s code…) but this is a step too close to trying to recreate rulebooks out of stone knives and bearskins. I don’t wanna.

Any opinions? Fix bug, or add an error message?

That’s a good catch. I feel like there should be an error message for replacing Main then, obscure though it may be. I can toss one in a second patch if you don’t beat me to it.

Okay. That’s not what I was hoping for, but I can make it work.

On that topic, I’m currently distinguishing the routine being replaced from its replacements by looking at the stypes entry at the time of parse, either CONSTANT_T or ROUTINE_T. Is there some other flag that I missed, or is that correct? (I thought there was some policy about library files always losing out, but I haven’t traced enough code yet to know if that implies some special casing.)

I’m strongly for an error message here, first because the example you gave seems far more likely than the code pattern I posted and second because allowing multiple replacement would mean that each routine could have several symbols, and that complicates life for debuggers without much practical gain.

Yeah, I agree. If we want to loosen the rules up later, we can.

I will file the issues.

Now that I look, it’s safe to replace Main(). It’s unsafe to replace Main__(), but that already produces an error, so it’s covered.