This was driving me insane that Angelscript sometimes ends up with corrupted functions after module cleanup and this took me a while to get around and debug properly.
There seem to be multiple things that need to fall in line for this crash to be produced and I'm not even sure whch part to start from. Did confirm that this bug still exists on version 2.34.
The crash is to do with shared classes and compiled scripts producing duplicate versions of virtual functions for base shared classes.
*scripts
You'll need 4 scripts for an easy scenario.
One for a bunch of functions to later unload and free up engine's function array slots at the front of the array.
One for base class.
One for derived script class that has the base class definition listed after (this bit is super important).
One for derived script class that calls base class virtual function.
*bytecode compilation
How you compile the scripts also matters :(
For an easy test all the scripts should be compiled in isolation except for the last one with base class virtual function call that we'll use to crash the engine. You'll want to create a base class module before making a derived class module that you'll call “SaveByteCode” on. What this will do is write the bytecode 's' instead of ‘m’. The ‘m’ will result in a function search inside the asCModule::scriptFunctions while ‘s’ will result in a search inside asCScriptEngine::scriptFunctions. This is probably one of the issues as asCModule::scriptFunctions already seems to have features to populate with shared functions from other modules.
The other special thing in the bytecode is if you have the base shared class defined after the derived class, then the restoration of the derived class function asCReader::ReadFunction gets the bytecode ‘f’ instead of ‘r’. Because the shared derived class doesn't exist, all the arguments (bool addToModule, bool addToEngine, bool addToGC) are set to true and a new copy of the base shared class function is forced to be created.
*crash
When a duplicate base shared class function gets made, it could end up more in front of the script engine function array than the already loaded original base class module functions. The bytecode loader for our last script would then correctly populates the module script function array with functions from the shared base class module, but the “asCReader::ReadUsedFunctions()” would then use bytecode 's', search through the engine functions for a matchng one and find the new duplicate version that will be recorded for execution. If we now unload the module that created the duplicate shared base class function, well clear that duplicate function content (because it will not find a new owner module). The last script will now crash when trying to use the discarded function.
Took me a while to unravel everything. The bytecode codebase needs few more tweaks. Did not expect to get different results by just changing class definition order. Will need to make a simple example later and submit a bug.
If anyone else were getting invalid weird script function crashes from bytecodes (not sure if this also happens on raw script compilation) the the easiest thing to do would probably be to change the script compiler to include scripts in front of the file but that will mess up all the debug line numbers.