Classes work more reliably + Optimizations

This commit is contained in:
Ne_Eo
2024-08-20 04:50:20 +02:00
parent f18c7a2210
commit 13bdf30055
7 changed files with 650 additions and 185 deletions
+227 -108
View File
@@ -28,9 +28,7 @@
*/
package hscript;
import haxe.iterators.StringKeyValueIteratorUnicode;
import haxe.EnumTools;
import haxe.display.Protocol.InitializeResult;
import hscript.utils.UnsafeReflect;
import haxe.PosInfos;
import hscript.Expr;
import haxe.Constraints.IMap;
@@ -43,10 +41,58 @@ private enum Stop {
SReturn;
}
enum abstract ScriptObjectType(UInt8) {
var SClass;
var SObject;
var SStaticClass;
var SCustomClass;
var SBehaviourClass;
var SNull;
}
@:structInit
class DeclaredVar {
public var r:Dynamic;
public var depth:Int;
}
@:structInit
class RedeclaredVar {
public var n:String;
public var old:DeclaredVar;
public var depth:Int;
}
class Interp {
public var scriptObject(default, set):Dynamic;
private var _hasScriptObject(default, null):Bool = false;
private var _scriptObjectType(default, null):ScriptObjectType = SNull;
public function set_scriptObject(v:Dynamic) {
__instanceFields = (v == null) ? [] : Type.getInstanceFields(Type.getClass(v));
switch(Type.typeof(v)) {
case TClass(c): // Class Access
__instanceFields = Type.getInstanceFields(c);
if(c is IHScriptCustomClassBehaviour) {
_scriptObjectType = SCustomClass;
} else if(c is IHScriptCustomBehaviour) {
_scriptObjectType = SBehaviourClass;
} else {
_scriptObjectType = SClass;
}
case TObject: // Object Access or Static Class Access
var cls = Type.getClass(v);
switch(Type.typeof(cls)) {
case TClass(c): // Static Class Access
__instanceFields = Type.getInstanceFields(c);
_scriptObjectType = SStaticClass;
default: // Object Access
__instanceFields = Reflect.fields(v);
_scriptObjectType = SObject;
}
default: // Null or other
__instanceFields = [];
_scriptObjectType = SNull;
}
_hasScriptObject = v != null;
return scriptObject = v;
}
public var errorHandler:Error->Void;
@@ -57,7 +103,7 @@ class Interp {
public var publicVariables:Map<String, Dynamic>;
public var staticVariables:Map<String, Dynamic>;
public var locals:Map<String, {r:Dynamic, depth:Int}>;
public var locals:Map<String, DeclaredVar>;
var binops:Map<String, Expr->Expr->Dynamic>;
#else
public var customClasses:Hash<Dynamic>;
@@ -65,13 +111,13 @@ class Interp {
public var publicVariables:Hash<Dynamic>;
public var staticVariables:Hash<Dynamic>;
public var locals:Hash<{r:Dynamic, depth:Int}>;
public var locals:Hash<DeclaredVar>;
var binops:Hash<Expr->Expr->Dynamic>;
#end
var depth:Int = 0;
var inTry:Bool;
var declared:Array<{n:String, old:{r:Dynamic, depth:Int}, depth:Int}>;
var declared:Array<RedeclaredVar>;
var returnValue:Dynamic;
var isBypassAccessor:Bool = false;
@@ -214,22 +260,34 @@ class Interp {
var v = expr(e2);
switch (Tools.expr(e1)) {
case EIdent(id):
var l = locals.get(id);
if (l == null) {
if (!variables.exists(id) && !staticVariables.exists(id) && !publicVariables.exists(id) && scriptObject != null) {
if (Type.typeof(scriptObject) == TObject) {
Reflect.setField(scriptObject, id, v);
if (!locals.exists(id)) {
if (_hasScriptObject && !variables.exists(id) && !staticVariables.exists(id) && !publicVariables.exists(id)) {
if (_scriptObjectType == SObject) {
UnsafeReflect.setField(scriptObject, id, v);
} else {
if (_scriptObjectType == SCustomClass) {
var obj = cast(scriptObject, IHScriptCustomClassBehaviour);
if(isBypassAccessor) {
obj.__allowSetGet = false;
var res = obj.hset(id, v);
obj.__allowSetGet = true;
return res;
}
return obj.hset(id, v);
} else if (_scriptObjectType == SBehaviourClass) {
var obj = cast(scriptObject, IHScriptCustomBehaviour);
return obj.hset(id, v);
}
if (isBypassAccessor) {
if (__instanceFields.contains(id)) {
Reflect.setField(scriptObject, id, v);
UnsafeReflect.setField(scriptObject, id, v);
return v;
}
}
if (__instanceFields.contains(id)) {
Reflect.setProperty(scriptObject, id, v);
UnsafeReflect.setProperty(scriptObject, id, v);
} else if (__instanceFields.contains('set_$id')) { // setter
Reflect.getProperty(scriptObject, 'set_$id')(v);
UnsafeReflect.getProperty(scriptObject, 'set_$id')(v);
} else {
setVar(id, v);
}
@@ -238,6 +296,7 @@ class Interp {
setVar(id, v);
}
} else {
var l = locals.get(id);
l.r = v;
if (l.depth == 0) {
setVar(id, v);
@@ -275,12 +334,38 @@ class Interp {
var l = locals.get(id);
v = fop(expr(e1), expr(e2));
if (l == null) {
if (__instanceFields.contains(id)) {
Reflect.setProperty(scriptObject, id, v);
} else if (__instanceFields.contains('set_$id')) { // setter
Reflect.getProperty(scriptObject, 'set_$id')(v);
} else {
setVar(id, v);
if(_hasScriptObject) {
if(_scriptObjectType == SObject) {
UnsafeReflect.setField(scriptObject, id, v);
return v;
} else if (_scriptObjectType == SCustomClass) {
var obj = cast(scriptObject, IHScriptCustomClassBehaviour);
if(isBypassAccessor) {
obj.__allowSetGet = false;
var res = obj.hset(id, v);
obj.__allowSetGet = true;
return res;
}
return obj.hset(id, v);
} else if(_scriptObjectType == SBehaviourClass) {
var obj = cast(scriptObject, IHScriptCustomBehaviour);
return obj.hset(id, v);
}
if (isBypassAccessor) {
if (__instanceFields.contains(id)) {
UnsafeReflect.setField(scriptObject, id, v);
return v;
}
}
if (__instanceFields.contains(id)) {
UnsafeReflect.setProperty(scriptObject, id, v);
} else if (__instanceFields.contains('set_$id')) { // setter
UnsafeReflect.getProperty(scriptObject, 'set_$id')(v);
} else {
setVar(id, v);
}
}
}
else
@@ -294,8 +379,10 @@ class Interp {
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
v = fop(getMapValue(arr, index), expr(e2));
setMapValue(arr, index, v);
var map = getMap(arr);
v = fop(map.get(index), expr(e2));
map.set(index, v);
} else {
v = fop(arr[index], expr(e2));
arr[index] = v;
@@ -313,19 +400,24 @@ class Interp {
#end
switch (e) {
case EIdent(id):
var l = locals.get(id);
var v:Dynamic = (l == null) ? resolve(id) : l.r;
if (prefix) {
v += delta;
if (l == null)
setVar(id, v)
else
if(locals.exists(id)) {
var l = locals.get(id);
var v:Dynamic = l.r;
if (prefix) {
v += delta;
l.r = v;
} else if (l == null)
setVar(id, v + delta)
else
l.r = v + delta;
return v;
} else
l.r = v + delta;
return v;
} else {
var v:Dynamic = resolve(id);
if (prefix) {
v += delta;
setVar(id, v);
} else
setVar(id, v + delta);
return v;
}
case EField(e, f, s):
var obj = expr(e);
if(s && obj == null) return null;
@@ -340,12 +432,14 @@ class Interp {
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
var v = getMapValue(arr, index);
var map = getMap(arr);
var v = map.get(index);
if (prefix) {
v += delta;
setMapValue(arr, index, v);
map.set(index, v);
} else {
setMapValue(arr, index, v + delta);
map.set(index, v + delta);
}
return v;
} else {
@@ -445,32 +539,48 @@ class Interp {
if (id == null)
return null;
id = StringTools.trim(id);
var l = locals.get(id);
if (l != null)
return l.r;
if (locals.exists(id))
return locals.get(id).r;
var v = variables.get(id);
for(map in [variables, publicVariables, staticVariables, customClasses])
if (map.exists(id))
return map[id];
if(variables.exists(id))
return variables.get(id);
if(publicVariables.exists(id))
return publicVariables.get(id);
if(staticVariables.exists(id))
return staticVariables.get(id);
if(customClasses.exists(id))
return customClasses.get(id);
if (scriptObject != null) {
if (_hasScriptObject) {
// search in object
if (id == "this") {
return scriptObject;
} else if ((Type.typeof(scriptObject) == TObject) && Reflect.hasField(scriptObject, id)) {
return Reflect.field(scriptObject, id);
} else {
} else if (_scriptObjectType == SObject && UnsafeReflect.hasField(scriptObject, id)) {
return UnsafeReflect.field(scriptObject, id);
} else if(_scriptObjectType == SCustomClass) {
var obj = cast(scriptObject, IHScriptCustomClassBehaviour);
if(isBypassAccessor) {
obj.__allowSetGet = false;
var res = obj.hget(id);
obj.__allowSetGet = true;
return res;
}
return obj.hget(id);
} else if(_scriptObjectType == SBehaviourClass) {
var obj = cast(scriptObject, IHScriptCustomBehaviour);
return obj.hget(id);
}
else {
if (__instanceFields.contains(id)) {
return Reflect.getProperty(scriptObject, id);
return UnsafeReflect.getProperty(scriptObject, id);
} else if (__instanceFields.contains('get_$id')) { // getter
return Reflect.getProperty(scriptObject, 'get_$id')();
return UnsafeReflect.getProperty(scriptObject, 'get_$id')();
}
}
}
if (doException)
error(EUnknownVariable(id));
return v;
return null;
}
public function expr(e:Expr):Dynamic {
@@ -541,10 +651,10 @@ class Interp {
var enumThingy = {};
for (c in en.getConstructors()) {
try {
Reflect.setField(enumThingy, c, en.createByName(c));
UnsafeReflect.setField(enumThingy, c, en.createByName(c));
} catch(e) {
try {
Reflect.setField(enumThingy, c, Reflect.field(en, c));
UnsafeReflect.setField(enumThingy, c, UnsafeReflect.field(en, c));
} catch(ex) {
throw e;
}
@@ -621,9 +731,7 @@ class Interp {
error(EInvalidOp(op));
}
case ECall(e, params):
var args = new Array();
for (p in params)
args.push(expr(p));
var args:Array<Dynamic> = [for(p in params) expr(p)];
switch (Tools.expr(e)) {
case EField(e, f, s):
@@ -656,7 +764,7 @@ class Interp {
throw SReturn;
case EFunction(params, fexpr, name, _, isPublic, isStatic, isOverride):
var __capturedLocals = duplicate(locals);
var capturedLocals:Map<String, {r:Dynamic, depth:Int}> = [];
var capturedLocals:Map<String, DeclaredVar> = [];
for(k=>e in __capturedLocals)
if (e != null && e.depth > 0)
capturedLocals.set(k, e);
@@ -727,7 +835,7 @@ class Interp {
} else {
// function-in-function is a local function
declared.push({n: name, old: locals.get(name), depth: depth});
var ref = {r: f, depth: depth};
var ref:DeclaredVar = {r: f, depth: depth};
locals.set(name, ref);
capturedLocals.set(name, ref); // allow self-recursion
}
@@ -844,7 +952,7 @@ class Interp {
case EObject(fl):
var o = {};
for (f in fl)
set(o, f.name, expr(f.e));
UnsafeReflect.setField(o, f.name, expr(f.e));
return o;
case ETernary(econd, e1, e2):
return if (expr(econd) == true) expr(e1) else expr(e2);
@@ -945,8 +1053,10 @@ class Interp {
declared.push({n: ithv, old: locals.get(ithv), depth: depth});
declared.push({n: n, old: locals.get(n), depth: depth});
var it = makeIterator(expr(it), isKeyValue);
while (it.hasNext()) {
var next = it.next();
var _hasNext = it.hasNext;
var _next = it.next;
while (_hasNext()) {
var next = _next();
if(isKeyValue)
locals.set(ithv, {r: next.key, depth: depth});
locals.set(n, {r: isKeyValue ? next.value : next, depth: depth});
@@ -969,81 +1079,91 @@ class Interp {
return (o is IMap);
}
inline function getMap(map:Dynamic):IMap<Dynamic, Dynamic> {
return cast(map, IMap<Dynamic, Dynamic>);
}
inline function getMapValue(map:Dynamic, key:Dynamic):Dynamic {
return cast(map, haxe.Constraints.IMap<Dynamic, Dynamic>).get(key);
return cast(map, IMap<Dynamic, Dynamic>).get(key);
}
inline function setMapValue(map:Dynamic, key:Dynamic, value:Dynamic):Void {
cast(map, haxe.Constraints.IMap<Dynamic, Dynamic>).set(key, value);
cast(map, IMap<Dynamic, Dynamic>).set(key, value);
}
public static var getRedirects:Map<String, Dynamic->String->Dynamic> = [];
public static var setRedirects:Map<String, Dynamic->String->Dynamic->Dynamic> = [];
private static var _getRedirect:Dynamic->String->Dynamic;
private static var _setRedirect:Dynamic->String->Dynamic->Dynamic;
public var useRedirects:Bool = false;
static function getClassType(o:Dynamic, ?cls:Class<Any>):Null<String> {
return switch (Type.typeof(o)) {
case TNull: "Null";
case TInt: "Int";
case TFloat: "Float";
case TBool: "Bool";
case _:
if (cls == null)
cls = Type.getClass(o);
cls != null ? Type.getClassName(cls) : null;
};
}
function get(o:Dynamic, f:String):Dynamic {
if (o == null)
error(EInvalidAccess(f));
return {
var redirect:Dynamic->String->Dynamic = null;
var cls = Type.getClass(o);
var cl:Null<String> = switch (Type.typeof(o)) {
case TNull: "Null";
case TInt: "Int";
case TFloat: "Float";
case TBool: "Bool";
case _: cls != null ? Type.getClassName(cls) : null;
};
if (cl != null && getRedirects.exists(cl) && (redirect = getRedirects[cl]) != null) {
return redirect(o, f);
} else if (o is IHScriptCustomBehaviour) {
var obj = cast(o, IHScriptCustomBehaviour);
return obj.hget(f);
} else {
var v = null;
if(isBypassAccessor) {
if ((v = Reflect.field(o, f)) == null)
v = Reflect.field(cls, f);
}
if(v == null) {
if ((v = Reflect.getProperty(o, f)) == null)
v = Reflect.getProperty(cls, f);
}
return v;
}
var cls = Type.getClass(o);
if (useRedirects && {
var cl:Null<String> = getClassType(o, cls);
cl != null && getRedirects.exists(cl) && (_getRedirect = getRedirects[cl]) != null;
}) {
return _getRedirect(o, f);
} else if (o is IHScriptCustomBehaviour) {
var obj = cast(o, IHScriptCustomBehaviour);
return obj.hget(f);
}
var v = null;
if(isBypassAccessor) {
if ((v = UnsafeReflect.field(o, f)) == null)
v = Reflect.field(cls, f);
}
if(v == null) {
if ((v = UnsafeReflect.getProperty(o, f)) == null)
v = Reflect.getProperty(cls, f);
}
return v;
}
function set(o:Dynamic, f:String, v:Dynamic):Dynamic {
if (o == null)
error(EInvalidAccess(f));
var redirect:Dynamic->String->Dynamic->Dynamic = null;
var cls = Type.getClass(o);
var cl:Null<String> = switch (Type.typeof(o)) {
case TNull: "Null";
case TInt: "Int";
case TFloat: "Float";
case TBool: "Bool";
case _: cls != null ? Type.getClassName(cls) : null;
};
if (cl != null && setRedirects.exists(cl) && (redirect = setRedirects[cl]) != null)
return redirect(o, f, v);
if (useRedirects && {
var cl:Null<String> = getClassType(o);
cl != null && setRedirects.exists(cl) && (_setRedirect = setRedirects[cl]) != null;
})
return _setRedirect(o, f, v);
else if (o is IHScriptCustomBehaviour) {
var obj = cast(o, IHScriptCustomBehaviour);
return obj.hset(f, v);
}
// Can use unsafe reflect here, since we checked for null above
if(isBypassAccessor) {
Reflect.setField(o, f, v);
UnsafeReflect.setField(o, f, v);
} else {
Reflect.setProperty(o, f, v);
UnsafeReflect.setProperty(o, f, v);
}
return v;
}
function fcall(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic {
if(o == CustomClassHandler.staticHandler && scriptObject != null) {
return Reflect.callMethod(scriptObject, Reflect.field(scriptObject, "_HX_SUPER__" + f), args);
if(_hasScriptObject && o == CustomClassHandler.staticHandler) {
return UnsafeReflect.callMethodUnsafe(scriptObject, UnsafeReflect.field(scriptObject, "_HX_SUPER__" + f), args);
}
return call(o, get(o, f), args);
}
@@ -1052,11 +1172,10 @@ 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>):Dynamic {
var cl:String = cast cl;
var c:Dynamic = resolve(cl);
if (c == null)
c = Type.resolveClass(cl);