diff --git a/hscript/macros/ClassExtendMacro.hx b/hscript/macros/ClassExtendMacro.hx index 34e29d5..f7c03f8 100644 --- a/hscript/macros/ClassExtendMacro.hx +++ b/hscript/macros/ClassExtendMacro.hx @@ -65,15 +65,13 @@ class ClassExtendMacro { if(cl.module.contains("_")) return fields; // Weird issue, sorry var superFields = []; - if(cl.superClass != null) { + if(false && cl.superClass != null) { var _superFields = cl.superClass.t.get().fields.get(); _superFields = []; // Comment to enable super support, (broken) - for(field in _superFields) { - if(!field.kind.match(FMethod(_))) // only catch methods - continue; + function convertField(field:ClassField) { try { - var nfield = @:privateAccess TypeTools.toField(field); + var nfield = FixedTypeTools.toSimpleField(field); switch ([field.kind, field.type]) { case [FMethod(kind), TFun(args, ret)]: if(kind == MethInline) @@ -86,9 +84,9 @@ class ClassExtendMacro { switch(nfield.kind) { case FFun(fun): if (fun.params != null && fun.params.length > 0) - continue; + return null; - fun.ret = Utils.fixStdTypes(fun.ret); + //sfun.ret = Utils.fixStdTypes(fun.ret); var metas = nfield.meta; var defaultValues:Map = []; @@ -107,7 +105,7 @@ class ClassExtendMacro { if(m.name == ":generic") isGeneric = true; } - if(isGeneric) continue; + if(isGeneric) return null; if(defaultEntry != null) metas.remove(defaultEntry); @@ -119,20 +117,80 @@ class ClassExtendMacro { arg.opt = false; } - arg.type = Utils.fixStdTypes(arg.type); + arg.type = null;//Utils.fixStdTypes(arg.type); - if(arg.opt) { - if(arg.type.getParameters()[0].name != "Null") - arg.type = TPath({name: "Null", params: [TPType(arg.type)], pack: []});//macro {Null}; - } + //if(arg.opt) { + // if(arg.type.getParameters()[0].name != "Null") + // arg.type = TPath({name: "Null", params: [TPType(arg.type)], pack: []});//macro {Null}; + //} } + + trace(nfield.name); default: } - superFields.push(nfield); + return nfield; } catch(e) { - + trace(field.name, e); + return null; } } + + var didPrint = false; + + var fieldNames = [for(f in fields) f.name]; + + /*for(field in _superFields) { + if(fieldNames.contains(field.name)) + continue; + + if(!field.kind.match(FMethod(_))) // only catch methods + continue; + + if(field.name.startsWith("get_")) { + var access = FixedTypeTools.getAccess(field); + if(access.contains(AInline) || access.contains(AFinal) || field.isFinal) + continue; + var name = field.name; + superFields.push({ + name: field.name, + pos: field.pos, + kind: FFun({ + ret: null, + params: [], + expr: macro { + return super.$name(); + }, + args: [] + }), + access: access, + meta: field.meta.get(), + }); + //var f = convertField(field); + //if(f != null) + // superFields.push(f); + if(field.name == "get_bgColor") { + if(!didPrint) { + trace(cl.name); + didPrint = true; + } + trace("> " + field.name + " : " + access, field); + } + } + + }*/ + + // want to get this working + /*for(field in _superFields) { + if(fieldNames.contains(field.name)) + continue; + + if(!field.kind.match(FMethod(_))) // only catch methods + continue; + + var f = convertField(field); + if(f != null) + superFields.push(f); + }*/ //superFields = []; } diff --git a/hscript/macros/FixedTypeTools.hx b/hscript/macros/FixedTypeTools.hx new file mode 100644 index 0000000..15cc5ae --- /dev/null +++ b/hscript/macros/FixedTypeTools.hx @@ -0,0 +1,529 @@ +package hscript.macros; + +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.Type; +import Type as StdType; + +using Lambda; + +/** + This class provides some utility methods to work with types. It is + best used through 'using haxe.macro.TypeTools' syntax and then provides + additional methods on haxe.macro.Type instances. +**/ +#if hl +@:hlNative("macro") +#end +class FixedTypeTools { + static function nullable(complexType:ComplexType):ComplexType + return macro:Null<$complexType>; + + public static function toField(cf:ClassField):Field { + function varAccessToString(va:VarAccess, getOrSet:String):String { + return { + switch (va) { + case AccNormal | AccCtor: "default"; + case AccNo: "null"; + case AccNever: "never"; + case AccResolve: throw "Invalid " + StdType.enumConstructor(cf.type) + " in varAccessToString"; + case AccCall: getOrSet; + case AccInline: "default"; + case AccRequire(_, _): "default"; + } + } + } + + var access = cf.isPublic ? [APublic] : [APrivate]; + if (cf.meta.has(":final")) { + access.push(AFinal); + } + + if (cf.params.length != 0) + throw "Invalid " + StdType.enumConstructor(cf.type) + " has more than 0 params"; + + return { + name: cf.name, + doc: cf.doc, + access: access, + kind: switch ([cf.kind, cf.type]) { + case [FVar(read, write), ret]: + FProp(varAccessToString(read, "get"), varAccessToString(write, "set"), toComplexType(ret), null); + case [FMethod(_), TFun(args, ret)]: + Sys.println("Converting " + cf.name); + FFun({ + args: [ + for (a in args) + { + name: a.name, + opt: a.opt, + type: toComplexType(a.t), + } + ], + ret: toComplexType(ret), + expr: null, + }); + case [FMethod(_), TLazy(f)]: + Sys.println("Converting lazy " + cf.name + " in " + cf.pos); + switch(f()) { + case TFun(args, ret): + FFun({ + args: [ + for (a in args) + { + name: a.name, + opt: a.opt, + type: toComplexType(a.t), + } + ], + ret: toComplexType(ret), + expr: null, + }); + default: + throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type; + } + default: + throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type; + }, + pos: cf.pos, + meta: cf.meta.get(), + } + } + + public static function getAccess(cf:ClassField):Array { + var access = cf.isPublic ? [APublic] : [APrivate]; + if (cf.meta.has(":final") || cf.isFinal) { + access.push(AFinal); + } + switch ([cf.kind, cf.type]) { + case [FMethod(kind), TFun(_, _)] | [FMethod(kind), TLazy(_)]: + if(kind == MethInline) + access.push(AInline); + if(kind == MethDynamic) + access.push(ADynamic); + default: + } + return access; + } + + public static function toSimpleField(cf:ClassField):Field { + function varAccessToString(va:VarAccess, getOrSet:String):String { + return { + switch (va) { + case AccNormal | AccCtor: "default"; + case AccNo: "null"; + case AccNever: "never"; + case AccResolve: throw "Invalid " + StdType.enumConstructor(cf.type) + " in varAccessToString"; + case AccCall: getOrSet; + case AccInline: "default"; + case AccRequire(_, _): "default"; + } + } + } + + var access = cf.isPublic ? [APublic] : [APrivate]; + if (cf.meta.has(":final")) { + access.push(AFinal); + } + + if (cf.params.length != 0) + throw "Invalid " + StdType.enumConstructor(cf.type) + " has more than 0 params"; + + return { + name: cf.name, + doc: cf.doc, + access: access, + kind: switch ([cf.kind, cf.type]) { + case [FVar(read, write), ret]: + FProp(varAccessToString(read, "get"), varAccessToString(write, "set"), toComplexType(ret), null); + case [FMethod(_), TFun(args, ret)]: + Sys.println("Converting " + cf.name); + FFun({ + args: [ + for (a in args) + { + name: a.name, + opt: a.opt, + type: null,//toComplexType(a.t), + } + ], + ret: null,//toComplexType(ret), + expr: null, + }); + case [FMethod(_), TLazy(f)]: + Sys.println("Converting lazy " + cf.name + " in " + cf.pos); + switch(f()) { + case TFun(args, ret): + FFun({ + args: [ + for (a in args) + { + name: a.name, + opt: a.opt, + type: null,//toComplexType(a.t), + } + ], + ret: null,//toComplexType(ret), + expr: null, + }); + default: + throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type; + } + default: + throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type; + }, + pos: cf.pos, + meta: cf.meta.get(), + } + } + + /** + Returns a syntax-level type corresponding to Type `t`. + + This function is mostly inverse to `ComplexTypeTools.toType`, but may + lose some information on types that do not have a corresponding syntax + version, such as monomorphs. In these cases, the result is null. + + If `t` is null, an internal exception is thrown. + **/ + public static function toComplexType(type:Null):Null + return { + #if macro + Context.toComplexType(type); + #else + switch (type) { + case null: + null; + case TMono(_.get() => t): + t == null ? null : toComplexType(t); + case TEnum(_.get() => baseType, params): + TPath(toTypePath(baseType, params)); + case TInst(_.get() => classType, params): + switch (classType.kind) { + case KTypeParameter(_): + TPath({ + name: classType.name, + pack: [], + }); + default: + TPath(toTypePath(classType, params)); + } + case TType(_.get() => baseType, params): + TPath(toTypePath(baseType, params)); + case TFun(args, ret): + TFunction([for (a in args) a.opt ? nullable(toComplexType(a.t)) : toComplexType(a.t)], toComplexType(ret)); + case TAnonymous(_.get() => {fields: fields}): + TAnonymous([for (cf in fields) toField(cf)]); + case TDynamic(t): + if (t == null) { + macro:Dynamic; + } else { + var ct = toComplexType(t); + macro:Dynamic<$ct>; + } + case TLazy(f): + toComplexType(f()); + case TAbstract(_.get() => baseType, params): + TPath(toTypePath(baseType, params)); + default: + throw "Invalid type"; + } + #end + } + + static function toTypeParam(type:Type):TypeParam + return { + switch (type) { + case TInst(_.get() => {kind: KExpr(e)}, _): TPExpr(e); + case _: TPType(toComplexType(type)); + } + } + + static function toTypePath(baseType:BaseType, params:Array):TypePath + return { + var module = baseType.module; + { + pack: baseType.pack, + name: module.substring(module.lastIndexOf(".") + 1), + sub: baseType.name, + params: [for (t in params) toTypeParam(t)], + } + } + + #if macro + /** + Follows all typedefs of `t` to reach the actual type. + + If `once` is true, this function does not call itself recursively, + otherwise it does. This can be useful in cases where intermediate + typedefs might be of interest. + + Affected types are monomorphs `TMono` and typedefs `TType(t,pl)`. + + If `t` is null, an internal exception is thrown. + + Usage example with monomorphs: + var t = Context.typeof(macro null); // TMono() + var ts = Context.typeof(macro "foo"); //TInst(String,[]) + Context.unify(t, ts); + trace(t); // TMono() + trace(t.follow()); //TInst(String,[]) + + Usage example with typedefs: + var t = Context.typeof(macro ("foo" :MyString)); // typedef MyString = String + trace(t); // TType(MyString,[]) + trace(t.follow()); //TInst(String,[]) + **/ + static public inline function follow(t:Type, ?once:Bool):Type + return Context.follow(t, once); + + /** + Like `follow`, follows all typedefs of `t` to reach the actual type. + + Will however follow also abstracts to their underlying implementation, + if they are not a @:coreType abstract + + If `t` is null, an internal exception is thrown. + + Usage example: + var t = Context.typeof(macro new Map()); + trace(t); // TAbstract(Map,[TInst(String,[]),TInst(String,[])]) + trace(t.followWithAbstracts()); // TInst(haxe.ds.StringMap, [TInst(String,[])]) + **/ + static public inline function followWithAbstracts(t:Type, once:Bool = false):Type + return Context.followWithAbstracts(t, once); + + /** + Returns true if `t1` and `t2` unify, false otherwise. + **/ + static public inline function unify(t1:Type, t2:Type):Bool + return Context.unify(t1, t2); + + /** + Tries to extract the class instance stored inside `t`. + + If `t` is a class instance `TInst(c,pl)`, c is returned. + + If `t` is of a different type, an exception of type String is thrown. + + If `t` is null, the result is null. + **/ + static public function getClass(t:Type) + return t == null ? null : switch (follow(t)) { + case TInst(c, _): c.get(); + case _: throw "Class instance expected"; + } + + /** + Tries to extract the enum instance stored inside `t`. + + If `t` is an enum instance `TEnum(e,pl)`, e is returned. + + If `t` is of a different type, an exception of type String is thrown. + + If `t` is null, the result is null. + **/ + static public function getEnum(t:Type) + return t == null ? null : switch (follow(t)) { + case TEnum(e, _): e.get(); + case _: throw "Enum instance expected"; + } + + /** + Applies the type parameters `typeParameters` to type `t` with the given + types `concreteTypes`. + + This function replaces occurrences of type parameters in `t` if they are + part of `typeParameters`. The array index of such a type parameter is + then used to lookup the concrete type in `concreteTypes`. + + If `typeParameters.length` is not equal to `concreteTypes.length`, an + exception of type `String` is thrown. + + If `typeParameters.length` is 0, `t` is returned unchanged. + + If either argument is `null`, the result is unspecified. + **/ + static public function applyTypeParameters(t:Type, typeParameters:Array, concreteTypes:Array):Type { + if (typeParameters.length != concreteTypes.length) + throw 'Incompatible arguments: ${typeParameters.length} type parameters and ${concreteTypes.length} concrete types'; + else if (typeParameters.length == 0) + return t; + #if (neko || eval) + return @:privateAccess Context.load("apply_params", 3)(typeParameters, concreteTypes, t); + #else + return applyParams(typeParameters, concreteTypes, t); + #end + } + + #if !neko + private static function applyParams(typeParameters:Array, concreteTypes:Array, t:Type):Type { + return null; + } + #end + + /** + Transforms `t` by calling `f` on each of its subtypes. + + If `t` is a compound type, `f` is called on each of its components. + + Otherwise `t` is returned unchanged. + + The following types are considered compound: + - TInst, TEnum, TType and TAbstract with type parameters + - TFun + - TAnonymous + + If `t` or `f` are null, the result is unspecified. + **/ + static public function map(t:Type, f:Type->Type):Type { + return switch (t) { + case TMono(tm): + switch (tm.get()) { + case null: t; + case var t: f(t); + } + case TEnum(_, []) | TInst(_, []) | TType(_, []): + t; + case TEnum(en, tl): + TEnum(en, tl.map(f)); + case TInst(cl, tl): + TInst(cl, tl.map(f)); + case TType(t2, tl): + TType(t2, tl.map(f)); + case TAbstract(a, tl): + TAbstract(a, tl.map(f)); + case TFun(args, ret): + TFun(args.map(function(arg) return { + name: arg.name, + opt: arg.opt, + t: f(arg.t) + }), f(ret)); + case TAnonymous(an): + TAnonymous(@:privateAccess Context.load("map_anon_ref", 2)(an, f)); + case TDynamic(t2): + t == t2 ? t : TDynamic(f(t2)); + case TLazy(ft): + var ft = ft(); + var ft2 = f(ft); + ft == ft2 ? t : ft2; + } + } + + /** + Calls function `f` on each component of type `t`. + + If `t` is not a compound type, this operation has no effect. + + The following types are considered compound: + - TInst, TEnum, TType and TAbstract with type parameters + - TFun + - TAnonymous + + If `t` or `f` are null, the result is unspecified. + **/ + static public function iter(t:Type, f:Type->Void):Void { + switch (t) { + case TMono(tm): + var t = tm.get(); + if (t != null) + f(t); + case TEnum(_, tl) | TInst(_, tl) | TType(_, tl) | TAbstract(_, tl): + for (t in tl) + f(t); + case TDynamic(t2): + if (t != t2) + f(t2); + case TLazy(ft): + f(ft()); + case TAnonymous(an): + for (field in an.get().fields) + f(field.type); + case TFun(args, ret): + for (arg in args) + f(arg.t); + f(ret); + } + } + + /** + Converts type `t` to a human-readable String representation. + **/ + static public function toString(t:Type):String { + #if (neko || eval) + return @:privateAccess Context.load("s_type", 1)(t); + #else + return null; + #end + } + + /** + Changes the name of the variable in the typed expression. + **/ + static public function setVarName(t:TVar, name:String) { + @:privateAccess Context.load("set_var_name", 2)(t, name); + } + + /** + Converts type `t` to `ModuleType`. + **/ + static public function toModuleType(t:Type):ModuleType { + #if (neko || eval) + return @:privateAccess Context.load("type_to_module_type", 1)(t); + #else + return null; + #end + } + + /** + Creates a type from the `ModuleType` argument. + **/ + static public function fromModuleType(mt:ModuleType):Type { + #if (neko || eval) + return @:privateAccess Context.load("module_type_to_type", 1)(mt); + #else + return null; + #end + } + #end + + /** + Resolves the field named `name` on class `c`. + + If `isStatic` is true, the classes' static fields are checked. Otherwise + the classes' member fields are checked. + + If the field is found, it is returned. Otherwise if `c` has a super + class, `findField` recursively checks that super class. Otherwise null + is returned. + + If any argument is null, the result is unspecified. + **/ + static public function findField(c:ClassType, name:String, isStatic:Bool = false):Null { + var field = (isStatic ? c.statics : c.fields).get().find(function(field) return field.name == name); + return if (field != null) field; else if (c.superClass != null) findField(c.superClass.t.get(), name, isStatic); else null; + } +}