Last active
December 3, 2018 15:23
-
-
Save skial/5764049 to your computer and use it in GitHub Desktop.
Get function name in 3 different ways, each in increasing amounts of magic.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package example.methodName; | |
| import haxe.macro.Printer; | |
| import haxe.macro.Type; | |
| import haxe.macro.Expr; | |
| import haxe.macro.Context; | |
| /** | |
| * ... | |
| * @author Skial Bainn | |
| */ | |
| class Macro { | |
| public static function build():Array<Field> { | |
| var cls = Context.getLocalClass().get(); | |
| var fields = Context.getBuildFields(); | |
| for (field in fields) { | |
| switch (field.kind) { | |
| case FFun(f): | |
| f.expr = loop( f.expr ); | |
| case _: | |
| } | |
| } | |
| return fields; | |
| } | |
| private static function loop(e:Expr):Expr { | |
| var result = e; | |
| switch (e.expr) { | |
| case EVars(vars): | |
| var nvars = []; | |
| for (v in vars) { | |
| if (v.expr != null) v.expr = loop( v.expr ); | |
| nvars.push( v ); | |
| } | |
| result.expr = EVars( nvars ); | |
| case ECall(expr, params): | |
| result.expr = ECall( loop( expr ), [for (p in params) loop( p ) ] ); | |
| case EMeta(meta, expr): | |
| var name = new Printer().printExpr( expr ); | |
| result = macro new Callback2( { method:$expr, name:$v{name} } ); | |
| case EBlock(exprs): | |
| result.expr = EBlock( [for (ex in exprs) loop(ex)] ); | |
| case _: | |
| } | |
| return result; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package example.methodName; | |
| using Reflect; | |
| @:autoBuild( example.methodName.Macro.build() ) | |
| interface MethodName {} | |
| class Main implements MethodName { | |
| public static function main() { | |
| // Using reflection | |
| var obj:Dynamic = something; | |
| trace( methodName( Main, obj ) ); | |
| obj(); | |
| // Using an abstract type | |
| var cb1:Callback1 = something; | |
| trace( cb1.name(Main) ); | |
| untyped cb1(); // Callback1 cant call `this`, compiler doesnt allow it. | |
| // Using an abstract type and the macro interface MethodName | |
| var cb2:Callback2 = @:cb something; | |
| trace( cb2.name ); | |
| cb2.call(); // Only way to call the original method, as far as I know. | |
| } | |
| public static function something():Void { | |
| trace('Hello World'); | |
| } | |
| public static function methodName(cls:Class<Dynamic>, obj:Dynamic):String { | |
| var result:String = null; | |
| if (obj.isFunction()) { | |
| for (name in Reflect.fields( cls )) { | |
| var field:Dynamic = Reflect.field( cls, name ); | |
| if (field.isFunction() && Reflect.compareMethods( obj, field )) { | |
| result = name; | |
| break; | |
| } | |
| } | |
| } | |
| return result; | |
| } | |
| } | |
| abstract Callback1(Void->Void) from Void->Void to Void->Void { | |
| public function name(cls:Class<Dynamic>):String { | |
| var result:String = null; | |
| for (name in Reflect.fields( cls )) { | |
| var field:Dynamic = Reflect.field( cls, name ); | |
| if (field.isFunction() && Reflect.compareMethods( this, field )) { | |
| result = name; | |
| break; | |
| } | |
| } | |
| return result; | |
| } | |
| public inline function new(v:Void->Void) { | |
| this = v; | |
| } | |
| } | |
| abstract Callback2( { method:Void->Void, name:String } ) { | |
| public var name(get, never):String; | |
| private inline function get_name():String { | |
| return this.name; | |
| } | |
| public inline function new(v:{method:Void->Void, name:String}) { | |
| this = v; | |
| } | |
| public inline function call():Void { | |
| return this.method(); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (function () { "use strict"; | |
| var Reflect = function() { } | |
| Reflect.__name__ = true; | |
| Reflect.field = function(o,field) { | |
| var v = null; | |
| try { | |
| v = o[field]; | |
| } catch( e ) { | |
| } | |
| return v; | |
| } | |
| Reflect.fields = function(o) { | |
| var a = []; | |
| if(o != null) { | |
| var hasOwnProperty = Object.prototype.hasOwnProperty; | |
| for( var f in o ) { | |
| if(f != "__id__" && f != "hx__closures__" && hasOwnProperty.call(o,f)) a.push(f); | |
| } | |
| } | |
| return a; | |
| } | |
| Reflect.isFunction = function(f) { | |
| return typeof(f) == "function" && !(f.__name__ || f.__ename__); | |
| } | |
| Reflect.compareMethods = function(f1,f2) { | |
| if(f1 == f2) return true; | |
| if(!Reflect.isFunction(f1) || !Reflect.isFunction(f2)) return false; | |
| return f1.scope == f2.scope && f1.method == f2.method && f1.method != null; | |
| } | |
| var example = {} | |
| example.methodName = {} | |
| example.methodName.MethodName = function() { } | |
| example.methodName.MethodName.__name__ = true; | |
| example.methodName.Main = function() { } | |
| example.methodName.Main.__name__ = true; | |
| example.methodName.Main.__interfaces__ = [example.methodName.MethodName]; | |
| example.methodName.Main.main = function() { | |
| var obj = example.methodName.Main.something; | |
| console.log(example.methodName.Main.methodName(example.methodName.Main,obj)); | |
| obj(); | |
| var cb1 = example.methodName.Main.something; | |
| console.log(example.methodName._Main.Callback1_Impl_.$name(cb1,example.methodName.Main)); | |
| cb1(); | |
| var cb2 = { method : example.methodName.Main.something, name : "something"}; | |
| console.log(cb2.name); | |
| cb2.method(); | |
| } | |
| example.methodName.Main.something = function() { | |
| console.log("Hello World"); | |
| } | |
| example.methodName.Main.methodName = function(cls,obj) { | |
| var result = null; | |
| if(Reflect.isFunction(obj)) { | |
| var _g = 0, _g1 = Reflect.fields(cls); | |
| while(_g < _g1.length) { | |
| var name = _g1[_g]; | |
| ++_g; | |
| var field = Reflect.field(cls,name); | |
| if(Reflect.isFunction(field) && Reflect.compareMethods(obj,field)) { | |
| result = name; | |
| break; | |
| } | |
| } | |
| } | |
| return result; | |
| } | |
| example.methodName._Main = {} | |
| example.methodName._Main.Callback1_Impl_ = function() { } | |
| example.methodName._Main.Callback1_Impl_.__name__ = true; | |
| example.methodName._Main.Callback1_Impl_.$name = function(this1,cls) { | |
| var result = null; | |
| var _g = 0, _g1 = Reflect.fields(cls); | |
| while(_g < _g1.length) { | |
| var name = _g1[_g]; | |
| ++_g; | |
| var field = Reflect.field(cls,name); | |
| if(Reflect.isFunction(field) && Reflect.compareMethods(this1,field)) { | |
| result = name; | |
| break; | |
| } | |
| } | |
| return result; | |
| } | |
| String.__name__ = true; | |
| Array.__name__ = true; | |
| example.methodName.Main.main(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment