Refactor property fields

Now you can call directly the getter/setter functions without triggering them again.
This commit is contained in:
Jaime Humberto Macias Bustamante
2026-04-30 11:02:47 -07:00
parent 7f3de28e02
commit 634d819cc2
2 changed files with 78 additions and 38 deletions
+37 -32
View File
@@ -28,6 +28,7 @@
*/
package hscript;
import haxe.Exception;
import haxe.ds.StringMap;
import hscript.HEnum.HEnumValue;
import haxe.CallStack;
@@ -167,8 +168,12 @@ class Interp {
var usingHandler:UsingHandler;
var varLocationCache:Map<String, VarLocation> = new Map();
var cacheValid:Bool = true;
// TODO: separate cache into a class
var varLocationCache:Map<String, VarLocation> = [];
public var cacheValid(default, set):Bool = true;
function set_cacheValid(valid:Bool):Bool {
return cacheValid = valid;
}
#if hscriptPos
var curExpr:Expr;
@@ -291,14 +296,16 @@ class Interp {
var obj = resolve(id, false, false);
if (obj != null && obj is Property) {
var prop:Property = cast obj;
return prop.callSetter(id, v);
prop.__callingProperty = true;
return prop.callSetter(v);
}
varLocationCache.remove(id);
setVar(id, v);
}
} else if (l.r is Property) {
var prop:Property = cast l.r;
return prop.callSetter(id, v);
prop.__callingProperty = true;
return prop.callSetter(v);
} else {
l.r = v;
if (l.depth == 0) {
@@ -372,7 +379,8 @@ class Interp {
var obj = resolve(id, true, false);
if (obj != null && obj is Property) {
var prop:Property = cast obj;
return prop.callSetter(id, v);
prop.__callingProperty = true;
return prop.callSetter(v);
}
varLocationCache.remove(id);
setVar(id, v);
@@ -382,7 +390,8 @@ class Interp {
var l = locals.get(id);
if (l.r is Property) {
var prop:Property = cast l.r;
return prop.callSetter(id, v);
prop.__callingProperty = true;
return prop.callSetter(v);
}
l.r = v;
if (l.depth == 0) {
@@ -426,18 +435,19 @@ class Interp {
var prop:Property = null;
if (v is Property) {
prop = cast v;
v = prop.callGetter(id);
prop.__callingProperty = true;
v = prop.callGetter();
}
if (prefix) {
v += delta;
if (prop != null)
prop.callSetter(id, v);
prop.callSetter(v);
else
l.r = v;
} else {
if (prop != null)
prop.callSetter(id, v + delta);
prop.callSetter(v + delta);
else
l.r = v + delta;
}
@@ -448,20 +458,21 @@ class Interp {
var prop:Property = null;
if (v is Property) {
prop = cast v;
v = prop.callGetter(id);
prop.__callingProperty = true;
v = prop.callGetter();
}
if (prefix) {
v += delta;
if (prop != null)
prop.callSetter(id, v);
prop.callSetter(v);
else {
varLocationCache.remove(id);
setVar(id, v);
}
} else {
if (prop != null)
prop.callSetter(id, v + delta);
prop.callSetter(v + delta);
else {
varLocationCache.remove(id);
setVar(id, v + delta);
@@ -601,8 +612,11 @@ class Interp {
}
inline function getProperty(o:Null<Dynamic>, n:String, allowProperty:Bool = true):Dynamic {
if(allowProperty && o != null && o is Property)
return cast(o, Property).callGetter(n);
if(allowProperty && o != null && o is Property) {
var prop:Property = cast o;
prop.__callingProperty = true;
return cast(o, Property).callGetter();
}
else
return o;
}
@@ -725,14 +739,10 @@ class Interp {
}
public function invalidateCache():Void {
varLocationCache = new Map();
varLocationCache.clear();
cacheValid = true;
}
public function setCacheValid(valid:Bool):Void {
cacheValid = valid;
}
public static var importRedirects:Map<String, String> = new Map();
public static function getImportRedirect(className:String):String {
return importRedirects.exists(className) ? importRedirects.get(className) : className;
@@ -748,6 +758,7 @@ class Interp {
return className;
}
// TODO: separate large declarations (EClass, EEnum, etc...) into inline functions
public function expr(e:Expr):Dynamic {
#if hscriptPos
curExpr = e;
@@ -1001,20 +1012,14 @@ class Interp {
return null;
}
declared.push({n: n, old: locals.get(n), depth: depth});
var r:Dynamic = (e == null) ? null : expr(e);
var declProp:Property = null;
if (hasGetSet) {
declProp = {
r: r,
getter: getter,
setter: setter,
isVar: isVar,
isStatic: isStatic,
interp: this,
}
}
var v:Dynamic = (e == null) ? null : expr(e);
var r:Dynamic = null;
if (hasGetSet)
r = new Property(n, v, getter, setter, isVar, isStatic, this);
else
r = v;
var declVar:DeclaredVar = {
r: (!hasGetSet) ? r : declProp,
r: r,
depth: depth
};
locals.set(n, declVar);
+41 -6
View File
@@ -26,11 +26,11 @@ import hscript.Expr.FieldPropertyAccess;
* @see https://haxe.org/manual/class-field-property.html
*/
@:access(hscript.Interp)
@:structInit
class Property {
private static inline var GET = 'get_';
private static inline var SET = 'set_';
public var name:String;
public var r:Dynamic;
public var getter:FieldPropertyAccess;
public var setter:FieldPropertyAccess;
@@ -43,24 +43,54 @@ class Property {
var isVar:Bool;
var interp:Interp;
public function new(r:Dynamic, getter:FieldPropertyAccess, setter:FieldPropertyAccess, isVar:Bool, isStatic:Bool, interp:Interp) {
@:allow(hscript.Interp)
private var getterFunc(get, never):String;
private function get_getterFunc():String {
return '$GET$name';
}
@:allow(hscript.Interp)
private var setterFunc(get, never):String;
private function get_setterFunc():String {
return '$SET$name';
}
public function new(name:String, r:Dynamic, getter:FieldPropertyAccess, setter:FieldPropertyAccess, isVar:Bool, isStatic:Bool, interp:Interp) {
this.name = name;
this.r = r;
this.getter = getter;
this.setter = setter;
this.isVar = isVar;
this.__isStatic = isStatic;
this.interp = interp;
setLookup();
}
private inline function setLookup() {
/*
if(getter == AGet)
interp.propertyLookup.set(getterFunc, {isStatic: isStatic, func: () -> return callGetter()});
if(setter == ASet)
interp.propertyLookup.set(setterFunc, {isStatic: isStatic, func: (v) -> return callSetter(v)});
*/
}
var __allowReadAccess:Bool = false;
var __allowWriteAccess:Bool = false;
var __allowSetGet:Null<Bool> = null;
@:allow(hscript.Interp)
var __callingProperty:Bool = false; // flag to check if the get/set function hasn't been called directly
final __isStatic:Bool = false;
public function callGetter(name:String) {
public function callGetter():Dynamic {
switch (getter) {
case AGet | ADynamic:
var fName:String = '$GET$name';
var fName:String = getterFunc;//'$GET$name';
if(!__callingProperty)
__allowReadAccess = true;
if (!__allowReadAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
if (varExists(fName)) {
return callAccessor(fName);
@@ -80,10 +110,12 @@ class Property {
return r;
}
public function callSetter(name:String, val:Dynamic) {
public function callSetter(val:Dynamic):Dynamic {
switch (setter) {
case ASet | ADynamic:
var fName:String = '$SET$name';
var fName:String = setterFunc;//'$SET$name';
if(!__callingProperty)
__allowWriteAccess = true;
if (!__allowWriteAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
if (varExists(fName))
return callAccessor(fName, [val], true);
@@ -115,6 +147,9 @@ class Property {
if (isWrite) __allowWriteAccess = false;
else __allowReadAccess = false;
if(__callingProperty)
__callingProperty = false;
return rt;
} else
interp.error(ECustom('Method $f required by property ${f.substr(3)} is missing'));