diff --git a/hscript/CustomClass.hx b/hscript/CustomClass.hx index bf0cb91..5ed24e2 100644 --- a/hscript/CustomClass.hx +++ b/hscript/CustomClass.hx @@ -13,7 +13,6 @@ using Lambda; * @author Jamextreme140 */ @:access(hscript.CustomClassHandler) -@:access(hscript.Property) class CustomClass implements IHScriptCustomClassBehaviour { public var className(get, never):String; @@ -174,9 +173,9 @@ class CustomClass implements IHScriptCustomClassBehaviour { var f = __interp.variables.get(name); if (f != null && allowProperty && f is Property) { var prop:Property = cast f; - prop.__allowSetGet = this.__allowSetGet; - var r = prop.callGetter(name); - prop.__allowSetGet = null; + //prop.__allowSetGet = this.__allowSetGet; + var r = prop.get(!__allowSetGet); + //prop.__allowSetGet = true; return r; } return f; @@ -186,9 +185,9 @@ class CustomClass implements IHScriptCustomClassBehaviour { var f = getField(name, false); if (f != null && f is Property) { var prop:Property = cast f; - prop.__allowSetGet = this.__allowSetGet; - var r = prop.callSetter(name, val); - prop.__allowSetGet = null; + //prop.__allowSetGet = this.__allowSetGet; + var r = prop.set(val, !__allowSetGet); + //prop.__allowSetGet = true; return r; } __interp.variables.set(name, val); @@ -354,6 +353,7 @@ class CustomClass implements IHScriptCustomClassBehaviour { return cls; } + // TODO: scriptable "toString" function public function toString():String return className; } diff --git a/hscript/CustomClassHandler.hx b/hscript/CustomClassHandler.hx index b200545..067c0d2 100644 --- a/hscript/CustomClassHandler.hx +++ b/hscript/CustomClassHandler.hx @@ -60,23 +60,19 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip if(!__interp.variables.exists(f)) __interp.variables.set(f, v); - for(e in fields.copy()) { - var validField:Bool = false; + for(i => e in fields.copy()) { + var isValid:Bool = false; var staticField:Bool = false; - var fieldName:String = ""; + var fieldName:String = null; switch (Tools.expr(e)) { - case EVar(n, _, _, _, isStatic): - validField = true; - staticField = isStatic; - fieldName = n; - case EFunction(_, _, n, _, _, isStatic, _, _, _, _): - validField = true; + case EVar(n, _, _, _, isStatic) | EFunction(_, _, n, _, _, isStatic): + isValid = true; staticField = isStatic; fieldName = n; default: } - if(staticField && validField) { + if(staticField && isValid) { __interp.exprReturn(e); __staticFields.push(fieldName); fields.remove(e); @@ -88,7 +84,7 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip return new CustomClass(this, args); @:allow(hscript.Interp) - function hasField(name:String) { + inline function hasField(name:String) { return __staticFields.contains(name); } @@ -96,9 +92,9 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip var f = __interp.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; + //prop.__allowSetGet = this.__allowSetGet; + var r = prop.get(!__allowSetGet); + //prop.__allowSetGet = true; return r; } return f; @@ -108,9 +104,9 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip 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; + //prop.__allowSetGet = this.__allowSetGet; + var r = prop.set(val, !__allowSetGet); + //prop.__allowSetGet = true; return r; } __interp.variables.set(name, val); @@ -119,10 +115,9 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip public function hget(name:String):Dynamic { if(name == 'new') { - var __constructor = Reflect.makeVarArgs(function(args:Array) { - return this.hnew(args); + return Reflect.makeVarArgs(function(args:Array):Dynamic { + return inline this.hnew(args); }); - return __constructor; } if(hasField(name)) { diff --git a/hscript/Interp.hx b/hscript/Interp.hx index f50bdf0..6730ba0 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -28,6 +28,7 @@ */ package hscript; +import haxe.Exception; import haxe.ds.StringMap; import hscript.HEnum.HEnumValue; import haxe.CallStack; @@ -135,11 +136,16 @@ class Interp { public var errorHandler:Error->Void; public var warnHandler:Error->Void; + // TODO: set this callback as a Global Resolver + /** + * Custom Import resolver. It's called when an import couldn't be resolved. + */ public var importFailedCallback:Array->Null->Bool; public var customClasses:Map; public var variables:Map; public var publicVariables:Map; + // TODO: maybe turn this completely static public var staticVariables:Map; // warning can be null @@ -167,8 +173,12 @@ class Interp { var usingHandler:UsingHandler; - var varLocationCache:Map = new Map(); - var cacheValid:Bool = true; + // TODO: separate cache into a class + var varLocationCache:Map = []; + public var cacheValid(default, set):Bool = true; + function set_cacheValid(valid:Bool):Bool { + return cacheValid = valid; + } #if hscriptPos var curExpr:Expr; @@ -291,14 +301,14 @@ class Interp { var obj = resolve(id, false, false); if (obj != null && obj is Property) { var prop:Property = cast obj; - return prop.callSetter(id, v); + return prop.set(v, isBypassAccessor); } varLocationCache.remove(id); setVar(id, v); } } else if (l.r is Property) { var prop:Property = cast l.r; - return prop.callSetter(id, v); + return prop.set(v, isBypassAccessor); } else { l.r = v; if (l.depth == 0) { @@ -372,7 +382,7 @@ class Interp { var obj = resolve(id, true, false); if (obj != null && obj is Property) { var prop:Property = cast obj; - return prop.callSetter(id, v); + return prop.set(v, isBypassAccessor); } varLocationCache.remove(id); setVar(id, v); @@ -382,7 +392,7 @@ class Interp { var l = locals.get(id); if (l.r is Property) { var prop:Property = cast l.r; - return prop.callSetter(id, v); + return prop.set(v, isBypassAccessor); } l.r = v; if (l.depth == 0) { @@ -426,18 +436,18 @@ class Interp { var prop:Property = null; if (v is Property) { prop = cast v; - v = prop.callGetter(id); + v = prop.get(isBypassAccessor); } if (prefix) { v += delta; if (prop != null) - prop.callSetter(id, v); + prop.set(v, isBypassAccessor); else l.r = v; } else { if (prop != null) - prop.callSetter(id, v + delta); + prop.set(v + delta, isBypassAccessor); else l.r = v + delta; } @@ -448,20 +458,20 @@ class Interp { var prop:Property = null; if (v is Property) { prop = cast v; - v = prop.callGetter(id); + v = prop.get(isBypassAccessor); } if (prefix) { v += delta; if (prop != null) - prop.callSetter(id, v); + prop.set(v, isBypassAccessor); else { varLocationCache.remove(id); setVar(id, v); } } else { if (prop != null) - prop.callSetter(id, v + delta); + prop.set(v + delta, isBypassAccessor); else { varLocationCache.remove(id); setVar(id, v + delta); @@ -601,8 +611,10 @@ class Interp { } inline function getProperty(o:Null, 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; + return prop.get(isBypassAccessor); + } else return o; } @@ -725,14 +737,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 = new Map(); public static function getImportRedirect(className:String):String { return importRedirects.exists(className) ? importRedirects.get(className) : className; @@ -748,6 +756,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 +1010,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); @@ -1798,6 +1801,7 @@ class Interp { if (usingHandler.usingEntries.iterator().hasNext()) { // If is not empty var v:Dynamic = null; var clsName:String = o is CustomClassHandler ? cast(o, CustomClassHandler).name : Type.getClassName(Type.getClass(o)); + // TODO: optimize this if(!usingHandler.entryExists(clsName)) { for (n => us in usingHandler.usingEntries) { if (us.hasField(f)) { diff --git a/hscript/Property.hx b/hscript/Property.hx index 85dd576..fcd1355 100644 --- a/hscript/Property.hx +++ b/hscript/Property.hx @@ -26,15 +26,33 @@ 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 r:Dynamic; - public var getter:FieldPropertyAccess; - public var setter:FieldPropertyAccess; + /** + * Name of the attached field. + */ + public final name:String; + /** + * The current value. If isn't initialized, it's always `null`. + */ + public var r:Dynamic; + + /** + * The getter property kind + */ + public final getter:FieldPropertyAccess; + + /** + * The setter property kind + */ + public final setter:FieldPropertyAccess; + + /** + * If the field is declared as static. + */ public var isStatic(get, never):Bool; function get_isStatic() { return __isStatic && interp.allowStaticVariables; @@ -43,7 +61,20 @@ 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 inline function get_getterFunc():String { + return '$GET$name'; + } + + @:allow(hscript.Interp) + private var setterFunc(get, never):String; + private inline 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; @@ -52,23 +83,41 @@ class Property { this.interp = interp; } + // Internal flags to gain access to the current field value (if isn't a property field) var __allowReadAccess:Bool = false; var __allowWriteAccess:Bool = false; - var __allowSetGet:Null = null; + // Internal flag to gain access if the field is accessed with @:bypassAccessor + var __allowSetGet:Bool = true; + final __isStatic:Bool = false; - public function callGetter(name:String) { + public function get(isBypassAccessor:Bool) { + if(isBypassAccessor) __allowSetGet = false; + var r:Dynamic = callGetter(); + if(isBypassAccessor) __allowSetGet = true; + return r; + } + + public function set(value:Dynamic, isBypassAccessor:Bool) { + if(isBypassAccessor) __allowSetGet = false; + var r:Dynamic = callSetter(value); + if(isBypassAccessor) __allowSetGet = true; + return r; + } + + private function callGetter():Dynamic { switch (getter) { case AGet | ADynamic: - var fName:String = '$GET$name'; - if (!__allowReadAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) { + var fName:String = getterFunc; + if (!__allowReadAccess && __allowSetGet) { if (varExists(fName)) { return callAccessor(fName); } else interp.error(ECustom('Method $fName required by property $name is missing')); } else { - if ((setter == ADefault || setter == ANull) || isVar) + if ((setter == ADefault || setter == ANull) || isVar) { return r; + } else interp.error(ECustom('Field $name cannot be accessed because it is not a real variable${interp.isBypassAccessor ? '. Add @:isVar to enable it' : ''}')); } @@ -80,18 +129,19 @@ class Property { return r; } - public function callSetter(name:String, val:Dynamic) { + private function callSetter(val:Dynamic):Dynamic { switch (setter) { case ASet | ADynamic: - var fName:String = '$SET$name'; - if (!__allowWriteAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) { + var fName:String = setterFunc; + if (!__allowWriteAccess && __allowSetGet) { if (varExists(fName)) - return callAccessor(fName, [val], true); + return callAccessor(fName, val); else interp.error(ECustom('Method $fName required by property $name is missing')); } else { - if ((getter == ADefault || getter == ANull) || isVar) + if ((getter == ADefault || getter == ANull) || isVar) { return r = val; + } else interp.error(ECustom('Field $name cannot be accessed because it is not a real variable${interp.isBypassAccessor ? '. Add @:isVar to enable it' : ''}')); } @@ -103,14 +153,15 @@ class Property { return r = val; } - private function callAccessor(f:String, ?args:Array, isWrite:Bool = false):Dynamic { + private function callAccessor(f:String, ?value:Dynamic):Dynamic { var fn = isStatic ? interp.staticVariables.get(f) : interp.variables.get(f); var rt:Dynamic = null; + var isWrite:Bool = value != null; if (fn != null && Reflect.isFunction(fn)) { if (isWrite) __allowWriteAccess = true; else __allowReadAccess = true; - rt = UnsafeReflect.callMethodUnsafe(null, fn, args == null ? [] : args); + rt = UnsafeReflect.callMethodUnsafe(null, fn, isWrite ? [value] : []); if (isWrite) __allowWriteAccess = false; else __allowReadAccess = false; @@ -122,7 +173,7 @@ class Property { return rt; } - private function varExists(n:String) { + private inline function varExists(n:String) { return isStatic ? interp.staticVariables.exists(n) : interp.variables.exists(n); } } diff --git a/hscript/macros/ClassExtendMacro.hx b/hscript/macros/ClassExtendMacro.hx index e6f0eb3..9ddc05d 100644 --- a/hscript/macros/ClassExtendMacro.hx +++ b/hscript/macros/ClassExtendMacro.hx @@ -508,8 +508,9 @@ class ClassExtendMacro { if (__interp != null) { if(__class__fields.contains(name)) { var v:Dynamic = __interp.variables.get(name); + var ba:Bool = @:privateAccess __interp.isBypassAccessor; if(v != null && v is hscript.Property) - return cast(v, hscript.Property).callGetter(name); + return cast(v, hscript.Property).get(ba); return v; } else @:privateAccess { @@ -565,8 +566,9 @@ class ClassExtendMacro { if (__interp != null) { if(__class__fields.contains(name)) { var v:Dynamic = __interp.variables.get(name); + var ba:Bool = @:privateAccess __interp.isBypassAccessor; if(v != null && v is hscript.Property) - return cast(v, hscript.Property).callSetter(name, val); + return cast(v, hscript.Property).set(val, ba); __interp.variables.set(name, val); return val; }