diff --git a/hscript/Config.hx b/hscript/Config.hx index 9958600..30b4bec 100644 --- a/hscript/Config.hx +++ b/hscript/Config.hx @@ -26,4 +26,8 @@ class Config { public static final DISALLOW_ABSTRACT_AND_ENUM = [ ]; + + public static final DISALLOW_USING = [ + + ]; } \ No newline at end of file diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 42d85d2..baabfb7 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -28,6 +28,7 @@ */ package hscript; +import haxe.Constraints.Function; import hscript.utils.UsingHandler; import hscript.customclass.CustomClassDecl; import hscript.customclass.CustomClass; @@ -84,11 +85,11 @@ class Interp { return _customClasses.exists(name); } - private static function registerCustomClass(c:CustomClassDecl, ?as:String) { + 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) { + else if (c.pkg != null && (inCustomClass != null && inCustomClass)) { name = c.pkg.join(".") + "." + name; } _customClasses.set(name, c); @@ -920,32 +921,69 @@ class Interp { error(EInvalidOp(op)); } case ECall(e, params): - var args:Array = []; // [for (p in params) expr(p)]; - for (p in params) { - switch (Tools.expr(p)) { - case EIdent(id): - var ident = resolve(id); - if (ident is CustomClass) { - var customClass:CustomClass = cast ident; // Pass the underlying superclass if exist - args.push(customClass.superClass != null ? customClass.superClass : customClass); - } else { - args.push(ident); - } - default: - args.push(expr(p)); - } - } - switch (Tools.expr(e)) { case EField(e, f, s): - var obj = expr(e); + var obj:Dynamic = expr(e); if (obj == null) { if(s) return null; error(EInvalidAccess(f)); } - return fcall(obj, f, args); + + if (f == "bind" && Reflect.isFunction(obj)) { + var obj: Function = obj; + if (params.length == 0) { // Special case for function.bind() + return Reflect.makeVarArgs(function(ar: Array) { + return obj(); + }); + } + + // bind(_, false) => function(a1) return obj(a1, false); + // bind(false, _) => function(a2) return obj(false, a2); + // bind(_, _) => function(a1, a2) return obj(a1, a2); + + var totalNeeded = 0; + var bArgs = []; + for (p in params) { + switch (Tools.expr(p)) { + case EIdent(_): + bArgs.push(null); + totalNeeded++; + default: + bArgs.push(p); + } + } + var me = this; + // TODO: make it increment the depth? + return Reflect.makeVarArgs(function(ar: Array) { + if (ar.length < totalNeeded) + error(ECustom("Too few arguments")); // TODO: make it say like "Not enough arguments, expected a:Int" + var i = 0; + var actualArgs = [];//[for (a in bArgs) if (a != null) me.expr(a) else ar[i++]]; + for(a in bArgs) { + if(a != null) { + switch(Tools.expr(a)) { + case EIdent(id0): + var ident = resolve(id0); + if (ident is CustomClass) { + var customClass:CustomClass = cast ident; // Pass the underlying superclass if exist + actualArgs.push(customClass.superClass != null ? customClass.superClass : customClass); + } else { + actualArgs.push(ident); + } + default: + actualArgs.push(me.expr(a)); + } + } + else + actualArgs.push(ar[i++]); + } + return Reflect.callMethod(null, obj, actualArgs); + }); + } + + return fcall(obj, f, makeArgs(params)); default: - return call(null, expr(e), args); + return call(null, expr(e), makeArgs(params)); } case EIf(econd, e1, e2): return if (expr(econd) == true) expr(e1) else if (e2 == null) null else expr(e2); @@ -1324,6 +1362,26 @@ class Interp { return v; } + function makeArgs(params:Array):Array { + var args:Array = []; // [for (p in params) expr(p)]; + for (p in params) { + switch (Tools.expr(p)) { + case EIdent(id): + var ident = resolve(id); + if (ident is CustomClass) { + var customClass:CustomClass = cast ident; // Pass the underlying superclass if exist + args.push(customClass.superClass != null ? customClass.superClass : customClass); + } else { + args.push(ident); + } + default: + args.push(expr(p)); + } + } + + return args; + } + function forLoop(n:String, it:Expr, e:Expr, ?ithv:String):Void { var isKeyValue = ithv != null; var old = declared.length; @@ -1812,7 +1870,7 @@ class Interp { pkg: pkg } //customClassDecl.cacheFields(); - registerCustomClass(customClassDecl, !regAlias ? as : null); + registerCustomClass(customClassDecl, !regAlias ? as : null, _inCustomClass); if(as != null) regAlias = true; case DTypedef(td): // TODO: put this work diff --git a/hscript/Parser.hx b/hscript/Parser.hx index 4b3d167..1f4a53e 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -86,6 +86,11 @@ class Parser { **/ public var resumeErrors : Bool; + /* + package name, set when using "package;" in your script. + */ + public var packageName:String = null; + // implementation var input : String; var readPos : Int; @@ -905,6 +910,18 @@ class Parser { var tk = token(); push(tk); mk(EFunction(inf.args, inf.body, name, inf.ret, nextIsPublic, nextIsStatic, nextIsOverride, nextIsPrivate, nextIsFinal, nextIsInline),p1,pmax(inf.body)); + case "package": + // ignore package + var tk = token(); + push(tk); + packageName = ""; + if (tk == TSemicolon) + return mk(EIgnore(false)); + + var path = parsePath(); + // mk(EPackage(path.join("."))); + packageName = path.join("."); + mk(EIgnore(false)); case "import": var oldReadPos = readPos; var tk = token(); diff --git a/hscript/customclass/CustomClass.hx b/hscript/customclass/CustomClass.hx index a705a18..f1d8d00 100644 --- a/hscript/customclass/CustomClass.hx +++ b/hscript/customclass/CustomClass.hx @@ -16,24 +16,24 @@ using StringTools; * @see https://github.com/larsiusprime/polymod/tree/master/polymod/hscript/_internal */ @:access(hscript.customclass.CustomClassDecl) -class CustomClass implements IHScriptCustomAccessBehaviour{ - public var interp:Interp; +class CustomClass implements IHScriptCustomAccessBehaviour { + public var interp:Interp; - public var superClass:Dynamic; - public var superConstructor(default, null):Dynamic; + public var superClass:Dynamic; + public var superConstructor(default, null):Dynamic; - public var className(get, never):String; + public var className(get, never):String; - private var __class:CustomClassDecl; - private var _cachedSuperFields:Null> = null; + private var __class:CustomClassDecl; + private var _cachedSuperFields:Null> = null; - private var _cachedFieldDecls:Map = null; + private var _cachedFieldDecls:Map = null; private var _cachedFunctionDecls:Map = null; private var _cachedVarDecls:Map = null; public var __allowSetGet:Bool = true; - private function get_className():String { + private function get_className():String { var name = ""; if (__class.pkg != null) { name += __class.pkg.join("."); @@ -42,45 +42,46 @@ class CustomClass implements IHScriptCustomAccessBehaviour{ return name; } - public function new(__class:CustomClassDecl, args:Array, ?extendFieldDecl:Map, ?ogInterp:Interp) { - this.__class = __class; - this.interp = new Interp(this); + public function new(__class:CustomClassDecl, args:Array, ?extendFieldDecl:Map, ?ogInterp:Interp) { + this.__class = __class; + this.interp = new Interp(this); - if(ogInterp != null && ogInterp.importFailedCallback != null && ogInterp.errorHandler != null) { + if (ogInterp != null && ogInterp.importFailedCallback != null && ogInterp.errorHandler != null) { interp.importFailedCallback = ogInterp.importFailedCallback; interp.errorHandler = ogInterp.errorHandler; - interp.allowStaticVariables = ogInterp.allowStaticVariables; - interp.staticVariables = ogInterp.staticVariables; + interp.allowStaticVariables = ogInterp.allowStaticVariables; + interp.staticVariables = ogInterp.staticVariables; } - buildImports(); + buildImports(); buildUsings(); - if(extendFieldDecl != null) + if (extendFieldDecl != null) _cachedSuperFields = extendFieldDecl; - buildClass(); + buildClass(); - if(hasFunction('new')) { - buildSuperConstructor(); - callFunction('new', args); - if(this.superClass == null && this.__class.classDecl.extend != null) - this.interp.error(ECustom("super() not called")); - } - else if(__class.classDecl.extend != null) { - createSuperClass(args); - } - } + if (hasFunction('new')) { + buildSuperConstructor(); + callFunction('new', args); + if (this.superClass == null && this.__class.classDecl.extend != null) + this.interp.error(ECustom("super() not called")); + } else if (__class.classDecl.extend != null) { + createSuperClass(args); + } + } - function buildClass() { - _cachedFieldDecls = []; + function buildClass() { + _cachedFieldDecls = []; _cachedFunctionDecls = []; _cachedVarDecls = []; - if(_cachedSuperFields == null) _cachedSuperFields = []; + if (_cachedSuperFields == null) + _cachedSuperFields = []; - for (f in __class.classDecl.fields) { - if(f.access.contains(AStatic)) continue; // Skip static field. It's handled by CustomClassDecl.hx + for (f in __class.classDecl.fields) { + if (f.access.contains(AStatic)) + continue; // Skip static field. It's handled by CustomClassDecl.hx _cachedFieldDecls.set(f.name, f); switch (f.kind) { case KFunction(fn): @@ -107,21 +108,21 @@ class CustomClass implements IHScriptCustomAccessBehaviour{ } } - if(!_cachedSuperFields.empty()) { + if (!_cachedSuperFields.empty()) { for (f => v in _cachedSuperFields) { this.hset(f, v); } _cachedSuperFields.clear(); } - } + } - function buildSuperConstructor() { - superConstructor = Reflect.makeVarArgs(function(args:Array) { + function buildSuperConstructor() { + superConstructor = Reflect.makeVarArgs(function(args:Array) { createSuperClass(args); }); - } + } - private function createSuperClass(args:Array = null) { + private function createSuperClass(args:Array = null) { if (args == null) args = []; @@ -138,28 +139,28 @@ class CustomClass implements IHScriptCustomAccessBehaviour{ if (c == null) { interp.error(ECustom("could not resolve super class: " + extendString)); } - if(_cachedSuperFields != null) { + if (_cachedSuperFields != null) { Reflect.setField(c, "__cachedFields", _cachedSuperFields); // Static field } - + superClass = Type.createInstance(c, args); superClass.__customClass = this; superClass.__real_fields = Type.getInstanceFields(c); } } - function buildImports() { - // TODO: implement Alias imports + function buildImports() { + // TODO: implement Alias imports var i:Int = 0; - for(_import in __class.imports) { + for (_import in __class.imports) { var importedClass = _import.fullPath; var importAlias = _import.as; - - if(Interp.customClassExist(importedClass) && this.interp.importFailedCallback != null) { + + if (Interp.customClassExist(importedClass) && this.interp.importFailedCallback != null) { this.interp.importFailedCallback(importedClass.split("."), importAlias); continue; } - + #if hscriptPos var e:Expr = { e: ExprDef.EImport(importedClass, importAlias), @@ -174,10 +175,10 @@ class CustomClass implements IHScriptCustomAccessBehaviour{ this.interp.expr(e); i++; } - } + } - function buildUsings() { - for(us in __class.usings) { + inline function buildUsings() { + for (us in __class.usings) { @:privateAccess this.interp.useUsing(us); } } @@ -222,46 +223,47 @@ class CustomClass implements IHScriptCustomAccessBehaviour{ // Field check - private function hasField(name:String):Bool { + private function hasField(name:String):Bool { return _cachedFieldDecls != null ? _cachedFieldDecls.exists(name) : false; } - private function getField(name:String):FieldDecl { + private function getField(name:String):FieldDecl { return _cachedFieldDecls != null ? _cachedFieldDecls.get(name) : null; } - private function hasVar(name:String):Bool { + private function hasVar(name:String):Bool { return _cachedVarDecls != null ? _cachedVarDecls.exists(name) : false; } - private function getVar(name:String):VarDecl { - return _cachedVarDecls != null ? _cachedVarDecls.get(name): null; + private function getVar(name:String):VarDecl { + return _cachedVarDecls != null ? _cachedVarDecls.get(name) : null; } - private function hasFunction(name:String):Bool { - return _cachedFunctionDecls != null ? _cachedFunctionDecls.exists(name) : false; - } + private function hasFunction(name:String):Bool { + return _cachedFunctionDecls != null ? _cachedFunctionDecls.exists(name) : false; + } - private function getFunction(name:String):Function { + private function getFunction(name:String):Function { var fn = this.interp.variables.get(name); return Reflect.isFunction(fn) ? fn : null; - } + } // SuperClass field check - private function cacheSuperField(name:String, value:Dynamic) { - if(_cachedSuperFields != null) { + private function cacheSuperField(name:String, value:Dynamic) { + if (_cachedSuperFields != null) { _cachedSuperFields.set(name, value); } } - var __superClassFieldList:Array = null; + var __superClassFieldList:Array = null; public function superHasField(name:String):Bool { - if(superClass == null) return false; + if (superClass == null) + return false; // Reflect.hasField(this, name) is REALLY expensive so we use a cache. - if(__superClassFieldList == null) { + if (__superClassFieldList == null) { __superClassFieldList = Reflect.fields(superClass).concat(Type.getInstanceFields(Type.getClass(superClass))); } @@ -279,68 +281,63 @@ class CustomClass implements IHScriptCustomAccessBehaviour{ } } - // Access fields + // Access fields - public function hget(name:String):Dynamic { - return resolveField(name); - } + public function hget(name:String):Dynamic { + return resolveField(name); + } - public function hset(name:String, val:Dynamic):Dynamic { - switch (name) { - default: - if (hasVar(name)) { - if(__allowSetGet && hasFunction('set_${name}')) - return __callSetter(name, val); + public function hset(name:String, val:Dynamic):Dynamic { + switch (name) { + default: + if (hasVar(name)) { + if (__allowSetGet && hasFunction('set_${name}')) + return __callSetter(name, val); - this.interp.variables.set(name, val); - } - else if (this.superClass != null) { - if(Type.getClass(this.superClass) == null) { - // Anonymous structure - if(Reflect.hasField(this.superClass, name)) { - Reflect.setField(this.superClass, name, val); - } - return val; - } - else if (this.superClass is CustomClass) { + this.interp.variables.set(name, val); + } else if (this.superClass != null) { + if (Type.getClass(this.superClass) == null) { + // Anonymous structure + if (Reflect.hasField(this.superClass, name)) { + Reflect.setField(this.superClass, name, val); + } + return val; + } else if (this.superClass is CustomClass) { var superCustomClass:CustomClass = cast(this.superClass, CustomClass); try { - superCustomClass.__allowSetGet = this.__allowSetGet; + superCustomClass.__allowSetGet = this.__allowSetGet; return superCustomClass.hset(name, val); } catch (e:Dynamic) {} - } - else if(superHasField(name)){ - if(__allowSetGet) - Reflect.setProperty(this.superClass, name, val); - else - Reflect.setField(this.superClass, name, val); - } - else { + } else if (superHasField(name)) { + if (__allowSetGet) + Reflect.setProperty(this.superClass, name, val); + else + Reflect.setField(this.superClass, name, val); + } else { throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'"; - } - } - else { + } + } else { throw "field '" + name + "' does not exist in custom class '" + this.className + "'"; } - } - return val; - } + } + return val; + } - public function __callGetter(name:String):Dynamic { - __allowSetGet = false; - var r = callFunction('get_${name}'); - __allowSetGet = true; - return r; - } + public function __callGetter(name:String):Dynamic { + __allowSetGet = false; + var r = callFunction('get_${name}'); + __allowSetGet = true; + return r; + } public function __callSetter(name:String, val:Dynamic):Dynamic { - __allowSetGet = false; - var r = callFunction('set_${name}', [val]); - __allowSetGet = true; - return r; - } - + __allowSetGet = false; + var r = callFunction('set_${name}', [val]); + __allowSetGet = true; + return r; + } + private function resolveField(name:String):Dynamic { switch (name) { case "superClass": diff --git a/hscript/macros/UsingMacro.hx b/hscript/macros/UsingMacro.hx index 3611188..3f1ac8a 100644 --- a/hscript/macros/UsingMacro.hx +++ b/hscript/macros/UsingMacro.hx @@ -13,7 +13,8 @@ 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! + * you can make classes be able to be used specifing the classes/packages on Config.hx! + * or implementing the interface `hscript.utils.UsingClass` * ```haxe * public static final ALLOWED_USING = ["my.pack.VeryNiceTools"]; * ``` @@ -21,11 +22,12 @@ using haxe.macro.Tools; * Usage: * * ```haxe + * package my.pack; * // @: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 {} + * class VeryNiceTools implements hscript.utils.UsingClass {} * ``` * * @author NeeEoo @@ -49,10 +51,12 @@ class UsingMacro { var cls: haxe.macro.Type.ClassType = Context.getLocalClass().get(); var fields = Context.getBuildFields(); + var key = cls.module; + var fkey = cls.module + "." + cls.name; 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) + if(cls.meta.get().find(function(m) return m.name == ':usingProcessed') != null) return fields; + if (Config.DISALLOW_USING.contains(key) || Config.DISALLOW_USING.contains(fkey) || Config.DISALLOW_USING.contains(packName)) return fields; var entryField = cls.meta.get().find(function(m) return m.name == ':usableEntry'); @@ -71,24 +75,16 @@ class UsingMacro { 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; + // It's called "static extensions" for some reason if(!field.access.contains(AStatic)) continue; - // if (field.isPublic == false) - // continue; - switch (field.kind) { default: case FFun(f): @@ -120,21 +116,23 @@ class UsingMacro { "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? + // MIGHT CRASH COMPILATION? YEAH, IT CRASH ON TYPE PARAMETERS if (!onlyBasic && valueType == null) { - var rtype = type.toType(); + var rtype:haxe.macro.Type = null; - switch (rtype) { - case TInst(t, []): - valueType = "TClass(" + t.toString() + ")"; - default: + try {rtype = type.toType();} catch(e) {} + + if(rtype != null) { + switch (rtype) { + case TInst(t, []): + valueType = "TClass(" + t.toString() + ")"; + default: + } } }