From 0f4c7544011719160507a2727d55cbe38d22fbdc Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 27 Jan 2026 09:50:44 -0500 Subject: [PATCH 01/16] Enable processing for all abstracts. --- .../_internal/PolymodScriptClassMacro.hx | 84 ++++++++++--------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 82afa8e2..48e47785 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -109,33 +109,37 @@ class PolymodScriptClassMacro { var abstractType = t.get(); var abstractImpl = abstractType.impl.get(); - var abstractImplPath = abstractType.impl.toString(); - // Context.info('${abstractImplPath} implements FlxColor', Context.currentPos()); + var abstractImplPath:String = abstractType.impl?.toString() ?? ''; + + if (abstractImpl == null) { + // If the abstract doesn't have an implementation, it's usually an extern or something, so we always want to ignore it. + continue; + } var entryData = [macro $v{abstractPath}, macro $v{abstractImplPath}]; abstractImplEntries.push(macro $a{entryData}); - for (field in abstractImpl.statics.get()) - { - switch (field.type) - { - case TAbstract(_, _): - // - case TType(_, _): - // - default: - continue; - } + for (field in abstractImpl.statics.get()) { + switch (field.type) { + case TAbstract(_, _): + // + case TType(_, _): + // + default: + continue; + } - var key:String = '${abstractImplPath}.${field.name}'; + var key:String = '${abstractImplPath}.${field.name}'; - if (!staticFieldToClass.exists(key)) - { - continue; - } + if (!staticFieldToClass.exists(key)) { + continue; + } - var staticEntryData = [macro $v{key}, macro $v{staticFieldToClass[key]},]; + var staticEntryData = [ + macro $v{key}, + macro $v{staticFieldToClass[key]}, + ]; abstractStaticEntries.push(macro $a{staticEntryData}); } @@ -149,6 +153,8 @@ class PolymodScriptClassMacro } } + Context.info('PolymodScriptClassMacro: Registering ${hscriptedClassEntries.length} HScriptedClasses, ${abstractImplEntries.length} abstract impls, ${abstractStaticEntries.length} abstract statics', Context.currentPos()); + var polymodScriptClassClassType:ClassType = MacroUtil.getClassType('polymod.hscript._internal.PolymodScriptClassMacro'); polymodScriptClassClassType.meta.remove('hscriptedClasses'); polymodScriptClassClassType.meta.add('hscriptedClasses', hscriptedClassEntries, Context.currentPos()); @@ -173,15 +179,13 @@ class PolymodScriptClassMacro var abstractPath = a.toString(); var abstractType = a.get(); - if (abstractPath != 'flixel.util.FlxColor') - { - continue; - } + if (abstractPath != 'flixel.util.FlxColor') { + continue; + } - if (abstractType.impl == null) - { - continue; - } + if (abstractType.impl == null) { + continue; + } var abstractImplPath = abstractType.impl.toString(); var abstractImplType = abstractType.impl.get(); @@ -207,7 +211,7 @@ class PolymodScriptClassMacro if (getter == null) { - throw 'This should not happen'; + throw 'Getter is null?'; } switch (getter.type) @@ -215,7 +219,7 @@ class PolymodScriptClassMacro case TFun(args, _): if (args.length != 0) continue; default: - throw 'This should not happen'; + throw 'Getter has an unknown type?'; } canGet = true; @@ -235,7 +239,7 @@ class PolymodScriptClassMacro if (setter == null) { - throw 'This should not happen'; + throw 'Setter is null?'; } switch (setter.type) @@ -243,7 +247,7 @@ class PolymodScriptClassMacro case TFun(args, _): if (args.length != 1) continue; default: - throw 'This should not happen'; + throw 'Setter has an unknown type?'; } canSet = true; @@ -337,11 +341,11 @@ class PolymodScriptClassMacro { var metaData = Meta.getType(PolymodScriptClassMacro); - // trace('Got metaData: ' + metaData); + // trace('Got metaData: ' + metaData); if (metaData.hscriptedClasses != null) { - trace('Got hscriptedClasses: ' + metaData.hscriptedClasses); + trace('Got hscriptedClasses: ' + metaData.hscriptedClasses); var result:Map> = []; @@ -349,16 +353,16 @@ class PolymodScriptClassMacro for (element in metaData.hscriptedClasses) { - if (element.length != 2) + if (element.length != 2) { - throw 'Malformed element in hscriptedClasses: ' + element; - } + throw 'Malformed element in hscriptedClasses: ' + element; + } - var superClassPath:String = element[0]; - var classPath:String = element[1]; + var superClassPath:String = element[0]; + var classPath:String = element[1]; var classType:Class = cast Type.resolveClass(classPath); - result.set(superClassPath, classType); - } + result.set(superClassPath, classType); + } return result; } From 290333a26a2efccd45dc298858f7ecad80388b13 Mon Sep 17 00:00:00 2001 From: lemz <87707926+lemz1@users.noreply.github.com> Date: Fri, 30 Jan 2026 02:07:28 +0100 Subject: [PATCH 02/16] fix compile errors --- polymod/hscript/_internal/PolymodScriptClassMacro.hx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 48e47785..4b208874 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -265,7 +265,7 @@ class PolymodScriptClassMacro pos: Context.currentPos(), name: fieldName, access: [Access.APublic, Access.AStatic], - kind: FProp(canGet ? 'get' : 'never', canSet ? 'set' : 'never', Context.toComplexType(field.type), null) + kind: FProp(canGet ? 'get' : 'never', canSet ? 'set' : 'never', (macro: Dynamic), null) }); if (canGet) @@ -282,7 +282,7 @@ class PolymodScriptClassMacro expr: macro { @:privateAccess - return ${Context.parse(abstractPath + '.' + field.name, Context.currentPos())}; + return ${fieldExpr}; } }) }); @@ -303,7 +303,7 @@ class PolymodScriptClassMacro expr: macro { @:privateAccess - return ${Context.parse(abstractPath + '.' + field.name, Context.currentPos())} = value; + return ${fieldExpr} = value; } }) }); From fa147e056af8e503d3ea24559697caf2b27f3ebf Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 31 Jan 2026 06:46:16 -0500 Subject: [PATCH 03/16] Gracefully fail (and display a script error) rather than throwing an exception and crashing if an abstract can't be resolved. --- polymod/hscript/_internal/PolymodInterpEx.hx | 17 ++++++++++++----- .../_internal/PolymodScriptClassMacro.hx | 3 +-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/polymod/hscript/_internal/PolymodInterpEx.hx b/polymod/hscript/_internal/PolymodInterpEx.hx index 97e88dd9..78fc2265 100644 --- a/polymod/hscript/_internal/PolymodInterpEx.hx +++ b/polymod/hscript/_internal/PolymodInterpEx.hx @@ -339,7 +339,7 @@ class PolymodInterpEx extends Interp continue; } - Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not import ${imp.fullPath}', clsPath); + Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not import ${imp.fullPath}. Check to ensure the module exists and is spelled correctly.', clsPath); } // Check if the scripted classes extend the right type. @@ -353,7 +353,7 @@ class PolymodInterpEx extends Interp case CTPath(path, params): if (params != null && params.length > 0) { - Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not extend ${superClassPath}, do not include type parameters in super class name', clsPath); + Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not extend ${superClassPath}, do not include type parameters in super class name.', clsPath); } default: @@ -361,7 +361,7 @@ class PolymodInterpEx extends Interp } // Default - Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not extend ${superClassPath}, is the type imported?', clsPath); + Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not extend ${superClassPath}. Make sure the type to extend has been imported.', clsPath); } else { @@ -1981,8 +1981,15 @@ class PolymodInterpEx extends Interp { // We used a macro to map each abstract to its implementation. importedClass.cls = PolymodScriptClass.abstractClassImpls.get(importedClass.fullPath); - trace('RESOLVED ABSTRACT CLASS ${importedClass.fullPath} -> ${Type.getClassName(importedClass.cls)}'); - trace(Type.getClassFields(importedClass.cls)); + + if (importedClass.cls == null) { + trace('UNRESOLVED ABSTRACT CLASS ${importedClass.fullPath}'); + Polymod.warning(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Abstract type ${importedClass.fullPath} could not be resolved. Try using the underlying type instead.', origin); + } else { + trace('RESOLVED ABSTRACT CLASS ${importedClass.fullPath} -> ${Type.getClassName(importedClass.cls)}'); + trace(Type.getClassFields(importedClass.cls)); + } + } else if (_scriptEnumDescriptors.exists(importedClass.fullPath)) { diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 4b208874..36460ffa 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -391,7 +391,6 @@ class PolymodScriptClassMacro var abstractPath:String = element[0]; var abstractImplPath:String = element[1]; - // var abstractType:Class = cast Type.resolveClass(abstractPath); #if js trace('Resolving using JS method'); var abstractImplType:Class = resolveClass(abstractPath); @@ -406,7 +405,7 @@ class PolymodScriptClassMacro if (abstractImplType == null) { - throw 'Could not resolve ' + abstractImplPath; + // trace('POLYMOD ABSTRACTS: Could not resolve $abstractImplPath'); } #end From 978a3607e4a4c634411a429188e319dcb2fea979 Mon Sep 17 00:00:00 2001 From: Hyper_ <40342021+NotHyper-474@users.noreply.github.com> Date: Sun, 1 Feb 2026 03:42:03 -0300 Subject: [PATCH 04/16] break out from loops after finding getter/setter --- polymod/hscript/_internal/PolymodScriptClassMacro.hx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 36460ffa..1e34b40c 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -206,6 +206,7 @@ class PolymodScriptClassMacro if (f.name == 'get_${field.name}') { getter = f; + break; } } @@ -234,6 +235,7 @@ class PolymodScriptClassMacro if (f.name == 'set_${field.name}') { setter = f; + break; } } From a4b54791e55149cfc8f3d72de6ae0d728179ed23 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 1 Feb 2026 22:32:32 -0500 Subject: [PATCH 05/16] Remove some extra print calls. --- polymod/hscript/_internal/PolymodInterpEx.hx | 5 ++--- polymod/hscript/_internal/PolymodScriptClassMacro.hx | 5 ----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/polymod/hscript/_internal/PolymodInterpEx.hx b/polymod/hscript/_internal/PolymodInterpEx.hx index 78fc2265..a42d828e 100644 --- a/polymod/hscript/_internal/PolymodInterpEx.hx +++ b/polymod/hscript/_internal/PolymodInterpEx.hx @@ -1983,11 +1983,10 @@ class PolymodInterpEx extends Interp importedClass.cls = PolymodScriptClass.abstractClassImpls.get(importedClass.fullPath); if (importedClass.cls == null) { - trace('UNRESOLVED ABSTRACT CLASS ${importedClass.fullPath}'); Polymod.warning(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Abstract type ${importedClass.fullPath} could not be resolved. Try using the underlying type instead.', origin); } else { - trace('RESOLVED ABSTRACT CLASS ${importedClass.fullPath} -> ${Type.getClassName(importedClass.cls)}'); - trace(Type.getClassFields(importedClass.cls)); + // trace('RESOLVED ABSTRACT CLASS ${importedClass.fullPath} -> ${Type.getClassName(importedClass.cls)}'); + // trace(Type.getClassFields(importedClass.cls)); } } diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 1e34b40c..14d9e30b 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -343,12 +343,8 @@ class PolymodScriptClassMacro { var metaData = Meta.getType(PolymodScriptClassMacro); - // trace('Got metaData: ' + metaData); - if (metaData.hscriptedClasses != null) { - trace('Got hscriptedClasses: ' + metaData.hscriptedClasses); - var result:Map> = []; // Each element is formatted as `[superClassPath, classPath]`. @@ -394,7 +390,6 @@ class PolymodScriptClassMacro var abstractPath:String = element[0]; var abstractImplPath:String = element[1]; #if js - trace('Resolving using JS method'); var abstractImplType:Class = resolveClass(abstractPath); if (abstractImplType == null) From c053b82e7affad64b2b149c767d2c82bcbe62191 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 1 Feb 2026 22:33:56 -0500 Subject: [PATCH 06/16] Properly support enum abstract values (i.e. inline get-only static fields of abstracts) --- .../_internal/PolymodScriptClassMacro.hx | 170 +++++++++--------- 1 file changed, 87 insertions(+), 83 deletions(-) diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 14d9e30b..11f42909 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -184,36 +184,39 @@ class PolymodScriptClassMacro } if (abstractType.impl == null) { + // Only a few classes end up here, generally ones that are implemented directly in code. + // Includes like StdTypes.Float, StdTypes.Dynamic, StdTypes.Void, cpp.Int16, cpp.SizeT, Class, Enum continue; - } - - var abstractImplPath = abstractType.impl.toString(); - var abstractImplType = abstractType.impl.get(); + } else { + var abstractImplPath = abstractType.impl.toString(); + var abstractImplType = abstractType.impl.get(); + var abstractImplStatics = abstractImplType.statics.get(); + var underlyingType = abstractType.type; - for (field in abstractImplType.statics.get()) + for (field in abstractImplStatics) { - switch (field.kind) + switch (field.kind) { - case FVar(read, write): + case FVar(read, write): trace(field.name, read, write); - var canGet:Bool = read == AccInline || read == AccNormal; - if (read == AccCall) + var canGet:Bool = read == AccInline || read == AccNormal; + if (read == AccCall) { - var getter:Null = null; - for (f in abstractImplType.statics.get()) + var getter:Null = null; + for (f in abstractImplStatics) { - if (f.name == 'get_${field.name}') + if (f.name == 'get_${field.name}') { - getter = f; - break; - } - } + getter = f; + break; + } + } - if (getter == null) + if (getter == null) { - throw 'Getter is null?'; - } + throw 'Getter is null?'; + } switch (getter.type) { @@ -223,8 +226,8 @@ class PolymodScriptClassMacro throw 'Getter has an unknown type?'; } - canGet = true; - } + canGet = true; + } var canSet:Bool = write == AccNormal; if (write == AccCall) @@ -239,10 +242,10 @@ class PolymodScriptClassMacro } } - if (setter == null) + if (setter == null) { - throw 'Setter is null?'; - } + throw 'Setter is null?'; + } switch (setter.type) { @@ -252,68 +255,69 @@ class PolymodScriptClassMacro throw 'Setter has an unknown type?'; } - canSet = true; - } + canSet = true; + } - if (!canGet && !canSet) - { - continue; - } - - var fieldName:String = '${abstractImplPath.replace('.', '_')}_${field.name}'; + if (canGet) { + var fieldName:String = '${abstractImplPath.replace('.', '_')}_${field.name}'; - fields.push( + fields.push( { - pos: Context.currentPos(), - name: fieldName, - access: [Access.APublic, Access.AStatic], - kind: FProp(canGet ? 'get' : 'never', canSet ? 'set' : 'never', (macro: Dynamic), null) - }); - - if (canGet) - { - fields.push( - { - pos: Context.currentPos(), - name: 'get_${fieldName}', - access: [Access.APublic, Access.AStatic], - kind: FFun( - { - args: [], - ret: null, - expr: macro - { - @:privateAccess - return ${fieldExpr}; - } - }) - }); - } - - if (canSet) - { - fields.push( - { - pos: Context.currentPos(), - name: 'set_${fieldName}', - access: [Access.APublic, Access.AStatic], - kind: FFun( - { - args: [ - {name: 'value'}], - ret: null, - expr: macro - { - @:privateAccess - return ${fieldExpr} = value; - } - }) - }); - } - - staticFieldToClass.set('${abstractImplPath}.${field.name}', 'polymod.hscript._internal.AbstractStaticMembers_${iteration}'); - default: - continue; + pos: Context.currentPos(), + name: fieldName, + access: [Access.APublic, Access.AStatic], + kind: FProp(canGet ? 'get' : 'never', canSet ? 'set' : 'never', (macro: Dynamic), null) + }); + + var fieldExpr:Expr = null; + try { + // when this fails, this should mean that we are dealing with an enum abstract + // so we need to handle it differently + var fullPath:String = '${abstractType.module}.${abstractType.name}'; + Context.getType(fullPath); + fieldExpr = Context.parse('${fullPath}.${field.name}', Context.currentPos()); + } catch (_) { + fieldExpr = Context.getTypedExpr(field.expr()); + } + + if (canGet) { + fields.push({ + pos: Context.currentPos(), + name: 'get_${fieldName}', + access: [Access.APublic, Access.AStatic], + kind: FFun({ + args: [], + ret: null, + expr: macro { + @:privateAccess + return ${fieldExpr}; + } + }) + }); + } + + if (canSet) { + fields.push({ + pos: Context.currentPos(), + name: 'set_${fieldName}', + access: [Access.APublic, Access.AStatic], + kind: FFun({ + args: [{name: 'value'}], + ret: null, + expr: macro { + @:privateAccess + return ${fieldExpr} = value; + } + }) + }); + } + + staticFieldToClass.set('${abstractImplPath}.${field.name}', 'polymod.hscript._internal.AbstractStaticMembers_${iteration}'); + } + + default: + continue; + } } } default: From 00ed2ef8c1084a88632942912bb929d0be700c48 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 1 Feb 2026 22:34:15 -0500 Subject: [PATCH 07/16] Add timing data for processing abstracts to the compilation output. --- .../_internal/PolymodScriptClassMacro.hx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 11f42909..577017cf 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -73,6 +73,10 @@ class PolymodScriptClassMacro var abstractImplEntries:Array = []; var abstractStaticEntries:Array = []; + Context.info('PolymodScriptClassMacro: Processing abstracts...', Context.currentPos()); + + var startTime:Float = Sys.time(); + for (type in allTypes) { switch (type) @@ -153,7 +157,11 @@ class PolymodScriptClassMacro } } - Context.info('PolymodScriptClassMacro: Registering ${hscriptedClassEntries.length} HScriptedClasses, ${abstractImplEntries.length} abstract impls, ${abstractStaticEntries.length} abstract statics', Context.currentPos()); + var endTime:Float = Sys.time(); + + var duration:Float = endTime - startTime; + + Context.info('PolymodScriptClassMacro: Registered ${hscriptedClassEntries.length} HScriptedClasses, ${abstractImplEntries.length} abstract impls, ${abstractStaticEntries.length} abstract statics in ${duration} sec.', Context.currentPos()); var polymodScriptClassClassType:ClassType = MacroUtil.getClassType('polymod.hscript._internal.PolymodScriptClassMacro'); polymodScriptClassClassType.meta.remove('hscriptedClasses'); @@ -171,6 +179,10 @@ class PolymodScriptClassMacro { var fields:Array = []; + Context.info('PolymodScriptClassMacro: Processing abstract static fields...', Context.currentPos()); + + var startTime:Float = Sys.time(); + for (type in types) { switch (type) @@ -339,6 +351,12 @@ class PolymodScriptClassMacro fields: fields }); + var endTime:Float = Sys.time(); + + var duration:Float = endTime - startTime; + + Context.info('PolymodScriptClassMacro: Processed ${fields.length} static fields in ${duration} sec (iteration #${iteration}).', Context.currentPos()); + iteration++; } #end From cc0a29a2dd1f2e612d79f4c3977f297e9be675e8 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 3 Feb 2026 13:46:07 -0500 Subject: [PATCH 08/16] Refactored abstract class field access to account for impls that can't be resolved at runtime. --- .../hscript/_internal/PolymodClassDeclEx.hx | 85 +++++ polymod/hscript/_internal/PolymodInterpEx.hx | 50 +-- .../hscript/_internal/PolymodScriptClass.hx | 17 +- .../_internal/PolymodScriptClassMacro.hx | 350 +++++++++--------- 4 files changed, 295 insertions(+), 207 deletions(-) diff --git a/polymod/hscript/_internal/PolymodClassDeclEx.hx b/polymod/hscript/_internal/PolymodClassDeclEx.hx index fc73ddea..282c88f6 100644 --- a/polymod/hscript/_internal/PolymodClassDeclEx.hx +++ b/polymod/hscript/_internal/PolymodClassDeclEx.hx @@ -4,6 +4,8 @@ import polymod.hscript._internal.Expr.ClassDecl; import polymod.hscript._internal.Expr.FieldDecl; import polymod.hscript._internal.PolymodScriptClass; +using StringTools; + /** * A scripted class declaration, with a package declaration, imports, and potentially static fields. */ @@ -31,10 +33,17 @@ typedef PolymodClassImport = @:optional var name:String; @:optional var pkg:Array; @:optional var fullPath:String; // pkg.pkg.pkg.name + @:optional var cls:Class; @:optional var enm:Enum; + @:optional var abs:PolymodStaticAbstractReference; } +/** + * A class which holds a reference to another scripted class, + * for use in a static context. This allows for instantiation, + * or for accessing static fields or methods. + */ class PolymodStaticClassReference { public var cls:PolymodClassDeclEx; @@ -125,3 +134,79 @@ class PolymodStaticClassReference return 'PolymodStaticClassReference(${getFullyQualifiedName()})'; } } + +/** + * A class which holds a reference to an abstract class implementation, + * or (if the implementation class is not available) redirects + * to the abstract static value store built by PolymodScriptClassMacro at compile time. + */ +@:nullSafety +class PolymodStaticAbstractReference { + /** + * The name of the abstract class, as it was imported. + */ + public var absName:String; + /** + * The internal class that implements the abstract class's static functions, + * if it exists. + */ + public var absImpl:Null>; + public var absImplPath:String; + + public function new(absName:String, absImpl:Null>, absImplPath:String) + { + this.absName = absName; + this.absImpl = absImpl; + this.absImplPath = absImplPath; + } + + /** + * Retrieve a static field of the abstract class. + * @param fieldName The name of the field to retrieve. + * @return The value of the field. + */ + public function getField(fieldName:String):Dynamic { + if (this.absImpl != null) { + if (Reflect.hasField(this.absImpl, fieldName)) { + var result:Dynamic = Reflect.getProperty(this.absImpl, fieldName); + if (result != null) return result; + } + } + + return fetchAbstractClassStatic(fieldName); + } + + /** + * Call a static function of the abstract class. + * @param funcName The name of the function to call. + * @param args The arguments to pass to the function. + * @return The return value of the function. + */ + public function callFunction(funcName:String, args:Array):Dynamic { + // If we can just call the method directly, do that. + var func = getField(funcName); + if (func != null) { + return Reflect.callMethod(this.absImpl, func, args); + } + + throw 'Could not resolve abstract class static function ${funcName}'; + } + + function fetchAbstractClassStatic(fieldName:String):Dynamic { + var key:String = '${this.absImplPath}.${fieldName}'; + + if (PolymodScriptClass.abstractClassStatics.exists(key)) { + var holder = PolymodScriptClass.abstractClassStatics.get(key); + var property = key.replace('.', '_'); + + return Reflect.getProperty(holder, property); + } else { + throw 'Could not resolve abstract class static field ${fieldName}'; + } + } + + public function toString():String + { + return 'PolymodStaticAbstractReference(${absName} => ${absImpl})'; + } +} diff --git a/polymod/hscript/_internal/PolymodInterpEx.hx b/polymod/hscript/_internal/PolymodInterpEx.hx index a42d828e..57f37605 100644 --- a/polymod/hscript/_internal/PolymodInterpEx.hx +++ b/polymod/hscript/_internal/PolymodInterpEx.hx @@ -2,6 +2,7 @@ package polymod.hscript._internal; import polymod.hscript._internal.Expr; import polymod.hscript._internal.PolymodClassDeclEx.PolymodClassImport; +import polymod.hscript._internal.PolymodClassDeclEx.PolymodStaticAbstractReference; import polymod.hscript._internal.PolymodClassDeclEx.PolymodStaticClassReference; import polymod.hscript._internal.PolymodExprEx; import polymod.hscript._internal.Printer; @@ -144,6 +145,11 @@ class PolymodInterpEx extends Interp // Force call super function. return super.fcall(o, '__super_${f}', args); } + else if (Std.isOfType(o, PolymodStaticAbstractReference)) + { + var ref:PolymodStaticAbstractReference = cast(o, PolymodStaticAbstractReference); + return ref.callFunction(f, args); + } else if (Std.isOfType(o, PolymodStaticClassReference)) { var ref:PolymodStaticClassReference = cast(o, PolymodStaticClassReference); @@ -1276,7 +1282,13 @@ class PolymodInterpEx extends Interp } // Otherwise, we assume the field is fine to use. - if (Std.isOfType(o, PolymodStaticClassReference)) + if (Std.isOfType(o, PolymodStaticAbstractReference)) + { + var ref:PolymodStaticAbstractReference = cast(o, PolymodStaticAbstractReference); + + return ref.getField(f); + } + else if (Std.isOfType(o, PolymodStaticClassReference)) { var ref:PolymodStaticClassReference = cast(o, PolymodStaticClassReference); @@ -1337,12 +1349,6 @@ class PolymodInterpEx extends Interp // return result; } - var abstractKey:String = Type.getClassName(o) + '.' + f; - if (PolymodScriptClass.abstractClassStatics.exists(abstractKey)) - { - return Reflect.getProperty(PolymodScriptClass.abstractClassStatics[abstractKey], abstractKey.replace('.', '_')); - } - // Default behavior if (Reflect.hasField(o, f)) { @@ -1386,7 +1392,12 @@ class PolymodInterpEx extends Interp } // Otherwise, we assume the field is fine to use. - if (Std.isOfType(o, PolymodStaticClassReference)) + if (Std.isOfType(o, PolymodStaticAbstractReference)) + { + // If the target is an abstract, assignment is always invalid. + errorEx(EInvalidFinalSet(f)); + } + else if (Std.isOfType(o, PolymodStaticClassReference)) { var ref:PolymodStaticClassReference = cast(o, PolymodStaticClassReference); @@ -1512,6 +1523,7 @@ class PolymodInterpEx extends Interp { if (importedClass.cls != null) return importedClass.cls; if (importedClass.enm != null) return importedClass.enm; + if (importedClass.abs != null) return importedClass.abs; // Resolve imported scripted classes. var result = PolymodStaticClassReference.tryBuild(importedClass.fullPath); @@ -1917,7 +1929,7 @@ class PolymodInterpEx extends Interp } } - public function registerModules(module:Array, ?origin:String = "hscript") + public function registerModules(module:Array, ?origin:String = "hscript"):Void { var pkg:Array = null; var imports:Map = []; @@ -1967,7 +1979,8 @@ class PolymodInterpEx extends Interp pkg: path.slice(0, path.length - 1), fullPath: path.join("."), cls: null, - enm: null + enm: null, + abs: null }; if (PolymodScriptClass.importOverrides.exists(importedClass.fullPath)) @@ -1980,15 +1993,7 @@ class PolymodInterpEx extends Interp else if (PolymodScriptClass.abstractClassImpls.exists(importedClass.fullPath)) { // We used a macro to map each abstract to its implementation. - importedClass.cls = PolymodScriptClass.abstractClassImpls.get(importedClass.fullPath); - - if (importedClass.cls == null) { - Polymod.warning(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Abstract type ${importedClass.fullPath} could not be resolved. Try using the underlying type instead.', origin); - } else { - // trace('RESOLVED ABSTRACT CLASS ${importedClass.fullPath} -> ${Type.getClassName(importedClass.cls)}'); - // trace(Type.getClassFields(importedClass.cls)); - } - + importedClass.abs = PolymodScriptClass.abstractClassImpls.get(importedClass.fullPath); } else if (_scriptEnumDescriptors.exists(importedClass.fullPath)) { @@ -2044,7 +2049,8 @@ class PolymodInterpEx extends Interp pkg: path.slice(0, path.length - 1), fullPath: path.join("."), cls: null, - enm: null + enm: null, + abs: null }; if (PolymodScriptClass.importOverrides.exists(importedClass.fullPath)) @@ -2057,9 +2063,7 @@ class PolymodInterpEx extends Interp else if (PolymodScriptClass.abstractClassImpls.exists(importedClass.fullPath)) { // We used a macro to map each abstract to its implementation. - importedClass.cls = PolymodScriptClass.abstractClassImpls.get(importedClass.fullPath); - trace('RESOLVED ABSTRACT CLASS ${importedClass.fullPath} -> ${Type.getClassName(importedClass.cls)}'); - trace(Type.getClassFields(importedClass.cls)); + importedClass.abs = PolymodScriptClass.abstractClassImpls.get(importedClass.fullPath); } else if (_scriptEnumDescriptors.exists(importedClass.fullPath)) { diff --git a/polymod/hscript/_internal/PolymodScriptClass.hx b/polymod/hscript/_internal/PolymodScriptClass.hx index 95220013..e0f74948 100644 --- a/polymod/hscript/_internal/PolymodScriptClass.hx +++ b/polymod/hscript/_internal/PolymodScriptClass.hx @@ -92,21 +92,26 @@ class PolymodScriptClass * and map them to internal implementation classes. * We use this to access the functions of these abstracts. */ - public static var abstractClassImpls(get, never):Map>; + public static var abstractClassImpls(get, never):Map; - static var _abstractClassImpls:Map> = null; + static var _abstractClassImpls:Map = null; - static function get_abstractClassImpls():Map> + static function get_abstractClassImpls():Map { if (_abstractClassImpls == null) { - _abstractClassImpls = new Map>(); + _abstractClassImpls = new Map(); - var baseAbstractClassImpls:Map> = PolymodScriptClassMacro.listAbstractImpls(); + var baseAbstractClassImpls:Map, + clsName:String, + }> = PolymodScriptClassMacro.listAbstractImpls(); for (key => value in baseAbstractClassImpls) { - _abstractClassImpls.set(key, value); + if (value == null) continue; + + _abstractClassImpls.set(key, new PolymodStaticAbstractReference(key, value.cls, value.clsName)); } } diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 577017cf..422a0d04 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -32,7 +32,7 @@ class PolymodScriptClassMacro return macro polymod.hscript._internal.PolymodScriptClassMacro.fetchHScriptedClasses(); } - public static macro function listAbstractImpls():ExprOf>> + public static macro function listAbstractImpls():ExprOf> { if (!onGenerateCallbackRegistered) { @@ -73,9 +73,9 @@ class PolymodScriptClassMacro var abstractImplEntries:Array = []; var abstractStaticEntries:Array = []; - Context.info('PolymodScriptClassMacro: Processing abstracts...', Context.currentPos()); + Context.info('PolymodScriptClassMacro: Processing abstracts...', Context.currentPos()); - var startTime:Float = Sys.time(); + var startTime:Float = Sys.time(); for (type in allTypes) { @@ -109,57 +109,51 @@ class PolymodScriptClassMacro else {} case TAbstract(t, _params): var abstractPath:String = t.toString(); - if (abstractPath == 'flixel.util.FlxColor') - { - var abstractType = t.get(); - var abstractImpl = abstractType.impl.get(); - var abstractImplPath:String = abstractType.impl?.toString() ?? ''; - - if (abstractImpl == null) { - // If the abstract doesn't have an implementation, it's usually an extern or something, so we always want to ignore it. - continue; - } + var abstractType = t.get(); + var abstractImpl = abstractType.impl?.get(); - var entryData = [macro $v{abstractPath}, macro $v{abstractImplPath}]; + if (abstractImpl == null) { + // If the abstract doesn't have an implementation, it's usually an extern or something, so we always want to ignore it. + continue; + } - abstractImplEntries.push(macro $a{entryData}); + var abstractImplPath:String = abstractType.impl?.toString() ?? ''; - for (field in abstractImpl.statics.get()) { - switch (field.type) { - case TAbstract(_, _): - // - case TType(_, _): - // - default: - continue; - } + var entryData = [macro $v{abstractPath}, macro $v{abstractImplPath}]; - var key:String = '${abstractImplPath}.${field.name}'; + abstractImplEntries.push(macro $a{entryData}); - if (!staticFieldToClass.exists(key)) { - continue; - } + for (field in abstractImpl.statics.get()) { + switch (field.type) { + case TAbstract(_, _): + // + case TType(_, _): + // + default: + continue; + } - var staticEntryData = [ - macro $v{key}, - macro $v{staticFieldToClass[key]}, - ]; + var key:String = '${abstractImplPath}.${field.name}'; - abstractStaticEntries.push(macro $a{staticEntryData}); + if (!staticFieldToClass.exists(key)) { + continue; } - // Try to apply RTTI? - abstractType.meta.add(':rtti', [], Context.currentPos()); - abstractImpl.meta.add(':rtti', [], Context.currentPos()); + var staticEntryData = [ + macro $v{key}, + macro $v{staticFieldToClass[key]}, + ]; + + abstractStaticEntries.push(macro $a{staticEntryData}); } default: continue; } } - var endTime:Float = Sys.time(); + var endTime:Float = Sys.time(); - var duration:Float = endTime - startTime; + var duration:Float = endTime - startTime; Context.info('PolymodScriptClassMacro: Registered ${hscriptedClassEntries.length} HScriptedClasses, ${abstractImplEntries.length} abstract impls, ${abstractStaticEntries.length} abstract statics in ${duration} sec.', Context.currentPos()); @@ -179,9 +173,9 @@ class PolymodScriptClassMacro { var fields:Array = []; - Context.info('PolymodScriptClassMacro: Processing abstract static fields...', Context.currentPos()); + Context.info('PolymodScriptClassMacro: Processing abstract static fields...', Context.currentPos()); - var startTime:Float = Sys.time(); + var startTime:Float = Sys.time(); for (type in types) { @@ -191,145 +185,136 @@ class PolymodScriptClassMacro var abstractPath = a.toString(); var abstractType = a.get(); - if (abstractPath != 'flixel.util.FlxColor') { - continue; - } - - if (abstractType.impl == null) { - // Only a few classes end up here, generally ones that are implemented directly in code. - // Includes like StdTypes.Float, StdTypes.Dynamic, StdTypes.Void, cpp.Int16, cpp.SizeT, Class, Enum - continue; - } else { - var abstractImplPath = abstractType.impl.toString(); - var abstractImplType = abstractType.impl.get(); - var abstractImplStatics = abstractImplType.statics.get(); - var underlyingType = abstractType.type; - - for (field in abstractImplStatics) - { - switch (field.kind) + if (abstractType.impl == null) { + // Only a few classes end up here, generally ones that are implemented directly in code. + // Includes like StdTypes.Float, StdTypes.Dynamic, StdTypes.Void, cpp.Int16, cpp.SizeT, Class, Enum + continue; + } else { + var abstractImplPath = abstractType.impl.toString(); + var abstractImplType = abstractType.impl.get(); + var abstractImplStatics:Array = abstractImplType.statics.get(); + + for (field in abstractImplStatics) { - case FVar(read, write): - trace(field.name, read, write); - - var canGet:Bool = read == AccInline || read == AccNormal; - if (read == AccCall) - { - var getter:Null = null; - for (f in abstractImplStatics) + switch (field.kind) + { + case FVar(read, write): + var canGet:Bool = read == AccInline || read == AccNormal; + if (read == AccCall) { - if (f.name == 'get_${field.name}') + var getter:Null = null; + for (f in abstractImplStatics) { - getter = f; - break; - } - } + if (f.name == 'get_${field.name}') + { + getter = f; + break; + } + } - if (getter == null) - { - throw 'Getter is null?'; - } + if (getter == null) + { + throw 'Getter is null?'; + } - switch (getter.type) - { - case TFun(args, _): - if (args.length != 0) continue; - default: - throw 'Getter has an unknown type?'; - } + switch (getter.type) + { + case TFun(args, _): + if (args.length != 0) continue; + default: + throw 'Getter has an unknown type?'; + } - canGet = true; - } + canGet = true; + } - var canSet:Bool = write == AccNormal; - if (write == AccCall) - { - var setter:Null = null; - for (f in abstractImplType.statics.get()) + var canSet:Bool = write == AccNormal; + if (write == AccCall) { - if (f.name == 'set_${field.name}') + var setter:Null = null; + for (f in abstractImplType.statics.get()) { - setter = f; - break; + if (f.name == 'set_${field.name}') + { + setter = f; + break; + } } - } - if (setter == null) - { - throw 'Setter is null?'; - } + if (setter == null) + { + throw 'Setter is null?'; + } - switch (setter.type) - { - case TFun(args, _): - if (args.length != 1) continue; - default: - throw 'Setter has an unknown type?'; + switch (setter.type) + { + case TFun(args, _): + if (args.length != 1) continue; + default: + throw 'Setter has an unknown type?'; + } + + canSet = true; } - canSet = true; - } + if (canGet) { + var fieldName:String = '${abstractImplPath.replace('.', '_')}_${field.name}'; - if (canGet) { - var fieldName:String = '${abstractImplPath.replace('.', '_')}_${field.name}'; + fields.push( + { + pos: Context.currentPos(), + name: fieldName, + access: [Access.APublic, Access.AStatic], + kind: FProp(canGet ? 'get' : 'never', canSet ? 'set' : 'never', (macro: Dynamic), null) + }); + + var fieldExpr:Expr = null; + try { + // when this fails, this should mean that we are dealing with an enum abstract + // so we need to handle it differently + var fullPath:String = '${abstractType.module}.${abstractType.name}'; + Context.getType(fullPath); + fieldExpr = Context.parse('${fullPath}.${field.name}', Context.currentPos()); + } catch (_) { + fieldExpr = Context.getTypedExpr(field.expr()); + } - fields.push( - { - pos: Context.currentPos(), - name: fieldName, - access: [Access.APublic, Access.AStatic], - kind: FProp(canGet ? 'get' : 'never', canSet ? 'set' : 'never', (macro: Dynamic), null) - }); - - var fieldExpr:Expr = null; - try { - // when this fails, this should mean that we are dealing with an enum abstract - // so we need to handle it differently - var fullPath:String = '${abstractType.module}.${abstractType.name}'; - Context.getType(fullPath); - fieldExpr = Context.parse('${fullPath}.${field.name}', Context.currentPos()); - } catch (_) { - fieldExpr = Context.getTypedExpr(field.expr()); - } - - if (canGet) { - fields.push({ - pos: Context.currentPos(), - name: 'get_${fieldName}', - access: [Access.APublic, Access.AStatic], - kind: FFun({ - args: [], - ret: null, - expr: macro { - @:privateAccess - return ${fieldExpr}; - } - }) - }); - } - - if (canSet) { - fields.push({ - pos: Context.currentPos(), - name: 'set_${fieldName}', - access: [Access.APublic, Access.AStatic], - kind: FFun({ - args: [{name: 'value'}], - ret: null, - expr: macro { - @:privateAccess - return ${fieldExpr} = value; - } - }) - }); - } - - staticFieldToClass.set('${abstractImplPath}.${field.name}', 'polymod.hscript._internal.AbstractStaticMembers_${iteration}'); - } - - default: - continue; - } + if (canGet) { + fields.push({ + pos: Context.currentPos(), + name: 'get_${fieldName}', + access: [Access.APublic, Access.AStatic], + kind: FFun({ + args: [], + ret: null, + expr: macro { @:privateAccess return ${fieldExpr}; } + }) + }); + } + + if (canSet) { + fields.push({ + pos: Context.currentPos(), + name: 'set_${fieldName}', + access: [Access.APublic, Access.AStatic], + kind: FFun({ + args: [{name: 'value'}], + ret: null, + expr: macro { @:privateAccess return ${fieldExpr} = value; } + }) + }); + } + + staticFieldToClass.set('${abstractImplPath}.${field.name}', 'polymod.hscript._internal.AbstractStaticMembers_${iteration}'); + } + + case FMethod(k): + // Skip methods, since we will be able to access the implementation class directly via Reflection. + continue; + + default: + continue; + } } } default: @@ -351,11 +336,11 @@ class PolymodScriptClassMacro fields: fields }); - var endTime:Float = Sys.time(); + var endTime:Float = Sys.time(); - var duration:Float = endTime - startTime; + var duration:Float = endTime - startTime; - Context.info('PolymodScriptClassMacro: Processed ${fields.length} static fields in ${duration} sec (iteration #${iteration}).', Context.currentPos()); + Context.info('PolymodScriptClassMacro: Processed ${fields.length} static fields in ${duration} sec (iteration #${iteration}).', Context.currentPos()); iteration++; } @@ -373,16 +358,16 @@ class PolymodScriptClassMacro for (element in metaData.hscriptedClasses) { - if (element.length != 2) + if (element.length != 2) { - throw 'Malformed element in hscriptedClasses: ' + element; - } + throw 'Malformed element in hscriptedClasses: ' + element; + } - var superClassPath:String = element[0]; - var classPath:String = element[1]; + var superClassPath:String = element[0]; + var classPath:String = element[1]; var classType:Class = cast Type.resolveClass(classPath); - result.set(superClassPath, classType); - } + result.set(superClassPath, classType); + } return result; } @@ -392,13 +377,13 @@ class PolymodScriptClassMacro } } - public static function fetchAbstractImpls():Map> + public static function fetchAbstractImpls():Map { var metaData = Meta.getType(PolymodScriptClassMacro); if (metaData.abstractImpls != null) { - var result:Map> = []; + var result:Map = []; // Each element is formatted as `[abstractPath, abstractImplPath]`. @@ -419,16 +404,20 @@ class PolymodScriptClassMacro throw 'Could not resolve ' + abstractPath; } #else - // trace('Resolving using native method'); var abstractImplType:Class = cast Type.resolveClass(abstractImplPath); if (abstractImplType == null) { - // trace('POLYMOD ABSTRACTS: Could not resolve $abstractImplPath'); + // If the abstract type was found at compile time, but couldn't resolve at runtime, + // it probably got optimized out. + // We'll have to construct a PolymodStaticAbstractReference for it later. } #end - result.set(abstractPath, abstractImplType); + result.set(abstractPath, { + cls: abstractImplType, + clsName: abstractImplPath + }); } return result; @@ -484,3 +473,8 @@ class PolymodScriptClassMacro } #end } + +typedef AbstractImplEntry = { + cls: Class, + clsName:String, +}; From 26171e005e589524f5dd628777ee7a6b2c783c6f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 3 Feb 2026 16:01:56 -0500 Subject: [PATCH 09/16] Resolve an issue preventing abstracts from being instantiated. --- .../hscript/_internal/PolymodClassDeclEx.hx | 20 +++++++++++++++++++ polymod/hscript/_internal/PolymodInterpEx.hx | 20 ++++++++++++------- .../hscript/_internal/PolymodScriptClass.hx | 6 +++++- .../_internal/PolymodScriptClassMacro.hx | 19 +++++++++++------- 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/polymod/hscript/_internal/PolymodClassDeclEx.hx b/polymod/hscript/_internal/PolymodClassDeclEx.hx index 282c88f6..8b88d2bd 100644 --- a/polymod/hscript/_internal/PolymodClassDeclEx.hx +++ b/polymod/hscript/_internal/PolymodClassDeclEx.hx @@ -146,11 +146,17 @@ class PolymodStaticAbstractReference { * The name of the abstract class, as it was imported. */ public var absName:String; + /** * The internal class that implements the abstract class's static functions, * if it exists. */ public var absImpl:Null>; + + /** + * The path of the implementation class. + * Used for resolving static fields cached at macro time. + */ public var absImplPath:String; public function new(absName:String, absImpl:Null>, absImplPath:String) @@ -160,6 +166,20 @@ class PolymodStaticAbstractReference { this.absImplPath = absImplPath; } + /** + * Instantiate an instance of the underlying implementation class. + * @param args The arguments to pass to the constructor. + * @return The resulting instance. + * @throws ex Thrown if the underlying implementation class is not available. + */ + public function instantiate(args:Array):Dynamic { + if (this.absImpl == null) { + throw 'Could not resolve abstract class ${absName}.'; + } + + return Type.createInstance(this.absImpl, args); + } + /** * Retrieve a static field of the abstract class. * @param fieldName The name of the field to retrieve. diff --git a/polymod/hscript/_internal/PolymodInterpEx.hx b/polymod/hscript/_internal/PolymodInterpEx.hx index 0e5e0ac3..d64b133c 100644 --- a/polymod/hscript/_internal/PolymodInterpEx.hx +++ b/polymod/hscript/_internal/PolymodInterpEx.hx @@ -115,15 +115,21 @@ class PolymodInterpEx extends Interp // Ignore importedClass.enm as enums cannot be instantiated. // Importing a blacklisted module creates an import with a `null` class, so we check for that here. - var c = importedClass.cls; - if (c == null) - { - errorEx(EBlacklistedModule(importedClass.fullPath)); + var abs = importedClass.abs; + if (abs != null) { + try { + return abs.instantiate(args); + } catch (e) { + errorEx(EInvalidModule(importedClass.fullPath)); + } } - else - { - return Type.createInstance(c, args); + + var cls = importedClass.cls; + if (cls != null) { + return Type.createInstance(cls, args); } + + errorEx(EBlacklistedModule(importedClass.fullPath)); } // Attempt to resolve the class without overrides. diff --git a/polymod/hscript/_internal/PolymodScriptClass.hx b/polymod/hscript/_internal/PolymodScriptClass.hx index e0f74948..2f26acf8 100644 --- a/polymod/hscript/_internal/PolymodScriptClass.hx +++ b/polymod/hscript/_internal/PolymodScriptClass.hx @@ -54,7 +54,7 @@ class PolymodScriptClass /** * Register a scripted class by parsing the text of that script. */ - static function registerScriptClassByString(body:String, path:String = null):Void + static function registerScriptClassByString(body:String, ?path:String):Void { scriptInterp.addModule(body, path == null ? 'hscriptClass' : 'hscriptClass($path)'); } @@ -118,6 +118,10 @@ class PolymodScriptClass return _abstractClassImpls; } + /** + * Define a list of `fieldName -> Class` pointing to the generated class containing a reference + * to each static field of each abstract. + */ public static var abstractClassStatics(get, never):Map>; static var _abstractClassStatics:Map> = null; diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 422a0d04..80837599 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -19,7 +19,9 @@ class PolymodScriptClassMacro { /** * Returns a `Map>` which maps superclass paths to scripted classes. - * So `class ScriptedStage extends Stage implements HScriptable` will be `"Stage" -> ScriptedStage` + * So `class ScriptedStage extends Stage implements HScriptable` will be `"Stage" -> ScriptedStage` + * + * @return An expression containing a map of superclasses to their scripted classes */ public static macro function listHScriptedClasses():ExprOf>> { @@ -32,6 +34,9 @@ class PolymodScriptClassMacro return macro polymod.hscript._internal.PolymodScriptClassMacro.fetchHScriptedClasses(); } + /** + * @return An expression containing a map of abstract classes to their implementations + */ public static macro function listAbstractImpls():ExprOf> { if (!onGenerateCallbackRegistered) @@ -43,6 +48,10 @@ class PolymodScriptClassMacro return macro polymod.hscript._internal.PolymodScriptClassMacro.fetchAbstractImpls(); } + /** + * @return An expression containing a map of abstract field names to + * the internal class, generated by Polymod, to store that value (accessible via Reflection). + */ public static macro function listAbstractStatics():ExprOf>> { if (!onAfterTypingCallbackRegistered) @@ -81,8 +90,9 @@ class PolymodScriptClassMacro { switch (type) { - // Class instances case TInst(t, _params): + // Parse classes to check if they are `HScriptedClass` implementations, for processing later. + var classType:ClassType = t.get(); var classPath:String = '${classType.pack.concat([classType.name]).join(".")}'; @@ -92,8 +102,6 @@ class PolymodScriptClassMacro } else if (MacroUtil.implementsInterface(classType, hscriptedClassType)) { - // Context.info('${classPath} implements HScriptedClass? YEAH', Context.currentPos()); - // TODO: Do we need to parameterize? var superClass:Null = classType.superClass != null ? classType.superClass.t.get() : null; if (superClass == null) throw 'No superclass for ' + classPath; @@ -101,7 +109,6 @@ class PolymodScriptClassMacro var superClassPath:String = '${superClass.pack.concat([superClass.name]).join(".")}'; var entryData = [ macro $v{superClassPath}, - // TODO: How do we do reification to get a class? macro $v{classPath} ]; hscriptedClassEntries.push(macro $a{entryData}); @@ -173,8 +180,6 @@ class PolymodScriptClassMacro { var fields:Array = []; - Context.info('PolymodScriptClassMacro: Processing abstract static fields...', Context.currentPos()); - var startTime:Float = Sys.time(); for (type in types) From 3aada1dded6b3568f7d6d18190b44f5ea575ed9d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 3 Feb 2026 16:02:27 -0500 Subject: [PATCH 10/16] Add support for resolving typedefs to their underlying types. --- polymod/hscript/_internal/PolymodInterpEx.hx | 3 + .../hscript/_internal/PolymodScriptClass.hx | 24 ++++ .../_internal/PolymodScriptClassMacro.hx | 114 +++++++++++++++++- 3 files changed, 137 insertions(+), 4 deletions(-) diff --git a/polymod/hscript/_internal/PolymodInterpEx.hx b/polymod/hscript/_internal/PolymodInterpEx.hx index d64b133c..e1f43534 100644 --- a/polymod/hscript/_internal/PolymodInterpEx.hx +++ b/polymod/hscript/_internal/PolymodInterpEx.hx @@ -2001,6 +2001,9 @@ class PolymodInterpEx extends Interp // We used a macro to map each abstract to its implementation. importedClass.abs = PolymodScriptClass.abstractClassImpls.get(importedClass.fullPath); } + else if (PolymodScriptClass.typedefs.exists(importedClass.fullPath)) { + importedClass.cls = PolymodScriptClass.typedefs.get(importedClass.fullPath); + } else if (_scriptEnumDescriptors.exists(importedClass.fullPath)) { // do nothing diff --git a/polymod/hscript/_internal/PolymodScriptClass.hx b/polymod/hscript/_internal/PolymodScriptClass.hx index 2f26acf8..99f95364 100644 --- a/polymod/hscript/_internal/PolymodScriptClass.hx +++ b/polymod/hscript/_internal/PolymodScriptClass.hx @@ -142,6 +142,30 @@ class PolymodScriptClass return _abstractClassStatics; } + /** + * Define a list of `typeName -> Class` which provides a reference to each typedef, + * since typedefs can't be normally resolved at runtime. + */ + public static var typedefs(get, never):Map>; + static var _typedefs:Map> = null; + + static function get_typedefs():Map> + { + if (_typedefs == null) + { + _typedefs = new Map>(); + + var baseTypedefs:Map> = PolymodScriptClassMacro.listTypedefs(); + + for (key => value in baseTypedefs) + { + _typedefs.set(key, value); + } + } + + return _typedefs; + } + /** * Register a scripted class by retrieving the script from the given path. */ diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 80837599..d618f7df 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -69,6 +69,20 @@ class PolymodScriptClassMacro return macro polymod.hscript._internal.PolymodScriptClassMacro.fetchAbstractStatics(); } + /** + * @return An expression containing a map of each typedef name to + * the underlying class type. + */ + public static macro function listTypedefs():ExprOf>> { + if (!onGenerateCallbackRegistered) + { + onGenerateCallbackRegistered = true; + haxe.macro.Context.onGenerate(onGenerate); + } + + return macro polymod.hscript._internal.PolymodScriptClassMacro.fetchTypedefs(); + } + #if macro static var onGenerateCallbackRegistered:Bool = false; static var onAfterTypingCallbackRegistered:Bool = false; @@ -81,8 +95,7 @@ class PolymodScriptClassMacro var hscriptedClassEntries:Array = []; var abstractImplEntries:Array = []; var abstractStaticEntries:Array = []; - - Context.info('PolymodScriptClassMacro: Processing abstracts...', Context.currentPos()); + var typedefEntries:Array = []; var startTime:Float = Sys.time(); @@ -113,8 +126,64 @@ class PolymodScriptClassMacro ]; hscriptedClassEntries.push(macro $a{entryData}); } - else {} + + case TType(t, _params): + var typedefType:DefType = t.get(); + var typedefPath:String = t.toString(); + var typedefTarget:Type = Context.followWithAbstracts(type); + + switch (typedefTarget) { + case TAnonymous(_): + // Ignore typedefs to anonymous structures. + continue; + case TDynamic(_): + // Ignore typedefs to Dynamic. + continue; + case TFun(_args, _ret): + // Ignore typedefs to functions. + continue; + + case TAbstract(t, _params): + var targetPath:String = t.toString(); + + var entryData = [ + macro $v{typedefPath}, + macro $v{targetPath} + ]; + + typedefEntries.push(macro $a{entryData}); + + case TEnum(t, _params): + var targetEnum:EnumType = t.get(); + var targetEnumPath:String = '${targetEnum.pack.concat([targetEnum.name]).join(".")}'; + + var entryData = [ + macro $v{typedefPath}, + macro $v{targetEnumPath} + ]; + + typedefEntries.push(macro $a{entryData}); + + case TInst(t, _params): + var targetClass:ClassType = t.get(); + var targetClassPath:String = '${targetClass.pack.concat([targetClass.name]).join(".")}'; + + var entryData = [ + macro $v{typedefPath}, + macro $v{targetClassPath} + ]; + + typedefEntries.push(macro $a{entryData}); + + + default: + // Unknown typedef target type? + trace('TYPEDEF: ${typedefPath} -> ${typedefTarget}'); + } + case TAbstract(t, _params): + // Parse abstracts to cache their static values to allow scripts to access them later. + var abstractPath:String = t.toString(); var abstractType = t.get(); var abstractImpl = abstractType.impl?.get(); @@ -162,7 +231,12 @@ class PolymodScriptClassMacro var duration:Float = endTime - startTime; - Context.info('PolymodScriptClassMacro: Registered ${hscriptedClassEntries.length} HScriptedClasses, ${abstractImplEntries.length} abstract impls, ${abstractStaticEntries.length} abstract statics in ${duration} sec.', Context.currentPos()); + Context.info('PolymodScriptClassMacro: ' + + 'Registered ${hscriptedClassEntries.length} HScriptedClasses, ' + + '${abstractImplEntries.length} abstract impls, ' + + '${abstractStaticEntries.length} abstract statics, ' + + '${typedefEntries.length} typedefs ' + + 'in ${duration} sec.', Context.currentPos()); var polymodScriptClassClassType:ClassType = MacroUtil.getClassType('polymod.hscript._internal.PolymodScriptClassMacro'); polymodScriptClassClassType.meta.remove('hscriptedClasses'); @@ -171,6 +245,8 @@ class PolymodScriptClassMacro polymodScriptClassClassType.meta.add('abstractImpls', abstractImplEntries, Context.currentPos()); polymodScriptClassClassType.meta.remove('abstractStatics'); polymodScriptClassClassType.meta.add('abstractStatics', abstractStaticEntries, Context.currentPos()); + polymodScriptClassClassType.meta.remove('typedefs'); + polymodScriptClassClassType.meta.add('typedefs', typedefEntries, Context.currentPos()); } static var iteration:Int = 0; @@ -465,6 +541,36 @@ class PolymodScriptClassMacro } } + public static function fetchTypedefs():Map> + { + var metaData = Meta.getType(PolymodScriptClassMacro); + + if (metaData.typedefs != null) + { + var result:Map> = []; + + for (element in metaData.typedefs) + { + if (element.length != 2) + { + throw 'Malformed element in typedefs: ' + element; + } + + var fieldPath:String = element[0]; + var reflectClassPath:String = element[1]; + var reflectClass:Class = cast Type.resolveClass(reflectClassPath); + + result.set(fieldPath, reflectClass); + } + + return result; + } + else + { + throw 'No typedefs found in PolymodScriptClassMacro!'; + } + } + #if js static var PACKAGE_NAME_INVALID = ~/[^.a-zA-Z0-9]/; From d6df8733369cd415ceb8cbabb09e52fde7ed9914 Mon Sep 17 00:00:00 2001 From: Hyper_ <40342021+NotHyper-474@users.noreply.github.com> Date: Mon, 2 Feb 2026 16:17:27 -0300 Subject: [PATCH 11/16] Store abstract statics as a Resource to prevent compiler stall --- .../_internal/PolymodScriptClassMacro.hx | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index d618f7df..b8d84bb3 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -17,6 +17,11 @@ using StringTools; */ class PolymodScriptClassMacro { + /** + * The name for the Haxe resource that stores the abstract static field paths. + */ + static inline final ABSTRACT_STATICS_RES_NAME:String = 'PolymodScriptClassMacro_AbstractStatics'; + /** * Returns a `Map>` which maps superclass paths to scripted classes. * So `class ScriptedStage extends Stage implements HScriptable` will be `"Stage" -> ScriptedStage` @@ -94,7 +99,7 @@ class PolymodScriptClassMacro var hscriptedClassEntries:Array = []; var abstractImplEntries:Array = []; - var abstractStaticEntries:Array = []; + var abstractStaticEntries:Array<{}> = []; var typedefEntries:Array = []; var startTime:Float = Sys.time(); @@ -215,12 +220,13 @@ class PolymodScriptClassMacro continue; } - var staticEntryData = [ - macro $v{key}, - macro $v{staticFieldToClass[key]}, - ]; + var staticEntryData = + { + fieldPath: key, + reflectClassPath: staticFieldToClass[key] + }; - abstractStaticEntries.push(macro $a{staticEntryData}); + abstractStaticEntries.push(staticEntryData); } default: continue; @@ -243,10 +249,12 @@ class PolymodScriptClassMacro polymodScriptClassClassType.meta.add('hscriptedClasses', hscriptedClassEntries, Context.currentPos()); polymodScriptClassClassType.meta.remove('abstractImpls'); polymodScriptClassClassType.meta.add('abstractImpls', abstractImplEntries, Context.currentPos()); - polymodScriptClassClassType.meta.remove('abstractStatics'); - polymodScriptClassClassType.meta.add('abstractStatics', abstractStaticEntries, Context.currentPos()); polymodScriptClassClassType.meta.remove('typedefs'); polymodScriptClassClassType.meta.add('typedefs', typedefEntries, Context.currentPos()); + + // It can get VERY BIG so we have to resort to store it as a resource instead. + var absStaticsData:String = haxe.Serializer.run(abstractStaticEntries); + Context.addResource(ABSTRACT_STATICS_RES_NAME, haxe.io.Bytes.ofString(absStaticsData)); } static var iteration:Int = 0; @@ -511,33 +519,36 @@ class PolymodScriptClassMacro public static function fetchAbstractStatics():Map> { - var metaData = Meta.getType(PolymodScriptClassMacro); + var resDataContent:Null = haxe.Resource.getString(ABSTRACT_STATICS_RES_NAME); + if (resDataContent == null) + { + throw '"$ABSTRACT_STATICS_RES_NAME" resource not found!'; + } + var abstractStatics:Array<{fieldPath:String, reflectClassPath:String}> = cast haxe.Unserializer.run(resDataContent); - if (metaData.abstractStatics != null) + if (abstractStatics != null) { var result:Map> = []; // Each element is formatted as `[abstractPathImpl.fieldName, reflectClass]`. - for (element in metaData.abstractStatics) + for (element in abstractStatics) { - if (element.length != 2) + if ((element.fieldPath?.length ?? 0) == 0 || (element.reflectClassPath?.length ?? 0) == 0) { throw 'Malformed element in abstractStatics: ' + element; } - var fieldPath:String = element[0]; - var reflectClassPath:String = element[1]; - var reflectClass:Class = cast Type.resolveClass(reflectClassPath); + var reflectClass:Class = cast Type.resolveClass(element.reflectClassPath); - result.set(fieldPath, reflectClass); + result.set(element.fieldPath, reflectClass); } return result; } else { - throw 'No abstractStatics found in PolymodScriptClassMacro!'; + throw 'No abstractStatics found in "${ABSTRACT_STATICS_RES_NAME}" resource!'; } } From 205d572a9732f8fd738209a942d41795dd154136 Mon Sep 17 00:00:00 2001 From: lemz <87707926+lemz1@users.noreply.github.com> Date: Wed, 4 Feb 2026 00:17:04 +0100 Subject: [PATCH 12/16] fix abstract static getter/setter if check --- polymod/hscript/_internal/PolymodScriptClassMacro.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index b8d84bb3..0dd34a91 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -346,7 +346,7 @@ class PolymodScriptClassMacro canSet = true; } - if (canGet) { + if (canGet || canSet) { var fieldName:String = '${abstractImplPath.replace('.', '_')}_${field.name}'; fields.push( From e2fa62b9982e102868f9d006076fa70935a199c1 Mon Sep 17 00:00:00 2001 From: lemz <87707926+lemz1@users.noreply.github.com> Date: Wed, 4 Feb 2026 00:37:08 +0100 Subject: [PATCH 13/16] call actual constructor of abstract --- polymod/hscript/_internal/PolymodClassDeclEx.hx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/polymod/hscript/_internal/PolymodClassDeclEx.hx b/polymod/hscript/_internal/PolymodClassDeclEx.hx index 8b88d2bd..efcda3aa 100644 --- a/polymod/hscript/_internal/PolymodClassDeclEx.hx +++ b/polymod/hscript/_internal/PolymodClassDeclEx.hx @@ -177,7 +177,12 @@ class PolymodStaticAbstractReference { throw 'Could not resolve abstract class ${absName}.'; } - return Type.createInstance(this.absImpl, args); + var ctor = Reflect.field(this.absImpl, '_new'); + if (ctor == null) { + throw 'Could not find constructor for abstract class ${absName}'; + } + + return Reflect.callMethod(this.absImpl, ctor, args); } /** From 2a0f3df8cbdd9b40afd2866e6b3a9f1ef4ef3a82 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 4 Feb 2026 00:03:27 -0500 Subject: [PATCH 14/16] Fixes to access for static fields of abstracts (getters and setters) --- .../hscript/_internal/PolymodClassDeclEx.hx | 28 +++++++++++++++++++ polymod/hscript/_internal/PolymodInterpEx.hx | 9 ++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/polymod/hscript/_internal/PolymodClassDeclEx.hx b/polymod/hscript/_internal/PolymodClassDeclEx.hx index efcda3aa..58e5f6b2 100644 --- a/polymod/hscript/_internal/PolymodClassDeclEx.hx +++ b/polymod/hscript/_internal/PolymodClassDeclEx.hx @@ -196,11 +196,39 @@ class PolymodStaticAbstractReference { var result:Dynamic = Reflect.getProperty(this.absImpl, fieldName); if (result != null) return result; } + var getterName:String = 'get_$fieldName'; + if (Reflect.hasField(this.absImpl, getterName)) { + var getter = Reflect.field(this.absImpl, getterName); + return Reflect.callMethod(this.absImpl, getter, []); + } } return fetchAbstractClassStatic(fieldName); } + /** + * Assign a static field of the abstract class. + * @param fieldName The name of the field to assign. + * @param fieldValue The value to assign to the field. + * @return The value of the field. + */ + public function setField(fieldName:String, fieldValue:Dynamic):Dynamic { + if (this.absImpl != null) { + if (Reflect.hasField(this.absImpl, fieldName)) { + Reflect.setProperty(this.absImpl, fieldName, fieldValue); + return fieldValue; + } + var setterName:String = 'set_$fieldName'; + if (Reflect.hasField(this.absImpl, setterName)) { + var setter = Reflect.field(this.absImpl, setterName); + var result = Reflect.callMethod(this.absImpl, setter, [fieldValue]); + return result; + } + } + + throw 'Could not resolve abstract static field ${fieldName}'; + } + /** * Call a static function of the abstract class. * @param funcName The name of the function to call. diff --git a/polymod/hscript/_internal/PolymodInterpEx.hx b/polymod/hscript/_internal/PolymodInterpEx.hx index e1f43534..14a11f4d 100644 --- a/polymod/hscript/_internal/PolymodInterpEx.hx +++ b/polymod/hscript/_internal/PolymodInterpEx.hx @@ -1400,8 +1400,13 @@ class PolymodInterpEx extends Interp // Otherwise, we assume the field is fine to use. if (Std.isOfType(o, PolymodStaticAbstractReference)) { - // If the target is an abstract, assignment is always invalid. - errorEx(EInvalidFinalSet(f)); + var ref:PolymodStaticAbstractReference = cast(o, PolymodStaticAbstractReference); + + try { + return ref.setField(f, v); + } catch (e:Dynamic) { + errorEx(EInvalidFinalSet(f)); + } } else if (Std.isOfType(o, PolymodStaticClassReference)) { From 43c605e73572fa671fe0b88a3df91fa1353a0836 Mon Sep 17 00:00:00 2001 From: AbnormalPoof Date: Tue, 3 Feb 2026 23:46:25 -0600 Subject: [PATCH 15/16] Run haxe formatter --- .../hscript/_internal/PolymodClassDeclEx.hx | 52 ++++--- polymod/hscript/_internal/PolymodInterpEx.hx | 23 ++- .../hscript/_internal/PolymodScriptClass.hx | 11 +- .../_internal/PolymodScriptClassMacro.hx | 140 ++++++++++-------- 4 files changed, 134 insertions(+), 92 deletions(-) diff --git a/polymod/hscript/_internal/PolymodClassDeclEx.hx b/polymod/hscript/_internal/PolymodClassDeclEx.hx index 58e5f6b2..61190d39 100644 --- a/polymod/hscript/_internal/PolymodClassDeclEx.hx +++ b/polymod/hscript/_internal/PolymodClassDeclEx.hx @@ -141,7 +141,8 @@ class PolymodStaticClassReference * to the abstract static value store built by PolymodScriptClassMacro at compile time. */ @:nullSafety -class PolymodStaticAbstractReference { +class PolymodStaticAbstractReference +{ /** * The name of the abstract class, as it was imported. */ @@ -172,13 +173,16 @@ class PolymodStaticAbstractReference { * @return The resulting instance. * @throws ex Thrown if the underlying implementation class is not available. */ - public function instantiate(args:Array):Dynamic { - if (this.absImpl == null) { + public function instantiate(args:Array):Dynamic + { + if (this.absImpl == null) + { throw 'Could not resolve abstract class ${absName}.'; } var ctor = Reflect.field(this.absImpl, '_new'); - if (ctor == null) { + if (ctor == null) + { throw 'Could not find constructor for abstract class ${absName}'; } @@ -190,14 +194,18 @@ class PolymodStaticAbstractReference { * @param fieldName The name of the field to retrieve. * @return The value of the field. */ - public function getField(fieldName:String):Dynamic { - if (this.absImpl != null) { - if (Reflect.hasField(this.absImpl, fieldName)) { + public function getField(fieldName:String):Dynamic + { + if (this.absImpl != null) + { + if (Reflect.hasField(this.absImpl, fieldName)) + { var result:Dynamic = Reflect.getProperty(this.absImpl, fieldName); if (result != null) return result; } var getterName:String = 'get_$fieldName'; - if (Reflect.hasField(this.absImpl, getterName)) { + if (Reflect.hasField(this.absImpl, getterName)) + { var getter = Reflect.field(this.absImpl, getterName); return Reflect.callMethod(this.absImpl, getter, []); } @@ -212,14 +220,18 @@ class PolymodStaticAbstractReference { * @param fieldValue The value to assign to the field. * @return The value of the field. */ - public function setField(fieldName:String, fieldValue:Dynamic):Dynamic { - if (this.absImpl != null) { - if (Reflect.hasField(this.absImpl, fieldName)) { + public function setField(fieldName:String, fieldValue:Dynamic):Dynamic + { + if (this.absImpl != null) + { + if (Reflect.hasField(this.absImpl, fieldName)) + { Reflect.setProperty(this.absImpl, fieldName, fieldValue); return fieldValue; } var setterName:String = 'set_$fieldName'; - if (Reflect.hasField(this.absImpl, setterName)) { + if (Reflect.hasField(this.absImpl, setterName)) + { var setter = Reflect.field(this.absImpl, setterName); var result = Reflect.callMethod(this.absImpl, setter, [fieldValue]); return result; @@ -235,25 +247,31 @@ class PolymodStaticAbstractReference { * @param args The arguments to pass to the function. * @return The return value of the function. */ - public function callFunction(funcName:String, args:Array):Dynamic { + public function callFunction(funcName:String, args:Array):Dynamic + { // If we can just call the method directly, do that. var func = getField(funcName); - if (func != null) { + if (func != null) + { return Reflect.callMethod(this.absImpl, func, args); } throw 'Could not resolve abstract class static function ${funcName}'; } - function fetchAbstractClassStatic(fieldName:String):Dynamic { + function fetchAbstractClassStatic(fieldName:String):Dynamic + { var key:String = '${this.absImplPath}.${fieldName}'; - if (PolymodScriptClass.abstractClassStatics.exists(key)) { + if (PolymodScriptClass.abstractClassStatics.exists(key)) + { var holder = PolymodScriptClass.abstractClassStatics.get(key); var property = key.replace('.', '_'); return Reflect.getProperty(holder, property); - } else { + } + else + { throw 'Could not resolve abstract class static field ${fieldName}'; } } diff --git a/polymod/hscript/_internal/PolymodInterpEx.hx b/polymod/hscript/_internal/PolymodInterpEx.hx index 14a11f4d..7189e4d4 100644 --- a/polymod/hscript/_internal/PolymodInterpEx.hx +++ b/polymod/hscript/_internal/PolymodInterpEx.hx @@ -116,16 +116,21 @@ class PolymodInterpEx extends Interp // Importing a blacklisted module creates an import with a `null` class, so we check for that here. var abs = importedClass.abs; - if (abs != null) { - try { + if (abs != null) + { + try + { return abs.instantiate(args); - } catch (e) { + } + catch (e) + { errorEx(EInvalidModule(importedClass.fullPath)); } } var cls = importedClass.cls; - if (cls != null) { + if (cls != null) + { return Type.createInstance(cls, args); } @@ -1402,9 +1407,12 @@ class PolymodInterpEx extends Interp { var ref:PolymodStaticAbstractReference = cast(o, PolymodStaticAbstractReference); - try { + try + { return ref.setField(f, v); - } catch (e:Dynamic) { + } + catch (e:Dynamic) + { errorEx(EInvalidFinalSet(f)); } } @@ -2006,7 +2014,8 @@ class PolymodInterpEx extends Interp // We used a macro to map each abstract to its implementation. importedClass.abs = PolymodScriptClass.abstractClassImpls.get(importedClass.fullPath); } - else if (PolymodScriptClass.typedefs.exists(importedClass.fullPath)) { + else if (PolymodScriptClass.typedefs.exists(importedClass.fullPath)) + { importedClass.cls = PolymodScriptClass.typedefs.get(importedClass.fullPath); } else if (_scriptEnumDescriptors.exists(importedClass.fullPath)) diff --git a/polymod/hscript/_internal/PolymodScriptClass.hx b/polymod/hscript/_internal/PolymodScriptClass.hx index 99f95364..900a6f63 100644 --- a/polymod/hscript/_internal/PolymodScriptClass.hx +++ b/polymod/hscript/_internal/PolymodScriptClass.hx @@ -102,10 +102,11 @@ class PolymodScriptClass { _abstractClassImpls = new Map(); - var baseAbstractClassImpls:Map, - clsName:String, - }> = PolymodScriptClassMacro.listAbstractImpls(); + var baseAbstractClassImpls:Map, + clsName:String, + }> = PolymodScriptClassMacro.listAbstractImpls(); for (key => value in baseAbstractClassImpls) { @@ -123,6 +124,7 @@ class PolymodScriptClass * to each static field of each abstract. */ public static var abstractClassStatics(get, never):Map>; + static var _abstractClassStatics:Map> = null; static function get_abstractClassStatics():Map> @@ -147,6 +149,7 @@ class PolymodScriptClass * since typedefs can't be normally resolved at runtime. */ public static var typedefs(get, never):Map>; + static var _typedefs:Map> = null; static function get_typedefs():Map> diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 0dd34a91..f94b3b9e 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -78,7 +78,8 @@ class PolymodScriptClassMacro * @return An expression containing a map of each typedef name to * the underlying class type. */ - public static macro function listTypedefs():ExprOf>> { + public static macro function listTypedefs():ExprOf>> + { if (!onGenerateCallbackRegistered) { onGenerateCallbackRegistered = true; @@ -125,10 +126,7 @@ class PolymodScriptClassMacro if (superClass == null) throw 'No superclass for ' + classPath; var superClassPath:String = '${superClass.pack.concat([superClass.name]).join(".")}'; - var entryData = [ - macro $v{superClassPath}, - macro $v{classPath} - ]; + var entryData = [macro $v{superClassPath}, macro $v{classPath}]; hscriptedClassEntries.push(macro $a{entryData}); } @@ -137,7 +135,8 @@ class PolymodScriptClassMacro var typedefPath:String = t.toString(); var typedefTarget:Type = Context.followWithAbstracts(type); - switch (typedefTarget) { + switch (typedefTarget) + { case TAnonymous(_): // Ignore typedefs to anonymous structures. continue; @@ -151,10 +150,7 @@ class PolymodScriptClassMacro case TAbstract(t, _params): var targetPath:String = t.toString(); - var entryData = [ - macro $v{typedefPath}, - macro $v{targetPath} - ]; + var entryData = [macro $v{typedefPath}, macro $v{targetPath}]; typedefEntries.push(macro $a{entryData}); @@ -162,10 +158,7 @@ class PolymodScriptClassMacro var targetEnum:EnumType = t.get(); var targetEnumPath:String = '${targetEnum.pack.concat([targetEnum.name]).join(".")}'; - var entryData = [ - macro $v{typedefPath}, - macro $v{targetEnumPath} - ]; + var entryData = [macro $v{typedefPath}, macro $v{targetEnumPath}]; typedefEntries.push(macro $a{entryData}); @@ -173,14 +166,10 @@ class PolymodScriptClassMacro var targetClass:ClassType = t.get(); var targetClassPath:String = '${targetClass.pack.concat([targetClass.name]).join(".")}'; - var entryData = [ - macro $v{typedefPath}, - macro $v{targetClassPath} - ]; + var entryData = [macro $v{typedefPath}, macro $v{targetClassPath}]; typedefEntries.push(macro $a{entryData}); - default: // Unknown typedef target type? trace('TYPEDEF: ${typedefPath} -> ${typedefTarget}'); @@ -193,7 +182,8 @@ class PolymodScriptClassMacro var abstractType = t.get(); var abstractImpl = abstractType.impl?.get(); - if (abstractImpl == null) { + if (abstractImpl == null) + { // If the abstract doesn't have an implementation, it's usually an extern or something, so we always want to ignore it. continue; } @@ -204,19 +194,22 @@ class PolymodScriptClassMacro abstractImplEntries.push(macro $a{entryData}); - for (field in abstractImpl.statics.get()) { - switch (field.type) { + for (field in abstractImpl.statics.get()) + { + switch (field.type) + { case TAbstract(_, _): // case TType(_, _): // - default: + default: continue; } var key:String = '${abstractImplPath}.${field.name}'; - if (!staticFieldToClass.exists(key)) { + if (!staticFieldToClass.exists(key)) + { continue; } @@ -242,7 +235,8 @@ class PolymodScriptClassMacro + '${abstractImplEntries.length} abstract impls, ' + '${abstractStaticEntries.length} abstract statics, ' + '${typedefEntries.length} typedefs ' - + 'in ${duration} sec.', Context.currentPos()); + + 'in ${duration} sec.', + Context.currentPos()); var polymodScriptClassClassType:ClassType = MacroUtil.getClassType('polymod.hscript._internal.PolymodScriptClassMacro'); polymodScriptClassClassType.meta.remove('hscriptedClasses'); @@ -274,11 +268,14 @@ class PolymodScriptClassMacro var abstractPath = a.toString(); var abstractType = a.get(); - if (abstractType.impl == null) { + if (abstractType.impl == null) + { // Only a few classes end up here, generally ones that are implemented directly in code. // Includes like StdTypes.Float, StdTypes.Dynamic, StdTypes.Void, cpp.Int16, cpp.SizeT, Class, Enum continue; - } else { + } + else + { var abstractImplPath = abstractType.impl.toString(); var abstractImplType = abstractType.impl.get(); var abstractImplStatics:Array = abstractImplType.statics.get(); @@ -346,52 +343,65 @@ class PolymodScriptClassMacro canSet = true; } - if (canGet || canSet) { + if (canGet || canSet) + { var fieldName:String = '${abstractImplPath.replace('.', '_')}_${field.name}'; fields.push( - { - pos: Context.currentPos(), - name: fieldName, - access: [Access.APublic, Access.AStatic], - kind: FProp(canGet ? 'get' : 'never', canSet ? 'set' : 'never', (macro: Dynamic), null) - }); + { + pos: Context.currentPos(), + name: fieldName, + access: [Access.APublic, Access.AStatic], + kind: FProp(canGet ? 'get' : 'never', canSet ? 'set' : 'never', (macro :Dynamic), null) + }); var fieldExpr:Expr = null; - try { + try + { // when this fails, this should mean that we are dealing with an enum abstract // so we need to handle it differently var fullPath:String = '${abstractType.module}.${abstractType.name}'; Context.getType(fullPath); fieldExpr = Context.parse('${fullPath}.${field.name}', Context.currentPos()); - } catch (_) { + } + catch (_) + { fieldExpr = Context.getTypedExpr(field.expr()); } - if (canGet) { - fields.push({ - pos: Context.currentPos(), - name: 'get_${fieldName}', - access: [Access.APublic, Access.AStatic], - kind: FFun({ - args: [], - ret: null, - expr: macro { @:privateAccess return ${fieldExpr}; } - }) - }); + if (canGet) + { + fields.push( + { + pos: Context.currentPos(), + name: 'get_${fieldName}', + access: [Access.APublic, Access.AStatic], + kind: FFun( + { + args: [], + ret: null, + expr: macro + {@:privateAccess return ${fieldExpr};} + }) + }); } - if (canSet) { - fields.push({ - pos: Context.currentPos(), - name: 'set_${fieldName}', - access: [Access.APublic, Access.AStatic], - kind: FFun({ - args: [{name: 'value'}], - ret: null, - expr: macro { @:privateAccess return ${fieldExpr} = value; } - }) - }); + if (canSet) + { + fields.push( + { + pos: Context.currentPos(), + name: 'set_${fieldName}', + access: [Access.APublic, Access.AStatic], + kind: FFun( + { + args: [ + {name: 'value'}], + ret: null, + expr: macro + {@:privateAccess return ${fieldExpr} = value;} + }) + }); } staticFieldToClass.set('${abstractImplPath}.${field.name}', 'polymod.hscript._internal.AbstractStaticMembers_${iteration}'); @@ -503,10 +513,11 @@ class PolymodScriptClassMacro } #end - result.set(abstractPath, { - cls: abstractImplType, - clsName: abstractImplPath - }); + result.set(abstractPath, + { + cls: abstractImplType, + clsName: abstractImplPath + }); } return result; @@ -596,7 +607,8 @@ class PolymodScriptClassMacro #end } -typedef AbstractImplEntry = { - cls: Class, +typedef AbstractImplEntry = +{ + cls:Class, clsName:String, }; From e5b7d8aedcc4b0f3c3fcaf2a6c6bf26420ba5b2d Mon Sep 17 00:00:00 2001 From: lemz <87707926+lemz1@users.noreply.github.com> Date: Thu, 5 Feb 2026 00:49:41 +0100 Subject: [PATCH 16/16] fix compiler errors when hscriptPos is not defined --- polymod/hscript/_internal/Parser.hx | 2 ++ polymod/hscript/_internal/PolymodInterpEx.hx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/polymod/hscript/_internal/Parser.hx b/polymod/hscript/_internal/Parser.hx index c3f8c12d..6ea03d35 100644 --- a/polymod/hscript/_internal/Parser.hx +++ b/polymod/hscript/_internal/Parser.hx @@ -1800,8 +1800,10 @@ class Parser input = oldInput; readPos = oldPos; offset = oldOffset; + #if hscriptPos tokenMin = oldTokenMin; tokenMax = oldTokenMax; + #end char = -1; b = new StringBuf(); diff --git a/polymod/hscript/_internal/PolymodInterpEx.hx b/polymod/hscript/_internal/PolymodInterpEx.hx index 7189e4d4..a0305fc3 100644 --- a/polymod/hscript/_internal/PolymodInterpEx.hx +++ b/polymod/hscript/_internal/PolymodInterpEx.hx @@ -2190,7 +2190,9 @@ class PolymodInterpEx extends Interp _clone._nextCallObject = this._nextCallObject; _clone._classDeclOverride = this._classDeclOverride; _clone.depth = this.depth; + #if hscriptPos _clone.curExpr = this.curExpr; + #end _clone.inTry = this.inTry; return _clone; }