From 9288a9df10d2f54b78f7d1ed76d62e443c843e56 Mon Sep 17 00:00:00 2001 From: NeeEoo Date: Tue, 23 Apr 2024 17:47:30 +0200 Subject: [PATCH] Bug fix on custom classes + Better performance with custom classes Also made the custom class generated code use less code --- hscript/CustomClassHandler.hx | 71 ++++++++----- hscript/IHScriptCustomClassBehaviour.hx | 16 +++ hscript/Interp.hx | 4 +- hscript/macros/ClassExtendMacro.hx | 134 +++++++++++++++++++----- hscript/utils/UnsafeReflect.hx | 47 +++++++-- 5 files changed, 209 insertions(+), 63 deletions(-) create mode 100644 hscript/IHScriptCustomClassBehaviour.hx diff --git a/hscript/CustomClassHandler.hx b/hscript/CustomClassHandler.hx index f59a5e4..3efbd40 100644 --- a/hscript/CustomClassHandler.hx +++ b/hscript/CustomClassHandler.hx @@ -1,5 +1,6 @@ package hscript; +import hscript.Interp.DeclaredVar; import hscript.utils.UnsafeReflect; using StringTools; @@ -13,27 +14,28 @@ class CustomClassHandler implements IHScriptCustomConstructor { public var extend:String; public var interfaces:Array; + public var cl:Class; + public function new(ogInterp:Interp, name:String, fields:Array, ?extend:String, ?interfaces:Array) { this.ogInterp = ogInterp; this.name = name; this.fields = fields; this.extend = extend; this.interfaces = interfaces; + + this.cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX'); + if(cl == null) + ogInterp.error(EInvalidClass(extend)); } public function hnew(args:Array):Dynamic { var interp = new Interp(); - interp.errorHandler = ogInterp.errorHandler; - var cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX'); - if(cl == null) - ogInterp.error(EInvalidClass(extend)); - - var _class = Type.createInstance(cl, args); + var _class:IHScriptCustomClassBehaviour = Type.createInstance(cl, args); var __capturedLocals = ogInterp.duplicate(ogInterp.locals); - var capturedLocals:Map = []; + var capturedLocals:Map = []; for(k=>e in __capturedLocals) if (e != null && e.depth <= 0) capturedLocals.set(k, e); @@ -51,6 +53,8 @@ class CustomClassHandler implements IHScriptCustomConstructor { } } + _class.__custom__variables = interp.variables; + for(expr in fields) { @:privateAccess interp.exprReturn(expr); @@ -58,16 +62,20 @@ class CustomClassHandler implements IHScriptCustomConstructor { interp.variables.set("super", staticHandler); - _class.__interp = interp; + _class.__interp = interp; // TODO: Remove interp.scriptObject = _class; - var newFunc = interp.variables.get("new"); - if(newFunc != null) { - Reflect.callMethod(null, newFunc, args); - } - for(variable => value in interp.variables) { if(variable == "this") continue; + + if(variable.startsWith("set_") || variable.startsWith("get_")) { + _class.__allowSetGet = false; + } + } + + var newFunc = interp.variables.get("new"); + if(newFunc != null) { + UnsafeReflect.callMethodUnsafe(null, newFunc, args); } return _class; @@ -78,27 +86,42 @@ class CustomClassHandler implements IHScriptCustomConstructor { } } -class TemplateClass implements IHScriptCustomBehaviour { - public var __interp:Interp; +class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomBehaviour { + public var __interp:Interp; // TODO: Remove + public var __custom__variables:Map; + public var __allowSetGet:Bool = true; public function hset(name:String, val:Dynamic):Dynamic { - if(this.__interp.variables.exists("set_" + name)) { - return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function - } - if (this.__interp.variables.exists(name)) { - this.__interp.variables.set(name, val); + if(__allowSetGet && __custom__variables.exists("set_" + name)) + return __callSetter(name, val); + if (__custom__variables.exists(name)) { + __custom__variables.set(name, val); return val; } UnsafeReflect.setProperty(this, name, val); return UnsafeReflect.field(this, name); } public function hget(name:String):Dynamic { - if(this.__interp.variables.exists("get_" + name)) - return this.__interp.variables.get("get_" + name)(); - if (this.__interp.variables.exists(name)) - return this.__interp.variables.get(name); + if(__allowSetGet && __custom__variables.exists("get_" + name)) + return __callGetter(name); + if (__custom__variables.exists(name)) + return __custom__variables.get(name); return UnsafeReflect.getProperty(this, name); } + + public function __callGetter(name:String):Dynamic { + __allowSetGet = false; + var v = __custom__variables.get("get_" + name)(); + __allowSetGet = true; + return v; + } + + public function __callSetter(name:String, val:Dynamic):Dynamic { + __allowSetGet = false; + var v = __custom__variables.get("set_" + name)(val); + __allowSetGet = true; + return v; + } } class StaticHandler { diff --git a/hscript/IHScriptCustomClassBehaviour.hx b/hscript/IHScriptCustomClassBehaviour.hx new file mode 100644 index 0000000..ab0af41 --- /dev/null +++ b/hscript/IHScriptCustomClassBehaviour.hx @@ -0,0 +1,16 @@ +package hscript; + +import hscript.Interp; + +interface IHScriptCustomClassBehaviour { + public var __interp:Interp; // TODO: Remove + public var __custom__variables:Map; + public var __allowSetGet:Bool; + + public function hset(name:String, val:Dynamic):Dynamic; + public function hget(name:String):Dynamic; + + public function __callGetter(name:String):Dynamic; + + public function __callSetter(name:String, val:Dynamic):Dynamic; +} \ No newline at end of file diff --git a/hscript/Interp.hx b/hscript/Interp.hx index e16d093..9af958e 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -1144,7 +1144,7 @@ class Interp { function fcall(o:Dynamic, f:String, args:Array):Dynamic { if(o == CustomClassHandler.staticHandler && _hasScriptObject) { - return UnsafeReflect.callMethod(scriptObject, UnsafeReflect.field(scriptObject, "_HX_SUPER__" + f), args); + return UnsafeReflect.callMethodUnsafe(scriptObject, UnsafeReflect.field(scriptObject, "_HX_SUPER__" + f), args); } return call(o, get(o, f), args); } @@ -1153,7 +1153,7 @@ class Interp { if(f == CustomClassHandler.staticHandler) { return null; } - return Reflect.callMethod(o, f, args); + return UnsafeReflect.callMethodSafe(o, f, args); } function cnew(cl:String, args:Array):Dynamic { diff --git a/hscript/macros/ClassExtendMacro.hx b/hscript/macros/ClassExtendMacro.hx index b6d3d9a..a3551b1 100644 --- a/hscript/macros/ClassExtendMacro.hx +++ b/hscript/macros/ClassExtendMacro.hx @@ -184,9 +184,11 @@ class ClassExtendMacro { var name:String = $v{name}; if (__interp != null) { - var v:Dynamic = null; - if (__interp.variables.exists(name) && Reflect.isFunction(v = __interp.variables.get(name))) { - return v($a{arguments}); + if (__custom__variables.exists(name)) { + var v:Dynamic = null; + if(Reflect.isFunction(v = __custom__variables.get(name))) { + return v($a{arguments}); + } } } return super.$name($a{arguments}); @@ -196,10 +198,12 @@ class ClassExtendMacro { var name:String = $v{name}; if (__interp != null) { - var v:Dynamic = null; - if (__interp != null && __interp.variables.exists(name) && Reflect.isFunction(v = __interp.variables.get(name))) { - v($a{arguments}); - return; + if (__custom__variables.exists(name)) { + var v:Dynamic = null; + if(Reflect.isFunction(v = __custom__variables.get(name))) { + v($a{arguments}); + return; + } } } super.$name($a{arguments}); @@ -257,7 +261,8 @@ class ClassExtendMacro { pack: cl.pack.copy(), name: cl.name }, [ - {name: "IHScriptCustomBehaviour", pack: ["hscript"]} + {name: "IHScriptCustomBehaviour", pack: ["hscript"]}, + {name: "IHScriptCustomClassBehaviour", pack: ["hscript"]} ], false, true, false); shadowClass.name = '${cl.name}$CLASS_SUFFIX'; var imports = Context.getLocalImports().copy(); @@ -266,13 +271,90 @@ class ClassExtendMacro { // Adding hscript getters and setters + // TODO: Remove shadowClass.fields.push({ name: "__interp", pos: Context.currentPos(), kind: FVar(TPath({ pack: ['hscript'], name: 'Interp' - })) + })), + access: [APublic] + }); + + shadowClass.fields.push({ + name: "__custom__variables", + pos: Context.currentPos(), + kind: FVar(TPath({ + pack: [], + name: 'Map', + params: [TPType(TPath({name: "String", pack: []})), TPType(TPath({name: "Dynamic", pack: []}))] + })), + access: [APublic] + }); + + shadowClass.fields.push({ + name: "__allowSetGet", + pos: Context.currentPos(), + kind: FVar(TPath({ + pack: [], + name: 'Bool', + }), macro true), + access: [APublic] + }); + + shadowClass.fields.push({ + name: "__callGetter", + pos: Context.currentPos(), + kind: FFun({ + ret: TPath({name: 'Dynamic', pack: []}), + params: [], + expr: macro { + __allowSetGet = false; + var v = __custom__variables.get("get_" + name)(); + __allowSetGet = true; + return v; + }, + args: [ + { + name: "name", + opt: false, + meta: [], + type: TPath({name: "String", pack: []}) + } + ] + }), + access: [APublic] + }); + + shadowClass.fields.push({ + name: "__callSetter", + pos: Context.currentPos(), + kind: FFun({ + ret: TPath({name: 'Dynamic', pack: []}), + params: [], + expr: macro { + __allowSetGet = false; + var v = __custom__variables.get("set_" + name)(val); + __allowSetGet = true; + return v; + }, + args: [ + { + name: "name", + opt: false, + meta: [], + type: TPath({name: "String", pack: []}) + }, + { + name: "val", + opt: false, + meta: [], + type: TPath({name: "Dynamic", pack: []}) + } + ] + }), + access: [APublic] }); // Todo: make it possible to override @@ -317,40 +399,38 @@ class ClassExtendMacro { var hgetField = if(hasHgetInSuper) { macro { - if(this.__interp.variables.exists("get_" + name)) - return this.__interp.variables.get("get_" + name)(); - if (this.__interp.variables.exists(name)) - return this.__interp.variables.get(name); + if(__allowSetGet && __custom__variables.exists("get_" + name)) + return __callGetter(name); + if (__custom__variables.exists(name)) + return __custom__variables.get(name); return super.hget(name); } } else { macro { - if(this.__interp.variables.exists("get_" + name)) - return this.__interp.variables.get("get_" + name)(); - if (this.__interp.variables.exists(name)) - return this.__interp.variables.get(name); + if(__allowSetGet && __custom__variables.exists("get_" + name)) + return __callGetter(name); + if (__custom__variables.exists(name)) + return __custom__variables.get(name); return UnsafeReflect.getProperty(this, name); } } var hsetField = if(hasHsetInSuper) { macro { - if(this.__interp.variables.exists("set_" + name)) { - return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function - } - if (this.__interp.variables.exists(name)) { - this.__interp.variables.set(name, val); + if(__allowSetGet && __custom__variables.exists("set_" + name)) + return __callSetter(name, val); + if (__custom__variables.exists(name)) { + __custom__variables.set(name, val); return val; } return super.hset(this, name); } } else { macro { - if(this.__interp.variables.exists("set_" + name)) { - return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function - } - if (this.__interp.variables.exists(name)) { - this.__interp.variables.set(name, val); + if(__allowSetGet && __custom__variables.exists("set_" + name)) + return __callSetter(name, val); + if (__custom__variables.exists(name)) { + __custom__variables.set(name, val); return val; } UnsafeReflect.setProperty(this, name, val); diff --git a/hscript/utils/UnsafeReflect.hx b/hscript/utils/UnsafeReflect.hx index 99403ce..267b6c3 100644 --- a/hscript/utils/UnsafeReflect.hx +++ b/hscript/utils/UnsafeReflect.hx @@ -56,19 +56,45 @@ class UnsafeReflect { #end } - public inline static function callMethod(o:Dynamic, func:haxe.Constraints.Function, args:Array):Dynamic + public #if !cpp inline #end static function callFieldUnsafe(o:Dynamic, field:String, args:Array):Dynamic { + #if cpp + untyped { + var func:Dynamic = o.__Field(field, untyped __cpp__("::hx::paccDynamic")); + untyped func.__SetThis(o); + return untyped func.__Run(args); + } + #else return Reflect.callMethod(o, func, args); - /*untyped { - if (func != null && func.__GetType() == ObjectType.vtString) { - if (o == null) - throw cpp.ErrorConstants.invalidObject; - func = o.__Field(func, untyped __cpp__("::hx::paccDynamic")); - } + #end + } + + public inline static function callMethod(o:Dynamic, func:haxe.Constraints.Function, args:Array):Dynamic { + return Reflect.callMethod(o, func, args); + } + + public #if !cpp inline #end static function callMethodSafe(o:Dynamic, func:haxe.Constraints.Function, args:Array):Dynamic { + #if cpp + untyped { if (func == null) throw cpp.ErrorConstants.nullFunctionPointer; untyped func.__SetThis(o); return untyped func.__Run(args); - }*/ + } + #else + return Reflect.callMethod(o, func, args); + #end + } + + public #if !cpp inline #end static function callMethodUnsafe(o:Dynamic, func:haxe.Constraints.Function, args:Array):Dynamic { + #if cpp + untyped { + untyped func.__SetThis(o); + return untyped func.__Run(args); + } + #else + return Reflect.callMethod(o, func, args); + #end + } public inline static function fields(o:Dynamic):Array return Reflect.fields(o); @@ -133,9 +159,10 @@ class UnsafeReflect { #if cpp if (o == null) return null; - if (untyped o.__GetType() == ObjectType.vtString) + var t:Int = untyped o.__GetType(); + if (t == ObjectType.vtString) return o; - if (untyped o.__GetType() == ObjectType.vtArray) + if (t == ObjectType.vtArray) return untyped o.__Field("copy", untyped __cpp__("::hx::paccDynamic"))(); var o2:Dynamic = {}; for (f in UnsafeReflect.fields(o))