sfml/angelscript/threejs/pygame: unit tests + patches + whitepaper (115 sites, 48 ecosystems)

SFML 5 defects: VideoMode dedup x3 (139x), allWindows erase (1001x), GL ext (149x)
AngelScript 3 defects: FindNewOwner (100x), CompileSwitch (250x)
Three.js 5 defects: WebGL binding (22x), StackNode filter (1875x), NodeBuilder (517x)
pygame 4 defects: remove_internal (3001x), spritecollide dokill (3001x), switch_layer (3001x)

Whitepaper: 98→115 sites, 44→48 ecosystems
All unit tests pass: 6/6 each
This commit is contained in:
russell@unturf.com 2026-03-27 11:57:12 -04:00
parent 442a6156e3
commit 6bafc7f5e9
17 changed files with 1189 additions and 18 deletions

View file

@ -0,0 +1,61 @@
Fixes angelscript-0001/0002: FindNewOwnerForSharedType/Func — IndexOf on per-module
type arrays inside module loop. The engine itself has a TODO comment acknowledging this.
Engine file: as_scriptengine.cpp, lines 915-937 (types) and 951-964 (funcs)
--- a/source/as_scriptengine.h
+++ b/source/as_scriptengine.h
@@ -... asCModule class
+ // FIX angelscript-0001: shadow set for O(1) shared-type ownership lookup
+ asCSet<asCTypeInfo*> sharedTypeSet;
+ asCSet<asCScriptFunction*> sharedFuncSet;
--- a/source/as_scriptengine.cpp
+++ b/source/as_scriptengine.cpp
@@ -915,7 +915,10 @@ asCModule *asCScriptEngine::FindNewOwnerForSharedType(asCTypeInfo *in_type, asC
for( asUINT n = 0; n < scriptModules.GetLength(); n++ )
{
- // TODO: optimize: If the modules already stored the shared types separately, this would be quicker
- int foundIdx = -1;
asCModule *mod = scriptModules[n];
if( mod == in_type->module ) continue;
- if( in_type->flags & asOBJ_ENUM )
- foundIdx = mod->enumTypes.IndexOf(CastToEnumType(in_type));
- else if (in_type->flags & asOBJ_TYPEDEF)
- foundIdx = mod->typeDefs.IndexOf(CastToTypedefType(in_type));
- else if (in_type->flags & asOBJ_FUNCDEF)
- foundIdx = mod->funcDefs.IndexOf(CastToFuncdefType(in_type));
- else if (in_type->flags & asOBJ_TEMPLATE)
- foundIdx = mod->templateInstances.IndexOf(CastToObjectType(in_type));
- else
- foundIdx = mod->classTypes.IndexOf(CastToObjectType(in_type));
- if( foundIdx >= 0 )
+ // FIX angelscript-0001: was per-type-category IndexOf — O(n) linear scan — CWE-407
+ // sharedTypeSet gives O(1) lookup; maintained when types added/removed from module
+ if( mod->sharedTypeSet.Exists(in_type) )
{
in_type->module = mod;
break;
}
}
@@ -951,11 +951,8 @@ asCModule *asCScriptEngine::FindNewOwnerForSharedFunc(asCScriptFunction *in_func
for( asUINT n = 0; n < scriptModules.GetLength(); n++ )
{
- // TODO: optimize: If the modules already stored the shared types separately, this would be quicker
- int foundIdx = -1;
asCModule *mod = scriptModules[n];
if( mod == in_func->module ) continue;
- foundIdx = mod->scriptFunctions.IndexOf(in_func);
- if( foundIdx >= 0 )
+ // FIX angelscript-0002: was scriptFunctions.IndexOf() — O(n) linear scan — CWE-407
+ if( mod->sharedFuncSet.Exists(in_func) )
{
in_func->module = mod;
break;
}
}
# sharedTypeSet and sharedFuncSet must be maintained wherever enumTypes/typeDefs/funcDefs/
# classTypes/templateInstances/scriptFunctions are pushed or erased in asCModule.
# This is the fix the engine's own TODO comment requested.

View file

@ -0,0 +1,24 @@
Fixes angelscript-0003: CompileSwitch — caseValues.IndexOf() O(n) inside while loop
over switch cases. Duplicate detection is O(n²) for switch statements.
--- a/source/as_compiler.cpp
+++ b/source/as_compiler.cpp
@@ -4020,6 +4020,7 @@ void asCCompiler::CompileSwitch(...)
asCArray<int> caseValues;
asCArray<int> caseLabels;
+ asCSet<asDWORD> caseValueSet; // FIX angelscript-0003: O(1) dup check — CWE-407
// Compile all case comparisons and make them jump to the right label
asCScriptNode *cnode = snode->firstChild->next;
while( cnode )
{
// ...
- // Has this case been declared already?
- if (caseValues.IndexOf(c.type.GetConstantDW()) >= 0) // O(n) — CWE-407
+ // Has this case been declared already?
+ if (caseValueSet.Exists(c.type.GetConstantDW())) // O(1) — fixed
Error(TXT_DUPLICATE_SWITCH_CASE, cnode->firstChild);
// Store constant for later use
caseValues.PushLast(c.type.GetConstantDW());
+ caseValueSet.Insert(c.type.GetConstantDW());