From 9d4b89ba155d4f6e47e140276511d43c65543c28 Mon Sep 17 00:00:00 2001 From: "Enzo Persillet (Tutez)" Date: Sat, 14 Mar 2026 19:00:51 +0100 Subject: [PATCH] Optimize runtime symbol and text resolution paths Cache resolved runtime classes for Animate and SWFLite sprite/button symbols so repeated instantiations do not call Type.resolveClass for the same symbol names over and over. Cache SWFLite unserializer class/enum resolution results to avoid repeating the same fallback resolution work across deserialization. Cache dynamic text font resolution for Animate and SWFLite symbols so TextField creation does not repeatedly enumerate and normalize the same embedded font names. These changes keep runtime behavior unchanged while reducing overhead on hot asset instantiation paths. --- .../exporters/animate/AnimateButtonSymbol.hx | 49 +++++--- .../animate/AnimateDynamicTextSymbol.hx | 105 ++++++++++++------ .../exporters/animate/AnimateSpriteSymbol.hx | 95 ++++++++++------ src/swf/exporters/swflite/ButtonSymbol.hx | 49 +++++--- .../exporters/swflite/DynamicTextSymbol.hx | 105 ++++++++++++------ src/swf/exporters/swflite/SWFLite.hx | 43 +++++++ src/swf/exporters/swflite/SpriteSymbol.hx | 95 ++++++++++------ 7 files changed, 371 insertions(+), 170 deletions(-) diff --git a/src/swf/exporters/animate/AnimateButtonSymbol.hx b/src/swf/exporters/animate/AnimateButtonSymbol.hx index 602939e..eb82d90 100644 --- a/src/swf/exporters/animate/AnimateButtonSymbol.hx +++ b/src/swf/exporters/animate/AnimateButtonSymbol.hx @@ -17,6 +17,8 @@ class AnimateButtonSymbol extends AnimateSymbol public var upState:AnimateSpriteSymbol; private var library:AnimateLibrary; + private var resolvedSymbolType:Class; + private var resolvedSymbolTypeReady = false; public function new() { @@ -51,26 +53,11 @@ class AnimateButtonSymbol extends AnimateSymbol var simpleButton:SimpleButton = null; SimpleButton.__constructor = __constructor; this.library = library; + var symbolType = __resolveSymbolType(); - #if flash - if (className == "flash.display.SimpleButton") - { - className = "flash.display.SimpleButton2"; - } - #end - - if (className != null) + if (symbolType != null) { - var symbolType = Type.resolveClass(SymbolUtils.formatClassName(className)); - - if (symbolType != null) - { - simpleButton = Type.createInstance(symbolType, []); - } - else - { - // Log.warn ("Could not resolve class \"" + symbol.className + "\""); - } + simpleButton = Type.createInstance(symbolType, []); } if (simpleButton == null) @@ -99,4 +86,30 @@ class AnimateButtonSymbol extends AnimateSymbol this.library = library; __constructor(cast instance); } + + private function __resolveSymbolType():Class + { + if (className == null) + { + return null; + } + + if (resolvedSymbolTypeReady) + { + return resolvedSymbolType; + } + + var resolvedClassName = className; + + #if flash + if (resolvedClassName == "flash.display.SimpleButton") + { + resolvedClassName = "flash.display.SimpleButton2"; + } + #end + + resolvedSymbolType = Type.resolveClass(SymbolUtils.formatClassName(resolvedClassName)); + resolvedSymbolTypeReady = true; + return resolvedSymbolType; + } } diff --git a/src/swf/exporters/animate/AnimateDynamicTextSymbol.hx b/src/swf/exporters/animate/AnimateDynamicTextSymbol.hx index 3bda147..1f0797e 100644 --- a/src/swf/exporters/animate/AnimateDynamicTextSymbol.hx +++ b/src/swf/exporters/animate/AnimateDynamicTextSymbol.hx @@ -15,6 +15,10 @@ import openfl.text.TextFormatAlign; @:access(openfl.text.TextFormat) class AnimateDynamicTextSymbol extends AnimateSymbol { + private static var cachedFontCount = -1; + private static var cachedFontNames:Array; + private static var resolvedFontNames:Map = new Map(); + public var align: /*TextFormatAlign*/ String; public var border:Bool; public var color:Null; @@ -98,39 +102,12 @@ class AnimateDynamicTextSymbol extends AnimateSymbol } format.font = fontName; + var resolvedFontName = resolveFontName(format.font); + var found = resolvedFontName != null; - var found = false; - - switch (format.font) - { - case "_sans", "_serif", "_typewriter", "", null: - found = true; - - default: - for (font in Font.enumerateFonts()) - { - if (font.fontName == format.font) - { - found = true; - break; - } - } - } - - if (!found) + if (found && resolvedFontName != format.font) { - var alpha = ~/[^a-zA-Z]+/g; - var spaces = ~/\s/g; - - for (font in Font.enumerateFonts()) - { - if (alpha.replace(font.fontName, "").substr(0, fontName.length) == spaces.replace(fontName, "")) - { - format.font = font.fontName; - found = true; - break; - } - } + format.font = resolvedFontName; } if (found) @@ -174,4 +151,70 @@ class AnimateDynamicTextSymbol extends AnimateSymbol // textField.autoSize = (tag.autoSize) ? TextFieldAutoSize.LEFT : TextFieldAutoSize.NONE; return textField; } + + private static function getCachedFontNames():Array + { + var fonts = Font.enumerateFonts(); + + if (cachedFontNames == null || cachedFontCount != fonts.length) + { + cachedFontCount = fonts.length; + cachedFontNames = []; + resolvedFontNames = new Map(); + + for (font in fonts) + { + cachedFontNames.push(font.fontName); + } + } + + return cachedFontNames; + } + + private static function resolveFontName(name:String):String + { + switch (name) + { + case "_sans", "_serif", "_typewriter", "", null: + return name; + default: + } + + if (resolvedFontNames.exists(name)) + { + return resolvedFontNames.get(name); + } + + var fontNames = getCachedFontNames(); + + for (fontName in fontNames) + { + if (fontName == name) + { + resolvedFontNames.set(name, fontName); + return fontName; + } + } + + var normalizedName = normalizeFontName(name); + + for (fontName in fontNames) + { + if (normalizeFontName(fontName).substr(0, normalizedName.length) == normalizedName) + { + resolvedFontNames.set(name, fontName); + return fontName; + } + } + + resolvedFontNames.set(name, null); + return null; + } + + private static function normalizeFontName(name:String):String + { + var alpha = ~/[^a-zA-Z]+/g; + var spaces = ~/\s/g; + return spaces.replace(alpha.replace(name, ""), ""); + } } diff --git a/src/swf/exporters/animate/AnimateSpriteSymbol.hx b/src/swf/exporters/animate/AnimateSpriteSymbol.hx index 1a6bf2f..20062e3 100644 --- a/src/swf/exporters/animate/AnimateSpriteSymbol.hx +++ b/src/swf/exporters/animate/AnimateSpriteSymbol.hx @@ -18,6 +18,10 @@ class AnimateSpriteSymbol extends AnimateSymbol public var scale9Grid:Rectangle; private var library:AnimateLibrary; + private var resolvedBaseSymbolType:Class; + private var resolvedBaseSymbolTypeReady = false; + private var resolvedSymbolType:Class; + private var resolvedSymbolTypeReady = false; public function new() { @@ -49,42 +53,7 @@ class AnimateSpriteSymbol extends AnimateSymbol private override function __createObject(library:AnimateLibrary):Sprite { __init(library); - - #if flash - if (className == "flash.display.MovieClip") - { - className = "flash.display.MovieClip2"; - } - #end - - var symbolType = null; - - if (className != null) - { - symbolType = Type.resolveClass(SymbolUtils.formatClassName(className)); - - if (symbolType == null) - { - // Log.warn ("Could not resolve class \"" + className + "\""); - } - } - - if (symbolType == null && baseClassName != null) - { - #if flash - if (baseClassName == "flash.display.MovieClip") - { - baseClassName = "flash.display.MovieClip2"; - } - #end - - symbolType = Type.resolveClass(SymbolUtils.formatClassName(baseClassName)); - - if (symbolType == null) - { - // Log.warn ("Could not resolve class \"" + className + "\""); - } - } + var symbolType = __resolveSymbolType(); var sprite:Sprite = null; @@ -128,4 +97,58 @@ class AnimateSpriteSymbol extends AnimateSymbol this.library = library; __constructor(cast instance); } + + private function __resolveSymbolType():Class + { + var symbolType = __resolveClassName(className, false); + + if (symbolType == null) + { + symbolType = __resolveClassName(baseClassName, true); + } + + return symbolType; + } + + private function __resolveClassName(name:String, useBaseClass:Bool):Class + { + if (name == null) + { + return null; + } + + if (useBaseClass) + { + if (resolvedBaseSymbolTypeReady) + { + return resolvedBaseSymbolType; + } + } + else if (resolvedSymbolTypeReady) + { + return resolvedSymbolType; + } + + #if flash + if (name == "flash.display.MovieClip") + { + name = "flash.display.MovieClip2"; + } + #end + + var symbolType = Type.resolveClass(SymbolUtils.formatClassName(name)); + + if (useBaseClass) + { + resolvedBaseSymbolType = symbolType; + resolvedBaseSymbolTypeReady = true; + } + else + { + resolvedSymbolType = symbolType; + resolvedSymbolTypeReady = true; + } + + return symbolType; + } } diff --git a/src/swf/exporters/swflite/ButtonSymbol.hx b/src/swf/exporters/swflite/ButtonSymbol.hx index 792dc4c..63c3dd6 100644 --- a/src/swf/exporters/swflite/ButtonSymbol.hx +++ b/src/swf/exporters/swflite/ButtonSymbol.hx @@ -17,6 +17,8 @@ class ButtonSymbol extends SWFSymbol public var upState:SpriteSymbol; private var swf:SWFLite; + private var resolvedSymbolType:Class; + private var resolvedSymbolTypeReady = false; public function new() { @@ -53,26 +55,11 @@ class ButtonSymbol extends SWFSymbol SimpleButton.__constructor = __constructor; #end this.swf = swf; + var symbolType = __resolveSymbolType(); - #if flash - if (className == "flash.display.SimpleButton") - { - className = "flash.display.SimpleButton2"; - } - #end - - if (className != null) + if (symbolType != null) { - var symbolType = Type.resolveClass(className); - - if (symbolType != null) - { - simpleButton = Type.createInstance(symbolType, []); - } - else - { - // Log.warn ("Could not resolve class \"" + symbol.className + "\""); - } + simpleButton = Type.createInstance(symbolType, []); } if (simpleButton == null) @@ -103,4 +90,30 @@ class ButtonSymbol extends SWFSymbol this.swf = swf; __constructor(cast instance); } + + private function __resolveSymbolType():Class + { + if (className == null) + { + return null; + } + + if (resolvedSymbolTypeReady) + { + return resolvedSymbolType; + } + + var resolvedClassName = className; + + #if flash + if (resolvedClassName == "flash.display.SimpleButton") + { + resolvedClassName = "flash.display.SimpleButton2"; + } + #end + + resolvedSymbolType = Type.resolveClass(resolvedClassName); + resolvedSymbolTypeReady = true; + return resolvedSymbolType; + } } diff --git a/src/swf/exporters/swflite/DynamicTextSymbol.hx b/src/swf/exporters/swflite/DynamicTextSymbol.hx index 971f0f1..5f3f866 100644 --- a/src/swf/exporters/swflite/DynamicTextSymbol.hx +++ b/src/swf/exporters/swflite/DynamicTextSymbol.hx @@ -15,6 +15,10 @@ import swf.exporters.swflite.SWFLite; @:access(openfl.text.TextFormat) class DynamicTextSymbol extends SWFSymbol { + private static var cachedFontCount = -1; + private static var cachedFontNames:Array; + private static var resolvedFontNames:Map = new Map(); + public var align: /*TextFormatAlign*/ String; public var border:Bool; public var color:Null; @@ -98,39 +102,12 @@ class DynamicTextSymbol extends SWFSymbol } format.font = fontName; + var resolvedFontName = resolveFontName(format.font); + var found = resolvedFontName != null; - var found = false; - - switch (format.font) - { - case "_sans", "_serif", "_typewriter", "", null: - found = true; - - default: - for (font in Font.enumerateFonts()) - { - if (font.fontName == format.font) - { - found = true; - break; - } - } - } - - if (!found) + if (found && resolvedFontName != format.font) { - var alpha = ~/[^a-zA-Z]+/g; - var spaces = ~/\s/g; - - for (font in Font.enumerateFonts()) - { - if (alpha.replace(font.fontName, "").substr(0, fontName.length) == spaces.replace(fontName, "")) - { - format.font = font.fontName; - found = true; - break; - } - } + format.font = resolvedFontName; } if (found) @@ -174,4 +151,70 @@ class DynamicTextSymbol extends SWFSymbol // textField.autoSize = (tag.autoSize) ? TextFieldAutoSize.LEFT : TextFieldAutoSize.NONE; return textField; } + + private static function getCachedFontNames():Array + { + var fonts = Font.enumerateFonts(); + + if (cachedFontNames == null || cachedFontCount != fonts.length) + { + cachedFontCount = fonts.length; + cachedFontNames = []; + resolvedFontNames = new Map(); + + for (font in fonts) + { + cachedFontNames.push(font.fontName); + } + } + + return cachedFontNames; + } + + private static function resolveFontName(name:String):String + { + switch (name) + { + case "_sans", "_serif", "_typewriter", "", null: + return name; + default: + } + + if (resolvedFontNames.exists(name)) + { + return resolvedFontNames.get(name); + } + + var fontNames = getCachedFontNames(); + + for (fontName in fontNames) + { + if (fontName == name) + { + resolvedFontNames.set(name, fontName); + return fontName; + } + } + + var normalizedName = normalizeFontName(name); + + for (fontName in fontNames) + { + if (normalizeFontName(fontName).substr(0, normalizedName.length) == normalizedName) + { + resolvedFontNames.set(name, fontName); + return fontName; + } + } + + resolvedFontNames.set(name, null); + return null; + } + + private static function normalizeFontName(name:String):String + { + var alpha = ~/[^a-zA-Z]+/g; + var spaces = ~/\s/g; + return spaces.replace(alpha.replace(name, ""), ""); + } } diff --git a/src/swf/exporters/swflite/SWFLite.hx b/src/swf/exporters/swflite/SWFLite.hx index a830059..fe06c9f 100644 --- a/src/swf/exporters/swflite/SWFLite.hx +++ b/src/swf/exporters/swflite/SWFLite.hx @@ -20,6 +20,11 @@ import openfl.Assets; { public static var instances:Map = new Map(); + private static var resolvedClasses:Map> = new Map(); + private static var resolvedClassMisses:Map = new Map(); + private static var resolvedEnums:Map = new Map(); + private static var resolvedEnumMisses:Map = new Map(); + public var frameRate:Float; public var library:SWFLiteLibrary; public var root:SpriteSymbol; @@ -85,6 +90,16 @@ import openfl.Assets; @SuppressWarnings("checkstyle:Dynamic") private static function resolveClass(name:String):Class { + if (resolvedClasses.exists(name)) + { + return resolvedClasses.get(name); + } + + if (resolvedClassMisses.exists(name)) + { + return null; + } + var value = Type.resolveClass(name); #if flash @@ -98,12 +113,31 @@ import openfl.Assets; if (value == null) value = Type.resolveClass(StringTools.replace(name, "openfl._v2", "openfl")); #end + if (value != null) + { + resolvedClasses.set(name, value); + } + else + { + resolvedClassMisses.set(name, true); + } + return value; } @SuppressWarnings("checkstyle:Dynamic") private static function resolveEnum(name:String):Enum { + if (resolvedEnums.exists(name)) + { + return cast resolvedEnums.get(name); + } + + if (resolvedEnumMisses.exists(name)) + { + return null; + } + var value = Type.resolveEnum(name); #if flash @@ -121,6 +155,15 @@ import openfl.Assets; if (value == null) value = Type.resolveEnum(StringTools.replace(name, "openfl._v2", "openfl")); #end + if (value != null) + { + resolvedEnums.set(name, value); + } + else + { + resolvedEnumMisses.set(name, true); + } + return value; } diff --git a/src/swf/exporters/swflite/SpriteSymbol.hx b/src/swf/exporters/swflite/SpriteSymbol.hx index 2b3c975..cab67fc 100644 --- a/src/swf/exporters/swflite/SpriteSymbol.hx +++ b/src/swf/exporters/swflite/SpriteSymbol.hx @@ -20,6 +20,10 @@ class SpriteSymbol extends SWFSymbol public var scale9Grid:Rectangle; private var swf:SWFLite; + private var resolvedBaseSymbolType:Class; + private var resolvedBaseSymbolTypeReady = false; + private var resolvedSymbolType:Class; + private var resolvedSymbolTypeReady = false; public function new() { @@ -54,42 +58,7 @@ class SpriteSymbol extends SWFSymbol Sprite.__constructor = __constructor; #end this.swf = swf; - - #if flash - if (className == "flash.display.MovieClip") - { - className = "flash.display.MovieClip2"; - } - #end - - var symbolType = null; - - if (className != null) - { - symbolType = Type.resolveClass(className); - - if (symbolType == null) - { - // Log.warn ("Could not resolve class \"" + className + "\""); - } - } - - if (symbolType == null && baseClassName != null) - { - #if flash - if (baseClassName == "flash.display.MovieClip") - { - baseClassName = "flash.display.MovieClip2"; - } - #end - - symbolType = Type.resolveClass(baseClassName); - - if (symbolType == null) - { - // Log.warn ("Could not resolve class \"" + className + "\""); - } - } + var symbolType = __resolveSymbolType(); var sprite:Sprite = null; @@ -129,4 +98,58 @@ class SpriteSymbol extends SWFSymbol this.swf = swf; __constructor(cast instance); } + + private function __resolveSymbolType():Class + { + var symbolType = __resolveClassName(className, false); + + if (symbolType == null) + { + symbolType = __resolveClassName(baseClassName, true); + } + + return symbolType; + } + + private function __resolveClassName(name:String, useBaseClass:Bool):Class + { + if (name == null) + { + return null; + } + + if (useBaseClass) + { + if (resolvedBaseSymbolTypeReady) + { + return resolvedBaseSymbolType; + } + } + else if (resolvedSymbolTypeReady) + { + return resolvedSymbolType; + } + + #if flash + if (name == "flash.display.MovieClip") + { + name = "flash.display.MovieClip2"; + } + #end + + var symbolType = Type.resolveClass(name); + + if (useBaseClass) + { + resolvedBaseSymbolType = symbolType; + resolvedBaseSymbolTypeReady = true; + } + else + { + resolvedSymbolType = symbolType; + resolvedSymbolTypeReady = true; + } + + return symbolType; + } }