Classes work more reliably + Optimizations
This commit is contained in:
+227
-108
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user