1. Re: bind/shroud bug
- Posted by "Cuny, David" <David.Cuny at DSS.CA.GOV> Jun 25, 1999
- 423 views
Putting together a test for the routine_id problem, I discovered that I've ecountered a problem with bind/shroud, not routine_id. Here's an example that demonstrates the bug; it uses 3 files: --- file: TEST1.E --- procedure test() end procedure global constant test1 = routine_id("test") --- file: TEST2.E --- procedure test() end procedure global constant test1 = routine_id("test") --- file: TEST.EX -- include test1.e include test2.e ? test1 ? test2 --- end of files --- If you run the interpreted version of the code, you get the results: 0 1 This indicates that each constant resolved to a *different* function. I maintain that a return value of 0 is confusing (well, *I* keep tripping up on it), but that's a different kettle of fish. Now, if you bind the file, with -clear_routines: c:\euphoria> bind -clear_routines test.ex creating test.exe ... local symbol test conflicts with an earlier symbol it will be renamed as C. executing the program gets the result: 0 0 The first problem is that since bind renamed the second procedure to 'C' (insert obligatory C programming joke here), it's effectively invisible to routine_id. The second problem is that the first routine is local to a different include file. Scoping rules say that the second routine_id shouldn't see it at all. I've tested this with a shrouded file, and gotten the same results. Now, I *know* I can solve the problem by renaming the routines. But that sidesteps the real issue here: bound/shrouded code BEHAVES DIFFERENTLY. This is a Bad Thing, and people are not going to be able to convince me otherwise. Why do the errors happen? My guess is that it has to do with how bind/shroud merge multiple files into a single file. It would appear that they try to convert the files into something like this: --- bound/shrouded file without clear_routines --- procedure A() end procedure global constant B = routine_id("test") procedure C() end procedure global constant D = routine_id("test") ? B ? D --- end bound/shrouded file without clear_routines --- The reason that bind/shroud renames your routines is not just to make your code more compact or secure. Since all the routines are placed into the same module, there are no local module items any more. Renaming avoids name collisions. As is obvious from the above example, routine_id will fail, because there are no longer any routines called "test" - they are now named "A" and "B". Euphoria knows this, and issues a warning that you need to use the -clear_routines option if it sees routine_id in your code. If -clear_routines simply left the routine names alone, it would create this: --- shrouded file with -clear_routine --- procedure test() end procedure global constant test1 = routine_id("test") procedure test() end procedure global constant test2 = routine_id("test") ? test1 ? test2 --- end shrouded file with -clear_routine --- This obviously won't work, because there are two routines with the name 'test'. Robert's solution was clever, but (sorry to say this) wrong: if there is a name conflict, only allow the first routine to keep it's original name. So the resulting file (apparently) looks like this: --- shrouded file with -clear_routine --- procedure test() end procedure global constant test1 = routine_id("test") procedure C() end procedure global constant test2 = routine_id("test") ? test1 ? test2 --- end shrouded file with -clear_routine --- Note that the first version of 'test' keeps the name, but the second is renamed to 'C'. (The naming of the procedure to 'C' instead of 'A' hints at how bind/shround was modified, but that's another tangent.) Why would Robert use a solution like this? My guess is that it meant he would only have to rewrite the bind/shroud routine, instead of altering Euphoria itself. The inclusion of routine_id into Euphoria was causing major hassles, and this was a quick fix. But it's a bad fix, because it causes code to behave differently once it's been bound/shrouded. As Robert says in the C.DOC file about why Euphoria is better than C: because you hate it when your program starts working just because you added a debug print statement or compiled with the debug option I say the same thing about bind/shroud. So here's the conflict: bind/shroud needs to rename things (so they happily coexist in a single namespace), while routine_id needs the actual name. I suggest (in my infinite naivite) that a good solution should be fairly easy to obtain - just add two new tokens to bind/shroud: <<start of include>> <<end of include>> These would serve to simulate what include does - tell the interpreter that a new file is being loaded, or exited. Here's what the bound/shrouded file would look like (with -clear_routines): -- suggested solution -- <<start of include>> procedure test() end procedure global constant test1 = routine_id("test") <<end of include>> <<start of include>> procedure test() end procedure global constant test1 = routine_id("test") <<end of include>> ? test1 ? test2 -- end suggested solution -- Adding these tokens would ensure that bound/shrouded files are built internally the same as plain interpreted programs. I think that this is a good solution because it: - makes code bound/shrouded code run correctly - should be easy to implement - doesn't add any new features - doesn't break any old features - is backward compatible - is simple Comments? -- David Cuny