Custom Classes rework progress - 2

This commit is contained in:
Jaime Humberto Macias Bustamante
2025-06-16 22:24:22 -07:00
parent 54ab2fc509
commit a4d2d4c788
7 changed files with 357 additions and 153 deletions
+185 -86
View File
@@ -2,9 +2,11 @@ package hscript;
import hscript.utils.UnsafeReflect;
import haxe.Constraints.Function;
using Lambda;
// TODO
/**
* The Custom Class core.
*
@@ -13,117 +15,214 @@ using Lambda;
* @author Jamextreme140
*/
@:access(hscript.CustomClassHandler)
class CustomClass implements IHScriptCustomClassBehaviour{
@:access(hscript.Property)
class CustomClass implements IHScriptCustomClassBehaviour {
public var className(get, never):String;
public var className(get, never):String;
private function get_className():String
return __class.name;
private function get_className():String
return __class.name;
public var __interp:Interp;
public var __custom__variables:Map<String, Dynamic>; // TODO: remove this
public var __real_fields:Array<String>;
public var __class__fields:Array<String>; // Declared fields
public var __interp:Interp;
public var __real_fields:Array<String> = []; // UNUSED
public var __class__fields:Array<String> = []; // Declared fields
public var __allowSetGet:Bool = true;
public var __allowSetGet:Bool = true;
var __class:CustomClassHandler;
var __superClass:IHScriptCustomClassBehaviour;
var __constructor:Function;
var fields:Array<Expr>;
public function new(__class:CustomClassHandler, args:Array<Dynamic>) {
this.__class = __class;
var __class:CustomClassHandler;
var __superClass:IHScriptCustomClassBehaviour;
var __constructor:Function;
var fields:Array<Expr>;
__interp = new Interp();
__interp.errorHandler = __class.staticInterp.errorHandler;
__interp.importFailedCallback = __class.staticInterp.importFailedCallback;
public function new(__class:CustomClassHandler, args:Array<Dynamic>) {
this.__class = __class;
__interp.variables = __class.staticInterp.variables; // This will access to static fields
__interp.allowPublicVariables = __class.ogInterp.allowPublicVariables;
__interp.publicVariables = __class.ogInterp.publicVariables;
__interp.allowStaticVariables = __class.ogInterp.allowStaticVariables;
__interp.staticVariables = __class.ogInterp.staticVariables;
__interp = new Interp();
__interp.errorHandler = __class.staticInterp.errorHandler;
__interp.importFailedCallback = __class.staticInterp.importFailedCallback;
__interp.scriptObject = this;
__interp.variables = __class.staticInterp.variables; // This will access to static fields
__interp.publicVariables = __class.ogInterp.publicVariables;
__interp.staticVariables = __class.ogInterp.staticVariables;
this.fields = __class.fields;
for(f in fields) {
switch(Tools.expr(f)) {
case EVar(n):
__class__fields.push(n);
case EFunction(_, _, n):
__class__fields.push(n);
default:
}
@:privateAccess __interp.exprReturn(f);
}
this.fields = __class.fields;
for (f in fields) {
switch (Tools.expr(f)) {
case EVar(n):
__class__fields.push(n);
case EFunction(_, _, n):
__class__fields.push(n);
default:
}
@:privateAccess __interp.exprReturn(f);
}
if(hasField('new')) {
buildConstructor();
call('new', args);
__interp.scriptObject = this;
if(this.__superClass == null && __class.extend != null)
__interp.error(ECustom("super() not called"));
}
else if(__class.extend != null) {
buildSuperClass(args);
}
}
if (hasField('new')) {
buildConstructor();
call('new', args);
function buildConstructor() {
__constructor = Reflect.makeVarArgs(buildSuperClass);
}
if (this.__superClass == null && __class.extend != null)
__interp.error(ECustom("super() not called"));
} else if (__class.extend != null) {
buildSuperClass(args);
}
}
function buildSuperClass(?args:Array<Dynamic>) {
if(args == null)
args = [];
function buildConstructor() {
__constructor = Reflect.makeVarArgs(buildSuperClass);
}
if(__class.cl == null) __interp.error(ECustom('Current class does not have a super'));
function buildSuperClass(?args:Array<Dynamic>) {
if (args == null)
args = [];
if (__class.cl == null) {
__interp.error(ECustom('Current class does not have a super'));
return;
}
if (__class.cl == CustomClassHandler) {
__superClass = new CustomClass(__class.cl, args);
@:privateAccess
__interp.__instanceFields.concat(__superClass.__class__fields);
} else {
var disallowCopy = Type.getInstanceFields(__class.cl);
__superClass = Type.createInstance(__class.cl, args);
__superClass.__real_fields = disallowCopy;
__superClass.__interp = this.__interp;
__superClass.__class__fields = this.__class__fields;
__superClass.__interp = this.__interp;
}
}
}
function call(name:String, ?args:Array<Dynamic>):Dynamic {
var fn = __interp.variables.get(name);
if(fn != null && Reflect.isFunction(fn))
return UnsafeReflect.callMethodUnsafe(null, fn, (args == null) ? [] : args);
__interp.error(ECustom('$name is not a function'));
return null;
}
public function call(name:String, ?args:Array<Dynamic>):Dynamic {
var fn = __interp.variables.get(name);
if (fn != null && Reflect.isFunction(fn))
return UnsafeReflect.callMethodUnsafe(null, fn, (args == null) ? [] : args);
else
__interp.error(ECustom('$name is not a function'));
return null;
}
function hasField(name:String) {
return __interp.variables.exists(name);
}
function hasField(name:String) {
// return __interp.variables.exists(name);
return __class__fields.contains(name);
}
public function hget(name:String):Dynamic {
switch (name) {
case 'superClass': return __superClass;
case 'superConstructor': return __constructor;
}
return null;
}
function getField(name:String, allowProperty:Bool = true):Dynamic {
var f = __interp.variables.get(name);
if (allowProperty && f is Property) {
var prop:Property = cast f;
prop.__allowSetGet = this.__allowSetGet;
var r = prop.callGetter(name);
prop.__allowSetGet = null;
return r;
}
return f;
}
public function __callGetter(name:String):Dynamic {
return null;
}
function setField(name:String, val:Dynamic):Dynamic {
var f = getField(name, false);
if (f is Property) {
var prop:Property = cast f;
prop.__allowSetGet = this.__allowSetGet;
var r = prop.callSetter(name, val);
prop.__allowSetGet = null;
return r;
}
__interp.variables.set(name, val);
return val;
}
public function hset(name:String, val:Dynamic):Dynamic {
return null;
}
function superHasField(name:String) {
if (__superClass == null)
return false;
public function __callSetter(name:String, val:Dynamic):Dynamic {
return null;
}
var realFieldExists = __superClass.__real_fields != null && __superClass.__real_fields.contains(name);
var classFieldExists = __superClass.__class__fields != null && __superClass.__class__fields.contains(name);
public function toString():String
return className;
}
return realFieldExists || classFieldExists;
}
public function hget(name:String):Dynamic {
switch (name) {
case 'superClass': return __superClass;
case 'superConstructor': return __constructor;
default:
if (hasField(name))
return getField(name);
if (__superClass != null) {
if (superHasField(name)) {
__superClass.__allowSetGet = this.__allowSetGet;
return __superClass.hget(name);
}
}
throw "field '"
+ name
+ "' does not exist in custom class '"
+ this.className
+ "'"
+ (__superClass != null ? "' or super class '" + Type.getClassName(Type.getClass(this.__superClass)) + "'" : "");
}
return null;
}
public function hset(name:String, val:Dynamic):Dynamic {
if (hasField(name)) {
return setField(name, val);
}
if (__superClass != null) {
if (superHasField(name)) {
__superClass.__allowSetGet = this.__allowSetGet;
return __superClass.hset(name, val);
}
}
throw "field '"
+ name
+ "' does not exist in custom class '"
+ this.className
+ "'"
+ (__superClass != null ? "' or super class '" + Type.getClassName(Type.getClass(this.__superClass)) + "'" : "");
return null;
}
// UNUSED
public function __callGetter(name:String):Dynamic {
return null;
}
public function __callSetter(name:String, val:Dynamic):Dynamic {
return null;
}
/**
* Returns the real superClass if the Custom Class
* extends another Custom Class, and so on until
* it reaches a real class, otherwise it will
* return the last fetched Custom Class
* @return Null<Dynamic>
*/
public function getSuperclass():IHScriptCustomClassBehaviour {
var cls:Null<IHScriptCustomClassBehaviour> = this.__superClass;
// Check if the superClass is another custom class,
// so it will find for a real class, otherwise
// returns the last super CustomClass parent.
while (cls != null && cls is CustomClass) {
var next = cast(cls, CustomClass).__superClass;
if (next == null)
break; // Return the Custom Class itself
cls = next;
}
return cls;
}
public function toString():String
return className;
}
+61 -18
View File
@@ -8,7 +8,8 @@ using StringTools;
/**
* Provides handlers for static custom class fields and instantiation.
*/
class CustomClassHandler implements IHScriptCustomConstructor implements IHScriptCustomBehaviour{
@:access(hscript.Property)
class CustomClassHandler implements IHScriptCustomConstructor implements IHScriptCustomAccessBehaviour{
public static var staticHandler = new StaticHandler();
public var __allowSetGet:Bool = true;
@@ -36,10 +37,11 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip
this.cl = ogInterp.customClasses.get(extend);
else
this.cl = Type.resolveClass('${extend}_HSX');
if(cl == null)
ogInterp.error(EInvalidClass(extend));
}
//this.cl = extend == null ? CustomTemplateClass : Type.resolveClass('${extend}_HSX');
//if(cl == null)
// ogInterp.error(EInvalidClass(extend));
initStatic();
}
@@ -51,28 +53,30 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip
staticInterp.importFailedCallback = ogInterp.importFailedCallback;
staticInterp.variables = ogInterp.variables;
staticInterp.allowPublicVariables = ogInterp.allowPublicVariables;
staticInterp.publicVariables = ogInterp.publicVariables;
staticInterp.allowStaticVariables = ogInterp.allowStaticVariables;
staticInterp.staticVariables = ogInterp.staticVariables;
// TODO: optimize this
for(ex in fields) {
var varOrFunction:Int = -1; // 0 - variable | 1 - function
var staticField:Bool = false;
var fieldName:String = "";
switch (Tools.expr(ex)) {
case EVar(n, _, _, _, isStatic, _, _, _, _, _, _):
if(isStatic) {
staticInterp.exprReturn(ex);
staticFields.push(n);
fields.remove(ex);
}
varOrFunction = 0;
staticField = isStatic;
fieldName = n;
case EFunction(_, _, n, _, _, isStatic, _, _, _, _):
if(isStatic) {
staticInterp.exprReturn(ex);
staticFields.push(n);
fields.remove(ex);
}
varOrFunction = 1;
staticField = isStatic;
fieldName = n;
default:
}
if(staticField && varOrFunction > -1) {
staticInterp.exprReturn(ex);
staticFields.push(fieldName);
fields.remove(ex);
}
}
}
@@ -195,19 +199,58 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip
}
*/
// I can't believe all these lines of code above got reduced to this sentence.
var _class = new CustomClass(this, args);
return _class;
}
function hasField(name:String) {
return staticFields.contains(name);
}
function getField(name:String, allowProperty:Bool = true):Dynamic {
var f = staticInterp.variables.get(name);
if(f is Property && allowProperty) {
var prop:Property = cast f;
prop.__allowSetGet = this.__allowSetGet;
var r = prop.callGetter(name);
prop.__allowSetGet = null;
return r;
}
return f;
}
function setField(name:String, val:Dynamic):Dynamic {
var f = getField(name, false);
if(f is Property) {
var prop:Property = cast f;
prop.__allowSetGet = this.__allowSetGet;
var r = prop.callSetter(name, val);
prop.__allowSetGet = null;
return r;
}
staticInterp.variables.set(name, val);
return val;
}
public function hget(name:String):Dynamic {
if(hasField(name)) {
return getField(name);
}
throw "field '"+ name+ "' does not exist in class '"+ this.name+ "'";
return null;
}
public function hset(name:String, val:Dynamic):Dynamic {
if(hasField(name))
return setField(name, val);
throw "field '"+ name+ "' does not exist in class '"+ this.name+ "'";
return null;
}
// UNUSED
public function __callGetter(name:String):Dynamic {
return null;
}
@@ -220,7 +263,7 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip
return name;
}
}
/*
class CustomTemplateClass implements IHScriptCustomClassBehaviour {
public var __interp:Interp;
public var __allowSetGet:Bool = true;
@@ -265,7 +308,7 @@ class CustomTemplateClass implements IHScriptCustomClassBehaviour {
return v;
}
}
*/
/**
* This is for backwards compatibility with old hscript-improved, since some scripts use it
-1
View File
@@ -2,7 +2,6 @@ package hscript;
interface IHScriptCustomClassBehaviour extends IHScriptCustomAccessBehaviour{
public var __interp:Interp;
public var __custom__variables:Map<String, Dynamic>;
public var __real_fields:Array<String>;
public var __class__fields:Array<String>;
+46 -15
View File
@@ -65,6 +65,7 @@ class RedeclaredVar {
public var depth:Int;
}
@:access(hscript.CustomClass)
@:analyzer(optimize, local_dce, fusion, user_var_fusion)
class Interp {
private var hasScriptObject(get, never):Bool;
@@ -117,7 +118,7 @@ class Interp {
public var errorHandler:Error->Void;
public var importFailedCallback:Array<String>->Bool;
public var customClasses:Map<String, CustomClassHandler>; // make this static ???
public var customClasses:Map<String, CustomClassHandler>;
public var variables:Map<String, Dynamic>;
public var publicVariables:Map<String, Dynamic>;
public var staticVariables:Map<String, Dynamic>;
@@ -587,7 +588,7 @@ class Interp {
#end
}
public function resolve(id:String, doException:Bool = true, getProperty:Bool = true):Dynamic {
public function resolve(id:String, doException:Bool = true, allowProperty:Bool = true):Dynamic {
if (id == null)
return null;
id = StringTools.trim(id);
@@ -601,7 +602,7 @@ class Interp {
if (locals.exists(id)) {
var l = locals.get(id);
if(l != null) {
if(l.r != null && l.r is Property && getProperty)
if(l.r != null && l.r is Property && allowProperty)
return cast(l.r, Property).callGetter(id);
else
return l.r;
@@ -618,7 +619,7 @@ class Interp {
r = staticVariables.get(id);
if(r != null) {
if(r is Property && getProperty)
if(r is Property && allowProperty)
return cast(r, Property).callGetter(id);
else
return r;
@@ -846,7 +847,7 @@ class Interp {
error(EInvalidOp(op));
}
case ECall(e, params):
var args:Array<Dynamic> = [for(p in params) expr(p)];
var args:Array<Dynamic> = makeArgs(params);
switch (Tools.expr(e)) {
case EField(e, f, s):
@@ -1063,10 +1064,7 @@ class Interp {
return arr[index];
}
case ENew(cl, params):
var a = [];
for (e in params)
a.push(expr(e));
return cnew(cl, a);
return cnew(cl, makeArgs(params));
case EThrow(e):
throw expr(e);
case ETry(e, n, _, ecatch):
@@ -1189,6 +1187,26 @@ class Interp {
return v;
}
function makeArgs(params:Array<Expr>):Array<Dynamic> {
var args:Array<Dynamic> = [];
for (p in params) {
switch (Tools.expr(p)) {
case EIdent(id):
var ident = resolve(id);
if (ident is CustomClass) {
var customClass:CustomClass = cast ident; // Pass the underlying superclass if exist
args.push(customClass.__superClass != null ? customClass.getSuperclass() : customClass);
} else {
args.push(ident);
}
default:
args.push(expr(p));
}
}
return args;
}
function forLoop(n:String, it:Expr, e:Expr, ?ithv:String):Void {
var isKeyValue = ithv != null;
var old = declared.length;
@@ -1333,16 +1351,29 @@ class Interp {
}
function fcall(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic {
if(hasScriptObject && o == CustomClassHandler.staticHandler) {
return UnsafeReflect.callMethodUnsafe(scriptObject, UnsafeReflect.field(scriptObject, "_HX_SUPER__" + f), args);
// Custom logic to handle super calls to prevent infinite recursion
if(inCustomClass) {
if (o == scriptObject.__superClass) {
if (scriptObject.__superClass is CustomClass)
return cast(scriptObject.__superClass, CustomClass).call(f, args);
else
return return UnsafeReflect.callMethodUnsafe(scriptObject.__superClass, UnsafeReflect.field(scriptObject.__superClass, '_HX_SUPER__$f'), args);
}
}
return call(o, get(o, f), args);
var func = get(o, f);
// Workaround for an HTML5-specific issue.
// https://github.com/HaxeFoundation/haxe/issues/11298
#if js
if (func == null && f == "contains") {
func = get(o, "includes");
}
#end
return call(o, func, args);
}
function call(o:Dynamic, f:Dynamic, args:Array<Dynamic>):Dynamic {
if(f == CustomClassHandler.staticHandler) {
return null;
}
return UnsafeReflect.callMethodSafe(o, f, args);
}
+12 -2
View File
@@ -465,15 +465,25 @@ class Parser {
case TBkOpen:
var a = [];
tk = token();
var first = true;
while( tk != TBkClose && (!resumeErrors || tk != TEof) ) {
if (!first) {
if (tk != TComma)
unexpected(tk);
else {
tk = token();
if (tk == TBkClose)
break;
}
}
first = false;
push(tk);
var oldoo = disableOrOp;
disableOrOp = false;
a.push(parseExpr());
disableOrOp = oldoo;
tk = token();
if( tk == TComma )
tk = token();
}
if( a.length == 1 && a[0] != null ) // What is this for???
switch( expr(a[0]) ) {
+3 -2
View File
@@ -16,12 +16,13 @@ class Property {
var __allowReadAccess:Bool = false;
var __allowWriteAccess:Bool = false;
var __allowSetGet:Null<Bool> = null;
public function callGetter(name:String) {
switch (getter) {
case AGet | ADynamic:
var fName:String = 'get_$name';
if (!__allowReadAccess && !interp.isBypassAccessor) {
if (!__allowReadAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
if (interp.varExists(fName)) {
return callAccessor(fName);
} else
@@ -44,7 +45,7 @@ class Property {
switch (setter) {
case ASet | ADynamic:
var fName:String = 'set_$name';
if (!__allowWriteAccess && !interp.isBypassAccessor) {
if (!__allowWriteAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
if (interp.varExists(fName))
return callAccessor(fName, [val], true);
else
+50 -29
View File
@@ -61,6 +61,7 @@ class ClassExtendMacro {
if(key == "away3d.tools.commands.Weld") return fields; // Error: Unknown identifier
if(fkey == "hscript.CustomClassHandler.TemplateClass") return fields; // Error: Redefined
if(fkey == "hscript.CustomClassHandler.CustomTemplateClass") return fields; // Error: Redefined
if(fkey == "hscript.CustomClass") return fields; // Error: Redefined
if(key == "sys.thread.EventLoop") return fields; // Error: cant override force inlined
if(Config.DISALLOW_CUSTOM_CLASSES.contains(cl.module) || Config.DISALLOW_CUSTOM_CLASSES.contains(fkey)) return fields;
if(cl.module.contains("_")) return fields; // Weird issue, sorry
@@ -210,7 +211,15 @@ class ClassExtendMacro {
if (f == null)
continue;
if (f.name == "new") {
hasNew = true;
switch (f.kind) {
case FFun(fn):
var constructor:Field = buildConstructor(fn.args);
shadowClass.fields.push(constructor);
definedFields.push(f.name);
default:
continue;
}
continue;
}
if (f.name.startsWith(FUNC_PREFIX))
@@ -245,7 +254,7 @@ class ClassExtendMacro {
if (returns) {
overrideExpr = macro {
var name:String = $v{name};
/*
if (__custom__variables != null) {
if(__custom__variables.exists(name)) {
var v:Dynamic = null;
@@ -254,12 +263,20 @@ class ClassExtendMacro {
}
}
}
*/
if (__interp != null && __class__fields.contains(name)) {
var v:Dynamic = null;
if (Reflect.isFunction(v = __interp.variables.get(name))) {
return v($a{arguments});
}
}
return super.$name($a{arguments});
};
} else {
overrideExpr = macro {
var name:String = $v{name};
/*
if (__custom__variables != null) {
if(__custom__variables.exists(name)) {
var v:Dynamic = null;
@@ -269,6 +286,14 @@ class ClassExtendMacro {
}
}
}
*/
if (__interp != null && __class__fields.contains(name)) {
var v:Dynamic = null;
if (Reflect.isFunction(v = __interp.variables.get(name))) {
v($a{arguments});
return;
}
}
super.$name($a{arguments});
};
}
@@ -331,7 +356,6 @@ class ClassExtendMacro {
pack: cl.pack.copy(),
name: cl.name
}, [
{name: "IHScriptCustomAccessBehaviour", pack: ["hscript"]},
{name: "IHScriptCustomClassBehaviour", pack: ["hscript"]}
], false, true, false);
shadowClass.name = '${cl.name}$CLASS_SUFFIX';
@@ -347,14 +371,14 @@ class ClassExtendMacro {
kind: FVar(macro: hscript.Interp),
access: [APublic]
});
/*
shadowClass.fields.push({
name: "__custom__variables",
pos: Context.currentPos(),
kind: FVar(macro: Map<String, Dynamic>),
access: [APublic]
});
*/
shadowClass.fields.push({
name: "__allowSetGet",
pos: Context.currentPos(),
@@ -384,7 +408,7 @@ class ClassExtendMacro {
params: [],
expr: macro {
__allowSetGet = false;
var v = __custom__variables.get("get_" + name)();
//var v = __custom__variables.get("get_" + name)();
__allowSetGet = true;
return v;
},
@@ -408,7 +432,7 @@ class ClassExtendMacro {
params: [],
expr: macro {
__allowSetGet = false;
var v = __custom__variables.get("set_" + name)(val);
//var v = __custom__variables.get("set_" + name)(val);
__allowSetGet = true;
return v;
},
@@ -472,30 +496,16 @@ class ClassExtendMacro {
var hgetField = if(hasHgetInSuper) {
macro {
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(__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(__allowSetGet && __custom__variables.exists("set_" + name))
return __callSetter(name, val);
if (__custom__variables.exists(name)) {
__custom__variables.set(name, val);
return val;
}
if(__real_fields.contains(name)) {
UnsafeReflect.setProperty(this, name, val);
return UnsafeReflect.field(this, name);
@@ -504,17 +514,11 @@ class ClassExtendMacro {
}
} else {
macro {
if(__allowSetGet && __custom__variables.exists("set_" + name))
return __callSetter(name, val);
if (__custom__variables.exists(name)) {
__custom__variables.set(name, val);
return val;
}
if(__real_fields.contains(name)) {
UnsafeReflect.setProperty(this, name, val);
return UnsafeReflect.field(this, name);
}
__custom__variables.set(name, val);
//__custom__variables.set(name, val);
return val;
}
}
@@ -579,6 +583,23 @@ class ClassExtendMacro {
return fields;
}
static function buildConstructor(constArgs:Array<FunctionArg>):Field {
var superCallArgs:Array<Expr> = [for (arg in constArgs) macro $i{arg.name}];
return {
name: 'new',
access: [APublic],
pos: Context.currentPos(),
kind: FFun({
args: constArgs,
expr: macro {
// Call the super constructor with appropriate args
super($a{superCallArgs});
}
}),
};
}
}
#else
class ClassExtendMacro {