diff --git a/hscript/CustomClassHandler.hx b/hscript/CustomClassHandler.hx index 445a362..9ad9624 100644 --- a/hscript/CustomClassHandler.hx +++ b/hscript/CustomClassHandler.hx @@ -1,5 +1,7 @@ package hscript; +import hscript.Interp.DeclaredVar; +import hscript.utils.UnsafeReflect; using StringTools; class CustomClassHandler implements IHScriptCustomConstructor { @@ -11,12 +13,18 @@ class CustomClassHandler implements IHScriptCustomConstructor { public var extend:String; public var interfaces:Array; + public var cl:Class; + public function new(ogInterp:Interp, name:String, fields:Array, ?extend:String, ?interfaces:Array) { this.ogInterp = ogInterp; this.name = name; this.fields = fields; this.extend = extend; this.interfaces = interfaces; + + this.cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX'); + if(cl == null) + ogInterp.error(EInvalidClass(extend)); } public function hnew(args:Array):Dynamic { @@ -24,14 +32,10 @@ class CustomClassHandler implements IHScriptCustomConstructor { interp.errorHandler = ogInterp.errorHandler; - var cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX'); - if(cl == null) - ogInterp.error(EInvalidClass(extend)); - - var _class = Type.createInstance(cl, args); + var _class:IHScriptCustomClassBehaviour = Type.createInstance(cl, args); var __capturedLocals = ogInterp.duplicate(ogInterp.locals); - var capturedLocals:Map = []; + var capturedLocals:Map = []; for(k=>e in __capturedLocals) if (e != null && e.depth <= 0) capturedLocals.set(k, e); @@ -49,6 +53,15 @@ class CustomClassHandler implements IHScriptCustomConstructor { } } + var comparisonMap = new Map(); + for(key => value in interp.variables) { + comparisonMap.set(key, value); + } + + _class.__custom__variables = interp.variables; + + //trace(fields); + for(expr in fields) { @:privateAccess interp.exprReturn(expr); @@ -58,14 +71,33 @@ class CustomClassHandler implements IHScriptCustomConstructor { _class.__interp = interp; interp.scriptObject = _class; - - var newFunc = interp.variables.get("new"); - if(newFunc != null) { - Reflect.callMethod(null, newFunc, args); + // get only variables that were not set before + var classVariables = [for(key => value in interp.variables) if(!comparisonMap.exists(key) || comparisonMap[key] != value) key => value]; + for(variable => value in classVariables) { + if(variable == "this" || variable == "super") continue; + @:privateAccess + if(!interp.__instanceFields.contains(variable)) { + interp.__instanceFields.push(variable); + } } + //trace([for(key => value in classVariables) key]); + //@:privateAccess + //trace(interp.__instanceFields); + + _class.__allowSetGet = false; + for(variable => value in interp.variables) { if(variable == "this") continue; + + if(variable.startsWith("set_") || variable.startsWith("get_")) { + _class.__allowSetGet = true; + } + } + + var newFunc = interp.variables.get("new"); + if(newFunc != null) { + UnsafeReflect.callMethodUnsafe(null, newFunc, args); } return _class; @@ -76,29 +108,44 @@ class CustomClassHandler implements IHScriptCustomConstructor { } } -class TemplateClass implements IHScriptCustomBehaviour { +class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomBehaviour { public var __interp:Interp; + public var __allowSetGet:Bool = true; + public var __custom__variables:Map; public function hset(name:String, val:Dynamic):Dynamic { - if(this.__interp.variables.exists("set_" + name)) { - return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function - } - if (this.__interp.variables.exists(name)) { - this.__interp.variables.set(name, val); + if(__allowSetGet && __custom__variables.exists("set_" + name)) + return __callSetter(name, val); + if (__custom__variables.exists(name)) { + __custom__variables.set(name, val); return val; } - Reflect.setProperty(this, name, val); - return Reflect.field(this, name); + UnsafeReflect.setProperty(this, name, val); + return UnsafeReflect.field(this, name); } public function hget(name:String):Dynamic { - if(this.__interp.variables.exists("get_" + name)) - return this.__interp.variables.get("get_" + name)(); - if (this.__interp.variables.exists(name)) - return this.__interp.variables.get(name); - return Reflect.getProperty(this, name); + 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); + } + + public function __callGetter(name:String):Dynamic { + __allowSetGet = false; + var v = __custom__variables.get("get_" + name)(); + __allowSetGet = true; + return v; + } + + public function __callSetter(name:String, val:Dynamic):Dynamic { + __allowSetGet = false; + var v = __custom__variables.get("set_" + name)(val); + __allowSetGet = true; + return v; } } -class StaticHandler { +final class StaticHandler { public function new() {} } \ No newline at end of file diff --git a/hscript/Expr.hx b/hscript/Expr.hx index b8c43dc..269bc5e 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -21,6 +21,16 @@ */ package hscript; +typedef Int8 = #if cpp cpp.Int8 #else Int #end; +typedef Int16 = #if cpp cpp.Int16 #else Int #end; +typedef Int32 = #if cpp cpp.Int32 #else Int #end; +typedef Int64 = #if cpp cpp.Int64 #else Int #end; + +typedef UInt8 = #if cpp cpp.UInt8 #else Int #end; +typedef UInt16 = #if cpp cpp.UInt16 #else Int #end; +typedef UInt32 = #if cpp cpp.UInt32 #else Int #end; +typedef UInt64 = #if cpp cpp.UInt64 #else Int #end; + enum Const { CInt( v : Int ); CFloat( f : Float ); @@ -31,12 +41,13 @@ enum Const { } #if hscriptPos -typedef Expr = { - var e : ExprDef; - var pmin : Int; - var pmax : Int; - var origin : String; - var line : Int; +@:structInit +final class Expr { + public var e : ExprDef; + public var pmin : Int; + public var pmax : Int; + public var origin : String; + public var line : Int; } enum ExprDef { #else @@ -66,7 +77,7 @@ enum Expr { ETry( e : Expr, v : String, t : Null, ecatch : Expr ); EObject( fl : Array<{ name : String, e : Expr }> ); ETernary( cond : Expr, e1 : Expr, e2 : Expr ); - ESwitch( e : Expr, cases : Array<{ values : Array, expr : Expr }>, ?defaultExpr : Expr ); + ESwitch( e : Expr, cases : Array, ?defaultExpr : Expr ); EDoWhile( cond : Expr, e : Expr); EMeta( name : String, args : Array, e : Expr ); ECheckType( e : Expr, t : CType ); @@ -75,6 +86,12 @@ enum Expr { EClass( name:String, fields:Array, ?extend:String, interfaces:Array ); } +@:structInit +final class SwitchCase { + public var values : Array; + public var expr : Expr; +} + typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr }; typedef Metadata = Array<{ name : String, params : Array }>; @@ -157,13 +174,13 @@ typedef FieldDecl = { var access : Array; } -enum FieldAccess { - APublic; - APrivate; - AInline; - AOverride; - AStatic; - AMacro; +enum abstract FieldAccess(UInt8) { + var APublic; + var APrivate; + var AInline; + var AOverride; + var AStatic; + var AMacro; } enum FieldKind { diff --git a/hscript/IHScriptCustomClassBehaviour.hx b/hscript/IHScriptCustomClassBehaviour.hx new file mode 100644 index 0000000..a291a1e --- /dev/null +++ b/hscript/IHScriptCustomClassBehaviour.hx @@ -0,0 +1,14 @@ +package hscript; + +interface IHScriptCustomClassBehaviour { + public var __interp:Interp; + public var __custom__variables:Map; + public var __allowSetGet:Bool; + + public function hset(name:String, val:Dynamic):Dynamic; + public function hget(name:String):Dynamic; + + public function __callGetter(name:String):Dynamic; + + public function __callSetter(name:String, val:Dynamic):Dynamic; +} \ No newline at end of file diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 6833b4c..491d2ba 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -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; public var staticVariables:Map; - public var locals:Map; + public var locals:Map; var binops:MapExpr->Dynamic>; #else public var customClasses:Hash; @@ -65,13 +111,13 @@ class Interp { public var publicVariables:Hash; public var staticVariables:Hash; - public var locals:Hash<{r:Dynamic, depth:Int}>; + public var locals:Hash; var binops:HashExpr->Dynamic>; #end var depth:Int = 0; var inTry:Bool; - var declared:Array<{n:String, old:{r:Dynamic, depth:Int}, depth:Int}>; + var declared:Array; 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 = [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 = []; + var capturedLocals:Map = []; 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 { + return cast(map, IMap); + } + inline function getMapValue(map:Dynamic, key:Dynamic):Dynamic { - return cast(map, haxe.Constraints.IMap).get(key); + return cast(map, IMap).get(key); } inline function setMapValue(map:Dynamic, key:Dynamic, value:Dynamic):Void { - cast(map, haxe.Constraints.IMap).set(key, value); + cast(map, IMap).set(key, value); } public static var getRedirects:MapString->Dynamic> = []; public static var setRedirects:MapString->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):Null { + 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 = 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 = 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 = 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 = 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 { - 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 { - var cl:String = cast cl; var c:Dynamic = resolve(cl); if (c == null) c = Type.resolveClass(cl); diff --git a/hscript/Parser.hx b/hscript/Parser.hx index 9d96213..b4f6478 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -907,10 +907,12 @@ class Parser { var fields = []; ensure(TBrOpen); - while( !maybe(TBrClose) ) { - if(token() == TSemicolon) continue; - var a = parseExpr(); - fields.push(a); + while( true ) { + parseFullExpr(fields); + tk = token(); + if( tk == TBrClose || (resumeErrors && tk == TEof) ) + break; + push(tk); } var tk = token(); @@ -964,7 +966,7 @@ class Parser { var tk = token(); switch( tk ) { case TId("case"): - var c = { values : [], expr : null }; + var c:SwitchCase = { values : [], expr : null }; cases.push(c); disableOrOp = true; while( true ) { @@ -1601,7 +1603,8 @@ class Parser { return b.toString(); } - function token(?infos : Null) { + function token() { + //function token(?infos : Null) { //function ttrace(v:Dynamic, ?infos : Null) { // Sys.print(infos.fileName+":"+infos.lineNumber+": " + Std.string(v)); // Sys.print("\r\n"); @@ -1619,6 +1622,7 @@ class Parser { oldTokenMax = tokenMax; tokenMin = (this.char < 0) ? readPos : readPos - 1; var t = _token(); + //trace(t, infos); //ttrace(t, infos); tokenMax = (this.char < 0) ? readPos - 1 : readPos - 2; return t; diff --git a/hscript/macros/ClassExtendMacro.hx b/hscript/macros/ClassExtendMacro.hx index 0d480d4..ebba2da 100644 --- a/hscript/macros/ClassExtendMacro.hx +++ b/hscript/macros/ClassExtendMacro.hx @@ -187,10 +187,12 @@ class ClassExtendMacro { overrideExpr = macro { var name:String = $v{name}; - if (__interp != null) { - var v:Dynamic = null; - if (__interp.variables.exists(name) && Reflect.isFunction(v = __interp.variables.get(name))) { - return v($a{arguments}); + if (__custom__variables != null) { + if(__custom__variables.exists(name)) { + var v:Dynamic = null; + if (Reflect.isFunction(v = __custom__variables.get(name))) { + return v($a{arguments}); + } } } return super.$name($a{arguments}); @@ -199,11 +201,13 @@ class ClassExtendMacro { overrideExpr = macro { var name:String = $v{name}; - if (__interp != null) { - var v:Dynamic = null; - if (__interp != null && __interp.variables.exists(name) && Reflect.isFunction(v = __interp.variables.get(name))) { - v($a{arguments}); - return; + if (__custom__variables != null) { + if(__custom__variables.exists(name)) { + var v:Dynamic = null; + if (Reflect.isFunction(v = __custom__variables.get(name))) { + v($a{arguments}); + return; + } } } super.$name($a{arguments}); @@ -268,11 +272,13 @@ class ClassExtendMacro { pack: cl.pack.copy(), name: cl.name }, [ - {name: "IHScriptCustomBehaviour", pack: ["hscript"]} + {name: "IHScriptCustomBehaviour", pack: ["hscript"]}, + {name: "IHScriptCustomClassBehaviour", pack: ["hscript"]} ], false, true, false); shadowClass.name = '${cl.name}$CLASS_SUFFIX'; var imports = Context.getLocalImports().copy(); Utils.setupMetas(shadowClass, imports); + Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect"); // Adding hscript getters and setters @@ -282,7 +288,83 @@ class ClassExtendMacro { kind: FVar(TPath({ pack: ['hscript'], name: 'Interp' - })) + })), + access: [APublic] + }); + + shadowClass.fields.push({ + name: "__custom__variables", + pos: Context.currentPos(), + kind: FVar(TPath({ + pack: [], + name: 'Map', + params: [TPType(TPath({name: "String", pack: []})), TPType(TPath({name: "Dynamic", pack: []}))] + })), + access: [APublic] + }); + + shadowClass.fields.push({ + name: "__allowSetGet", + pos: Context.currentPos(), + kind: FVar(TPath({ + pack: [], + name: 'Bool', + }), macro true), + access: [APublic] + }); + + shadowClass.fields.push({ + name: "__callGetter", + pos: Context.currentPos(), + kind: FFun({ + ret: TPath({name: 'Dynamic', pack: []}), + params: [], + expr: macro { + __allowSetGet = false; + var v = __custom__variables.get("get_" + name)(); + __allowSetGet = true; + return v; + }, + args: [ + { + name: "name", + opt: false, + meta: [], + type: TPath({name: "String", pack: []}) + } + ] + }), + access: [APublic] + }); + + shadowClass.fields.push({ + name: "__callSetter", + pos: Context.currentPos(), + kind: FFun({ + ret: TPath({name: 'Dynamic', pack: []}), + params: [], + expr: macro { + __allowSetGet = false; + var v = __custom__variables.get("set_" + name)(val); + __allowSetGet = true; + return v; + }, + args: [ + { + name: "name", + opt: false, + meta: [], + type: TPath({name: "String", pack: []}) + }, + { + name: "val", + opt: false, + meta: [], + type: TPath({name: "Dynamic", pack: []}) + } + ] + }), + access: [APublic] }); // Todo: make it possible to override @@ -327,44 +409,42 @@ class ClassExtendMacro { var hgetField = if(hasHgetInSuper) { macro { - if(this.__interp.variables.exists("get_" + name)) - return this.__interp.variables.get("get_" + name)(); - if (this.__interp.variables.exists(name)) - return this.__interp.variables.get(name); + 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(this.__interp.variables.exists("get_" + name)) - return this.__interp.variables.get("get_" + name)(); - if (this.__interp.variables.exists(name)) - return this.__interp.variables.get(name); - return Reflect.getProperty(this, name); + 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(this.__interp.variables.exists("set_" + name)) { - return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function - } - if (this.__interp.variables.exists(name)) { - this.__interp.variables.set(name, val); + if(__allowSetGet && __custom__variables.exists("set_" + name)) + return __callSetter(name, val); + if (__custom__variables.exists(name)) { + __custom__variables.set(name, val); return val; } - return super.hset(name, val); + return super.hset(this, name); } } else { macro { - if(this.__interp.variables.exists("set_" + name)) { - return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function - } - if (this.__interp.variables.exists(name)) { - this.__interp.variables.set(name, val); + if(__allowSetGet && __custom__variables.exists("set_" + name)) + return __callSetter(name, val); + if (__custom__variables.exists(name)) { + __custom__variables.set(name, val); return val; } - Reflect.setProperty(this, name, val); - return Reflect.field(this, name); + UnsafeReflect.setProperty(this, name, val); + return UnsafeReflect.field(this, name); } } diff --git a/hscript/utils/UnsafeReflect.hx b/hscript/utils/UnsafeReflect.hx new file mode 100644 index 0000000..267b6c3 --- /dev/null +++ b/hscript/utils/UnsafeReflect.hx @@ -0,0 +1,184 @@ +package hscript.utils; + +#if cpp +import cpp.ObjectType; +#end + +@:analyzer(ignore) +class UnsafeReflect { + public #if !cpp inline #end static function hasField(o:Dynamic, field:String):Bool { + #if cpp + untyped { + return o.__HasField(field); + } + #else + return Reflect.hasField(o, field); + #end + } + + public #if !cpp inline #end static function field(o:Dynamic, field:String):Dynamic { + #if cpp + untyped { + return o.__Field(field, untyped __cpp__("::hx::paccNever")); + } + #else + return Reflect.field(o, field); + #end + } + + public #if !cpp inline #end static function setField(o:Dynamic, field:String, value:Dynamic):Void { + #if cpp + untyped { + o.__SetField(field, value, untyped __cpp__("::hx::paccNever")); + } + #else + return Reflect.setField(o, field, value); + #end + } + + public #if !cpp inline #end static function getProperty(o:Dynamic, field:String):Dynamic { + #if cpp + untyped { + return o.__Field(field, untyped __cpp__("::hx::paccAlways")); + } + #else + return Reflect.getProperty(o, field); + #end + } + + public #if !cpp inline #end static function setProperty(o:Dynamic, field:String, value:Dynamic):Void { + #if cpp + untyped { + o.__SetField(field, value, untyped __cpp__("::hx::paccAlways")); + } + #else + Reflect.setProperty(o, field); + #end + } + + public #if !cpp inline #end static function callFieldUnsafe(o:Dynamic, field:String, args:Array):Dynamic { + #if cpp + untyped { + var func:Dynamic = o.__Field(field, untyped __cpp__("::hx::paccDynamic")); + untyped func.__SetThis(o); + return untyped func.__Run(args); + } + #else + return Reflect.callMethod(o, func, args); + #end + } + + public inline static function callMethod(o:Dynamic, func:haxe.Constraints.Function, args:Array):Dynamic { + return Reflect.callMethod(o, func, args); + } + + public #if !cpp inline #end static function callMethodSafe(o:Dynamic, func:haxe.Constraints.Function, args:Array):Dynamic { + #if cpp + untyped { + if (func == null) + throw cpp.ErrorConstants.nullFunctionPointer; + untyped func.__SetThis(o); + return untyped func.__Run(args); + } + #else + return Reflect.callMethod(o, func, args); + #end + } + + public #if !cpp inline #end static function callMethodUnsafe(o:Dynamic, func:haxe.Constraints.Function, args:Array):Dynamic { + #if cpp + untyped { + untyped func.__SetThis(o); + return untyped func.__Run(args); + } + #else + return Reflect.callMethod(o, func, args); + #end + } + + public inline static function fields(o:Dynamic):Array + return Reflect.fields(o); + /*untyped { + if (o == null) + return new Array(); + var a:Array = []; + o.__GetFields(a); + return a; + }*/ + + public #if !cpp inline #end static function isFunction(f:Dynamic):Bool + #if cpp + untyped { + return f.__GetType() == ObjectType.vtFunction; + } + #else + return Reflect.isFunction(f); + #end + + public inline static function compare(a:T, b:T):Int { + return Reflect.compare(a, b); + //return (a == b) ? 0 : (((a : Dynamic) > (b : Dynamic)) ? 1 : -1); + } + + public inline static function compareMethods(f1:Dynamic, f2:Dynamic):Bool { + return Reflect.compareMethods(f1, f2); + } + + public #if !cpp inline #end static function isObject(v:Dynamic):Bool { + #if cpp + untyped { + var t:Int = v.__GetType(); + return t == ObjectType.vtObject || t == ObjectType.vtClass || t == ObjectType.vtString || t == ObjectType.vtArray; + } + #else + return Reflect.isObject(v); + #end + } + + public #if !cpp inline #end static function isEnumValue(v:Dynamic):Bool { + #if cpp + untyped { + return v.__GetType() == ObjectType.vtEnum; + } + #else + return Reflect.isEnumValue(v); + #end + } + + public #if !cpp inline #end static function deleteField(o:Dynamic, field:String):Bool { + #if cpp + untyped { + return untyped __global__.__hxcpp_anon_remove(o, field); + } + #else + return Reflect.deleteField(o, field); + #end + } + + public #if !cpp inline #end static function copy(o:Null):Null { + #if cpp + if (o == null) + return null; + var t:Int = untyped o.__GetType(); + if (t == ObjectType.vtString) + return o; + if (t == ObjectType.vtArray) + return untyped o.__Field("copy", untyped __cpp__("::hx::paccDynamic"))(); + var o2:Dynamic = {}; + for (f in UnsafeReflect.fields(o)) + UnsafeReflect.setField(o2, f, UnsafeReflect.field(o, f)); + return o2; + #else + return Reflect.copy(o); + #end + } + + @:overload(function(f:Array->Void):Dynamic {}) + public static function makeVarArgs(f:Array->Dynamic):Dynamic { + #if cpp + return untyped __global__.__hxcpp_create_var_args(f); + #else + return inline Reflect.makeVarArgs(f); + #end + } +}