diff --git a/hscript/Interp.hx b/hscript/Interp.hx index baabfb7..950cc5d 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -28,6 +28,9 @@ */ package hscript; +import hscript.proxy.ProxyType; +import hscript.Tools.EnumValue; +import hscript.Tools.HScriptEnum; import haxe.Constraints.Function; import hscript.utils.UsingHandler; import hscript.customclass.CustomClassDecl; @@ -76,22 +79,26 @@ class RedeclaredVar { @:analyzer(optimize, local_dce, fusion, user_var_fusion) class Interp { private static var _customClasses:Map = new Map(); + private static var _customClassAliases:Map = new Map(); public static function getCustomClass(name:String) { + // Retrieves the real class name + var realName = _customClassAliases.get(name); + if(realName != null) name = realName; return _customClasses.get(name); } public static function customClassExist(name:String):Bool { - return _customClasses.exists(name); + return _customClassAliases.exists(name) || _customClasses.exists(name); } private static function registerCustomClass(c:CustomClassDecl, ?as:String, ?inCustomClass:Bool) { #if CUSTOM_CLASSES var name = c.classDecl.name; - if (as != null) name = as; - else if (c.pkg != null && (inCustomClass != null && inCustomClass)) { - name = c.pkg.join(".") + "." + name; + if (c.pkg != null && (inCustomClass != null && inCustomClass)) { + name = '${c.pkg.join(".")}.$name';//c.pkg.join(".") + "." + name; } + if (as != null) _customClassAliases.set(as, name); _customClasses.set(name, c); #end } @@ -755,6 +762,7 @@ class Interp { return null; } + // backwards compatibility public static var importRedirects:Map = new Map(); public static function getImportRedirect(className:String):String { return importRedirects.exists(className) ? importRedirects.get(className) : className; @@ -763,6 +771,8 @@ class Interp { public var localImportRedirects:Map = new Map(); public function getLocalImportRedirect(className:String):String { var className = className; + if (InterpConfig.IMPORT_REDIRECTS.exists(className)) + className = InterpConfig.IMPORT_REDIRECTS.get(className); if (importRedirects.exists(className)) className = importRedirects.get(className); if (localImportRedirects.exists(className)) @@ -805,6 +815,8 @@ class Interp { if (importBlocklist.contains(realClassName)) return null; + if (InterpConfig.DISALLOW_IMPORT.contains(realClassName)) + return null; var cl = Type.resolveClass(realClassName); if (cl == null) cl = Type.resolveClass('${realClassName}_HSC'); @@ -1256,11 +1268,12 @@ class Interp { case EUsing(name): useUsing(name); case EEnum(enumName, fields): - var obj = {}; + var obj:HScriptEnum = new HScriptEnum(); for (index => field in fields) { switch (field) { case ESimple(name): - Reflect.setField(obj, name, new Tools.EnumValue(enumName, name, index, null)); + //Reflect.setField(obj, name, new Tools.EnumValue(enumName, name, index, obj, null)); + obj.setEnum(name, new EnumValue(enumName, name, index, obj, null)); case EConstructor(name, params): var hasOpt = false, minParams = 0; for (p in params) @@ -1291,11 +1304,12 @@ class Interp { args2.push(args[pos++]); args = args2; } - return new Tools.EnumValue(enumName, name, index, args); + return new Tools.EnumValue(enumName, name, index, obj, args); }; var f = Reflect.makeVarArgs(f); - Reflect.setField(obj, name, f); + //Reflect.setField(obj, name, f); + obj.setEnum(name, f); } } diff --git a/hscript/InterpConfig.hx b/hscript/InterpConfig.hx new file mode 100644 index 0000000..aa6afb6 --- /dev/null +++ b/hscript/InterpConfig.hx @@ -0,0 +1,14 @@ +package hscript; + +class InterpConfig { + // Automatic import redirect for certain classes + public static var IMPORT_REDIRECTS(default, never) = [ + "Type" => "hscript.proxy.ProxyType" + ]; + + // Incase an import fails + // These are the module names + @:unreflective public static var DISALLOW_IMPORT(default, never) = [ + "Type" + ]; +} \ No newline at end of file diff --git a/hscript/Tools.hx b/hscript/Tools.hx index 5ea4e64..a34902b 100644 --- a/hscript/Tools.hx +++ b/hscript/Tools.hx @@ -173,11 +173,13 @@ class EnumValue { public var name: String; public var index: Int; public var args: Array; + public var enumParent:Dynamic; - public function new(enumName: String, name: String, index: Int, ?args: Array) { + public function new(enumName: String, name: String, index: Int, enumParent:Dynamic, ?args: Array) { this.enumName = enumName; this.name = name; this.index = index; + this.enumParent = enumParent; this.args = args; } @@ -211,4 +213,29 @@ class EnumValue { return true; } +} + +class HScriptEnum implements IHScriptCustomBehaviour{ + public var enumValues(default, null) = {}; + + public function new() {} + + public function setEnum(name:String, enumValue:Dynamic) { + //enumValues.set(name, enumValue); + Reflect.setField(enumValues, name, enumValue); + } + + public function getEnum(name:String):Null { + //if(enumValues.exists(name)) return enumValues.get(name); + if(Reflect.hasField(enumValues, name)) return Reflect.field(enumValues, name); + return null; + } + + public function hget(name:String):Dynamic { + return getEnum(name); + } + + public function hset(name:String, val:Dynamic):Dynamic { + throw new haxe.exceptions.NotImplementedException(); + } } \ No newline at end of file diff --git a/hscript/proxy/ProxyType.hx b/hscript/proxy/ProxyType.hx new file mode 100644 index 0000000..2c0436a --- /dev/null +++ b/hscript/proxy/ProxyType.hx @@ -0,0 +1,341 @@ +package hscript.proxy; + +import hscript.customclass.CustomClass; +import hscript.customclass.CustomClassDecl; +import hscript.Tools.HScriptEnum; +import hscript.Tools.EnumValue as HScriptEnumValue; + +abstract OneOfTwo(Dynamic) from T1 from T2 to T1 to T2 {} + +class ProxyType { + /** + Returns the class of `o`, if `o` is a class instance. + + If `o` is null or of a different type, null is returned. + + In general, type parameter information cannot be obtained at runtime. + **/ + inline static function getClass(o:T):Null> { + return Type.getClass(o); + } + + /** + Returns the enum of enum instance `o`. + + An enum instance is the result of using an enum constructor. Given an + `enum Color { Red; }`, `getEnum(Red)` returns `Enum`. + + If `o` is null, null is returned. + + In general, type parameter information cannot be obtained at runtime. + **/ + inline static function getEnum(o:OneOfTwo):Dynamic { + // TODO: SUPPORT HSCRIPT ENUMS + var isScripted:Bool = Std.isOfType(o, HScriptEnumValue); + if(isScripted) { + return cast(o, HScriptEnumValue).enumParent; + } + return Type.getEnum(cast o); + } + + /** + Returns the super-class of class `c`. + + If `c` has no super class, null is returned. + + If `c` is null, the result is unspecified. + + In general, type parameter information cannot be obtained at runtime. + **/ + inline static function getSuperClass(c:Class):Class { + return Type.getSuperClass(c); + } + + /** + Returns the name of class `c`, including its path. + + If `c` is inside a package, the package structure is returned dot- + separated, with another dot separating the class name: + `pack1.pack2.(...).packN.ClassName` + If `c` is a sub-type of a Haxe module, that module is not part of the + package structure. + + If `c` has no package, the class name is returned. + + If `c` is null, the result is unspecified. + + The class name does not include any type parameters. + **/ + inline static function getClassName(c:Class):String { + return Type.getClassName(c); + } + + /** + Returns the name of enum `e`, including its path. + + If `e` is inside a package, the package structure is returned dot- + separated, with another dot separating the enum name: + `pack1.pack2.(...).packN.EnumName` + If `e` is a sub-type of a Haxe module, that module is not part of the + package structure. + + If `e` has no package, the enum name is returned. + + If `e` is null, the result is unspecified. + + The enum name does not include any type parameters. + **/ + inline static function getEnumName(e:Enum):String { + var isScripted:Bool = Std.isOfType(e, HScriptEnumValue); + if (isScripted) + return cast(e, HScriptEnumValue).getEnumName(); + return Type.getEnumName(e); + } + + /** + Resolves a class by name. + + If `name` is the path of an existing class, that class is returned. + + Otherwise null is returned. + + If `name` is null or the path to a different type, the result is + unspecified. + + The class name must not include any type parameters. + **/ + inline static function resolveClass(name:String):Dynamic { + // Dynamic to no mess with casting + if(Interp.customClassExist(name)) { + return Interp.getCustomClass(name); + } + return Type.resolveClass(name); + } + + /** + Resolves an enum by name. + + If `name` is the path of an existing enum, that enum is returned. + + Otherwise null is returned. + + If `name` is null the result is unspecified. + + If `name` is the path to a different type, null is returned. + + The enum name must not include any type parameters. + **/ + inline static function resolveEnum(name:String):Enum { + return Type.resolveEnum(name); + } + + /** + Creates an instance of class `cl`, using `args` as arguments to the + class constructor. + + This function guarantees that the class constructor is called. + + Default values of constructors arguments are not guaranteed to be + taken into account. + + If `cl` or `args` are null, or if the number of elements in `args` does + not match the expected number of constructor arguments, or if any + argument has an invalid type, or if `cl` has no own constructor, the + result is unspecified. + + In particular, default values of constructor arguments are not + guaranteed to be taken into account. + **/ + inline static function createInstance(cl:OneOfTwo, CustomClassDecl>, args:Array):T { + if(cl is CustomClassDecl) { + //return cast new CustomClass(cast cl, args); + return cast Type.createInstance(CustomClass, [cast cl, args]); + } + return Type.createInstance(cast cl, args); + } + + /** + Creates an instance of class `cl`. + + This function guarantees that the class constructor is not called. + + If `cl` is null, the result is unspecified. + **/ + inline static function createEmptyInstance(cl:Class):T { + return Type.createEmptyInstance(cl); + } + + /** + Creates an instance of enum `e` by calling its constructor `constr` with + arguments `params`. + + If `e` or `constr` is null, or if enum `e` has no constructor named + `constr`, or if the number of elements in `params` does not match the + expected number of constructor arguments, or if any argument has an + invalid type, the result is unspecified. + **/ + inline static function createEnum(e:OneOfTwo, HScriptEnum>, constr:String, ?params:Array):T { + // TODO: SUPPORT HSCRIPT ENUMS + var isScripted:Bool = Std.isOfType(e, HScriptEnum); + if(isScripted) { + var scriptEnum:HScriptEnum = cast e; + var enumToCreate = scriptEnum.getEnum(constr); + if(Reflect.isFunction(enumToCreate)) { + return Reflect.callMethod(null, enumToCreate, params); + } + return enumToCreate; + } + return Type.createEnum(cast e, constr, params); + } + + /** + Creates an instance of enum `e` by calling its constructor number + `index` with arguments `params`. + + The constructor indices are preserved from Haxe syntax, so the first + declared is index 0, the next index 1 etc. + + If `e` or `constr` is null, or if enum `e` has no constructor named + `constr`, or if the number of elements in `params` does not match the + expected number of constructor arguments, or if any argument has an + invalid type, the result is unspecified. + **/ + inline static function createEnumIndex(e:Enum, index:Int, ?params:Array):T { + // TODO: SUPPORT HSCRIPT ENUMS + return Type.createEnumIndex(e, index, params); + } + + /** + Returns a list of the instance fields of class `c`, including + inherited fields. + + This only includes fields which are known at compile-time. In + particular, using `getInstanceFields(getClass(obj))` will not include + any fields which were added to `obj` at runtime. + + The order of the fields in the returned Array is unspecified. + + If `c` is null, the result is unspecified. + **/ + inline static function getInstanceFields(c:Class):Array { + return Type.getInstanceFields(c); + } + + /** + Returns a list of static fields of class `c`. + + This does not include static fields of parent classes. + + The order of the fields in the returned Array is unspecified. + + If `c` is null, the result is unspecified. + **/ + inline static function getClassFields(c:Class):Array { + return Type.getClassFields(c); + } + + /** + Returns a list of the names of all constructors of enum `e`. + + The order of the constructor names in the returned Array is preserved + from the original syntax. + + If `e` is null, the result is unspecified. + **/ + inline static function getEnumConstructs(e:OneOfTwo, HScriptEnum>):Array { + // TODO: SUPPORT HSCRIPT ENUMS + return Type.getEnumConstructs(cast e); + } + + /** + Returns the runtime type of value `v`. + + The result corresponds to the type `v` has at runtime, which may vary + per platform. Assumptions regarding this should be minimized to avoid + surprises. + **/ + inline static function typeof(v:Dynamic):Type.ValueType { + return Type.typeof(v); + } + + /** + Recursively compares two enum instances `a` and `b` by value. + + Unlike `a == b`, this function performs a deep equality check on the + arguments of the constructors, if exists. + + If `a` or `b` are null, the result is unspecified. + **/ + inline static function enumEq(a:Dynamic, b:Dynamic):Bool { + var isScripted:Bool = Std.isOfType(a, HScriptEnumValue) && Std.isOfType(b, HScriptEnumValue); + if (isScripted) + return cast(a, HScriptEnumValue).compare(cast(b, HScriptEnumValue)); + return Type.enumEq(a, b); + } + + /** + Returns the constructor name of enum instance `e`. + + The result String does not contain any constructor arguments. + + If `e` is null, the result is unspecified. + **/ + inline static function enumConstructor(e:Dynamic):String { + if (Std.isOfType(e, HScriptEnumValue)) + return cast(e, HScriptEnumValue).name; + return Type.enumConstructor(e); + } + + /** + Returns a list of the constructor arguments of enum instance `e`. + + If `e` has no arguments, the result is []. + + Otherwise the result are the values that were used as arguments to `e`, + in the order of their declaration. + + If `e` is null, the result is unspecified. + **/ + inline static function enumParameters(e:Dynamic):Array { + if (Std.isOfType(e, HScriptEnumValue)) + return cast(e, HScriptEnumValue).args; + return Type.enumParameters(e); + } + + /** + Returns the index of enum instance `e`. + + This corresponds to the original syntactic position of `e`. The index of + the first declared constructor is 0, the next one is 1 etc. + + If `e` is null, the result is unspecified. + **/ + inline static function enumIndex(e:Dynamic):Int { + if (Std.isOfType(e, HScriptEnumValue)) + return cast(e, HScriptEnumValue).index; + return Type.enumIndex(e); + } + + /** + Returns a list of all constructors of enum `e` that require no + arguments. + + This may return the empty Array `[]` if all constructors of `e` require + arguments. + + Otherwise an instance of `e` constructed through each of its non- + argument constructors is returned, in the order of the constructor + declaration. + + If `e` is null, the result is unspecified. + **/ + inline static function allEnums(e:Enum):Array { + // TODO: SUPPORT HSCRIPT ENUMS + var isScripted:Bool = Std.isOfType(e, HScriptEnum); + if(isScripted) { + var hEnum:HScriptEnum = cast e; + return [for(en in Reflect.fields(hEnum.enumValues)) Reflect.field(hEnum, en)]; + } + return Type.allEnums(e); + } +}