Compare commits
51
Commits
+1
-1
@@ -1,4 +1,4 @@
|
||||
/hscript.swf
|
||||
/release.zip
|
||||
|
||||
dump/*
|
||||
tests/bin/*
|
||||
@@ -3,14 +3,22 @@ hscript-improved
|
||||
|
||||
How to install
|
||||
```
|
||||
haxelib git hscript-improved https://github.com/FNF-CNE-Devs/hscript-improved.git
|
||||
haxelib git hscript-improved https://github.com/CodenameCrew/hscript-improved.git
|
||||
```
|
||||
|
||||
To enable custom classes support you have to do this in project.xml
|
||||
|
||||
```xml
|
||||
<define name="CUSTOM_CLASSES" />
|
||||
```
|
||||
Warning: custom classes are sometimes broken, would like help to fix them. You can only override functions from the current class, not from the extended part, like you cant override update in FlxText because FlxText doesnt have a update function overriden
|
||||
|
||||
or set this in build.hxml
|
||||
|
||||
```hxml
|
||||
-D CUSTOM_CLASSES
|
||||
```
|
||||
|
||||
Warning: custom class system is still in development, would like help to test it. For now, you can only override functions from the current class, not from the extended part, like you cant override update in FlxText because FlxText doesnt have a update function overriden. Also you cannot create custom classes that extends a typed class (those ones that has `<T>`), this will be implemented in the future.
|
||||
|
||||
-----------
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
--macro keep('IntIterator')
|
||||
--macro hscript.macros.UsingHandler.init()
|
||||
--macro hscript.macros.AbstractHandler.init()
|
||||
--macro hscript.macros.ClassExtendMacro.init()
|
||||
|
||||
@@ -128,6 +128,10 @@ class Bytes {
|
||||
// TODO
|
||||
case EClass(_, _, _, _):
|
||||
// TODO
|
||||
case EEnum(en):
|
||||
// TODO
|
||||
case ECast(e, _):
|
||||
// TODO
|
||||
case EConst(c):
|
||||
doEncodeConst(c);
|
||||
case EIdent(v):
|
||||
|
||||
@@ -422,6 +422,9 @@ class Checker {
|
||||
return makeType(t,e);
|
||||
case CTOpt(t):
|
||||
return makeType(t,e);
|
||||
case CTExpr(_):
|
||||
error("Unsupported expr type parameter", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
package hscript;
|
||||
|
||||
import hscript.utils.UnsafeReflect;
|
||||
import haxe.Constraints.Function;
|
||||
|
||||
using Lambda;
|
||||
|
||||
/**
|
||||
* The Custom Class core.
|
||||
*
|
||||
* Provides handlers for custom classes.
|
||||
*
|
||||
* @author Jamextreme140
|
||||
*/
|
||||
@:access(hscript.CustomClassHandler)
|
||||
@:access(hscript.Property)
|
||||
class CustomClass implements IHScriptCustomClassBehaviour {
|
||||
public var className(get, never):String;
|
||||
|
||||
private function get_className():String
|
||||
return __class.name;
|
||||
|
||||
public var __interp:Interp;
|
||||
public var __real_fields:Array<String> = []; // UNUSED
|
||||
public var __class__fields:Array<String> = []; // Declared fields
|
||||
|
||||
public var __allowSetGet:Bool = true;
|
||||
|
||||
var __class:CustomClassHandler;
|
||||
var __superClass:IHScriptCustomClassBehaviour;
|
||||
var __upperClass:IHScriptCustomClassBehaviour;
|
||||
var __constructor:Function;
|
||||
|
||||
var __overrideFields:Array<String> = [];
|
||||
var __cachedFieldSet:Map<String, Dynamic> = null;
|
||||
var initializing:Bool = false;
|
||||
|
||||
public function new(__class:CustomClassHandler, ?args:Array<Dynamic>, ?cachedFieldSet:Map<String, Dynamic>) {
|
||||
this.__class = __class;
|
||||
|
||||
__interp = new Interp();
|
||||
__interp.errorHandler = __class.__interp.errorHandler;
|
||||
__interp.importFailedCallback = __class.__interp.importFailedCallback;
|
||||
|
||||
// __interp.variables = __class.staticInterp.variables;
|
||||
@:privateAccess __interp.usingHandler.usingEntries = __class.ogInterp.usingHandler.usingEntries;
|
||||
__interp.publicVariables = __class.ogInterp.publicVariables;
|
||||
__interp.staticVariables = __class.ogInterp.staticVariables;
|
||||
__interp.customClasses = __class.ogInterp.customClasses;
|
||||
|
||||
for (f in __class.fields) {
|
||||
switch (Tools.expr(f)) {
|
||||
case EVar(n): __class__fields.push(n);
|
||||
case EFunction(_, _, n, _, _, _, isOverride):
|
||||
if(isOverride) __overrideFields.push(n);
|
||||
__class__fields.push(n);
|
||||
default: continue;
|
||||
}
|
||||
@:privateAccess __interp.exprReturn(f);
|
||||
}
|
||||
|
||||
for (f => v in __class.__interp.variables) {
|
||||
if (!__interp.variables.exists(f))
|
||||
__interp.variables.set(f, v);
|
||||
}
|
||||
|
||||
__interp.scriptObject = this;
|
||||
|
||||
initializing = true;
|
||||
|
||||
if(cachedFieldSet != null)
|
||||
for(f => v in cachedFieldSet)
|
||||
this.hset(f, v);
|
||||
|
||||
if (hasField('new')) {
|
||||
buildConstructor();
|
||||
call('new', args);
|
||||
|
||||
if(__cachedFieldSet != null) {
|
||||
__cachedFieldSet.clear();
|
||||
__cachedFieldSet = null;
|
||||
}
|
||||
|
||||
if (this.__superClass == null && __class.extend != null)
|
||||
__interp.error(ECustom("super() not called"));
|
||||
} else if (__class.extend != null) {
|
||||
buildSuperClass(args);
|
||||
}
|
||||
|
||||
initializing = false;
|
||||
}
|
||||
|
||||
function cacheFieldSet(name:String, val:Dynamic) {
|
||||
if(!initializing) return;
|
||||
if(__cachedFieldSet == null) __cachedFieldSet = [];
|
||||
__cachedFieldSet.set(name, val);
|
||||
}
|
||||
|
||||
function buildConstructor() {
|
||||
__constructor = Reflect.makeVarArgs(buildSuperClass);
|
||||
}
|
||||
|
||||
function buildSuperClass(?args:Array<Dynamic>) {
|
||||
if (args == null)
|
||||
args = [];
|
||||
|
||||
if (__class.cl == null) {
|
||||
__interp.error(ECustom('Current class does not have a super'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (__class.cl is CustomClassHandler) {
|
||||
var customClass = new CustomClass(__class.cl, args, __cachedFieldSet);
|
||||
if(__overrideFields.length > 0) {
|
||||
for (field in __overrideFields) {
|
||||
var func = __interp.variables.get(field);
|
||||
customClass.overrideField(field, func);
|
||||
}
|
||||
}
|
||||
customClass.__upperClass = this;
|
||||
__superClass = customClass;
|
||||
@:privateAccess __interp.__instanceFields = __interp.__instanceFields.concat(getSuperFields());
|
||||
} else {
|
||||
if(__cachedFieldSet != null)
|
||||
UnsafeReflect.setField(__class.cl, "__cachedFieldSet", __cachedFieldSet);
|
||||
|
||||
var disallowCopy = Type.getInstanceFields(__class.cl);
|
||||
__superClass = Type.createInstance(__class.cl, args);
|
||||
__superClass.__real_fields = disallowCopy;
|
||||
__superClass.__class__fields = this.__class__fields;
|
||||
__superClass.__interp = this.__interp;
|
||||
}
|
||||
}
|
||||
|
||||
public function call(name:String, ?args:Array<Dynamic>, ?toSuper:Bool = false):Dynamic {
|
||||
// TODO: optimize this
|
||||
var fnName = toSuper ? '_HX_SUPER__$name' : name;
|
||||
var fn:Dynamic = __interp.variables.get(fnName);
|
||||
if(toSuper && fn == null)
|
||||
fn = __interp.variables.get(name);
|
||||
|
||||
if (fn != null && Reflect.isFunction(fn))
|
||||
return UnsafeReflect.callMethodUnsafe(null, fn, (args == null) ? [] : args);
|
||||
else
|
||||
__interp.error(ECustom('$name doesn\'t exists or is not a function'));
|
||||
return null;
|
||||
}
|
||||
|
||||
function hasField(name:String) {
|
||||
return __class__fields.contains(name);
|
||||
}
|
||||
|
||||
function hasStaticField(name:String):Bool {
|
||||
return __class.hasField(name);
|
||||
}
|
||||
|
||||
function getField(name:String, allowProperty:Bool = true):Dynamic {
|
||||
var f = __interp.variables.get(name);
|
||||
if (f != null && allowProperty && f is Property) {
|
||||
var prop:Property = cast f;
|
||||
prop.__allowSetGet = this.__allowSetGet;
|
||||
var r = prop.callGetter(name);
|
||||
prop.__allowSetGet = null;
|
||||
return r;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
function setField(name:String, val:Dynamic):Dynamic {
|
||||
var f = getField(name, false);
|
||||
if (f != null && f is Property) {
|
||||
var prop:Property = cast f;
|
||||
prop.__allowSetGet = this.__allowSetGet;
|
||||
var r = prop.callSetter(name, val);
|
||||
prop.__allowSetGet = null;
|
||||
return r;
|
||||
}
|
||||
__interp.variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides (replaces) the declared function.
|
||||
* @param name
|
||||
* @param func
|
||||
*/
|
||||
function overrideField(name:String, func:Function) {
|
||||
var f = getField(name, false);
|
||||
if(f != null && Reflect.isFunction(f)) {
|
||||
__interp.variables.set(name, func);
|
||||
__interp.variables.set('_HX_SUPER__$name', f);
|
||||
}
|
||||
else if(__superClass != null && __superClass is CustomClass) {
|
||||
cast(__superClass, CustomClass).overrideField(name, func);
|
||||
}
|
||||
}
|
||||
|
||||
function superHasField(name:String) {
|
||||
if (__superClass == null)
|
||||
return false;
|
||||
|
||||
var realFieldExists = __superClass.__real_fields != null && __superClass.__real_fields.contains(name);
|
||||
var classFieldExists = __superClass.__class__fields != null && __superClass.__class__fields.contains(name);
|
||||
|
||||
if(!realFieldExists && !classFieldExists && __superClass is CustomClass)
|
||||
return cast(__superClass, CustomClass).superHasField(name);
|
||||
|
||||
return realFieldExists || classFieldExists;
|
||||
}
|
||||
|
||||
function getSuperFields():Array<String> {
|
||||
if(__superClass == null) return [];
|
||||
|
||||
var classFields:Map<String, String> = []; // Prevents duplicated values
|
||||
var cls:Null<IHScriptCustomClassBehaviour> = __superClass;
|
||||
|
||||
while (cls != null) {
|
||||
for(fieldSet in [cls.__class__fields, cls.__real_fields])
|
||||
for(f in fieldSet)
|
||||
classFields.set(f, f);
|
||||
var next:IHScriptCustomClassBehaviour = null;
|
||||
if(cls is CustomClass)
|
||||
next = cast(cls, CustomClass).__superClass;
|
||||
if (next == null)
|
||||
break;
|
||||
cls = next;
|
||||
}
|
||||
|
||||
return [for(f in classFields) f];
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
switch (name) {
|
||||
case 'superClass': return __superClass;
|
||||
case 'superConstructor': return __constructor;
|
||||
default:
|
||||
if (hasField(name))
|
||||
return getField(name);
|
||||
|
||||
if (hasStaticField(name)) {
|
||||
__interp.error(ECustom('The field ${name} should be accessed in a static way.'));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (__superClass != null) {
|
||||
if (superHasField(name)) {
|
||||
__superClass.__allowSetGet = this.__allowSetGet;
|
||||
return __superClass.hget(name);
|
||||
}
|
||||
}
|
||||
|
||||
throw "field '"
|
||||
+ name
|
||||
+ "' does not exist in custom class '"
|
||||
+ this.className
|
||||
+ "'"
|
||||
+ (__superClass != null ? "' or super class '" + Type.getClassName(Type.getClass(this.__superClass)) + "'" : "");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if (hasField(name))
|
||||
return setField(name, val);
|
||||
|
||||
if (hasStaticField(name)) {
|
||||
__interp.error(ECustom('The field ${name} should be accessed in a static way.'));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (__superClass != null) {
|
||||
if (superHasField(name)) {
|
||||
__superClass.__allowSetGet = this.__allowSetGet;
|
||||
return __superClass.hset(name, val);
|
||||
}
|
||||
}
|
||||
else if(__class.extend != null && initializing) {
|
||||
cacheFieldSet(name, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
throw "field '"
|
||||
+ name
|
||||
+ "' does not exist in custom class '"
|
||||
+ this.className
|
||||
+ "'"
|
||||
+ (__superClass != null ? "' or super class '" + Type.getClassName(Type.getClass(this.__superClass)) + "'" : "");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// UNUSED
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the real superClass if the Custom Class
|
||||
* extends another Custom Class, and so on until
|
||||
* it reaches a real class, otherwise it will
|
||||
* return the last fetched Custom Class
|
||||
* @return Null<Dynamic>
|
||||
*/
|
||||
public function getSuperclass():IHScriptCustomClassBehaviour {
|
||||
if(__superClass == null) return null;
|
||||
|
||||
var cls:Null<IHScriptCustomClassBehaviour> = __superClass;
|
||||
|
||||
// Check if the superClass is another custom class,
|
||||
// so it will find for a real class, otherwise
|
||||
// returns the last super CustomClass parent.
|
||||
while (cls != null && cls is CustomClass) {
|
||||
var next = cast(cls, CustomClass).__superClass;
|
||||
if (next == null)
|
||||
break; // Return the Custom Class itself
|
||||
cls = next;
|
||||
}
|
||||
return cls is CustomClass ? this : cls;
|
||||
}
|
||||
|
||||
// TODO: scripted safe cast for custom classes
|
||||
public function getUpperclass():IHScriptCustomClassBehaviour {
|
||||
if(__upperClass == null) return this;
|
||||
|
||||
var cls:CustomClass = cast __upperClass;
|
||||
|
||||
while (cls != null) {
|
||||
var prev:CustomClass = cast cls.__upperClass;
|
||||
if(prev == null)
|
||||
break;
|
||||
cls = prev;
|
||||
}
|
||||
|
||||
return cls;
|
||||
}
|
||||
|
||||
public function toString():String
|
||||
return className;
|
||||
}
|
||||
+115
-170
@@ -1,151 +1,145 @@
|
||||
package hscript;
|
||||
|
||||
import hscript.Interp.DeclaredVar;
|
||||
import hscript.utils.UnsafeReflect;
|
||||
using StringTools;
|
||||
|
||||
class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
public static var staticHandler = new StaticHandler();
|
||||
|
||||
/**
|
||||
* Provides handlers for static custom class fields and instantiation.
|
||||
*/
|
||||
@:access(hscript.Property)
|
||||
class CustomClassHandler implements IHScriptCustomConstructor implements IHScriptCustomAccessBehaviour{
|
||||
public var ogInterp:Interp;
|
||||
public var name:String;
|
||||
public var fields:Array<Expr>;
|
||||
public var extend:String;
|
||||
public var extend:Null<String>;
|
||||
public var interfaces:Array<String>;
|
||||
public final isFinal:Bool;
|
||||
|
||||
public var cl:Class<Dynamic>;
|
||||
public var cl:Dynamic;
|
||||
|
||||
public function new(ogInterp:Interp, name:String, fields:Array<Expr>, ?extend:String, ?interfaces:Array<String>) {
|
||||
private var __interp:Interp;
|
||||
private var __staticFields:Array<String> = [];
|
||||
|
||||
public var __allowSetGet:Bool = true;
|
||||
|
||||
public function new(ogInterp:Interp, name:String, fields:Array<Expr>, ?extend:String, ?interfaces:Array<String>, ?isFinal:Bool) {
|
||||
this.ogInterp = ogInterp;
|
||||
this.name = name;
|
||||
this.fields = fields;
|
||||
this.extend = extend;
|
||||
this.interfaces = interfaces;
|
||||
this.isFinal = isFinal != null ? isFinal : false;
|
||||
|
||||
this.cl = extend == null ? CustomTemplateClass : Type.resolveClass('${extend}_HSX');
|
||||
if(cl == null)
|
||||
ogInterp.error(EInvalidClass(extend));
|
||||
if(extend != null) {
|
||||
if(ogInterp.customClasses.exists(extend)) {
|
||||
var customCls:CustomClassHandler = ogInterp.customClasses.get(extend);
|
||||
if(customCls.isFinal)
|
||||
ogInterp.error(ECustom('Cannot extend a final class'));
|
||||
this.cl = customCls;
|
||||
}
|
||||
else
|
||||
this.cl = Type.resolveClass('${extend}_HSX');
|
||||
|
||||
if(cl == null)
|
||||
ogInterp.error(EInvalidClass(extend));
|
||||
}
|
||||
|
||||
initStatic();
|
||||
}
|
||||
|
||||
public function hnew(args:Array<Dynamic>):Dynamic {
|
||||
// TODO: clean this up, it sucks, i hate it
|
||||
// TODO: make static vars work correctly
|
||||
var interp = new Interp();
|
||||
@:access(hscript.Interp)
|
||||
function initStatic() {
|
||||
__interp = new Interp();
|
||||
__interp.errorHandler = ogInterp.errorHandler;
|
||||
__interp.importFailedCallback = ogInterp.importFailedCallback;
|
||||
|
||||
interp.errorHandler = ogInterp.errorHandler;
|
||||
//__interp.variables = ogInterp.variables;
|
||||
__interp.usingHandler.usingEntries = ogInterp.usingHandler.usingEntries;
|
||||
__interp.publicVariables = ogInterp.publicVariables;
|
||||
__interp.staticVariables = ogInterp.staticVariables;
|
||||
__interp.customClasses = ogInterp.customClasses;
|
||||
|
||||
var _class:IHScriptCustomClassBehaviour = Type.createInstance(cl, args);
|
||||
|
||||
//var __capturedLocals = ogInterp.duplicate(ogInterp.locals);
|
||||
//var capturedLocals:Map<String, DeclaredVar> = [];
|
||||
//for(k=>e in __capturedLocals)
|
||||
// if (e != null && e.depth <= 0)
|
||||
// capturedLocals.set(k, e);
|
||||
|
||||
var disallowCopy = Type.getInstanceFields(cl);
|
||||
|
||||
_class.__real_fields = disallowCopy;
|
||||
|
||||
// todo: make it so you can use variables from the same scope as where the class was defined
|
||||
|
||||
//for (key => value in capturedLocals) {
|
||||
// if(!disallowCopy.contains(key)) {
|
||||
// interp.locals.set(key, {r: value, depth: -1});
|
||||
// }
|
||||
//}
|
||||
for (key => value in ogInterp.variables) {
|
||||
if(!disallowCopy.contains(key)) {
|
||||
interp.variables.set(key, value);
|
||||
for(e in fields.copy()) {
|
||||
var validField:Bool = false;
|
||||
var staticField:Bool = false;
|
||||
var fieldName:String = "";
|
||||
switch (Tools.expr(e)) {
|
||||
case EVar(n, _, _, _, isStatic):
|
||||
validField = true;
|
||||
staticField = isStatic;
|
||||
fieldName = n;
|
||||
case EFunction(_, _, n, _, _, isStatic, _, _, _, _):
|
||||
validField = true;
|
||||
staticField = isStatic;
|
||||
fieldName = n;
|
||||
default:
|
||||
}
|
||||
}
|
||||
for(key => value in ogInterp.customClasses) {
|
||||
if(!disallowCopy.contains(key)) {
|
||||
interp.customClasses.set(key, value);
|
||||
}
|
||||
}
|
||||
// todo: clone static vars, but make it so setting it only sets it on the class
|
||||
// todo: clone public vars
|
||||
|
||||
//trace("Before: " + [for(key => value in interp.variables) key]);
|
||||
|
||||
interp.variables.set("super", staticHandler);
|
||||
|
||||
var comparisonMap:Map<String, Dynamic> = [];
|
||||
for(key => value in interp.variables) {
|
||||
comparisonMap.set(key, value);
|
||||
}
|
||||
|
||||
_class.__custom__variables = interp.variables;
|
||||
|
||||
//trace(fields);
|
||||
for(expr in fields) {
|
||||
@:privateAccess
|
||||
interp.exprReturn(expr);
|
||||
}
|
||||
|
||||
//trace("After: " + [for(key => value in interp.variables) key]);
|
||||
|
||||
// get only variables that were not set before
|
||||
var classVariables = [
|
||||
for(key => value in interp.variables)
|
||||
if(!comparisonMap.exists(key) || comparisonMap[key] != value)
|
||||
key => value
|
||||
];
|
||||
//for(variable => value in classVariables) {
|
||||
// if(variable == "this" || variable == "super" || variable == "new") continue;
|
||||
// @:privateAccess
|
||||
// if(!interp.__instanceFields.contains(variable)) {
|
||||
// interp.__instanceFields.push(variable);
|
||||
// }
|
||||
//}
|
||||
|
||||
_class.__class__fields = [for(key => value in classVariables) key];
|
||||
|
||||
//trace(_class.__class__fields);
|
||||
//@:privateAccess
|
||||
//trace(interp.__instanceFields);
|
||||
|
||||
_class.__interp = interp;
|
||||
_class.__allowSetGet = false;
|
||||
interp.scriptObject = _class;
|
||||
|
||||
for(variable => value in interp.variables) {
|
||||
if(variable == "this" || variable == "super" || variable == "new") continue;
|
||||
|
||||
if(variable.startsWith("set_") || variable.startsWith("get_")) {
|
||||
_class.__allowSetGet = true;
|
||||
if(staticField && validField) {
|
||||
__interp.exprReturn(e);
|
||||
__staticFields.push(fieldName);
|
||||
fields.remove(e);
|
||||
}
|
||||
}
|
||||
|
||||
var newFunc = interp.variables.get("new");
|
||||
if(newFunc != null) {
|
||||
var comparisonMap:Map<String, Dynamic> = [];
|
||||
for(key => value in interp.variables) {
|
||||
comparisonMap.set(key, value);
|
||||
}
|
||||
for(f => v in ogInterp.variables)
|
||||
if(!__interp.variables.exists(f))
|
||||
__interp.variables.set(f, v);
|
||||
}
|
||||
|
||||
UnsafeReflect.callMethodUnsafe(null, newFunc, args);
|
||||
public function hnew(args:Array<Dynamic>):Dynamic
|
||||
return new CustomClass(this, args);
|
||||
|
||||
// get only variables that were not set before
|
||||
var classVariables = [
|
||||
for(key => value in interp.variables)
|
||||
if(!comparisonMap.exists(key) || comparisonMap[key] != value)
|
||||
key => value
|
||||
];
|
||||
for(variable => value in classVariables) {
|
||||
if(variable == "this" || variable == "super" || variable == "new") continue;
|
||||
@:privateAccess
|
||||
if(!interp.__instanceFields.contains(variable)) {
|
||||
interp.__instanceFields.push(variable);
|
||||
}
|
||||
if(!_class.__class__fields.contains(variable)) {
|
||||
_class.__class__fields.push(variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@:allow(hscript.Interp)
|
||||
function hasField(name:String) {
|
||||
return __staticFields.contains(name);
|
||||
}
|
||||
|
||||
function getField(name:String, allowProperty:Bool = true):Dynamic {
|
||||
var f = __interp.variables.get(name);
|
||||
if(f is Property && allowProperty) {
|
||||
var prop:Property = cast f;
|
||||
prop.__allowSetGet = this.__allowSetGet;
|
||||
var r = prop.callGetter(name);
|
||||
prop.__allowSetGet = null;
|
||||
return r;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
return _class;
|
||||
function setField(name:String, val:Dynamic):Dynamic {
|
||||
var f = getField(name, false);
|
||||
if(f is Property) {
|
||||
var prop:Property = cast f;
|
||||
prop.__allowSetGet = this.__allowSetGet;
|
||||
var r = prop.callSetter(name, val);
|
||||
prop.__allowSetGet = null;
|
||||
return r;
|
||||
}
|
||||
__interp.variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
if(hasField(name)) {
|
||||
return getField(name);
|
||||
}
|
||||
throw "field '"+ name+ "' does not exist in class '"+ this.name+ "'";
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if(hasField(name))
|
||||
return setField(name, val);
|
||||
|
||||
throw "field '"+ name+ "' does not exist in class '"+ this.name+ "'";
|
||||
return null;
|
||||
}
|
||||
|
||||
// UNUSED
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
@@ -153,51 +147,6 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
class CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomAccessBehaviour {
|
||||
public var __interp:Interp;
|
||||
public var __allowSetGet:Bool = true;
|
||||
public var __custom__variables:Map<String, Dynamic>;
|
||||
public var __real_fields:Array<String>;
|
||||
public var __class__fields:Array<String>;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
if(__real_fields.contains(name)) {
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
public function hget(name:String):Dynamic {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
|
||||
return UnsafeReflect.getProperty(this, name);
|
||||
}
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("get_" + name)();
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("set_" + name)(val);
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is for backwards compatibility with old hscript-improved, since some scripts use it
|
||||
@@ -236,7 +185,3 @@ class TemplateClass implements IHScriptCustomBehaviour implements IHScriptCustom
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
final class StaticHandler {
|
||||
public function new() {}
|
||||
}
|
||||
+27
-3
@@ -53,7 +53,7 @@ enum Expr {
|
||||
#end
|
||||
EConst( c : Const );
|
||||
EIdent( v : String );
|
||||
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool );
|
||||
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool, ?get : FieldPropertyAccess, ?set : FieldPropertyAccess, ?isVar:Bool );
|
||||
EParent( e : Expr );
|
||||
EBlock( e : Array<Expr> );
|
||||
EField( e : Expr, f : String , ?safe : Bool );
|
||||
@@ -69,7 +69,7 @@ enum Expr {
|
||||
EReturn( ?e : Expr );
|
||||
EArray( e : Expr, index : Expr );
|
||||
EArrayDecl( e : Array<Expr>, ?wantedType: CType );
|
||||
ENew( cl : String, params : Array<Expr> );
|
||||
ENew( cl : String, params : Array<Expr>, ?paramType:Array<CType> );
|
||||
EThrow( e : Expr );
|
||||
ETry( e : Expr, v : String, t : Null<CType>, ecatch : Expr );
|
||||
EObject( fl : Array<ObjectField> );
|
||||
@@ -79,8 +79,10 @@ enum Expr {
|
||||
EMeta( name : String, args : Array<Expr>, e : Expr );
|
||||
ECheckType( e : Expr, t : CType );
|
||||
|
||||
EImport( c : String, ?asname:String );
|
||||
EImport( c : String, ?asname:String, ?isUsing:Bool );
|
||||
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String>, ?isFinal:Bool, ?isPrivate:Bool );
|
||||
EEnum( en:EnumDecl, ?isAbstract:Bool );
|
||||
ECast(e:Expr, ?t:CType);
|
||||
}
|
||||
|
||||
@:structInit
|
||||
@@ -111,6 +113,18 @@ final class MetadataEntry {
|
||||
|
||||
typedef Metadata = Array<MetadataEntry>;
|
||||
|
||||
@:structInit
|
||||
final class EnumDecl {
|
||||
public var name : String;
|
||||
public var fields : Array<EnumField>;
|
||||
}
|
||||
|
||||
@:structInit
|
||||
final class EnumField {
|
||||
public var name : String;
|
||||
public var args : Array<Argument>;
|
||||
}
|
||||
|
||||
enum CType {
|
||||
CTPath( path : Array<String>, ?params : Array<CType> );
|
||||
CTFun( args : Array<CType>, ret : CType );
|
||||
@@ -118,6 +132,7 @@ enum CType {
|
||||
CTParent( t : CType );
|
||||
CTOpt( t : CType );
|
||||
CTNamed( n : String, t : CType );
|
||||
CTExpr( e : Expr ); // for type parameters only
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
@@ -198,6 +213,15 @@ enum abstract FieldAccess(UInt8) {
|
||||
var AMacro;
|
||||
}
|
||||
|
||||
enum abstract FieldPropertyAccess(UInt8) {
|
||||
var ADefault;
|
||||
var ANull;
|
||||
var AGet;
|
||||
var ASet;
|
||||
var ADynamic;
|
||||
var ANever;
|
||||
}
|
||||
|
||||
enum FieldKind {
|
||||
KFunction( f : FunctionDecl );
|
||||
KVar( v : VarDecl );
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package hscript;
|
||||
|
||||
import hscript.utils.UnsafeReflect;
|
||||
|
||||
// TODO: EnumTools for scripted enums
|
||||
/**
|
||||
* Wrapper class for enums, both for real and scripted.
|
||||
*/
|
||||
@:structInit
|
||||
class HEnum implements IHScriptCustomBehaviour {
|
||||
private var enumValues(default, null) = {};
|
||||
|
||||
public function setEnum(name:String, enumValue:Dynamic):Void {
|
||||
UnsafeReflect.setField(enumValues, name, enumValue);
|
||||
}
|
||||
|
||||
public function getEnum(name:String):Null<Dynamic> {
|
||||
if (UnsafeReflect.hasField(enumValues, name))
|
||||
return UnsafeReflect.field(enumValues, name);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
return getEnum(name);
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
@:structInit
|
||||
class HEnumValue {
|
||||
public var enumName:String;
|
||||
public var fieldName:String;
|
||||
public var index:Int;
|
||||
public var args:Array<Dynamic>;
|
||||
|
||||
public function toString():String {
|
||||
return '$enumName.$fieldName${args.length > 0 ? '(${[for (a in args) a].join(", ")})' : ''}';
|
||||
}
|
||||
|
||||
public inline function getEnumName():String
|
||||
return this.enumName;
|
||||
|
||||
public inline function getConstructorArgs():Array<Dynamic>
|
||||
return this.args;
|
||||
|
||||
public function compare(other:HEnumValue):Bool {
|
||||
if (enumName != other.enumName || fieldName != other.fieldName)
|
||||
return false;
|
||||
if (args.length == 0 && other.args.length == 0)
|
||||
return true;
|
||||
if (args.length == 0 || other.args.length == 0)
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package hscript;
|
||||
|
||||
// Soon...
|
||||
interface IHScriptAbstractBehaviour extends IHScriptCustomBehaviour {
|
||||
public var hasOp:Bool;
|
||||
public var hasArr:Bool;
|
||||
// @:op(A * B), @:op(A++), etc...
|
||||
public function hop(kind:String, a:Dynamic, ?b:Dynamic):Dynamic;
|
||||
|
||||
public function harrayget(key:Dynamic):Dynamic;
|
||||
public function harrayset(key:Dynamic, val:Dynamic):Dynamic;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package hscript;
|
||||
|
||||
interface IHScriptCustomAccessBehaviour {
|
||||
/**
|
||||
* Same Interface as IHScriptCustomBehaviour but for Property.
|
||||
*/
|
||||
interface IHScriptCustomAccessBehaviour extends IHScriptCustomBehaviour {
|
||||
var __allowSetGet:Bool;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic;
|
||||
public function hget(name:String):Dynamic;
|
||||
|
||||
public function __callGetter(name:String):Dynamic;
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic;
|
||||
}
|
||||
@@ -1,6 +1,23 @@
|
||||
package hscript;
|
||||
|
||||
/**
|
||||
* Special Interface for handling field access behaviour.
|
||||
* Basically works like the operator overload `@:op(a.b)`
|
||||
* for an abstract.
|
||||
*/
|
||||
interface IHScriptCustomBehaviour {
|
||||
/**
|
||||
* Field Write Access
|
||||
* @param name - Field Name
|
||||
* @param val - Value to assign
|
||||
* @return Dynamic - The assigned value
|
||||
*/
|
||||
public function hset(name:String, val:Dynamic):Dynamic;
|
||||
|
||||
/**
|
||||
* Field Read Access
|
||||
* @param name - Field Name
|
||||
* @return Dynamic - The returned field
|
||||
*/
|
||||
public function hget(name:String):Dynamic;
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
package hscript;
|
||||
|
||||
interface IHScriptCustomClassBehaviour {
|
||||
/**
|
||||
* Special Interface to make a class usable for Custom Classes.
|
||||
*/
|
||||
interface IHScriptCustomClassBehaviour extends IHScriptCustomAccessBehaviour{
|
||||
public var __interp:Interp;
|
||||
public var __custom__variables:Map<String, Dynamic>;
|
||||
public var __allowSetGet:Bool;
|
||||
public var __real_fields:Array<String>;
|
||||
public var __class__fields:Array<String>;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic;
|
||||
public function hget(name:String):Dynamic;
|
||||
|
||||
public function __callGetter(name:String):Dynamic;
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic;
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package hscript;
|
||||
|
||||
/**
|
||||
* Special Interface for handling new instances of an object.
|
||||
*/
|
||||
interface IHScriptCustomConstructor {
|
||||
public function hnew(args:Array<Dynamic>):Dynamic;
|
||||
}
|
||||
+471
-162
@@ -28,7 +28,9 @@
|
||||
*/
|
||||
package hscript;
|
||||
|
||||
import hscript.HEnum.HEnumValue;
|
||||
import haxe.CallStack;
|
||||
import hscript.utils.UsingHandler;
|
||||
import hscript.utils.UnsafeReflect;
|
||||
import haxe.PosInfos;
|
||||
import hscript.Expr;
|
||||
@@ -65,11 +67,18 @@ class RedeclaredVar {
|
||||
public var depth:Int;
|
||||
}
|
||||
|
||||
@:access(hscript.CustomClass)
|
||||
@:analyzer(optimize, local_dce, fusion, user_var_fusion)
|
||||
class Interp {
|
||||
public var scriptObject(default, set):Dynamic;
|
||||
private var _hasScriptObject(default, null):Bool = false;
|
||||
private var hasScriptObject(get, never):Bool;
|
||||
private function get_hasScriptObject():Bool
|
||||
return scriptObject != null;
|
||||
|
||||
private var _scriptObjectType(default, null):ScriptObjectType = SNull;
|
||||
|
||||
var __instanceFields:Array<String> = [];
|
||||
|
||||
public var scriptObject(default, set):Dynamic;
|
||||
public function set_scriptObject(v:Dynamic) {
|
||||
switch(Type.typeof(v)) {
|
||||
case TClass(c): // Class Access
|
||||
@@ -101,13 +110,21 @@ class Interp {
|
||||
__instanceFields = [];
|
||||
_scriptObjectType = SNull;
|
||||
}
|
||||
_hasScriptObject = v != null;
|
||||
return scriptObject = v;
|
||||
}
|
||||
|
||||
var inCustomClass(get, never):Bool;
|
||||
private function get_inCustomClass():Bool
|
||||
return hasScriptObject && _scriptObjectType == SCustomClass;
|
||||
|
||||
var __customClass(get, never):CustomClass;
|
||||
private function get___customClass():CustomClass
|
||||
return inCustomClass ? cast scriptObject : null;
|
||||
|
||||
public var errorHandler:Error->Void;
|
||||
public var importFailedCallback:Array<String>->Bool;
|
||||
|
||||
public var customClasses:Map<String, Dynamic>;
|
||||
public var customClasses:Map<String, CustomClassHandler>;
|
||||
public var variables:Map<String, Dynamic>;
|
||||
public var publicVariables:Map<String, Dynamic>;
|
||||
public var staticVariables:Map<String, Dynamic>;
|
||||
@@ -122,17 +139,21 @@ class Interp {
|
||||
var returnValue:Dynamic;
|
||||
|
||||
var isBypassAccessor:Bool = false;
|
||||
var setAlias:Null<String> = null; // Custom Class import alias
|
||||
var beforeAlias:Null<String> = null;
|
||||
|
||||
public var importEnabled:Bool = true;
|
||||
|
||||
public var allowStaticVariables:Bool = false;
|
||||
public var allowPublicVariables:Bool = false;
|
||||
|
||||
// TODO: move this to an external class
|
||||
public var importBlocklist:Array<String> = [
|
||||
// "flixel.FlxG"
|
||||
];
|
||||
|
||||
var __instanceFields:Array<String> = [];
|
||||
var usingHandler:UsingHandler;
|
||||
|
||||
#if hscriptPos
|
||||
var curExpr:Expr;
|
||||
#end
|
||||
@@ -145,10 +166,13 @@ class Interp {
|
||||
}
|
||||
|
||||
private function resetVariables():Void {
|
||||
customClasses = new Map<String, Dynamic>();
|
||||
customClasses = new Map<String, CustomClassHandler>();
|
||||
variables = new Map<String, Dynamic>();
|
||||
publicVariables = new Map<String, Dynamic>();
|
||||
staticVariables = new Map<String, Dynamic>();
|
||||
|
||||
usingHandler = new UsingHandler();
|
||||
|
||||
variables.set("null", null);
|
||||
variables.set("true", true);
|
||||
variables.set("false", false);
|
||||
@@ -223,7 +247,17 @@ class Interp {
|
||||
Std.isOfType(expr1, IMap);
|
||||
default:
|
||||
var expr2:Dynamic = expr(e2);
|
||||
expr2 != null ? Std.isOfType(expr1, expr2) : false;
|
||||
if(expr2 != null) {
|
||||
if(expr1 is CustomClass && expr2 is CustomClassHandler) {
|
||||
var objName = cast(expr1, CustomClass).className;
|
||||
var clsName = cast(expr2, CustomClassHandler).name;
|
||||
objName == clsName;
|
||||
}
|
||||
else
|
||||
Std.isOfType(expr1, expr2);
|
||||
}
|
||||
else
|
||||
false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,22 +280,13 @@ class Interp {
|
||||
case EIdent(id):
|
||||
var l = locals.get(id);
|
||||
if (l == null) {
|
||||
if (_hasScriptObject && !varExists(id)) {
|
||||
if (hasScriptObject && !varExists(id)) {
|
||||
var instanceHasField = __instanceFields.contains(id);
|
||||
|
||||
if (_scriptObjectType == SObject && instanceHasField) {
|
||||
UnsafeReflect.setField(scriptObject, id, v);
|
||||
return v;
|
||||
} else if (_scriptObjectType == SCustomClass && instanceHasField) {
|
||||
var obj:IHScriptCustomClassBehaviour = cast scriptObject;
|
||||
if(isBypassAccessor) {
|
||||
obj.__allowSetGet = false;
|
||||
var res = obj.hset(id, v);
|
||||
obj.__allowSetGet = true;
|
||||
return res;
|
||||
}
|
||||
return obj.hset(id, v);
|
||||
} else if(_scriptObjectType == SAccessBehaviourObject) {
|
||||
} else if((_scriptObjectType == SCustomClass && instanceHasField) || _scriptObjectType == SAccessBehaviourObject) {
|
||||
var obj:IHScriptCustomAccessBehaviour = cast scriptObject;
|
||||
if(isBypassAccessor) {
|
||||
obj.__allowSetGet = false;
|
||||
@@ -290,8 +315,16 @@ class Interp {
|
||||
setVar(id, v);
|
||||
}
|
||||
} else {
|
||||
var obj = resolve(id, false, false);
|
||||
if (obj != null && obj is Property) {
|
||||
var prop:Property = cast obj;
|
||||
return prop.callSetter(id, v);
|
||||
}
|
||||
setVar(id, v);
|
||||
}
|
||||
} else if (l.r is Property) {
|
||||
var prop:Property = cast l.r;
|
||||
return prop.callSetter(id, v);
|
||||
} else {
|
||||
l.r = v;
|
||||
if (l.depth == 0) {
|
||||
@@ -330,22 +363,13 @@ class Interp {
|
||||
var l = locals.get(id);
|
||||
v = fop(expr(e1), expr(e2));
|
||||
if (l == null) {
|
||||
if(_hasScriptObject && !varExists(id)) {
|
||||
if(hasScriptObject && !varExists(id)) {
|
||||
var instanceHasField = __instanceFields.contains(id);
|
||||
|
||||
if (_scriptObjectType == SObject && instanceHasField) {
|
||||
UnsafeReflect.setField(scriptObject, id, v);
|
||||
return v;
|
||||
} else if (_scriptObjectType == SCustomClass && instanceHasField) {
|
||||
var obj:IHScriptCustomClassBehaviour = cast scriptObject;
|
||||
if(isBypassAccessor) {
|
||||
obj.__allowSetGet = false;
|
||||
var res = obj.hset(id, v);
|
||||
obj.__allowSetGet = true;
|
||||
return res;
|
||||
}
|
||||
return obj.hset(id, v);
|
||||
} else if(_scriptObjectType == SAccessBehaviourObject) {
|
||||
} else if((_scriptObjectType == SCustomClass && instanceHasField) || _scriptObjectType == SAccessBehaviourObject) {
|
||||
var obj:IHScriptCustomAccessBehaviour = cast scriptObject;
|
||||
if(isBypassAccessor) {
|
||||
obj.__allowSetGet = false;
|
||||
@@ -374,11 +398,20 @@ class Interp {
|
||||
setVar(id, v);
|
||||
}
|
||||
} else {
|
||||
var obj = resolve(id, true, false);
|
||||
if (obj != null && obj is Property) {
|
||||
var prop:Property = cast obj;
|
||||
return prop.callSetter(id, v);
|
||||
}
|
||||
setVar(id, v);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var l = locals.get(id);
|
||||
if (l.r is Property) {
|
||||
var prop:Property = cast l.r;
|
||||
return prop.callSetter(id, v);
|
||||
}
|
||||
l.r = v;
|
||||
if (l.depth == 0) {
|
||||
setVar(id, v);
|
||||
@@ -417,19 +450,45 @@ class Interp {
|
||||
var l = locals.get(id);
|
||||
if(l != null) {
|
||||
var v:Dynamic = l.r;
|
||||
var prop:Property = null;
|
||||
if (v is Property) {
|
||||
prop = cast v;
|
||||
v = prop.callGetter(id);
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
v += delta;
|
||||
l.r = v;
|
||||
} else
|
||||
l.r = v + delta;
|
||||
if (prop != null)
|
||||
prop.callSetter(id, v);
|
||||
else
|
||||
l.r = v;
|
||||
} else {
|
||||
if (prop != null)
|
||||
prop.callSetter(id, v + delta);
|
||||
else
|
||||
l.r = v + delta;
|
||||
}
|
||||
return v;
|
||||
} else {
|
||||
var v:Dynamic = resolve(id);
|
||||
var v:Dynamic = resolve(id, true, false);
|
||||
var prop:Property = null;
|
||||
if (v is Property) {
|
||||
prop = cast v;
|
||||
v = prop.callGetter(id);
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
v += delta;
|
||||
setVar(id, v);
|
||||
} else
|
||||
setVar(id, v + delta);
|
||||
if (prop != null)
|
||||
prop.callSetter(id, v);
|
||||
else
|
||||
setVar(id, v);
|
||||
} else {
|
||||
if (prop != null)
|
||||
prop.callSetter(id, v + delta);
|
||||
else
|
||||
setVar(id, v + delta);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
case EField(e, f, s):
|
||||
@@ -535,11 +594,11 @@ class Interp {
|
||||
public inline function error(e:#if hscriptPos ErrorDef #else Error #end, rethrow = false):Dynamic {
|
||||
#if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end
|
||||
|
||||
if (rethrow) {
|
||||
if (rethrow)
|
||||
this.rethrow(e);
|
||||
} else {
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -551,27 +610,41 @@ class Interp {
|
||||
#end
|
||||
}
|
||||
|
||||
public function resolve(id:String, doException:Bool = true):Dynamic {
|
||||
public function resolve(id:String, doException:Bool = true, allowProperty:Bool = true):Dynamic {
|
||||
if (id == null)
|
||||
return null;
|
||||
id = StringTools.trim(id);
|
||||
|
||||
if(inCustomClass && id == 'super') {
|
||||
var customClass:IHScriptCustomClassBehaviour = cast scriptObject;
|
||||
var superClass = customClass.hget('superClass');
|
||||
return superClass == null ? customClass.hget('superConstructor') : superClass;
|
||||
}
|
||||
|
||||
if (locals.exists(id)) {
|
||||
var l = locals.get(id);
|
||||
if(l != null)
|
||||
return l.r;
|
||||
if(l != null) {
|
||||
if(l.r != null && l.r is Property && allowProperty)
|
||||
return cast(l.r, Property).callGetter(id);
|
||||
else
|
||||
return l.r;
|
||||
}
|
||||
}
|
||||
|
||||
for(map in [variables, publicVariables, staticVariables]) {
|
||||
if(map.exists(id)) {
|
||||
var r:Null<Dynamic> = map.get(id);
|
||||
if(r != null && r is Property && allowProperty)
|
||||
return cast(r, Property).callGetter(id);
|
||||
else
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if(variables.exists(id))
|
||||
return variables.get(id);
|
||||
if(publicVariables.exists(id))
|
||||
return publicVariables.get(id);
|
||||
if(staticVariables.exists(id))
|
||||
return staticVariables.get(id);
|
||||
if(customClasses.exists(id))
|
||||
return customClasses.get(id);
|
||||
|
||||
if (_hasScriptObject) {
|
||||
if (hasScriptObject) {
|
||||
// search in object
|
||||
if (id == "this") {
|
||||
return scriptObject;
|
||||
@@ -580,16 +653,7 @@ class Interp {
|
||||
|
||||
if (_scriptObjectType == SObject && instanceHasField) {
|
||||
return UnsafeReflect.field(scriptObject, id);
|
||||
} else if(_scriptObjectType == SCustomClass && instanceHasField) {
|
||||
var obj:IHScriptCustomClassBehaviour = cast scriptObject;
|
||||
if(isBypassAccessor) {
|
||||
obj.__allowSetGet = false;
|
||||
var res = obj.hget(id);
|
||||
obj.__allowSetGet = true;
|
||||
return res;
|
||||
}
|
||||
return obj.hget(id);
|
||||
} else if(_scriptObjectType == SAccessBehaviourObject) {
|
||||
} else if((_scriptObjectType == SCustomClass && instanceHasField) || _scriptObjectType == SAccessBehaviourObject) {
|
||||
var obj:IHScriptCustomAccessBehaviour = cast scriptObject;
|
||||
if(isBypassAccessor) {
|
||||
obj.__allowSetGet = false;
|
||||
@@ -639,9 +703,13 @@ class Interp {
|
||||
var e = e.e;
|
||||
#end
|
||||
switch (e) {
|
||||
case EClass(name, fields, extend, interfaces):
|
||||
if (customClasses.exists(name))
|
||||
error(EAlreadyExistingClass(name));
|
||||
case EClass(name, fields, extend, interfaces, isFinal):
|
||||
var oldName:String = name;
|
||||
var hasAlias:Bool = (setAlias != null && beforeAlias == oldName);
|
||||
var toSetName:String = hasAlias ? setAlias : oldName;
|
||||
|
||||
if (customClasses.exists(toSetName))
|
||||
error(EAlreadyExistingClass(toSetName));
|
||||
|
||||
inline function importVar(thing:String):String {
|
||||
if (thing == null)
|
||||
@@ -649,8 +717,14 @@ class Interp {
|
||||
final variable:Class<Any> = variables.exists(thing) ? cast variables.get(thing) : null;
|
||||
return variable == null ? thing : Type.getClassName(variable);
|
||||
}
|
||||
customClasses.set(name, new CustomClassHandler(this, name, fields, importVar(extend), [for (i in interfaces) importVar(i)]));
|
||||
case EImport(c, n):
|
||||
var cls:CustomClassHandler = new CustomClassHandler(this, oldName, fields, importVar(extend), [for (i in interfaces) importVar(i)], isFinal);
|
||||
customClasses.set(toSetName, cls);
|
||||
if(hasAlias) {
|
||||
customClasses.set(oldName, cls); // Allow usage in the same module
|
||||
beforeAlias = null;
|
||||
setAlias = null;
|
||||
}
|
||||
case EImport(c, n, isUsing):
|
||||
if (!importEnabled)
|
||||
return null;
|
||||
var splitClassName = [for (e in c.split(".")) e.trim()];
|
||||
@@ -660,8 +734,21 @@ class Interp {
|
||||
var oldClassName = realClassName;
|
||||
var oldSplitName = splitClassName.copy();
|
||||
|
||||
if (variables.exists(toSetName)) // class is already imported
|
||||
if (variables.exists(toSetName)) { // class is already imported
|
||||
if(isUsing && !usingHandler.entryExists(toSetName))
|
||||
setUsing(toSetName, variables.get(toSetName));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if(customClasses.exists(toSetName)) { // custom class is already parsed and imported
|
||||
// NOTE: you will need to create/import
|
||||
// the custom class first before
|
||||
// setting the extension
|
||||
if(isUsing && !usingHandler.entryExists(toSetName))
|
||||
setCustomClassUsing(toSetName, customClasses.get(toSetName));
|
||||
return null;
|
||||
}
|
||||
|
||||
var realClassName = getLocalImportRedirect(realClassName);
|
||||
|
||||
@@ -697,18 +784,26 @@ class Interp {
|
||||
}
|
||||
|
||||
if (cl == null && en == null) {
|
||||
if (importFailedCallback == null || !importFailedCallback(oldSplitName))
|
||||
beforeAlias = claVarName;
|
||||
setAlias = n;
|
||||
if (importFailedCallback == null || !importFailedCallback(oldSplitName)){
|
||||
beforeAlias = null;
|
||||
setAlias = null;
|
||||
error(EInvalidClass(oldClassName));
|
||||
}
|
||||
} else {
|
||||
if (en != null) {
|
||||
if(isUsing) error(EInvalidClass(oldClassName));
|
||||
// ENUM!!!!
|
||||
var enumThingy = {};
|
||||
var enumThingy:HEnum = {};
|
||||
for (c in en.getConstructors()) {
|
||||
try {
|
||||
UnsafeReflect.setField(enumThingy, c, en.createByName(c));
|
||||
//UnsafeReflect.setField(enumThingy, c, en.createByName(c));
|
||||
enumThingy.setEnum(c, en.createByName(c));
|
||||
} catch(e) {
|
||||
try {
|
||||
UnsafeReflect.setField(enumThingy, c, UnsafeReflect.field(en, c));
|
||||
//UnsafeReflect.setField(enumThingy, c, UnsafeReflect.field(en, c));
|
||||
enumThingy.setEnum(c, UnsafeReflect.field(en, c));
|
||||
} catch(ex) {
|
||||
throw e;
|
||||
}
|
||||
@@ -716,12 +811,79 @@ class Interp {
|
||||
}
|
||||
variables.set(toSetName, enumThingy);
|
||||
} else {
|
||||
if(isUsing) setUsing(toSetName, cl);
|
||||
variables.set(toSetName, cl);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
case EEnum(en, _): // TODO: enum abstracts
|
||||
var enumThingy:HEnum = {};
|
||||
var enumName = en.name;
|
||||
var enumFields = en.fields;
|
||||
for (i => ef in enumFields) {
|
||||
var fieldName = ef.name;
|
||||
|
||||
if(ef.args.length < 1) {
|
||||
var enumValue:HEnumValue = {
|
||||
enumName: enumName,
|
||||
fieldName: fieldName,
|
||||
index: i,
|
||||
args: []
|
||||
}
|
||||
|
||||
enumThingy.setEnum(fieldName, enumValue);
|
||||
}
|
||||
else {
|
||||
var params = ef.args;
|
||||
var hasOpt = false, minParams = 0;
|
||||
for (p in params) {
|
||||
if (p.opt)
|
||||
hasOpt = true;
|
||||
else
|
||||
minParams++;
|
||||
}
|
||||
|
||||
var f = function(args:Array<Dynamic>):HEnumValue {
|
||||
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 {
|
||||
enumName: enumName,
|
||||
fieldName: fieldName,
|
||||
index: i,
|
||||
args: args
|
||||
};
|
||||
};
|
||||
var f = Reflect.makeVarArgs(f);
|
||||
|
||||
enumThingy.setEnum(fieldName, f);
|
||||
}
|
||||
}
|
||||
|
||||
variables.set(en.name, enumThingy);
|
||||
case ECast(e, _): // TODO
|
||||
return expr(e);
|
||||
case EConst(c):
|
||||
switch (c) {
|
||||
case CInt(v): return v;
|
||||
@@ -730,9 +892,29 @@ class Interp {
|
||||
}
|
||||
case EIdent(id):
|
||||
return resolve(id);
|
||||
case EVar(n, _, e, isPublic, isStatic):
|
||||
case EVar(n, _, e, isPublic, isStatic, _, isFinal, _, getter, setter, isVar):
|
||||
var hasGetSet:Bool = (getter != null || setter != null);
|
||||
if(depth > 0 && hasGetSet) {
|
||||
error(ECustom("Property Accessor for local variables is not allowed"));
|
||||
return null;
|
||||
}
|
||||
declared.push({n: n, old: locals.get(n), depth: depth});
|
||||
locals.set(n, {r: (e == null) ? null : expr(e), depth: depth});
|
||||
var r:Dynamic = (e == null) ? null : expr(e);
|
||||
var declProp:Property = null;
|
||||
if (hasGetSet) {
|
||||
declProp = {
|
||||
r: r,
|
||||
getter: getter,
|
||||
setter: setter,
|
||||
isVar: isVar,
|
||||
interp: this,
|
||||
}
|
||||
}
|
||||
var declVar:DeclaredVar = {
|
||||
r: (declProp == null) ? r : declProp,
|
||||
depth: depth
|
||||
};
|
||||
locals.set(n, declVar);
|
||||
if (depth == 0) {
|
||||
if(allowStaticVariables && isStatic == true) {
|
||||
if(!staticVariables.exists(n)) // make it so it only sets it once
|
||||
@@ -779,7 +961,7 @@ class Interp {
|
||||
error(EInvalidOp(op));
|
||||
}
|
||||
case ECall(e, params):
|
||||
var args:Array<Dynamic> = [for(p in params) expr(p)];
|
||||
var args:Array<Dynamic> = makeArgs(params);
|
||||
|
||||
switch (Tools.expr(e)) {
|
||||
case EField(e, f, s):
|
||||
@@ -995,10 +1177,8 @@ class Interp {
|
||||
} else {
|
||||
return arr[index];
|
||||
}
|
||||
case ENew(cl, params):
|
||||
var a = [];
|
||||
for (e in params)
|
||||
a.push(expr(e));
|
||||
case ENew(cl, params, _):
|
||||
var a:Array<Dynamic> = makeArgs(params);
|
||||
return cnew(cl, a);
|
||||
case EThrow(e):
|
||||
throw expr(e);
|
||||
@@ -1033,14 +1213,55 @@ class Interp {
|
||||
case ETernary(econd, e1, e2):
|
||||
return if (expr(econd) == true) expr(e1) else expr(e2);
|
||||
case ESwitch(e, cases, def):
|
||||
var old = declared.length;
|
||||
var val:Dynamic = expr(e);
|
||||
var match = false;
|
||||
for (c in cases) {
|
||||
for (v in c.values)
|
||||
if (expr(v) == val) {
|
||||
match = true;
|
||||
break;
|
||||
for (v in c.values) {
|
||||
// https://github.com/FunkinCrew/hscript/blob/funkin-dev/hscript/Interp.hx#L531
|
||||
switch (Tools.expr(v)) {
|
||||
case ECall(e, params):
|
||||
switch (Tools.expr(e)) {
|
||||
case EField(_, f):
|
||||
var isScripted:Bool = val is HEnumValue;
|
||||
var valStr:String = '';
|
||||
var valEnum:HEnumValue = null;
|
||||
if(isScripted) {
|
||||
valEnum = cast val;
|
||||
valStr = valEnum.fieldName;
|
||||
}
|
||||
else {
|
||||
valStr = cast val;
|
||||
valStr = valStr.substring(0, valStr.indexOf("("));
|
||||
}
|
||||
|
||||
if(valStr == f) {
|
||||
var valParams = isScripted ? valEnum.getConstructorArgs() : Type.enumParameters(val);
|
||||
for (i => p in params) {
|
||||
switch (Tools.expr(p)) {
|
||||
case EIdent(n):
|
||||
declared.push({
|
||||
n: n,
|
||||
old: {r: locals.get(n), depth: depth},
|
||||
depth: depth
|
||||
});
|
||||
locals.set(n, {r: valParams[i], depth: depth});
|
||||
default:
|
||||
}
|
||||
}
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
default:
|
||||
if (expr(v) == val) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
val = expr(c.expr);
|
||||
break;
|
||||
@@ -1048,6 +1269,7 @@ class Interp {
|
||||
}
|
||||
if (!match)
|
||||
val = def == null ? null : expr(def);
|
||||
restore(old);
|
||||
return val;
|
||||
case EMeta(a, b, e):
|
||||
var oldAccessor = isBypassAccessor;
|
||||
@@ -1067,17 +1289,8 @@ class Interp {
|
||||
function doWhileLoop(econd:Expr, e:Expr):Void {
|
||||
var old = declared.length;
|
||||
do {
|
||||
try {
|
||||
expr(e);
|
||||
} catch (err:Stop) {
|
||||
switch (err) {
|
||||
case SContinue:
|
||||
case SBreak:
|
||||
break;
|
||||
case SReturn:
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if (!loopRun(() -> expr(e)))
|
||||
break;
|
||||
} while (expr(econd) == true);
|
||||
restore(old);
|
||||
}
|
||||
@@ -1085,41 +1298,52 @@ class Interp {
|
||||
function whileLoop(econd:Expr, e:Expr):Void {
|
||||
var old = declared.length;
|
||||
while (expr(econd) == true) {
|
||||
try {
|
||||
expr(e);
|
||||
} catch (err:Stop) {
|
||||
switch (err) {
|
||||
case SContinue:
|
||||
case SBreak:
|
||||
break;
|
||||
case SReturn:
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if (!loopRun(() -> expr(e)))
|
||||
break;
|
||||
}
|
||||
restore(old);
|
||||
}
|
||||
|
||||
function makeIterator(v:Dynamic, ?allowKeyValue = false):Iterator<Dynamic> {
|
||||
#if ((flash && !flash9) || (php && !php7 && haxe_ver < '4.0.0'))
|
||||
if (v.iterator != null)
|
||||
#if js
|
||||
// don't use try/catch (very slow)
|
||||
if(v is Array) {
|
||||
return allowKeyValue ? (v:Array<Dynamic>).keyValueIterator() : (v:Array<Dynamic>).iterator();
|
||||
}
|
||||
if(allowKeyValue && v.keyValueIterator != null)
|
||||
v = v.keyValueIterator();
|
||||
else if (v.iterator != null)
|
||||
v = v.iterator();
|
||||
#else
|
||||
if(allowKeyValue) {
|
||||
try
|
||||
v = v.keyValueIterator()
|
||||
catch (e:Dynamic) {};
|
||||
if(allowKeyValue)
|
||||
try v = v.keyValueIterator() catch (e:Dynamic) {};
|
||||
|
||||
if(v.hasNext == null || v.next == null)
|
||||
try v = v.iterator() catch (e:Dynamic) {};
|
||||
|
||||
#end
|
||||
if (v.hasNext == null || v.next == null) error(EInvalidIterator(v));
|
||||
return v;
|
||||
}
|
||||
|
||||
function makeArgs(params:Array<Expr>):Array<Dynamic> {
|
||||
var args:Array<Dynamic> = [];
|
||||
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.getSuperclass() : customClass);
|
||||
} else {
|
||||
args.push(ident);
|
||||
}
|
||||
default:
|
||||
args.push(expr(p));
|
||||
}
|
||||
}
|
||||
|
||||
if(v.hasNext == null || v.next == null) {
|
||||
try
|
||||
v = v.iterator()
|
||||
catch (e:Dynamic) {};
|
||||
}
|
||||
#end
|
||||
if (v.hasNext == null || v.next == null)
|
||||
error(EInvalidIterator(v));
|
||||
return v;
|
||||
return args;
|
||||
}
|
||||
|
||||
function forLoop(n:String, it:Expr, e:Expr, ?ithv:String):Void {
|
||||
@@ -1136,21 +1360,28 @@ class Interp {
|
||||
if(isKeyValue)
|
||||
locals.set(ithv, {r: next.key, depth: depth});
|
||||
locals.set(n, {r: isKeyValue ? next.value : next, depth: depth});
|
||||
try {
|
||||
expr(e);
|
||||
} catch (err:Stop) {
|
||||
switch (err) {
|
||||
case SContinue:
|
||||
case SBreak:
|
||||
break;
|
||||
case SReturn:
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if (!loopRun(() -> expr(e)))
|
||||
break;
|
||||
}
|
||||
restore(old);
|
||||
}
|
||||
|
||||
inline function loopRun(f:Void -> Void) {
|
||||
var cont = true;
|
||||
try {
|
||||
f();
|
||||
} catch (err:Stop) {
|
||||
switch (err) {
|
||||
case SContinue:
|
||||
case SBreak:
|
||||
cont = false;
|
||||
case SReturn:
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
return cont;
|
||||
}
|
||||
|
||||
inline function isMap(o:Dynamic):Bool {
|
||||
return (o is IMap);
|
||||
}
|
||||
@@ -1202,18 +1433,7 @@ class Interp {
|
||||
}) {
|
||||
return _getRedirect(o, f);
|
||||
}
|
||||
|
||||
if(o is IHScriptCustomClassBehaviour) {
|
||||
var obj:IHScriptCustomClassBehaviour = cast o;
|
||||
if(isBypassAccessor) {
|
||||
obj.__allowSetGet = false;
|
||||
var res = obj.hget(f);
|
||||
obj.__allowSetGet = true;
|
||||
return res;
|
||||
}
|
||||
return obj.hget(f);
|
||||
}
|
||||
|
||||
|
||||
if (o is IHScriptCustomAccessBehaviour) {
|
||||
var obj:IHScriptCustomAccessBehaviour = cast o;
|
||||
if(isBypassAccessor) {
|
||||
@@ -1236,8 +1456,20 @@ class Interp {
|
||||
}
|
||||
|
||||
if(v == null) {
|
||||
#if php
|
||||
// https://github.com/HaxeFoundation/haxe/issues/4915
|
||||
try {
|
||||
if ((v = UnsafeReflect.getProperty(o, f)) == null)
|
||||
v = Reflect.getProperty(cls, f);
|
||||
}
|
||||
catch(e:Dynamic) {
|
||||
if ((v = UnsafeReflect.field(o, f)) == null)
|
||||
v = Reflect.field(cls, f);
|
||||
}
|
||||
#else
|
||||
if ((v = UnsafeReflect.getProperty(o, f)) == null)
|
||||
v = Reflect.getProperty(cls, f);
|
||||
#end
|
||||
}
|
||||
return v;
|
||||
}
|
||||
@@ -1251,18 +1483,7 @@ class Interp {
|
||||
cl != null && setRedirects.exists(cl) && (_setRedirect = setRedirects[cl]) != null;
|
||||
})
|
||||
return _setRedirect(o, f, v);
|
||||
|
||||
if(o is IHScriptCustomClassBehaviour) {
|
||||
var obj:IHScriptCustomClassBehaviour = cast o;
|
||||
if(isBypassAccessor) {
|
||||
obj.__allowSetGet = false;
|
||||
var res = obj.hset(f, v);
|
||||
obj.__allowSetGet = true;
|
||||
return res;
|
||||
}
|
||||
return obj.hset(f, v);
|
||||
}
|
||||
|
||||
|
||||
if (o is IHScriptCustomAccessBehaviour) {
|
||||
var obj:IHScriptCustomAccessBehaviour = cast o;
|
||||
if(isBypassAccessor) {
|
||||
@@ -1287,17 +1508,106 @@ class Interp {
|
||||
return v;
|
||||
}
|
||||
|
||||
function fcall(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic {
|
||||
if(_hasScriptObject && o == CustomClassHandler.staticHandler) {
|
||||
return UnsafeReflect.callMethodUnsafe(scriptObject, UnsafeReflect.field(scriptObject, "_HX_SUPER__" + f), args);
|
||||
// STATIC EXTENSION ("USING")
|
||||
|
||||
// Real class static extension
|
||||
function setUsing(name:String, obj:Dynamic) {
|
||||
if (usingHandler.entryExists(name)) return;
|
||||
if (UsingHandler.defaultExtension.exists(name)) {
|
||||
var us:UsingEntry = UsingHandler.defaultExtension.get(name);
|
||||
usingHandler.usingEntries.set(name, us);
|
||||
return;
|
||||
}
|
||||
return call(o, get(o, f), args);
|
||||
if (obj == null) error(ECustom("Unknown using class " + name));
|
||||
|
||||
var fn:Dynamic->String->Array<Dynamic>->Dynamic = null;
|
||||
var fields:Array<String> = [];
|
||||
var cls = obj;
|
||||
|
||||
switch (Type.typeof(cls)) {
|
||||
case TClass(c):
|
||||
fields = Type.getClassFields(c);
|
||||
case TObject:
|
||||
fields = Reflect.fields(cls);
|
||||
default:
|
||||
error(ECustom('$name is not a class'));
|
||||
}
|
||||
|
||||
fn = function(o:Dynamic, f:String, args:Array<Dynamic>) {
|
||||
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 UnsafeReflect.callMethodUnsafe(cls, field, [o].concat(args));
|
||||
}
|
||||
|
||||
if(fn != null) usingHandler.registerEntry(name, fn, fields);
|
||||
}
|
||||
|
||||
// Custom Class Static Extension
|
||||
@:access(hscript.CustomClassHandler)
|
||||
function setCustomClassUsing(name:String, cls:CustomClassHandler) {
|
||||
if (usingHandler.entryExists(name)) return;
|
||||
|
||||
var fn:Dynamic->String->Array<Dynamic> -> Dynamic;
|
||||
var customClass:CustomClassHandler = cls;
|
||||
var fields:Array<String> = customClass.__staticFields.copy();
|
||||
|
||||
fn = function(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic {
|
||||
var field:Dynamic = customClass.getField(f);
|
||||
if (!Reflect.isFunction(field))
|
||||
return null;
|
||||
/*
|
||||
var totalArgs:Int = Tools.argCount(field);
|
||||
if (totalArgs == 0)
|
||||
return null;
|
||||
*/
|
||||
return UnsafeReflect.callMethodUnsafe(null, field, [o].concat(args));
|
||||
}
|
||||
|
||||
usingHandler.registerEntry(name, fn, fields);
|
||||
}
|
||||
|
||||
function fcall(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic {
|
||||
// Custom logic to handle super calls to prevent infinite recursion
|
||||
if(inCustomClass) {
|
||||
if (o == scriptObject.__superClass) {
|
||||
if (scriptObject.__superClass is CustomClass)
|
||||
return cast(scriptObject.__superClass, CustomClass).call(f, args, true);
|
||||
else
|
||||
return UnsafeReflect.callMethodUnsafe(scriptObject.__superClass, UnsafeReflect.field(scriptObject.__superClass, '_HX_SUPER__$f'), args);
|
||||
}
|
||||
}
|
||||
|
||||
if (usingHandler.usingEntries.iterator().hasNext()) { // If is not empty
|
||||
var v:Dynamic = null;
|
||||
for (n => us in usingHandler.usingEntries) {
|
||||
if(us.hasField(f)) {
|
||||
v = us.call(o, f, args);
|
||||
if (v != null)
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var func = get(o, f);
|
||||
// Workaround for an HTML5-specific issue.
|
||||
// https://github.com/HaxeFoundation/haxe/issues/11298
|
||||
#if js
|
||||
if (func == null && f == "contains") {
|
||||
func = get(o, "includes");
|
||||
}
|
||||
#end
|
||||
|
||||
return call(o, func, args);
|
||||
}
|
||||
|
||||
function call(o:Dynamic, f:Dynamic, args:Array<Dynamic>):Dynamic {
|
||||
if(f == CustomClassHandler.staticHandler) {
|
||||
return null;
|
||||
}
|
||||
return UnsafeReflect.callMethodSafe(o, f, args);
|
||||
}
|
||||
|
||||
@@ -1305,11 +1615,10 @@ class Interp {
|
||||
var c:Dynamic = resolve(cl);
|
||||
if (c == null)
|
||||
c = Type.resolveClass(cl);
|
||||
if(c is IHScriptCustomConstructor) {
|
||||
if (c is IHScriptCustomConstructor) {
|
||||
var c:IHScriptCustomConstructor = cast c;
|
||||
return c.hnew(args);
|
||||
} else {
|
||||
} else
|
||||
return Type.createInstance(c, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,8 @@ class Macro {
|
||||
tf.push( { name : f.name, meta : meta, doc : null, access : [], kind : FVar(convertType(f.t), null), pos : p } );
|
||||
}
|
||||
TAnonymous(tf);
|
||||
case CTExpr(_):
|
||||
throw "assert";
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+211
-16
@@ -20,9 +20,10 @@
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package hscript;
|
||||
import haxe.rtti.Meta;
|
||||
import hscript.Expr;
|
||||
|
||||
using StringTools;
|
||||
|
||||
enum Token {
|
||||
TEof;
|
||||
TConst( c : Const );
|
||||
@@ -97,6 +98,8 @@ class Parser {
|
||||
|
||||
var disableOrOp : Bool = false;
|
||||
|
||||
var isVar : Bool = false;
|
||||
|
||||
#if hscriptPos
|
||||
var origin : String;
|
||||
var tokenMin : Int;
|
||||
@@ -132,13 +135,21 @@ class Parser {
|
||||
];
|
||||
opPriority = new Map();
|
||||
opRightAssoc = new Map();
|
||||
#if (haxe >= "4.0.0")
|
||||
for(i => p in priorities)
|
||||
for(x in p) {
|
||||
opPriority.set(x, i);
|
||||
if( i == 9 ) opRightAssoc.set(x, true);
|
||||
}
|
||||
#else
|
||||
for( i in 0...priorities.length )
|
||||
for( x in priorities[i] ) {
|
||||
opPriority.set(x, i);
|
||||
if( i == 9 ) opRightAssoc.set(x, true);
|
||||
}
|
||||
#end
|
||||
for( x in ["!", "++", "--", "~"] ) // unary "-" handled in parser directly!
|
||||
opPriority.set(x, x == "++" || x == "--" ? -1 : -2);
|
||||
opPriority.set(x, (x == "++" || x == "--") ? -1 : -2);
|
||||
}
|
||||
|
||||
public inline function error( err:#if hscriptPos ErrorDef #else Error #end, pmin:Int, pmax:Int ) {
|
||||
@@ -272,7 +283,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;
|
||||
@@ -352,7 +363,7 @@ class Parser {
|
||||
var p1 = tokenMin;
|
||||
#end
|
||||
switch( tk ) {
|
||||
case TId(id):
|
||||
case TId(id): // TODO: direct access to classes by typing their path
|
||||
var e = parseStructure(id, oldPos);
|
||||
if( e == null )
|
||||
e = mk(EIdent(id));
|
||||
@@ -463,15 +474,25 @@ class Parser {
|
||||
case TBkOpen:
|
||||
var a = [];
|
||||
tk = token();
|
||||
var first = true;
|
||||
while( tk != TBkClose && (!resumeErrors || tk != TEof) ) {
|
||||
if (!first) {
|
||||
if (tk != TComma)
|
||||
unexpected(tk);
|
||||
else {
|
||||
tk = token();
|
||||
if (tk == TBkClose)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
first = false;
|
||||
push(tk);
|
||||
var oldoo = disableOrOp;
|
||||
disableOrOp = false;
|
||||
a.push(parseExpr());
|
||||
disableOrOp = oldoo;
|
||||
tk = token();
|
||||
if( tk == TComma )
|
||||
tk = token();
|
||||
}
|
||||
if( a.length == 1 && a[0] != null ) // What is this for???
|
||||
switch( expr(a[0]) ) {
|
||||
@@ -487,8 +508,11 @@ class Parser {
|
||||
}
|
||||
return parseExprNext(mk(EArrayDecl(a, nextType), p1));
|
||||
case TMeta(id) if( allowMetadata ):
|
||||
if(id == ':isVar') isVar = true;
|
||||
var args = parseMetaArgs();
|
||||
return mk(EMeta(id, args, parseExpr()),p1);
|
||||
var e = parseExpr();
|
||||
isVar = false;
|
||||
return mk(EMeta(id, args, e),p1);
|
||||
default:
|
||||
return unexpected(tk);
|
||||
}
|
||||
@@ -574,7 +598,8 @@ class Parser {
|
||||
return mk(EBinop(op,e1,e),pmin(e1),pmax(e1));
|
||||
return switch( expr(e) ) {
|
||||
case EBinop(op2,e2,e3):
|
||||
if( opPriority.get(op) <= opPriority.get(op2) && !opRightAssoc.exists(op) )
|
||||
var delta = opPriority.get(op) - opPriority.get(op2);
|
||||
if( delta < 0 || (delta == 0 && !opRightAssoc.exists(op)) )
|
||||
mk(EBinop(op2,makeBinop(op,e1,e2),e3),pmin(e1),pmax(e3));
|
||||
else
|
||||
mk(EBinop(op, e1, e), pmin(e1), pmax(e));
|
||||
@@ -619,7 +644,7 @@ class Parser {
|
||||
if( semic ) push(TSemicolon);
|
||||
}
|
||||
mk(EIf(cond,e1,e2),p1,(e2 == null) ? tokenMax : pmax(e2));
|
||||
case "override":
|
||||
case "override": // BIG TODO: OPTIMIZE ALL THIS BELOW UNTIL "inline"
|
||||
nextIsOverride = true;
|
||||
var nextToken = token();
|
||||
switch(nextToken) {
|
||||
@@ -827,7 +852,42 @@ class Parser {
|
||||
}
|
||||
}
|
||||
var ident = getIdent();
|
||||
var get:Null<FieldPropertyAccess> = null;
|
||||
var set:Null<FieldPropertyAccess> = null;
|
||||
var hasGetSet:Bool = false;
|
||||
var oldIsVar:Bool = isVar;
|
||||
|
||||
var tk = token();
|
||||
if( tk == TPOpen) {
|
||||
if( id == "final" )
|
||||
unexpected(tk);
|
||||
|
||||
var getId = getIdent();
|
||||
switch (getId) {
|
||||
case 'default': get = ADefault;
|
||||
case 'get': get = AGet;
|
||||
case 'null': get = ANull;
|
||||
case 'dynamic': get = ADynamic;
|
||||
case 'never': get = ANever;
|
||||
default: error(ECustom("Custom property accessor is not supported"), p1, tokenMax);
|
||||
}
|
||||
ensure(TComma);
|
||||
var setId = getIdent();
|
||||
switch (setId) {
|
||||
case 'default': set = ADefault;
|
||||
case 'set': set = ASet;
|
||||
case 'null': set = ANull;
|
||||
case 'dynamic': set = ADynamic;
|
||||
case 'never': set = ANever;
|
||||
default: error(ECustom("Custom property accessor is not supported"), p1, tokenMax);
|
||||
}
|
||||
ensure(TPClose);
|
||||
|
||||
hasGetSet = true;
|
||||
|
||||
tk = token();
|
||||
}
|
||||
|
||||
var t = null;
|
||||
nextType = null;
|
||||
if( tk == TDoubleDot && allowTypes ) {
|
||||
@@ -841,8 +901,13 @@ class Parser {
|
||||
e = parseExpr();
|
||||
else
|
||||
push(tk);
|
||||
|
||||
if(hasGetSet)
|
||||
checkAccess(get, set, e, t);
|
||||
|
||||
nextType = null;
|
||||
mk(EVar(ident, t, e, nextIsPublic, nextIsStatic, nextIsPrivate, id == "final", nextIsInline), p1, (e == null) ? tokenMax : pmax(e));
|
||||
if(isVar) isVar = false;
|
||||
mk(EVar(ident, t, e, nextIsPublic, nextIsStatic, nextIsPrivate, id == "final", nextIsInline, get, set, oldIsVar), p1, (e == null) ? tokenMax : pmax(e));
|
||||
case "while":
|
||||
var econd = parseExpr();
|
||||
var e = parseExpr();
|
||||
@@ -892,11 +957,13 @@ 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 "import":
|
||||
case "import" | "using":
|
||||
var isUsing = id == "using";
|
||||
var oldReadPos = readPos;
|
||||
var tk = token();
|
||||
switch( tk ) {
|
||||
case TPOpen:
|
||||
if(isUsing) error(ECustom('Expected identifier'),tokenMin,tokenMax);
|
||||
var tok = token();
|
||||
switch(tok) {
|
||||
case TConst(c):
|
||||
@@ -922,6 +989,7 @@ class Parser {
|
||||
t = token();
|
||||
if( t != TDot ) {
|
||||
if(t.match(TId("as"))) {
|
||||
if(isUsing) error(ECustom('Expected . or ;'),tokenMin,tokenMax);
|
||||
t = token();
|
||||
switch( t ) {
|
||||
case TId(id):
|
||||
@@ -946,7 +1014,7 @@ class Parser {
|
||||
ensure(TSemicolon);
|
||||
push(TSemicolon);
|
||||
var p = path.join(".");
|
||||
mk(EImport(p, asname),p1);
|
||||
mk(EImport(p, asname, isUsing),p1);
|
||||
default:
|
||||
unexpected(tk);
|
||||
null;
|
||||
@@ -1034,6 +1102,65 @@ class Parser {
|
||||
push(tk);
|
||||
mk(EClass(name, fields, extend, interfaces, nextIsFinal, nextIsPrivate), p1);
|
||||
|
||||
case "enum": // TODO: enum abstract
|
||||
var name = getIdent();
|
||||
|
||||
ensure(TBrOpen);
|
||||
|
||||
// TODO: optimize this
|
||||
var fields:Array<EnumField> = [];
|
||||
var fieldName:String = '';
|
||||
var enumArgs:Array<Argument> = null;
|
||||
//var tk = token();
|
||||
|
||||
while(!maybe(TBrClose)) {
|
||||
var tk = token();
|
||||
|
||||
switch(tk) {
|
||||
//case TBrClose:
|
||||
// break;
|
||||
case TSemicolon | TComma:
|
||||
if(fieldName.trim().length == 0) continue;
|
||||
|
||||
fields.push({
|
||||
name: fieldName,
|
||||
args: enumArgs == null ? [] : enumArgs
|
||||
});
|
||||
fieldName = '';
|
||||
enumArgs = null;
|
||||
case TPOpen:
|
||||
if(enumArgs != null) {
|
||||
error(ECustom("Cannot have multiple argument lists in one enum constructor"), tokenMin, tokenMax);
|
||||
break;
|
||||
}
|
||||
enumArgs = parseFunctionArgs(true);
|
||||
default:
|
||||
if(fieldName.trim().length != 0) {
|
||||
error(ECustom("Expected comma or semicolon"), tokenMin, tokenMax);
|
||||
break;
|
||||
}
|
||||
push(tk);
|
||||
fieldName = getIdent();
|
||||
}
|
||||
}
|
||||
|
||||
mk(EEnum({ name: name, fields: fields }, false), p1);
|
||||
case "cast":
|
||||
var tk = token();
|
||||
var e:Expr = null;
|
||||
var t:CType = null; // null if is unsafe cast
|
||||
switch(tk) {
|
||||
case TPOpen:
|
||||
e = parseExpr();
|
||||
ensure(TComma);
|
||||
t = parseType();
|
||||
ensure(TPClose);
|
||||
default:
|
||||
push(tk);
|
||||
e = parseExpr();
|
||||
}
|
||||
|
||||
mk(ECast(e, t));
|
||||
case "return":
|
||||
var tk = token();
|
||||
push(tk);
|
||||
@@ -1041,12 +1168,47 @@ class Parser {
|
||||
mk(EReturn(e),p1,if( e == null ) tokenMax else pmax(e));
|
||||
case "new":
|
||||
var a = [];
|
||||
var params:Null<Array<CType>> = null;
|
||||
a.push(getIdent());
|
||||
while( true ) {
|
||||
var tk = token();
|
||||
switch( tk ) {
|
||||
case TDot:
|
||||
a.push(getIdent());
|
||||
case TOp(op) :
|
||||
if( op == '<' ) {
|
||||
params = [];
|
||||
while(true) {
|
||||
switch(token()) {
|
||||
case TConst(c):
|
||||
params.push(CTExpr(mk(EConst(c))));
|
||||
case tk:
|
||||
push(tk);
|
||||
params.push(parseType());
|
||||
}
|
||||
tk = token();
|
||||
switch(tk) {
|
||||
case TComma: continue;
|
||||
case TOp(op):
|
||||
if( op == ">") break;
|
||||
if( op.charCodeAt(0) == ">".code ) {
|
||||
#if hscriptPos
|
||||
tokens.add({ t : TOp(op.substr(1)), min : tokenMax - op.length - 1, max : tokenMax });
|
||||
#else
|
||||
tokens.add(TOp(op.substr(1)));
|
||||
#end
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
case TPOpen:
|
||||
break;
|
||||
default:
|
||||
@@ -1055,7 +1217,7 @@ class Parser {
|
||||
}
|
||||
}
|
||||
var args = parseExprList(TPClose);
|
||||
mk(ENew(a.join("."), args), p1);
|
||||
mk(ENew(a.join("."), args, params), p1);
|
||||
case "throw":
|
||||
var e = parseExpr();
|
||||
mk(EThrow(e),p1,pmax(e));
|
||||
@@ -1185,6 +1347,8 @@ class Parser {
|
||||
return parseExprNext(mk(EUnop(op,false,e1),pmin(e1)));
|
||||
}
|
||||
return makeBinop(op,e1,parseExpr());
|
||||
case TId(op) if( opPriority.exists(op) ):
|
||||
return parseExprNext(makeBinop(op,e1,parseExpr()));
|
||||
case TDot | TQuestionDot:
|
||||
var field = getIdent();
|
||||
return parseExprNext(mk(EField(e1, field, tk == TQuestionDot), pmin(e1)));
|
||||
@@ -1205,7 +1369,7 @@ class Parser {
|
||||
}
|
||||
}
|
||||
|
||||
function parseFunctionArgs():Array<Argument> {
|
||||
function parseFunctionArgs(?enumArgs:Bool = false):Array<Argument> {
|
||||
var args:Array<Argument> = [];
|
||||
var tk = token();
|
||||
if( tk != TPClose ) {
|
||||
@@ -1231,6 +1395,7 @@ class Parser {
|
||||
if( maybe(TDoubleDot) )
|
||||
arg.t = parseType();
|
||||
if( maybe(TOp("="))) {
|
||||
if(enumArgs) unexpected(TOp("="));
|
||||
arg.value = parseExpr();
|
||||
arg.opt = true;
|
||||
}
|
||||
@@ -1289,7 +1454,13 @@ class Parser {
|
||||
if( op == "<" ) {
|
||||
params = [];
|
||||
while( true ) {
|
||||
params.push(parseType());
|
||||
switch( token() ) {
|
||||
case TConst(c):
|
||||
params.push(CTExpr(mk(EConst(c))));
|
||||
case tk:
|
||||
push(tk);
|
||||
params.push(parseType());
|
||||
}
|
||||
t = token();
|
||||
switch( t ) {
|
||||
case TComma: continue;
|
||||
@@ -1448,6 +1619,30 @@ class Parser {
|
||||
return args;
|
||||
}
|
||||
|
||||
function checkAccess(get:FieldPropertyAccess, set:FieldPropertyAccess, ?expr:Expr, ?type:CType) {
|
||||
#if hscriptPos
|
||||
var p1 = tokenMin;
|
||||
#end
|
||||
if(!allowTypes) {
|
||||
error(ECustom('"allowTypes" must be enabled for Property fields'), p1, pmax(expr));
|
||||
return;
|
||||
}
|
||||
switch([get, set]) {
|
||||
case [AGet, ASet] | [AGet, ANever] | [ANever, ASet]:
|
||||
if(isVar && (expr == null && type == null))
|
||||
error(ECustom('Property requires type-hint or initialization'), p1, pmax(expr));
|
||||
if(expr != null && !isVar)
|
||||
error(ECustom("Attempt to assign on field that is not a real variable"), p1, pmax(expr));
|
||||
else if(type == null && !isVar)
|
||||
error(ECustom('Property requires type-hint'), p1, tokenMax);
|
||||
case [ANever, ANever]:
|
||||
error(ECustom("Unsupported property combination"), p1, (expr == null) ? tokenMax : pmax(expr));
|
||||
default:
|
||||
if(expr == null && type == null)
|
||||
error(ECustom('Property requires type-hint or initialization'), p1, tokenMax);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------ module -------------------------------
|
||||
|
||||
public function parseModule( content : String, ?origin : String = "hscript" ):Array<ModuleDecl> {
|
||||
@@ -1966,7 +2161,7 @@ class Parser {
|
||||
if( StringTools.isEof(char) ) char = 0;
|
||||
if( !idents[char] ) {
|
||||
this.char = char;
|
||||
if(id == "is") return TOp("is");
|
||||
//if(id == "is") return TOp("is");
|
||||
return TId(id);
|
||||
}
|
||||
id += String.fromCharCode(char);
|
||||
|
||||
+95
-10
@@ -98,6 +98,8 @@ class Printer {
|
||||
add("(");
|
||||
type(t);
|
||||
add(")");
|
||||
case CTExpr(e):
|
||||
expr(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,26 +115,72 @@ class Printer {
|
||||
add("??NULL??");
|
||||
return;
|
||||
}
|
||||
switch( #if hscriptPos e.e #else e #end ) {
|
||||
case EImport(c, n):
|
||||
add("import " + c);
|
||||
switch(Tools.expr(e)) {
|
||||
case EImport(c, n, u):
|
||||
add('${u ? 'using' : 'import'} $c');
|
||||
if(n != null)
|
||||
add(' as $n');
|
||||
case EClass(name, fields, extend, interfaces):
|
||||
case EClass(name, fields, extend, interfaces, fnal):
|
||||
var isFinal = fnal != null && fnal;
|
||||
if(isFinal)
|
||||
add('final ');
|
||||
add('class $name');
|
||||
if (extend != null)
|
||||
add(' extends $extend');
|
||||
for(_interface in interfaces) {
|
||||
add(' implements $_interface');
|
||||
}
|
||||
add(' {\n');
|
||||
tabs += "\t";
|
||||
add(" {\n");
|
||||
for( e in fields ) {
|
||||
add(tabs);
|
||||
expr(e);
|
||||
//add(";\n");
|
||||
}
|
||||
//for(field in fields) {
|
||||
// expr(field);
|
||||
//}
|
||||
|
||||
tabs = tabs.substr(1);
|
||||
add("}");
|
||||
case EEnum(en, _): // TODO: enum abstracts
|
||||
add('enum ${en.name}');
|
||||
if(en.fields.length == 0) {
|
||||
add(' {}');
|
||||
return;
|
||||
}
|
||||
tabs += "\t";
|
||||
add(" {\n");
|
||||
|
||||
for(e in en.fields) {
|
||||
add(tabs);
|
||||
add(e.name);
|
||||
if(e.args.length > 0) {
|
||||
add("(");
|
||||
var first = true;
|
||||
for( a in e.args ) {
|
||||
if( first ) first = false else add(", ");
|
||||
if( a.opt ) add("?");
|
||||
add(a.name);
|
||||
addType(a.t);
|
||||
}
|
||||
add(')');
|
||||
}
|
||||
add(";\n");
|
||||
}
|
||||
|
||||
tabs = tabs.substr(1);
|
||||
add("}");
|
||||
case ECast(e, t):
|
||||
var safe = t != null;
|
||||
add("cast ");
|
||||
if(safe) add("(");
|
||||
expr(e);
|
||||
if(safe) {
|
||||
add(", ");
|
||||
addType(t);
|
||||
add(")");
|
||||
}
|
||||
case EConst(c):
|
||||
switch( c ) {
|
||||
case CInt(i): add(i);
|
||||
@@ -141,8 +189,35 @@ class Printer {
|
||||
}
|
||||
case EIdent(v):
|
||||
add(v);
|
||||
case EVar(n, t, e): // TODO: static, public, override
|
||||
add("var " + n);
|
||||
case EVar(n, t, e, p, s, pr, isFinal, isInline, get, set, _):
|
||||
if(p) add("public ");
|
||||
else if(pr) add("private ");
|
||||
if(s) add("static ");
|
||||
if(isInline) add("inline ");
|
||||
if(isFinal) add("final " + n);
|
||||
else add("var " + n);
|
||||
|
||||
if(get != null || set != null) {
|
||||
add("(");
|
||||
switch(get) {
|
||||
case ADefault: add("default, ");
|
||||
case ANull: add("null, ");
|
||||
case AGet: add("get, ");
|
||||
case ADynamic: add("dynamic, ");
|
||||
case ANever: add("never, ");
|
||||
default:
|
||||
}
|
||||
switch(set) {
|
||||
case ADefault: add("default");
|
||||
case ANull: add("null");
|
||||
case ASet: add("set");
|
||||
case ADynamic: add("dynamic");
|
||||
case ANever: add("never");
|
||||
default:
|
||||
}
|
||||
add(")");
|
||||
}
|
||||
|
||||
addType(t);
|
||||
if( e != null ) {
|
||||
add(" = ");
|
||||
@@ -182,7 +257,7 @@ class Printer {
|
||||
case ECall(e, args):
|
||||
if( e == null )
|
||||
expr(e);
|
||||
else switch( #if hscriptPos e.e #else e #end ) {
|
||||
else switch( Tools.expr(e)) {
|
||||
case EField(_), EIdent(_), EConst(_):
|
||||
expr(e);
|
||||
default:
|
||||
@@ -264,8 +339,18 @@ class Printer {
|
||||
expr(e);
|
||||
}
|
||||
add("]");
|
||||
case ENew(cl, args):
|
||||
add("new " + cl + "(");
|
||||
case ENew(cl, args, params):
|
||||
add("new " + cl);
|
||||
if(params != null) {
|
||||
add("<");
|
||||
var first = true;
|
||||
for( p in params ) {
|
||||
if( first ) first = false else add(", ");
|
||||
type(p);
|
||||
}
|
||||
add(">");
|
||||
}
|
||||
add("(");
|
||||
var first = true;
|
||||
for( e in args ) {
|
||||
if( first ) first = false else add(", ");
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package hscript;
|
||||
|
||||
import hscript.utils.UnsafeReflect;
|
||||
import hscript.Interp;
|
||||
import hscript.Expr.FieldPropertyAccess;
|
||||
|
||||
/**
|
||||
* Special variable that handles 'getter/setter' function calls
|
||||
* depending of the read/write access combination.
|
||||
*
|
||||
* Example:
|
||||
* ```haxe
|
||||
* public var myvar(get, set):Int;
|
||||
* var _myvar:Int = 10;
|
||||
*
|
||||
* function get_myvar():Int {
|
||||
* return _myvar;
|
||||
* }
|
||||
*
|
||||
* function set_myvar(val:Int):Int {
|
||||
* if(val > 10) return _myvar = val;
|
||||
* return val;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see https://haxe.org/manual/class-field-property.html
|
||||
*/
|
||||
@:access(hscript.Interp)
|
||||
@:structInit
|
||||
class Property {
|
||||
public var r:Dynamic;
|
||||
public var getter:FieldPropertyAccess;
|
||||
public var setter:FieldPropertyAccess;
|
||||
|
||||
var isVar:Bool;
|
||||
var interp:Interp;
|
||||
|
||||
public function new(r:Dynamic, getter:FieldPropertyAccess, setter:FieldPropertyAccess, isVar:Bool, interp:Interp) {
|
||||
this.r = r;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
this.isVar = isVar;
|
||||
this.interp = interp;
|
||||
}
|
||||
|
||||
var __allowReadAccess:Bool = false;
|
||||
var __allowWriteAccess:Bool = false;
|
||||
var __allowSetGet:Null<Bool> = null;
|
||||
|
||||
public function callGetter(name:String) {
|
||||
switch (getter) {
|
||||
case AGet | ADynamic:
|
||||
var fName:String = 'get_$name';
|
||||
if (!__allowReadAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
|
||||
if (interp.varExists(fName)) {
|
||||
return callAccessor(fName);
|
||||
} else
|
||||
interp.error(ECustom('Method $fName required by property $name is missing'));
|
||||
} else {
|
||||
if ((setter == ADefault || setter == ANull) || isVar)
|
||||
return r;
|
||||
else
|
||||
interp.error(ECustom('Field $name cannot be accessed because it is not a real variable${interp.isBypassAccessor ? '. Add @:isVar to enable it' : ''}'));
|
||||
}
|
||||
case ANever:
|
||||
interp.error(ECustom('This expression cannot be accessed for reading'));
|
||||
default:
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public function callSetter(name:String, val:Dynamic) {
|
||||
switch (setter) {
|
||||
case ASet | ADynamic:
|
||||
var fName:String = 'set_$name';
|
||||
if (!__allowWriteAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
|
||||
if (interp.varExists(fName))
|
||||
return callAccessor(fName, [val], true);
|
||||
else
|
||||
interp.error(ECustom('Method $fName required by property $name is missing'));
|
||||
} else {
|
||||
if ((getter == ADefault || getter == ANull) || isVar)
|
||||
return r = val;
|
||||
else
|
||||
interp.error(ECustom('Field $name cannot be accessed because it is not a real variable${interp.isBypassAccessor ? '. Add @:isVar to enable it' : ''}'));
|
||||
}
|
||||
case ANever:
|
||||
interp.error(ECustom('This expression cannot be accessed for writing'));
|
||||
default:
|
||||
}
|
||||
|
||||
return r = val;
|
||||
}
|
||||
|
||||
private function callAccessor(f:String, ?args:Array<Dynamic>, isWrite:Bool = false):Dynamic {
|
||||
var fn = interp.variables.get(f);
|
||||
var rt:Dynamic = null;
|
||||
if (fn != null && Reflect.isFunction(fn)) {
|
||||
if (isWrite) __allowWriteAccess = true;
|
||||
else __allowReadAccess = true;
|
||||
|
||||
rt = UnsafeReflect.callMethodUnsafe(null, fn, args == null ? [] : args);
|
||||
|
||||
if (isWrite) __allowWriteAccess = false;
|
||||
else __allowReadAccess = false;
|
||||
|
||||
return rt;
|
||||
} else
|
||||
interp.error(ECustom('Method $f required by property ${f.substr(3)} is missing'));
|
||||
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
+24
-1
@@ -59,7 +59,7 @@ class Tools {
|
||||
if( def != null ) f(def);
|
||||
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
|
||||
case ECheckType(e,_): f(e);
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ class Tools {
|
||||
case ECheckType(e,t): ECheckType(f(e), t);
|
||||
case EImport(c): EImport(c);
|
||||
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
|
||||
default: expr(e);
|
||||
}
|
||||
return mk(edef, e);
|
||||
}
|
||||
@@ -111,4 +112,26 @@ class Tools {
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE INLINE ON THIS FUNCTION
|
||||
**/
|
||||
public static function argCount(func: haxe.Constraints.Function): Int {
|
||||
// https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/hscript/Tools.hx#L206
|
||||
#if cpp
|
||||
return untyped __cpp__("{0}->__ArgCount()", func);
|
||||
#elseif js
|
||||
return untyped js.Syntax.code("{0}.length", func);
|
||||
#elseif hl
|
||||
var ft = hl.Type.getDynamic(func);
|
||||
if (ft.kind != HFun)
|
||||
return -1;
|
||||
return ft.getArgsCount();
|
||||
#else
|
||||
return -1;
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function isCustomAbstract(obj:Dynamic):Bool
|
||||
return obj != null && obj is IHScriptAbstractBehaviour;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -10,10 +10,10 @@ import haxe.macro.Type.FieldKind;
|
||||
import haxe.macro.Type.ClassField;
|
||||
import haxe.macro.Type.VarAccess;
|
||||
import haxe.macro.*;
|
||||
import Sys;
|
||||
|
||||
using StringTools;
|
||||
|
||||
// BIG TODO: make typed classes scriptable
|
||||
class ClassExtendMacro {
|
||||
public static inline final FUNC_PREFIX = "_HX_SUPER__";
|
||||
public static inline final CLASS_SUFFIX = "_HSX";
|
||||
@@ -29,6 +29,7 @@ class ClassExtendMacro {
|
||||
for(apply in Config.ALLOWED_CUSTOM_CLASSES) {
|
||||
Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.ClassExtendMacro.build())");
|
||||
}
|
||||
//Context.onAfterTyping(buildTyped);
|
||||
#end
|
||||
#end
|
||||
}
|
||||
@@ -61,6 +62,7 @@ class ClassExtendMacro {
|
||||
if(key == "away3d.tools.commands.Weld") return fields; // Error: Unknown identifier
|
||||
if(fkey == "hscript.CustomClassHandler.TemplateClass") return fields; // Error: Redefined
|
||||
if(fkey == "hscript.CustomClassHandler.CustomTemplateClass") return fields; // Error: Redefined
|
||||
if(fkey == "hscript.CustomClass") return fields; // Error: Redefined
|
||||
if(key == "sys.thread.EventLoop") return fields; // Error: cant override force inlined
|
||||
if(Config.DISALLOW_CUSTOM_CLASSES.contains(cl.module) || Config.DISALLOW_CUSTOM_CLASSES.contains(fkey)) return fields;
|
||||
if(cl.module.contains("_")) return fields; // Weird issue, sorry
|
||||
@@ -211,6 +213,15 @@ class ClassExtendMacro {
|
||||
continue;
|
||||
if (f.name == "new") {
|
||||
hasNew = true;
|
||||
switch (f.kind) {
|
||||
case FFun(fn):
|
||||
var constructor:Field = buildConstructor(fn.args);
|
||||
|
||||
shadowClass.fields.push(constructor);
|
||||
definedFields.push(f.name);
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (f.name.startsWith(FUNC_PREFIX))
|
||||
@@ -246,27 +257,24 @@ class ClassExtendMacro {
|
||||
overrideExpr = macro {
|
||||
var name:String = $v{name};
|
||||
|
||||
if (__custom__variables != null) {
|
||||
if(__custom__variables.exists(name)) {
|
||||
var v:Dynamic = null;
|
||||
if (Reflect.isFunction(v = __custom__variables.get(name))) {
|
||||
return v($a{arguments});
|
||||
}
|
||||
if (__interp != null && __class__fields.contains(name)) {
|
||||
var v:Dynamic = null;
|
||||
if (Reflect.isFunction(v = __interp.variables.get(name))) {
|
||||
return v($a{arguments});
|
||||
}
|
||||
}
|
||||
|
||||
return super.$name($a{arguments});
|
||||
};
|
||||
} else {
|
||||
overrideExpr = macro {
|
||||
var name:String = $v{name};
|
||||
|
||||
if (__custom__variables != null) {
|
||||
if(__custom__variables.exists(name)) {
|
||||
var v:Dynamic = null;
|
||||
if (Reflect.isFunction(v = __custom__variables.get(name))) {
|
||||
v($a{arguments});
|
||||
return;
|
||||
}
|
||||
if (__interp != null && __class__fields.contains(name)) {
|
||||
var v:Dynamic = null;
|
||||
if (Reflect.isFunction(v = __interp.variables.get(name))) {
|
||||
v($a{arguments});
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.$name($a{arguments});
|
||||
@@ -331,7 +339,6 @@ class ClassExtendMacro {
|
||||
pack: cl.pack.copy(),
|
||||
name: cl.name
|
||||
}, [
|
||||
{name: "IHScriptCustomAccessBehaviour", pack: ["hscript"]},
|
||||
{name: "IHScriptCustomClassBehaviour", pack: ["hscript"]}
|
||||
], false, true, false);
|
||||
shadowClass.name = '${cl.name}$CLASS_SUFFIX';
|
||||
@@ -341,20 +348,27 @@ class ClassExtendMacro {
|
||||
|
||||
// Adding hscript getters and setters
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__cachedFieldSet",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: Map<String, Dynamic>),
|
||||
access: [APublic, AStatic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__interp",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: hscript.Interp),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
/*
|
||||
shadowClass.fields.push({
|
||||
name: "__custom__variables",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: Map<String, Dynamic>),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
*/
|
||||
shadowClass.fields.push({
|
||||
name: "__allowSetGet",
|
||||
pos: Context.currentPos(),
|
||||
@@ -383,10 +397,7 @@ class ClassExtendMacro {
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: macro {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("get_" + name)();
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
return null;
|
||||
},
|
||||
args: [
|
||||
{
|
||||
@@ -407,10 +418,7 @@ class ClassExtendMacro {
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: macro {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("set_" + name)(val);
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
return null;
|
||||
},
|
||||
args: [
|
||||
{
|
||||
@@ -472,30 +480,80 @@ class ClassExtendMacro {
|
||||
|
||||
var hgetField = if(hasHgetInSuper) {
|
||||
macro {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
if (__interp != null) {
|
||||
if(__class__fields.contains(name)) {
|
||||
var v:Dynamic = __interp.variables.get(name);
|
||||
if(v != null && v is hscript.Property)
|
||||
return cast(v, hscript.Property).callGetter(name);
|
||||
return v;
|
||||
}
|
||||
else @:privateAccess {
|
||||
var cls:hscript.CustomClass = cast __interp.__customClass.__upperClass;
|
||||
while(cls != null) {
|
||||
if(cls.hasField(name))
|
||||
return cls.getField(name);
|
||||
|
||||
var prev:hscript.CustomClass = cast cls.__upperClass;
|
||||
if(prev == null)
|
||||
break;
|
||||
cls = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.hget(name);
|
||||
}
|
||||
} else {
|
||||
macro {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
if (__interp != null) {
|
||||
if(__class__fields.contains(name)) {
|
||||
var v:Dynamic = __interp.variables.get(name);
|
||||
if(v != null && v is hscript.Property)
|
||||
return cast(v, hscript.Property).callGetter(name);
|
||||
return v;
|
||||
}
|
||||
else @:privateAccess {
|
||||
var cls:hscript.CustomClass = cast __interp.__customClass.__upperClass;
|
||||
while(cls != null) {
|
||||
if(cls.hasField(name))
|
||||
return cls.getField(name);
|
||||
|
||||
var prev:hscript.CustomClass = cast cls.__upperClass;
|
||||
if(prev == null)
|
||||
break;
|
||||
cls = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return UnsafeReflect.getProperty(this, name);
|
||||
}
|
||||
}
|
||||
|
||||
var hsetField = if(hasHsetInSuper) {
|
||||
macro {
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
if (__interp != null) {
|
||||
if(__class__fields.contains(name)) {
|
||||
var v:Dynamic = __interp.variables.get(name);
|
||||
if(v != null && v is hscript.Property)
|
||||
return cast(v, hscript.Property).callSetter(name, val);
|
||||
__interp.variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
else @:privateAccess {
|
||||
var cls:hscript.CustomClass = cast __interp.__customClass.__upperClass;
|
||||
while(cls != null) {
|
||||
if(cls.hasField(name))
|
||||
return cls.setField(name, val);
|
||||
|
||||
var prev:hscript.CustomClass = cast cls.__upperClass;
|
||||
if(prev == null)
|
||||
break;
|
||||
cls = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(__real_fields.contains(name)) {
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
@@ -504,17 +562,33 @@ class ClassExtendMacro {
|
||||
}
|
||||
} else {
|
||||
macro {
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
if (__interp != null) {
|
||||
if(__class__fields.contains(name)) {
|
||||
var v:Dynamic = __interp.variables.get(name);
|
||||
if(v != null && v is hscript.Property)
|
||||
return cast(v, hscript.Property).callSetter(name, val);
|
||||
__interp.variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
else @:privateAccess {
|
||||
var cls:hscript.CustomClass = cast __interp.__customClass.__upperClass;
|
||||
while(cls != null) {
|
||||
if(cls.hasField(name))
|
||||
return cls.setField(name, val);
|
||||
|
||||
var prev:hscript.CustomClass = cast cls.__upperClass;
|
||||
if(prev == null)
|
||||
break;
|
||||
cls = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(__real_fields.contains(name)) {
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
__custom__variables.set(name, val);
|
||||
//__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@@ -579,6 +653,51 @@ class ClassExtendMacro {
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
static function buildConstructor(constArgs:Array<FunctionArg>):Field {
|
||||
var superCallArgs:Array<Expr> = [for (arg in constArgs) macro $i{arg.name}];
|
||||
|
||||
return {
|
||||
name: 'new',
|
||||
access: [APublic],
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
args: constArgs,
|
||||
expr: macro {
|
||||
// Call the super constructor with appropriate args
|
||||
super($a{superCallArgs});
|
||||
|
||||
if(__cachedFieldSet != null) {
|
||||
for(k => v in __cachedFieldSet) {
|
||||
Reflect.setProperty(this, k, v);
|
||||
}
|
||||
__cachedFieldSet.clear();
|
||||
__cachedFieldSet = null;
|
||||
}
|
||||
}
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
static function buildTyped(modules:Array<haxe.macro.Type.ModuleType>) {
|
||||
for(m in modules) {
|
||||
switch(m) {
|
||||
case TClassDecl(c):
|
||||
var cl = c.get();
|
||||
if (cl.isAbstract || cl.isExtern || cl.isFinal || cl.isInterface)
|
||||
continue;
|
||||
if (cl.params.length == 0)
|
||||
continue;
|
||||
if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith(CLASS_SUFFIX) && !cl.name.endsWith("_HSC"))
|
||||
buildTypedClass(cl);
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static function buildTypedClass(cl:ClassType) {}
|
||||
|
||||
static function buildShadowClass(cl:ClassType) {}
|
||||
}
|
||||
#else
|
||||
class ClassExtendMacro {
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package hscript.utils;
|
||||
|
||||
@:structInit
|
||||
class UsingEntry {
|
||||
public var call:Dynamic->String->Array<Dynamic>->Dynamic;
|
||||
public var fields:Array<String>;
|
||||
|
||||
public function hasField(name:String) {
|
||||
return fields.contains(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special class that handles static extension function calls.
|
||||
*
|
||||
* A static extension allows pseudo-extending
|
||||
* existing types without modifying their source.
|
||||
* In Haxe this is achieved by declaring a static method with a first argument
|
||||
* of the extending type and then bringing the defining class into context through `using`.
|
||||
*
|
||||
* Example:
|
||||
* ```haxe
|
||||
* class IntExtender {
|
||||
* static public function triple(i:Int) {
|
||||
* return i * 3;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* using IntExtender;
|
||||
*
|
||||
* trace(12.triple()); // 36
|
||||
* ```
|
||||
*
|
||||
* @see https://haxe.org/manual/lf-static-extension.html
|
||||
*/
|
||||
class UsingHandler {
|
||||
// Predefined static extension classes
|
||||
public static final defaultExtension:Map<String, UsingEntry> = [
|
||||
"StringTools" => { // https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/iris/Iris.hx#L45
|
||||
fields: Type.getClassFields(StringTools),
|
||||
call: function(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic {
|
||||
if (f == "isEof") // has @:noUsing
|
||||
return null;
|
||||
return switch (Type.typeof(o)) {
|
||||
case TInt if (f == 'hex'):
|
||||
StringTools.hex(o, args[0]);
|
||||
case TClass(String):
|
||||
var field = UnsafeReflect.field(StringTools, f);
|
||||
if (UnsafeReflect.isFunction(field)) UnsafeReflect.callMethodUnsafe(StringTools, field, [o].concat(args)); else null;
|
||||
default:
|
||||
null;
|
||||
}
|
||||
}
|
||||
},
|
||||
"Lambda" => { // https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/iris/Iris.hx#L62
|
||||
fields: Type.getClassFields(Lambda),
|
||||
call: function(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic {
|
||||
if (o != null && o.iterator != null) {
|
||||
var field = UnsafeReflect.field(Lambda, f);
|
||||
if (UnsafeReflect.isFunction(field)) {
|
||||
return UnsafeReflect.callMethodUnsafe(Lambda, field, [o].concat(args));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@:allow(hscript.CustomClass)
|
||||
@:allow(hscript.CustomClassHandler)
|
||||
public var usingEntries(default, null):Map<String, UsingEntry> = [];
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function registerEntry(name:String, entry:Dynamic->String->Array<Dynamic>->Dynamic, fields:Array<String>) {
|
||||
usingEntries.set(name, {call: entry, fields: fields});
|
||||
}
|
||||
|
||||
public function entryExists(name:String):Bool {
|
||||
return usingEntries.exists(name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user