".bind" support + "package;"

This commit is contained in:
Jaime Humberto Macias Bustamante
2025-03-04 20:26:32 -07:00
parent 9833c61f78
commit e6b5d6af49
5 changed files with 230 additions and 156 deletions
+4
View File
@@ -26,4 +26,8 @@ class Config {
public static final DISALLOW_ABSTRACT_AND_ENUM = [
];
public static final DISALLOW_USING = [
];
}
+80 -22
View File
@@ -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<Dynamic> = []; // [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<Dynamic>) {
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<Dynamic>) {
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<Expr>):Array<Dynamic> {
var args:Array<Dynamic> = []; // [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
+17
View File
@@ -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();
+110 -113
View File
@@ -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<Map<String, Dynamic>> = null;
private var __class:CustomClassDecl;
private var _cachedSuperFields:Null<Map<String, Dynamic>> = null;
private var _cachedFieldDecls:Map<String, FieldDecl> = null;
private var _cachedFieldDecls:Map<String, FieldDecl> = null;
private var _cachedFunctionDecls:Map<String, FunctionDecl> = null;
private var _cachedVarDecls:Map<String, VarDecl> = 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<Dynamic>, ?extendFieldDecl:Map<String, Dynamic>, ?ogInterp:Interp) {
this.__class = __class;
this.interp = new Interp(this);
public function new(__class:CustomClassDecl, args:Array<Dynamic>, ?extendFieldDecl:Map<String, Dynamic>, ?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<Dynamic>) {
function buildSuperConstructor() {
superConstructor = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
createSuperClass(args);
});
}
}
private function createSuperClass(args:Array<Dynamic> = null) {
private function createSuperClass(args:Array<Dynamic> = 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<String> = null;
var __superClassFieldList:Array<String> = 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":
+19 -21
View File
@@ -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<Array<String>> = [];
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:
}
}
}