diff --git a/.gitignore b/.gitignore index 68ab983..8c29a0e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /hscript.swf /release.zip - +dump/* tests/bin/* \ No newline at end of file diff --git a/extraParams.hxml b/extraParams.hxml index 18d9021..a998086 100644 --- a/extraParams.hxml +++ b/extraParams.hxml @@ -1,3 +1,4 @@ --macro keep('IntIterator') ---macro hscript.macros.UsingHandler.init() +--macro hscript.macros.AbstractHandler.init() +--macro hscript.macros.UsingMacro.init() --macro hscript.macros.ClassExtendMacro.init() diff --git a/hscript/Bytes.hx b/hscript/Bytes.hx index 5bec616..abad197 100644 --- a/hscript/Bytes.hx +++ b/hscript/Bytes.hx @@ -131,6 +131,10 @@ class Bytes { // TODO case ERedirect(_, _): // TODO + case EUsing(name): + // TODO + case EEnum(name, fields): + // TODO case EConst(c): doEncodeConst(c); case EIdent(v): diff --git a/hscript/Config.hx b/hscript/Config.hx index 6b51aec..9958600 100644 --- a/hscript/Config.hx +++ b/hscript/Config.hx @@ -12,6 +12,11 @@ class Config { "openfl.display.BlendMode", ]; + // Runs support for using in specific classes. + public static final ALLOWED_USING = [ + + ]; + // Incase any of your files fail // These are the module names public static final DISALLOW_CUSTOM_CLASSES = [ diff --git a/hscript/Expr.hx b/hscript/Expr.hx index 5190cdd..e2a7430 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -83,6 +83,8 @@ enum Expr { EImport( c : String, ?asname:String ); EClass( name:String, fields:Array, ?extend:String, interfaces:Array, ?isFinal:Bool, ?isPrivate:Bool ); ERedirect( name:String, className:String, ?cl:Class ); + EUsing(name:String); + EEnum(name:String, fields:Array); } @:structInit @@ -162,6 +164,7 @@ enum Error { enum ModuleDecl { DPackage( path : Array ); DImport( path : Array, ?everything : Bool, ?asname:String ); + DUsing( path : Array); DClass( c : ClassDecl ); DTypedef( c : TypeDecl ); } @@ -218,3 +221,8 @@ typedef VarDecl = { var expr : Null; var type : Null; } + +enum EnumType { + ESimple(name: String); + EConstructor(name: String, args: Array); +} diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 1fe23f3..42d85d2 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -28,13 +28,16 @@ */ package hscript; +import hscript.utils.UsingHandler; import hscript.customclass.CustomClassDecl; import hscript.customclass.CustomClass; +import hscript.utils.UsingEntry; import haxe.CallStack; import hscript.utils.UnsafeReflect; import haxe.PosInfos; import hscript.Expr; import haxe.Constraints.IMap; +import Type.ValueType; using StringTools; @@ -1212,6 +1215,53 @@ class Interp { return val; case ECheckType(e, _): return expr(e); + case EUsing(name): + useUsing(name); + case EEnum(enumName, fields): + var obj = {}; + for (index => field in fields) { + switch (field) { + case ESimple(name): + Reflect.setField(obj, name, new Tools.EnumValue(enumName, name, index, null)); + case EConstructor(name, params): + var hasOpt = false, minParams = 0; + for (p in params) + if (p.opt) + hasOpt = true; + else + minParams++; + var f = function(args: Array) { + if (((args == null) ? 0 : args.length) != params.length) { + if (args.length < minParams) { + var str = "Invalid number of parameters. Got " + args.length + ", required " + minParams; + if (enumName != null) + str += " for enum '" + enumName + "'"; + error(ECustom(str)); + } + // make sure mandatory args are forced + var args2 = []; + var extraParams = args.length - minParams; + var pos = 0; + for (p in params) + if (p.opt) { + if (extraParams > 0) { + args2.push(args[pos++]); + extraParams--; + } else + args2.push(null); + } else + args2.push(args[pos++]); + args = args2; + } + return new Tools.EnumValue(enumName, name, index, args); + }; + var f = Reflect.makeVarArgs(f); + + Reflect.setField(obj, name, f); + } + } + + variables.set(enumName, obj); } return null; } @@ -1453,7 +1503,116 @@ class Interp { return v; } + /** + * Meant for people to add their own usings. + **/ + function registerUsingLocal(name: String, call: UsingCall): UsingEntry { + var entry = new UsingEntry(name, call); + usings.push(entry); + return entry; + } + + function useUsing(name:String) { + for (us in UsingHandler.usingEntries) { + if (us.name == name) { + if (usings.indexOf(us) == -1) + usings.push(us); + return; + } + } + + var cls = Tools.getClass(name); + if (cls != null) { + var entry:UsingEntry = null; + + var fieldName = '_HX_USING__' + StringTools.replace(name, ".", "_"); + if (Reflect.hasField(cls, fieldName)) { + var fields = Reflect.field(cls, fieldName); + if (fields == null) + return; + + entry = new UsingEntry(name, function(o:Dynamic, f:String, args:Array):Dynamic { + if (!fields.exists(f)) + return null; + var type:ValueType = Type.typeof(o); + var valueType:ValueType = fields.get(f); + + // If we figure out a better way to get the types as the real ValueType, we can use this instead + // if (Type.enumEq(valueType, type)) + // return Reflect.callMethod(cls, Reflect.field(cls, f), [o].concat(args)); + + var canCall = valueType == null ? true : switch (valueType) { + case TEnum(null): + type.match(TEnum(_)); + case TClass(null): + type.match(TClass(_)); + case TClass(IMap): // if we don't check maps like this, it just doesn't work + type.match(TClass(IMap) | TClass(haxe.ds.ObjectMap) | TClass(haxe.ds.StringMap) | TClass(haxe.ds.IntMap) | TClass(haxe.ds.EnumValueMap)); + default: + Type.enumEq(type, valueType); + } + + return canCall ? Reflect.callMethod(cls, Reflect.field(cls, f), [o].concat(args)) : null; + }); + + #if debug + trace("Registered macro based using entry for " + name); + #end + + UsingHandler.usingEntries.push(entry); + usings.push(entry); + return; + } + else { + // Use reflection to generate the using entry + entry = new UsingEntry(name, function(o:Dynamic, f:String, args:Array):Dynamic { + if (!Reflect.hasField(cls, f)) + return null; + var field = Reflect.field(cls, f); + if (!Reflect.isFunction(field)) + return null; + + // invalid if the function has no arguments + var totalArgs = Tools.argCount(field); + if (totalArgs == 0) + return null; + + return Reflect.callMethod(cls, field, [o].concat(args)); + }); + + #if debug + trace("Registered reflection based using entry for " + name); + #end + } + + UsingHandler.usingEntries.push(entry); + usings.push(entry); + return; + } + error(ECustom("Unknown using class " + name)); + } + + /** + * List of components that allow using static methods on objects. + * This only works if you do + * ```haxe + * var result = "Hello ".trim(); + * ``` + * and not + * ```haxe + * var trim = "Hello ".trim; + * var result = trim(); + * ``` + */ + var usings: Array = []; + function fcall(o:Dynamic, f:String, args:Array):Dynamic { + for(_us in usings) { + var v = _us.call(o, f, args); + if(v != null) + return v; + } + // Custom logic to handle super calls to prevent infinite recursion if(_inCustomClass) { // Force call super function. @@ -1548,7 +1707,7 @@ class Interp { return result; } catch (e) { - error(ECustom('Script threw an exception: \n${fun}')); + error(ECustom('Custom Class threw an exception: \n${fun}')); // Restore the local scope. this.locals = capturedLocals; @@ -1602,6 +1761,8 @@ class Interp { public function registerModule(module:Array, ?as:String) { var pkg:Array = null; var imports:Map = []; + var usings:Array = []; + var regAlias:Bool = false; for (decl in module) { switch (decl) { case DPackage(path): @@ -1619,6 +1780,8 @@ class Interp { as: asname } imports.set(hasAlias ? asname : importedClass.name, importedClass); + case DUsing(path): + usings.push(path.join(".")); case DClass(c): var extend = c.extend; if (extend != null) { @@ -1645,10 +1808,12 @@ class Interp { var customClassDecl:CustomClassDecl = { classDecl: classDecl, imports: imports, + usings: usings, pkg: pkg } - customClassDecl.cacheFields(); - registerCustomClass(customClassDecl, as); + //customClassDecl.cacheFields(); + registerCustomClass(customClassDecl, !regAlias ? as : null); + if(as != null) regAlias = true; case DTypedef(td): // TODO: put this work } diff --git a/hscript/Parser.hx b/hscript/Parser.hx index e8462f1..4b3d167 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -234,6 +234,16 @@ class Parser { } } + function extractIdent(tk: Token): String { + switch (tk) { + case TId(id): + return id; + default: + unexpected(tk); + return null; + } + } + inline function expr(e:Expr):#if hscriptPos ExprDef #else Expr #end { #if hscriptPos return e.e; @@ -272,7 +282,7 @@ class Parser { function isBlock(e:Expr):Bool { if( e == null ) return false; return switch( expr(e) ) { - case EBlock(_), EObject(_), ESwitch(_): true; + case EBlock(_), EObject(_), ESwitch(_), EEnum(_, _): true; case EFunction(_,e,_,_,_,_): isBlock(e); case EClass(_,e,_,_): true; case EVar(_, t, e, _,_): e != null ? isBlock(e) : t != null ? t.match(CTAnon(_)) : false; @@ -1069,6 +1079,50 @@ class Parser { error(ECustom("Typedef, unknown type " + t), tokenMin, tokenMax); null; } + case "using": + var path = parsePath(); + mk(EUsing(path.join("."))); + case "enum": + var name = getIdent(); + + ensure(TBrOpen); + + var fields = []; + + var currentName = ""; + var currentArgs: Array = null; + while (true) { + var tk = token(); + switch (tk) { + case TBrClose: + break; + case TSemicolon | TComma: + if (currentName == "") + continue; + + if (currentArgs != null && currentArgs.length > 0) { + fields.push(EnumType.EConstructor(currentName, currentArgs)); + currentArgs = null; + } else { + fields.push(EnumType.ESimple(currentName)); + } + currentName = ""; + case TPOpen: + if (currentArgs != null) { + error(ECustom("Cannot have multiple argument lists in one enum constructor"), tokenMin, tokenMax); + break; + } + currentArgs = parseFunctionArgs(); + default: + if (currentName != "") { + error(ECustom("Expected comma or semicolon"), tokenMin, tokenMax); + break; + } + var name = extractIdent(tk); + currentName = name; + } + } + mk(EEnum(name, fields)); case "return": var tk = token(); push(tk); @@ -1577,6 +1631,10 @@ class Parser { } ensure(TSemicolon); return DImport(path, star, asname); + case "using": + var path = parsePath(); + ensure(TSemicolon); + return DUsing(path); case "class": var name = getIdent(); var params = parseParams(); diff --git a/hscript/Printer.hx b/hscript/Printer.hx index 63491ff..b59ff83 100644 --- a/hscript/Printer.hx +++ b/hscript/Printer.hx @@ -347,9 +347,43 @@ class Printer { add(" : "); addType(t); add(")"); + case EUsing(name): + add("using "); + add(name); + case EEnum(name, params): + if (params.length == 0) { + add("enum " + name + " {}"); + return; + } + add("enum " + name + " {\n"); + tabs += "\t"; + for (p in params) { + add(tabs); + switch p { + case EConstructor(name, args): + add(name); + add("("); + for (a in args) + addArgument(a); + add(")"); + case ESimple(name): + add(name); + } + add(";\n"); + } + tabs = tabs.substr(1); + add(tabs); + add("}"); } } + function addArgument(a: Argument) { + if (a.opt) + add("?"); + add(a.name); + addType(a.t); + } + public static function toString( e : Expr ):String { return new Printer().exprToString(e); } diff --git a/hscript/Tools.hx b/hscript/Tools.hx index 0d9e806..5ea4e64 100644 --- a/hscript/Tools.hx +++ b/hscript/Tools.hx @@ -123,6 +123,7 @@ class Tools { return name.substr(ll + 1); } + // TODO: maybe use this function for import since also check for innerclasses public static function getClass(name: String): Dynamic { var c: Dynamic = Type.resolveClass(name); if (c == null) // try importing as enum @@ -144,4 +145,70 @@ class Tools { return c; } + public static function isIterable(v: Dynamic): Bool { + // TODO: test for php and lua, they might have issues with this check + return v != null && v.iterator != null; + } + + /** + * DO NOT USE INLINE ON THIS FUNCTION + **/ + public static function argCount(func: haxe.Constraints.Function): Int { + #if cpp + return untyped __cpp__("{0}->__ArgCount()", func); + #elseif js + return untyped js.Syntax.code("{0}.length", func); + #else + return -1; + #end + } + +} + +/** + * @see https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/hscript/Tools.hx#L217 + */ +class EnumValue { + public var enumName: String; + public var name: String; + public var index: Int; + public var args: Array; + + public function new(enumName: String, name: String, index: Int, ?args: Array) { + this.enumName = enumName; + this.name = name; + this.index = index; + this.args = args; + } + + public function toString(): String { + if (args == null) + return enumName + "." + name; + return enumName + "." + name + "(" + [for (arg in args) arg].join(", ") + ")"; + } + + public inline function getEnumName(): String + return this.enumName; + + public inline function getConstructorArgs(): Array + return this.args != null ? this.args : []; + + public function compare(other: EnumValue): Bool { + if (enumName != other.enumName || name != other.name) + return false; + if (args == null && other.args == null) + return true; + if (args == null || other.args == null) + return false; + if (args.length != other.args.length) + return false; + + for (i in 0...args.length) { + // TODO: allow deep comparison, like arrays + if (args[i] != other.args[i]) + return false; + } + + return true; + } } \ No newline at end of file diff --git a/hscript/customclass/CustomClass.hx b/hscript/customclass/CustomClass.hx index 1ec813a..a705a18 100644 --- a/hscript/customclass/CustomClass.hx +++ b/hscript/customclass/CustomClass.hx @@ -54,6 +54,7 @@ class CustomClass implements IHScriptCustomAccessBehaviour{ } buildImports(); + buildUsings(); if(extendFieldDecl != null) _cachedSuperFields = extendFieldDecl; @@ -175,6 +176,12 @@ class CustomClass implements IHScriptCustomAccessBehaviour{ } } + function buildUsings() { + for(us in __class.usings) { + @:privateAccess this.interp.useUsing(us); + } + } + public function callFunction(name:String, args:Array = null):Dynamic { var r:Dynamic = null; diff --git a/hscript/customclass/CustomClassDecl.hx b/hscript/customclass/CustomClassDecl.hx index a92e1ee..eb06e62 100644 --- a/hscript/customclass/CustomClassDecl.hx +++ b/hscript/customclass/CustomClassDecl.hx @@ -6,10 +6,12 @@ import hscript.Expr.FieldDecl; import hscript.Expr.VarDecl; import hscript.Expr.FunctionDecl; +@:access(hscript.Interp) @:structInit class CustomClassDecl implements IHScriptCustomAccessBehaviour { public var classDecl:Expr.ClassDecl; //This holds the class instantiation info public var imports:Map; + public var usings:Array; public var pkg:Null> = null; public var staticInterp:Interp = new Interp(); @@ -20,7 +22,17 @@ class CustomClassDecl implements IHScriptCustomAccessBehaviour { public var __allowSetGet:Bool = true; - public function cacheFields() { + public function new(classDecl:Expr.ClassDecl, imports:Map, usings:Array, pkg:Null>) { + this.classDecl = classDecl; + this.imports = imports; + this.usings = usings; + this.pkg = pkg; + + cacheFields(); + processUsings(); + } + + function cacheFields() { for (f in classDecl.fields) { if (f.access.contains(AStatic)) { _cachedStaticFields.set(f.name, f); @@ -51,6 +63,12 @@ class CustomClassDecl implements IHScriptCustomAccessBehaviour { } } + function processUsings() { + for(us in usings) { + this.staticInterp.useUsing(us); + } + } + public function callFunction(name:String, ?args:Array):Dynamic { var func:Function = getFunction(name); diff --git a/hscript/macros/UsingHandler.hx b/hscript/macros/AbstractHandler.hx similarity index 97% rename from hscript/macros/UsingHandler.hx rename to hscript/macros/AbstractHandler.hx index a85825c..58d3d05 100644 --- a/hscript/macros/UsingHandler.hx +++ b/hscript/macros/AbstractHandler.hx @@ -10,12 +10,12 @@ import haxe.macro.Compiler; using StringTools; -class UsingHandler { +class AbstractHandler { public static function init() { #if !display if(Context.defined("display")) return; for(apply in Config.ALLOWED_ABSTRACT_AND_ENUM) { - Compiler.addGlobalMetadata(apply, '@:build(hscript.macros.UsingHandler.build())'); + Compiler.addGlobalMetadata(apply, '@:build(hscript.macros.AbstractHandler.build())'); } #end } diff --git a/hscript/macros/UsingMacro.hx b/hscript/macros/UsingMacro.hx new file mode 100644 index 0000000..3611188 --- /dev/null +++ b/hscript/macros/UsingMacro.hx @@ -0,0 +1,175 @@ +package hscript.macros; + +#if macro +import Type.ValueType; +import haxe.macro.Compiler; +import haxe.macro.Context; +import haxe.macro.Expr; + +using Lambda; +using StringTools; +using haxe.macro.Tools; + +/** + * Macro used for the `using Class;` keyword + * + * you can make classes be able to be used specifing the class/classes on Config.hx! + * ```haxe + * public static final ALLOWED_USING = ["my.pack.VeryNiceTools"]; + * ``` + * + * Usage: + * + * ```haxe + * // @:usableEntry() // optional + * // @:usableEntry(forceAny) // optional // forces the class to be called with any type + * // @:usableEntry(onlyBasic) // optional // only basic types will be allowed + * // @:usableEntry(onlyBasic, forceAny) // optional // only basic types will be allowed, and the class will be called with any type + * class VeryNiceTools {} + * ``` + * + * @author NeeEoo + * @see https://github.com/pisayesiwsi/hscript-iris/blob/master/crowplexus/iris/macro/UsingMacro.macro.hx +**/ +class UsingMacro { + public static inline final USING_PREFIX = "_HX_USING__"; + + public static var unallowedMetas:Array = [":noUsing", ":noUse"]; + + public static function init() { + #if !display + if(Context.defined("display")) return; + for(apply in Config.ALLOWED_USING) { + Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.UsingMacro.build())"); + } + #end + } + + public static function build() { + var cls: haxe.macro.Type.ClassType = Context.getLocalClass().get(); + var fields = Context.getBuildFields(); + + var packName = (cls.pack.length > 0 ? cls.pack.join(".") + "." : "") + cls.name; + + var alreadyProcessed_metadata = cls.meta.get().find(function(m) return m.name == ':usingProcessed'); + if (alreadyProcessed_metadata != null) + return fields; + + var entryField = cls.meta.get().find(function(m) return m.name == ':usableEntry'); + var hasParams = entryField != null && entryField.params != null; + var forceAny = false; + var onlyBasic = false; + if (hasParams) { + for (i in 0...entryField.params.length) { + if (entryField.params[i].expr.match(EConst(CIdent("onlyBasic")))) + onlyBasic = true; + if (entryField.params[i].expr.match(EConst(CIdent("forceAny")))) + forceAny = true; + } + } + + var data: Array> = []; + + for (field in fields) { + /* + if (field.meta.find(function(m) return m.name == ':noUsing') != null) + continue; + if (field.meta.find(function(m) return m.name == ':noUse') != null) + continue; + */ + // functions marked with @:noUsing won't be able to be used by variables + // also if you want it to be usable in source, but not in the script, use @:noUse + for(m in field.meta) + if(unallowedMetas.contains(m.name)) + continue; + + if(!field.access.contains(AStatic)) + continue; + + // if (field.isPublic == false) + // continue; + + switch (field.kind) { + default: + case FFun(f): + if (f.args.length == 0) + continue; + var arg = f.args[0]; + if (arg.type == null) + continue; + var type = arg.type; + + var valueType: String = switch (type) { + case TPath({name: "Int", pack: []}): + "TInt"; + case TPath({name: "Float", pack: []}): + "TFloat"; + case TPath({name: "Single", pack: []}): + "TFloat"; + case TPath({name: "String", pack: []}): + "TClass(String)"; + case TPath({name: "Bool", pack: []}): + "TBool"; + case TPath({name: "Array", pack: []}): + "TClass(Array)"; + case TPath({name: "Map", pack: []}): + "TClass(haxe.Constraints.IMap)"; + case TPath({name: "Dynamic", pack: []}): + null; + case TPath({name: "Class", pack: []}): + "TClass(null)"; // this feels wrong + case TPath({name: "Enum", pack: []}): + "TEnum(null)"; + case TPath({name: "Null", pack: []}): + "TUnknown"; + // case TPath({name: "Void", pack: []}): "ValueType.TVoid"; + default: + null; // null acts as a wildcard + } + + // MIGHT CRASH COMPILATION? + if (!onlyBasic && valueType == null) { + var rtype = type.toType(); + + switch (rtype) { + case TInst(t, []): + valueType = "TClass(" + t.toString() + ")"; + default: + } + } + + if (forceAny) { + valueType = null; + } + + data.push([field.name, valueType]); + } + } + + if(data.length == 0) + return fields; + + fields.push({ + name: '$USING_PREFIX${packName.replace(".", "_")}', + access: [APrivate, AStatic], + kind: FVar(macro : Map, { + var arr: Array = []; + for (i in data) + if (i[1] != null) + arr.push(macro $v{i[0]} => ${Context.parse("Type.ValueType." + i[1], Context.currentPos())}); + else + arr.push(macro $v{i[0]} => null); + macro $a{arr}; + }), + pos: cls.pos, + }); + + //var printer = new haxe.macro.Printer(); + //trace(printer.printField(fields[fields.length - 1])); + + cls.meta.add(':usingProcessed', [], cls.pos); + + return fields; + } +} +#end diff --git a/hscript/utils/UsingClass.hx b/hscript/utils/UsingClass.hx new file mode 100644 index 0000000..3ffda87 --- /dev/null +++ b/hscript/utils/UsingClass.hx @@ -0,0 +1,9 @@ +package hscript.utils; + +/** + * This is used to mark classes that can be used with the `using` keyword. + * You can also add @:usableEntry to your class. + * If you wanna force the class to be called with any type, you can add @:usableEntry(forceAny) +**/ +@:autoBuild(hscript.macros.UsingMacro.build()) +interface UsingClass {} \ No newline at end of file diff --git a/hscript/utils/UsingEntry.hx b/hscript/utils/UsingEntry.hx new file mode 100644 index 0000000..6666040 --- /dev/null +++ b/hscript/utils/UsingEntry.hx @@ -0,0 +1,17 @@ +package hscript.utils; + +typedef UsingCall = (o: Dynamic, f: String, args: Array) -> Dynamic; + +/** + * Code based on Using system from "hscript-iris" + * @see https://github.com/pisayesiwsi/hscript-iris/blob/master/crowplexus/iris/utils/UsingEntry.hx + */ +class UsingEntry { + public var name: String; + public var call: UsingCall; + + public function new(name: String, call: UsingCall) { + this.name = name; + this.call = call; + } +} \ No newline at end of file diff --git a/hscript/utils/UsingHandler.hx b/hscript/utils/UsingHandler.hx new file mode 100644 index 0000000..ef12469 --- /dev/null +++ b/hscript/utils/UsingHandler.hx @@ -0,0 +1,45 @@ +package hscript.utils; + +import hscript.utils.UsingEntry.UsingCall; +import StringTools; +import Lambda; + +class UsingHandler { + public static var usingEntries:Array = [ + new UsingEntry("StringTools", function(o: Dynamic, f: String, args: Array): Dynamic { + if (f == "isEof") // has @:noUsing + return null; + switch (Type.typeof(o)) { + case TInt if (f == "hex"): + return StringTools.hex(o, args[0]); + case TClass(String): + if (Reflect.hasField(StringTools, f)) { + var field = Reflect.field(StringTools, f); + if (Reflect.isFunction(field)) { + return Reflect.callMethod(StringTools, field, [o].concat(args)); + } + } + default: + } + return null; + }), + new UsingEntry("Lambda", function(o: Dynamic, f: String, args: Array): Dynamic { + if (Tools.isIterable(o)) { + // TODO: Check if the values are Iterable + if (Reflect.hasField(Lambda, f)) { + var field = Reflect.field(Lambda, f); + if (Reflect.isFunction(field)) { + return Reflect.callMethod(Lambda, field, [o].concat(args)); + } + } + } + return null; + }), + ]; + + public static function registerUsingGlobal(name: String, call:UsingCall):UsingEntry { + var entry = new UsingEntry(name, call); + usingEntries.push(entry); + return entry; + } +} \ No newline at end of file