From e142f4318f3c46ad1540bb9ba2e84644efcb4076 Mon Sep 17 00:00:00 2001 From: lemz <87707926+lemz1@users.noreply.github.com> Date: Sun, 8 Feb 2026 03:29:58 +0100 Subject: [PATCH] fix inline instance abstract calls this also includes a rework of abstractClassStatics they are now moved into the new PolymodImpl class --- polymod/hscript/_internal/Interp.hx | 11 +- .../hscript/_internal/PolymodClassDeclEx.hx | 49 ++-- polymod/hscript/_internal/PolymodInterpEx.hx | 35 ++- .../hscript/_internal/PolymodScriptClass.hx | 29 +-- .../_internal/PolymodScriptClassMacro.hx | 239 +++++++++--------- 5 files changed, 181 insertions(+), 182 deletions(-) diff --git a/polymod/hscript/_internal/Interp.hx b/polymod/hscript/_internal/Interp.hx index 683c59a4..5f804ebc 100644 --- a/polymod/hscript/_internal/Interp.hx +++ b/polymod/hscript/_internal/Interp.hx @@ -128,7 +128,11 @@ class Interp function assign(e1:Expr, e2:Expr):Dynamic { - var v = expr(e2); + return assignValue(e1, expr(e2)); + } + + function assignValue(e1:Expr, v:Dynamic, _abstractInlineAssign:Bool = false):Dynamic + { switch (Tools.expr(e1)) { case EIdent(id): @@ -152,7 +156,10 @@ class Interp } default: - error(EInvalidOp("=")); + if (!_abstractInlineAssign) + { + error(EInvalidOp("=")); + } } return v; } diff --git a/polymod/hscript/_internal/PolymodClassDeclEx.hx b/polymod/hscript/_internal/PolymodClassDeclEx.hx index 61190d39..070efab2 100644 --- a/polymod/hscript/_internal/PolymodClassDeclEx.hx +++ b/polymod/hscript/_internal/PolymodClassDeclEx.hx @@ -155,16 +155,16 @@ class PolymodStaticAbstractReference public var absImpl:Null>; /** - * The path of the implementation class. - * Used for resolving static fields cached at macro time. + * The class, generated by Polymod, that implements the abstract class's static fields as well as inline instance functions, + * if it exists. */ - public var absImplPath:String; + public var polymodImpl:Null>; - public function new(absName:String, absImpl:Null>, absImplPath:String) + public function new(absName:String, absImpl:Null>, polymodImpl:Null>) { this.absName = absName; this.absImpl = absImpl; - this.absImplPath = absImplPath; + this.polymodImpl = polymodImpl; } /** @@ -211,7 +211,17 @@ class PolymodStaticAbstractReference } } - return fetchAbstractClassStatic(fieldName); + if (this.polymodImpl != null) + { + var getterName:String = 'get_$fieldName'; + if (Reflect.hasField(this.polymodImpl, getterName)) + { + var getter = Reflect.field(this.polymodImpl, getterName); + return Reflect.callMethod(this.polymodImpl, getter, []); + } + } + + throw 'Could not resolve abstract class static field ${fieldName}'; } /** @@ -259,21 +269,28 @@ class PolymodStaticAbstractReference throw 'Could not resolve abstract class static function ${funcName}'; } - function fetchAbstractClassStatic(fieldName:String):Dynamic + public function hasInlineFunction(funcName:String):Bool { - var key:String = '${this.absImplPath}.${fieldName}'; - - if (PolymodScriptClass.abstractClassStatics.exists(key)) + if (this.polymodImpl != null) { - var holder = PolymodScriptClass.abstractClassStatics.get(key); - var property = key.replace('.', '_'); - - return Reflect.getProperty(holder, property); + return Reflect.hasField(this.polymodImpl, funcName); } - else + + return false; + } + + public function callInlineFunction(interp:PolymodInterpEx, absInstanceExpr:Expr, funcName:String, args:Array):Dynamic + { + if (this.polymodImpl != null) { - throw 'Could not resolve abstract class static field ${fieldName}'; + var func = Reflect.field(this.polymodImpl, funcName); + if (func != null) + { + return Reflect.callMethod(this.polymodImpl, func, ([interp, absInstanceExpr] : Array).concat(args)); + } } + + throw 'Could not resolve abstract class inline instance function ${funcName}'; } public function toString():String diff --git a/polymod/hscript/_internal/PolymodInterpEx.hx b/polymod/hscript/_internal/PolymodInterpEx.hx index a0305fc3..b0eec714 100644 --- a/polymod/hscript/_internal/PolymodInterpEx.hx +++ b/polymod/hscript/_internal/PolymodInterpEx.hx @@ -410,7 +410,7 @@ class PolymodInterpEx extends Interp super.setVar(id, v); } - override function assign(e1:Expr, e2:Expr):Dynamic + override function assignValue(e1:Expr, v:Dynamic, _abstractInlineAssign:Bool = false):Dynamic { switch (Tools.expr(e1)) { @@ -419,8 +419,6 @@ class PolymodInterpEx extends Interp // Also ensures property functions are accounted for. if (_proxy != null && _proxy.superHasField(id)) { - var v = expr(e2); - if (Std.isOfType(_proxy.superClass, PolymodScriptClass)) { var superClass:PolymodAbstractScriptClass = cast(_proxy.superClass, PolymodScriptClass); @@ -442,7 +440,6 @@ class PolymodInterpEx extends Interp final setName = 'set_$id'; if (!_propTrack.exists(setName)) { - var v = expr(e2); _propTrack.set(setName, true); var out = _proxy.callFunction(setName, [v]); _propTrack.remove(setName); @@ -471,8 +468,6 @@ class PolymodInterpEx extends Interp { if (_proxy != null && _proxy.superHasField(id)) { - var v = expr(e2); - if (Std.isOfType(_proxy.superClass, PolymodScriptClass)) { var superClass:PolymodAbstractScriptClass = cast(_proxy.superClass, PolymodScriptClass); @@ -510,7 +505,7 @@ class PolymodInterpEx extends Interp default: } // Fallback, which calls set() - return super.assign(e1, e2); + return super.assignValue(e1, v, _abstractInlineAssign); } override function increment(e:Expr, prefix:Bool, delta:Int) @@ -875,14 +870,28 @@ class PolymodInterpEx extends Interp { case EField(e, f): var name = getIdent(e); - name = getClassDecl().imports.get(name)?.fullPath ?? name; - if (name != null && _scriptEnumDescriptors.exists(name)) + if (name != null) { - var args = new Array(); - for (p in params) - args.push(expr(p)); + var imp = getClassDecl().imports.get(name); + if (imp != null) + { + if (_scriptEnumDescriptors.exists(imp.fullPath)) + { + var args = new Array(); + for (p in params) + args.push(expr(p)); - return new PolymodEnum(_scriptEnumDescriptors.get(name), f, args); + return new PolymodEnum(_scriptEnumDescriptors.get(imp.fullPath), f, args); + } + else if (imp?.abs != null && imp.abs.hasInlineFunction(f)) + { + var args = new Array(); + for (p in params) + args.push(expr(p)); + + return imp.abs.callInlineFunction(this, params[0], f, args); + } + } } default: } diff --git a/polymod/hscript/_internal/PolymodScriptClass.hx b/polymod/hscript/_internal/PolymodScriptClass.hx index 900a6f63..3d1cabae 100644 --- a/polymod/hscript/_internal/PolymodScriptClass.hx +++ b/polymod/hscript/_internal/PolymodScriptClass.hx @@ -105,45 +105,20 @@ class PolymodScriptClass var baseAbstractClassImpls:Map, - clsName:String, + polymodCls:Null>, }> = PolymodScriptClassMacro.listAbstractImpls(); for (key => value in baseAbstractClassImpls) { if (value == null) continue; - _abstractClassImpls.set(key, new PolymodStaticAbstractReference(key, value.cls, value.clsName)); + _abstractClassImpls.set(key, new PolymodStaticAbstractReference(key, value.cls, value.polymodCls)); } } 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; - - static function get_abstractClassStatics():Map> - { - if (_abstractClassStatics == null) - { - _abstractClassStatics = new Map>(); - - var baseAbstractClassStatics:Map> = PolymodScriptClassMacro.listAbstractStatics(); - - for (key => value in baseAbstractClassStatics) - { - _abstractClassStatics.set(key, value); - } - } - - return _abstractClassStatics; - } - /** * Define a list of `typeName -> Class` which provides a reference to each typedef, * since typedefs can't be normally resolved at runtime. diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index f94b3b9e..200576c8 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -17,11 +17,6 @@ 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` @@ -43,21 +38,6 @@ class PolymodScriptClassMacro * @return An expression containing a map of abstract classes to their implementations */ public static macro function listAbstractImpls():ExprOf> - { - if (!onGenerateCallbackRegistered) - { - onGenerateCallbackRegistered = true; - haxe.macro.Context.onGenerate(onGenerate); - } - - 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) { @@ -71,7 +51,7 @@ class PolymodScriptClassMacro haxe.macro.Context.onGenerate(onGenerate); } - return macro polymod.hscript._internal.PolymodScriptClassMacro.fetchAbstractStatics(); + return macro polymod.hscript._internal.PolymodScriptClassMacro.fetchAbstractImpls(); } /** @@ -100,7 +80,6 @@ class PolymodScriptClassMacro var hscriptedClassEntries:Array = []; var abstractImplEntries:Array = []; - var abstractStaticEntries:Array<{}> = []; var typedefEntries:Array = []; var startTime:Float = Sys.time(); @@ -190,37 +169,23 @@ class PolymodScriptClassMacro var abstractImplPath:String = abstractType.impl?.toString() ?? ''; - var entryData = [macro $v{abstractPath}, macro $v{abstractImplPath}]; - - abstractImplEntries.push(macro $a{entryData}); - - for (field in abstractImpl.statics.get()) + var polymodImplPath:String = 'polymod.hscript._internal._abstract.${abstractPath}_PolymodImpl_'; + var hasPolymodImpl:Bool = false; + try { - switch (field.type) - { - case TAbstract(_, _): - // - case TType(_, _): - // - default: - continue; - } - - var key:String = '${abstractImplPath}.${field.name}'; + var _ = Context.getType(polymodImplPath); + hasPolymodImpl = true; + } + catch (e) {} - if (!staticFieldToClass.exists(key)) - { - continue; - } + var entryData = [ + macro $v{abstractPath}, + macro $v{abstractImplPath}, + hasPolymodImpl ? macro $v{polymodImplPath} : macro $v{null} + ]; - var staticEntryData = - { - fieldPath: key, - reflectClassPath: staticFieldToClass[key] - }; + abstractImplEntries.push(macro $a{entryData}); - abstractStaticEntries.push(staticEntryData); - } default: continue; } @@ -233,7 +198,6 @@ class PolymodScriptClassMacro Context.info('PolymodScriptClassMacro: ' + 'Registered ${hscriptedClassEntries.length} HScriptedClasses, ' + '${abstractImplEntries.length} abstract impls, ' - + '${abstractStaticEntries.length} abstract statics, ' + '${typedefEntries.length} typedefs ' + 'in ${duration} sec.', Context.currentPos()); @@ -245,23 +209,18 @@ class PolymodScriptClassMacro polymodScriptClassClassType.meta.add('abstractImpls', abstractImplEntries, 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; - static var staticFieldToClass:Map = []; - static function onAfterTyping(types:Array):Void { - var fields:Array = []; - var startTime:Float = Sys.time(); + var count:Int = 0; + for (type in types) { + var fields:Array = []; + switch (type) { case TAbstract(a): @@ -345,12 +304,10 @@ class PolymodScriptClassMacro if (canGet || canSet) { - var fieldName:String = '${abstractImplPath.replace('.', '_')}_${field.name}'; - fields.push( { pos: Context.currentPos(), - name: fieldName, + name: field.name, access: [Access.APublic, Access.AStatic], kind: FProp(canGet ? 'get' : 'never', canSet ? 'set' : 'never', (macro :Dynamic), null) }); @@ -374,7 +331,7 @@ class PolymodScriptClassMacro fields.push( { pos: Context.currentPos(), - name: 'get_${fieldName}', + name: 'get_${field.name}', access: [Access.APublic, Access.AStatic], kind: FFun( { @@ -391,7 +348,7 @@ class PolymodScriptClassMacro fields.push( { pos: Context.currentPos(), - name: 'set_${fieldName}', + name: 'set_${field.name}', access: [Access.APublic, Access.AStatic], kind: FFun( { @@ -403,45 +360,99 @@ class PolymodScriptClassMacro }) }); } - - 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; + if (k != MethInline) continue; + if (abstractPath.startsWith('cpp')) continue; + if (abstractPath.startsWith('flixel.graphics.atlas.HashOrArray')) continue; // has to be ragebait + if (abstractType.isPrivate) continue; + + switch (field.type) + { + case TFun(args, ret): + if (args.length == 0 || args[0].name != 'this') continue; + + var fieldArgs = [for (a in args.slice(1)) {name: a.name, type: null}]; + + var absType = TPath( + { + pack: abstractType.pack, + name: abstractType.module.split('.').pop(), + sub: abstractType.name, + params: [for (_ in abstractType.params) TPType((macro :Dynamic))] + }); + + var isVoid = haxe.macro.TypeTools.toString(ret) == 'Void'; + var callExprString = '${isVoid ? '' : 'returnValue = '}__typedThis.${field.name}(${[for (a in fieldArgs) a.name].join(', ')})'; + + fields.push( + { + pos: Context.currentPos(), + name: field.name, + access: [Access.APublic, Access.AStatic], + kind: FFun( + { + args: [ + {name: '__interp', type: (macro :polymod.hscript._internal.PolymodInterpEx)}, + {name: '__expr', type: (macro :polymod.hscript._internal.Expr)}, + {name: '__this', type: (macro :Dynamic)} + ].concat(fieldArgs), + ret: (macro :Dynamic), + expr: macro + { + var __typedThis = (__this : $absType); // we do this instead of typing __this in the parameter because of haxe.Rest edge case + var returnValue:Dynamic = null; + @:privateAccess ${Context.parse(callExprString, Context.currentPos())}; + @:privateAccess __interp.assignValue(__expr, __typedThis, true); + return returnValue; + } + }), + // dont generate warnings for this function + meta: [ + { + pos: Context.currentPos(), + params: [macro "-WAll"], + name: ':haxe.warning' + } + ] + }); + + default: + throw 'Method is not a function?'; + } default: continue; } } + + if (fields.length == 0) continue; + + Context.defineType( + { + pos: Context.currentPos(), + pack: ['polymod', 'hscript', '_internal', '_abstract'].concat(abstractType.pack), + name: '${abstractType.name}_PolymodImpl_', // we need to give them a different name, because else types with an empty package will not work + kind: TDClass(null, [], false, false, false), + fields: fields + }); + + count++; } default: continue; } } - if (fields.length == 0) - { - return; - } - - Context.defineType( - { - pos: Context.currentPos(), - pack: ['polymod', 'hscript', '_internal'], - name: 'AbstractStaticMembers_${iteration}', - kind: TDClass(null, [], false, false, false), - 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++; + if (count > 0) + { + Context.info('PolymodScriptClassMacro: Created ${count} custom abstract implementations in ${duration} sec.', Context.currentPos()); + } } #end @@ -484,17 +495,18 @@ class PolymodScriptClassMacro { var result:Map = []; - // Each element is formatted as `[abstractPath, abstractImplPath]`. + // Each element is formatted as `[abstractPath, abstractImplPath, ?abstractPolymodImplPath]`. for (element in metaData.abstractImpls) { - if (element.length != 2) + if (element.length != 3) { throw 'Malformed element in abstractImpls: ' + element; } var abstractPath:String = element[0]; var abstractImplPath:String = element[1]; + var abstractPolymodImplPath:Null = element[2]; #if js var abstractImplType:Class = resolveClass(abstractPath); @@ -502,6 +514,13 @@ class PolymodScriptClassMacro { throw 'Could not resolve ' + abstractPath; } + + var abstractPolymodImplType:Null> = null; + + if (abstractPolymodImplPath != null) + { + abstractPolymodImplType = resolveClass(abstractPolymodImplPath); + } #else var abstractImplType:Class = cast Type.resolveClass(abstractImplPath); @@ -511,12 +530,19 @@ class PolymodScriptClassMacro // it probably got optimized out. // We'll have to construct a PolymodStaticAbstractReference for it later. } + + var abstractPolymodImplType:Null> = null; + + if (abstractPolymodImplPath != null) + { + abstractPolymodImplType = cast Type.resolveClass(abstractPolymodImplPath); + } #end result.set(abstractPath, { cls: abstractImplType, - clsName: abstractImplPath + polymodCls: abstractPolymodImplType }); } @@ -528,41 +554,6 @@ class PolymodScriptClassMacro } } - public static function fetchAbstractStatics():Map> - { - 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 (abstractStatics != null) - { - var result:Map> = []; - - // Each element is formatted as `[abstractPathImpl.fieldName, reflectClass]`. - - for (element in abstractStatics) - { - if ((element.fieldPath?.length ?? 0) == 0 || (element.reflectClassPath?.length ?? 0) == 0) - { - throw 'Malformed element in abstractStatics: ' + element; - } - - var reflectClass:Class = cast Type.resolveClass(element.reflectClassPath); - - result.set(element.fieldPath, reflectClass); - } - - return result; - } - else - { - throw 'No abstractStatics found in "${ABSTRACT_STATICS_RES_NAME}" resource!'; - } - } - public static function fetchTypedefs():Map> { var metaData = Meta.getType(PolymodScriptClassMacro); @@ -610,5 +601,5 @@ class PolymodScriptClassMacro typedef AbstractImplEntry = { cls:Class, - clsName:String, + polymodCls:Null>, };