Custom Classes Rework
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
bin/build-each.hxml
|
||||
-D hscriptPos
|
||||
-hl bin/Test.hl
|
||||
@@ -155,6 +155,7 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
|
||||
class CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomAccessBehaviour {
|
||||
public var __interp:Interp;
|
||||
public var __customClass:hscript.customclass.CustomClass;
|
||||
public var __allowSetGet:Bool = true;
|
||||
public var __custom__variables:Map<String, Dynamic>;
|
||||
public var __real_fields:Array<String>;
|
||||
|
||||
@@ -2,6 +2,7 @@ package hscript;
|
||||
|
||||
interface IHScriptCustomClassBehaviour {
|
||||
public var __interp:Interp;
|
||||
public var __customClass:hscript.customclass.CustomClass;
|
||||
public var __custom__variables:Map<String, Dynamic>;
|
||||
public var __allowSetGet:Bool;
|
||||
public var __real_fields:Array<String>;
|
||||
|
||||
+338
-4
@@ -28,6 +28,8 @@
|
||||
*/
|
||||
package hscript;
|
||||
|
||||
import hscript.customclass.CustomClassDecl;
|
||||
import hscript.customclass.CustomClass;
|
||||
import haxe.CallStack;
|
||||
import hscript.utils.UnsafeReflect;
|
||||
import haxe.PosInfos;
|
||||
@@ -65,8 +67,30 @@ class RedeclaredVar {
|
||||
public var depth:Int;
|
||||
}
|
||||
|
||||
@:access(hscript.customclass.CustomClass)
|
||||
@:access(hscript.customclass.CustomClassDecl)
|
||||
@:analyzer(optimize, local_dce, fusion, user_var_fusion)
|
||||
class Interp {
|
||||
private static var _customClasses:Map<String, CustomClassDecl> = new Map<String, CustomClassDecl>();
|
||||
|
||||
public static function getCustomClass(name:String) {
|
||||
return _customClasses.get(name);
|
||||
}
|
||||
|
||||
public static function customClassExist(name:String):Bool {
|
||||
return _customClasses.exists(name);
|
||||
}
|
||||
|
||||
private static function registerCustomClass(c:CustomClassDecl) {
|
||||
#if CUSTOM_CLASSES
|
||||
var name = c.classDecl.name;
|
||||
if (c.pkg != null) {
|
||||
name = c.pkg.join(".") + "." + name;
|
||||
}
|
||||
_customClasses.set(name, c);
|
||||
#end
|
||||
}
|
||||
|
||||
public var scriptObject(default, set):Dynamic;
|
||||
private var _hasScriptObject(default, null):Bool = false;
|
||||
private var _scriptObjectType(default, null):ScriptObjectType = SNull;
|
||||
@@ -137,11 +161,21 @@ class Interp {
|
||||
var curExpr:Expr;
|
||||
#end
|
||||
|
||||
public function new() {
|
||||
var _proxy(default, null):Null<CustomClass> = null;
|
||||
var _nextCallObject(default, null):Dynamic = null;
|
||||
var _inCustomClass(get, never):Bool;
|
||||
|
||||
private function get__inCustomClass():Bool {
|
||||
return _proxy != null;
|
||||
}
|
||||
|
||||
public function new(?proxy:CustomClass) {
|
||||
locals = new Map();
|
||||
declared = [];
|
||||
resetVariables();
|
||||
initOps();
|
||||
|
||||
this._proxy = proxy;
|
||||
}
|
||||
|
||||
private function resetVariables():Void {
|
||||
@@ -232,6 +266,12 @@ class Interp {
|
||||
}
|
||||
|
||||
public function setVar(name:String, v:Dynamic):Void {
|
||||
if (_inCustomClass && _proxy.superClass != null) {
|
||||
if (_proxy.superHasField(name)) {
|
||||
Reflect.setProperty(_proxy.superClass, name, v);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowStaticVariables && staticVariables.exists(name))
|
||||
staticVariables.set(name, v);
|
||||
else if (allowPublicVariables && publicVariables.exists(name))
|
||||
@@ -244,6 +284,28 @@ class Interp {
|
||||
var v = expr(e2);
|
||||
switch (Tools.expr(e1)) {
|
||||
case EIdent(id):
|
||||
// Make sure setting superclass/static fields directly works.
|
||||
// Also ensures property functions are accounted for.
|
||||
if (_inCustomClass) {
|
||||
if (_proxy.__class.hasField(id)) {
|
||||
var v = expr(e2);
|
||||
_proxy.__class.hset(id, v);
|
||||
return v;
|
||||
} else if (_proxy.superClass != null && _proxy.superHasField(id)) {
|
||||
var v = expr(e2);
|
||||
Reflect.setProperty(_proxy.superClass, id, v);
|
||||
return v;
|
||||
} else if (_proxy.hasVar(id)) {
|
||||
var v = expr(e2);
|
||||
_proxy.hset(id, v);
|
||||
return v;
|
||||
} else if (_proxy.superClass == null && _proxy.__class.classDecl.extend != null) {
|
||||
// Caches the declaration to set it once superClass is created
|
||||
var v = expr(e2);
|
||||
_proxy.cacheSuperField(id, v);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
var l = locals.get(id);
|
||||
if (l == null) {
|
||||
if (_hasScriptObject && !varExists(id)) {
|
||||
@@ -300,6 +362,33 @@ class Interp {
|
||||
}
|
||||
// TODO
|
||||
case EField(e, f, s):
|
||||
if(_inCustomClass) {
|
||||
switch (Tools.expr(e)) {
|
||||
case EIdent(id0):
|
||||
if (id0 == "this") {
|
||||
if (_proxy.hasField(f)) {
|
||||
var v = expr(e2);
|
||||
_proxy.hset(f, v);
|
||||
return v;
|
||||
}
|
||||
if (_proxy.superClass != null) {
|
||||
if (_proxy.superHasField(f)) {
|
||||
var v = expr(e2);
|
||||
Reflect.setProperty(_proxy.superClass, f, v);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
else if(_proxy.superClass == null && _proxy.__class.classDecl.extend != null){
|
||||
// Caches the declaration to set it once superClass is created
|
||||
var v = expr(e2);
|
||||
_proxy.cacheSuperField(f, v);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
var obj = expr(e);
|
||||
if(s && obj == null) return null;
|
||||
v = set(obj, f, v);
|
||||
@@ -556,6 +645,20 @@ class Interp {
|
||||
return null;
|
||||
id = StringTools.trim(id);
|
||||
|
||||
// Custom Class
|
||||
if (_inCustomClass) {
|
||||
_nextCallObject = null;
|
||||
if (id == "super") {
|
||||
if (_proxy.superClass == null) {
|
||||
return _proxy.superConstructor;
|
||||
} else {
|
||||
return _proxy.superClass;
|
||||
}
|
||||
}
|
||||
else if (id == "this")
|
||||
return _proxy;
|
||||
}
|
||||
|
||||
if (locals.exists(id)) {
|
||||
var l = locals.get(id);
|
||||
if(l != null)
|
||||
@@ -571,6 +674,35 @@ class Interp {
|
||||
if(customClasses.exists(id))
|
||||
return customClasses.get(id);
|
||||
|
||||
if(customClassExist(id))
|
||||
return getCustomClass(id);
|
||||
|
||||
// Custom Class
|
||||
if (_inCustomClass) {
|
||||
// We are calling a LOCAL function from the same module.
|
||||
if (_proxy.__class.hasField(id)) {
|
||||
// Static access
|
||||
return _proxy.__class.hget(id);
|
||||
}
|
||||
if (_proxy.hasFunction(id)) {
|
||||
_nextCallObject = _proxy;
|
||||
return _proxy.resolveField(id);
|
||||
}
|
||||
else if (_proxy.superHasField(id)) {
|
||||
_nextCallObject = _proxy.superClass;
|
||||
return Reflect.getProperty(_proxy.superClass, id);
|
||||
} else {
|
||||
try {
|
||||
var r = _proxy.resolveField(id);
|
||||
_nextCallObject = _proxy;
|
||||
return r;
|
||||
} catch (e:Dynamic) {
|
||||
if(doException)
|
||||
error(EUnknownVariable(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_hasScriptObject) {
|
||||
// search in object
|
||||
if (id == "this") {
|
||||
@@ -639,7 +771,7 @@ class Interp {
|
||||
var e = e.e;
|
||||
#end
|
||||
switch (e) {
|
||||
case EClass(name, fields, extend, interfaces):
|
||||
case EClass(name, fields, extend, interfaces): //Not sure if leave the old system or implement the new one.
|
||||
if (customClasses.exists(name))
|
||||
error(EAlreadyExistingClass(name));
|
||||
|
||||
@@ -779,7 +911,21 @@ class Interp {
|
||||
error(EInvalidOp(op));
|
||||
}
|
||||
case ECall(e, params):
|
||||
var args:Array<Dynamic> = [for(p in params) expr(p)];
|
||||
var args:Array<Dynamic> = []; // [for (p in params) expr(p)];
|
||||
for (p in params) {
|
||||
switch (Tools.expr(p)) {
|
||||
case EIdent(id):
|
||||
var ident = resolve(id);
|
||||
if (ident is CustomClass) {
|
||||
var customClass:CustomClass = cast ident; // Pass the underlying superclass if exist
|
||||
args.push(customClass.superClass != null ? customClass.superClass : customClass);
|
||||
} else {
|
||||
args.push(ident);
|
||||
}
|
||||
default:
|
||||
args.push(expr(p));
|
||||
}
|
||||
}
|
||||
|
||||
switch (Tools.expr(e)) {
|
||||
case EField(e, f, s):
|
||||
@@ -1195,6 +1341,13 @@ class Interp {
|
||||
if (o == null)
|
||||
error(EInvalidAccess(f));
|
||||
|
||||
if (o is CustomClass) {
|
||||
if (cast(o, CustomClass).__class.hasField(f)) {
|
||||
var clsName:String = cast(o, CustomClass).className;
|
||||
error(ECustom('The field ${clsName}.${f} should be accessed in a static way.'));
|
||||
}
|
||||
}
|
||||
|
||||
var cls = Type.getClass(o);
|
||||
if (useRedirects && {
|
||||
var cl:Null<String> = getClassType(o, cls);
|
||||
@@ -1246,6 +1399,13 @@ class Interp {
|
||||
if (o == null)
|
||||
error(EInvalidAccess(f));
|
||||
|
||||
if (o is CustomClass) {
|
||||
if (cast(o, CustomClass).__class.hasField(f)) {
|
||||
var clsName:String = cast(o, CustomClass).className;
|
||||
error(ECustom('The field ${clsName}.${f} should be accessed in a static way.'));
|
||||
}
|
||||
}
|
||||
|
||||
if (useRedirects && {
|
||||
var cl:Null<String> = getClassType(o);
|
||||
cl != null && setRedirects.exists(cl) && (_setRedirect = setRedirects[cl]) != null;
|
||||
@@ -1288,20 +1448,140 @@ class Interp {
|
||||
}
|
||||
|
||||
function fcall(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic {
|
||||
// Custom logic to handle super calls to prevent infinite recursion
|
||||
if(_inCustomClass) {
|
||||
// Force call super function.
|
||||
if(o == _proxy.superClass)
|
||||
return call(o, Reflect.field(_proxy.superClass, '_HX_SUPER__${f}'), args);
|
||||
}
|
||||
else if (o is CustomClass) {
|
||||
_nextCallObject = null;
|
||||
var proxy:CustomClass = cast o;
|
||||
return proxy.callFunction(f, args);
|
||||
}
|
||||
else if (o is CustomClassDecl) {
|
||||
// Static function call
|
||||
var staticClass:CustomClassDecl = cast o;
|
||||
if(staticClass.hasFunction(f))
|
||||
return staticClass.callFunction(f, args);
|
||||
}
|
||||
|
||||
if(_hasScriptObject && o == CustomClassHandler.staticHandler) {
|
||||
return UnsafeReflect.callMethodUnsafe(scriptObject, UnsafeReflect.field(scriptObject, "_HX_SUPER__" + f), args);
|
||||
}
|
||||
return call(o, get(o, f), args);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (_inCustomClass) {
|
||||
if (o == null && _nextCallObject != null) {
|
||||
o = _nextCallObject;
|
||||
}
|
||||
if (f == null) {
|
||||
error(EInvalidAccess(f));
|
||||
}
|
||||
if (o == _proxy) {
|
||||
// If we are calling this.fn(), special handling is needed to prevent the local scope from being destroyed.
|
||||
// By checking `o == _proxy`, we handle BOTH fn() and this.fn().
|
||||
// super.fn() is exempt since it is not scripted.
|
||||
return callThis(f, args);
|
||||
}
|
||||
try {
|
||||
var r = Reflect.callMethod(o, f, args);
|
||||
_nextCallObject = null;
|
||||
return r;
|
||||
} catch (e) {
|
||||
error(ECustom('Script threw an exception: \n${f}'));
|
||||
_nextCallObject = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return UnsafeReflect.callMethodSafe(o, f, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a given function on the current proxy with the given arguments.
|
||||
* Ensures that the local scope is not destroyed.
|
||||
* @param fun The function to call.
|
||||
* @param args The arguments to apply to that function.
|
||||
* @return The result of the function call.
|
||||
*/
|
||||
function callThis(fun:Dynamic, args:Array<Dynamic>):Dynamic {
|
||||
// If we are calling this.fn(), special handling is needed to prevent the local scope from being destroyed.
|
||||
// Store the local scope.
|
||||
var capturedLocals = this.duplicate(locals);
|
||||
var capturedDeclared = this.declared;
|
||||
var capturedDepth = this.depth;
|
||||
|
||||
this.depth++;
|
||||
|
||||
// Call the function.
|
||||
try {
|
||||
var result = Reflect.callMethod(_proxy, fun, args);
|
||||
|
||||
// Restore the local scope.
|
||||
this.locals = capturedLocals;
|
||||
this.declared = capturedDeclared;
|
||||
this.depth = capturedDepth;
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
error(ECustom('Script threw an exception: \n${fun}'));
|
||||
|
||||
// Restore the local scope.
|
||||
this.locals = capturedLocals;
|
||||
this.declared = capturedDeclared;
|
||||
this.depth = capturedDepth;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function cnew(cl:String, args:Array<Dynamic>):Dynamic {
|
||||
// Custom Class
|
||||
if (customClassExist(cl)) {
|
||||
var proxy:CustomClass = new CustomClass(getCustomClass(cl), args, null, this);
|
||||
return proxy;
|
||||
}
|
||||
if (_inCustomClass) {
|
||||
if (_proxy.__class.pkg != null) {
|
||||
var packagedClass = _proxy.__class.pkg.join(".") + "." + cl;
|
||||
if (customClassExist(packagedClass)) {
|
||||
var proxy:CustomClass = new CustomClass(getCustomClass(cl), args, null, this);
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
|
||||
if (_proxy.__class.imports != null && _proxy.__class.imports.exists(cl)) {
|
||||
var importedClass = _proxy.__class.imports.get(cl).fullPath;
|
||||
if (customClassExist(importedClass)) {
|
||||
var proxy:CustomClass = new CustomClass(getCustomClass(cl), args, null, this);
|
||||
return proxy;
|
||||
}
|
||||
|
||||
var c = Type.resolveClass(importedClass);
|
||||
if (c != null) {
|
||||
return Type.createInstance(c, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var c:Dynamic = resolve(cl);
|
||||
if (c == null)
|
||||
c = Type.resolveClass(cl);
|
||||
@@ -1312,4 +1592,58 @@ class Interp {
|
||||
return Type.createInstance(c, args);
|
||||
}
|
||||
}
|
||||
|
||||
public function registerModule(module:Array<ModuleDecl>) {
|
||||
var pkg:Array<String> = null;
|
||||
var imports:Map<String, CustomClassImport> = [];
|
||||
for (decl in module) {
|
||||
switch (decl) {
|
||||
case DPackage(path):
|
||||
pkg = path;
|
||||
case DImport(path, _):
|
||||
if (importBlocklist.contains(path.join(".")))
|
||||
continue;
|
||||
var last = path[path.length - 1];
|
||||
|
||||
var importedClass:CustomClassImport = {
|
||||
name: last,
|
||||
pkg: [for (e in path) e.trim()],
|
||||
fullPath: path.join(".")
|
||||
}
|
||||
imports.set(importedClass.name, importedClass);
|
||||
case DClass(c):
|
||||
var extend = c.extend;
|
||||
if (extend != null) {
|
||||
var superClassPath = new Printer().typeToString(extend);
|
||||
if (imports.exists(superClassPath)) {
|
||||
switch (extend) {
|
||||
case CTPath(_, params):
|
||||
extend = CTPath(imports.get(superClassPath).pkg, params);
|
||||
case _:
|
||||
}
|
||||
}
|
||||
}
|
||||
var classDecl:ClassDecl = {
|
||||
name: c.name,
|
||||
params: c.params,
|
||||
meta: c.meta,
|
||||
isPrivate: c.isPrivate,
|
||||
extend: extend,
|
||||
implement: c.implement,
|
||||
fields: c.fields,
|
||||
isExtern: c.isExtern
|
||||
};
|
||||
|
||||
var customClassDecl:CustomClassDecl = {
|
||||
classDecl: classDecl,
|
||||
imports: imports,
|
||||
pkg: pkg
|
||||
}
|
||||
customClassDecl.cacheFields();
|
||||
registerCustomClass(customClassDecl);
|
||||
case DTypedef(_):
|
||||
//TODO: maybe make this work :3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,412 @@
|
||||
package hscript.customclass;
|
||||
|
||||
import hscript.utils.UnsafeReflect;
|
||||
import haxe.Constraints.Function;
|
||||
import hscript.Expr;
|
||||
import hscript.Expr.VarDecl;
|
||||
import hscript.Expr.FunctionDecl;
|
||||
import hscript.Expr.FieldDecl;
|
||||
|
||||
using Lambda;
|
||||
using StringTools;
|
||||
|
||||
/**
|
||||
* Provides handlers for custom classes.
|
||||
* Based on Polymod Hscript class system
|
||||
* @see https://github.com/larsiusprime/polymod/tree/master/polymod/hscript/_internal
|
||||
*/
|
||||
@:access(hscript.customclass.CustomClassDecl)
|
||||
class CustomClass implements IHScriptCustomAccessBehaviour{
|
||||
public var interp:Interp;
|
||||
|
||||
public var superClass:Dynamic;
|
||||
public var superConstructor(default, null):Dynamic;
|
||||
|
||||
public var className(get, never):String;
|
||||
|
||||
private var __class:CustomClassDecl;
|
||||
private var _cachedSuperFields:Null<Map<String, Dynamic>> = null;
|
||||
|
||||
private var _cachedFieldDecls:Map<String, FieldDecl> = null;
|
||||
private var _cachedFunctionDecls:Map<String, FunctionDecl> = null;
|
||||
private var _cachedVarDecls:Map<String, VarDecl> = null;
|
||||
|
||||
public var __allowSetGet:Bool = true;
|
||||
|
||||
private function get_className():String {
|
||||
var name = "";
|
||||
if (__class.pkg != null) {
|
||||
name += __class.pkg.join(".");
|
||||
}
|
||||
name += __class.classDecl.name;
|
||||
return name;
|
||||
}
|
||||
|
||||
public function new(__class:CustomClassDecl, args:Array<Dynamic>, ?extendFieldDecl:Map<String, Dynamic>, ?ogInterp:Interp) {
|
||||
this.__class = __class;
|
||||
this.interp = new Interp(this);
|
||||
|
||||
if(ogInterp != null && ogInterp.importFailedCallback != null && ogInterp.errorHandler != null) {
|
||||
interp.importFailedCallback = ogInterp.importFailedCallback;
|
||||
interp.errorHandler = ogInterp.errorHandler;
|
||||
interp.allowStaticVariables = ogInterp.allowStaticVariables;
|
||||
interp.staticVariables = ogInterp.staticVariables;
|
||||
}
|
||||
|
||||
buildImports();
|
||||
|
||||
if(extendFieldDecl != null)
|
||||
_cachedSuperFields = extendFieldDecl;
|
||||
|
||||
buildClass();
|
||||
|
||||
if(hasFunction('new')) {
|
||||
buildSuperConstructor();
|
||||
callFunction('new', args);
|
||||
if(this.superClass == null && this.__class.classDecl.extend != null)
|
||||
this.interp.error(ECustom("super() not called"));
|
||||
}
|
||||
else if(__class.classDecl.extend != null) {
|
||||
createSuperClass(args);
|
||||
}
|
||||
}
|
||||
|
||||
function buildClass() {
|
||||
_cachedFieldDecls = [];
|
||||
_cachedFunctionDecls = [];
|
||||
_cachedVarDecls = [];
|
||||
|
||||
if(_cachedSuperFields == null) _cachedSuperFields = [];
|
||||
|
||||
for (f in __class.classDecl.fields) {
|
||||
if(f.access.contains(AStatic)) continue; // Skip static field. It's handled by CustomClassDecl.hx
|
||||
_cachedFieldDecls.set(f.name, f);
|
||||
switch (f.kind) {
|
||||
case KFunction(fn):
|
||||
_cachedFunctionDecls.set(f.name, fn);
|
||||
case KVar(v):
|
||||
_cachedVarDecls.set(f.name, v);
|
||||
if (v.expr != null) {
|
||||
var varValue = this.interp.expr(v.expr);
|
||||
this.interp.variables.set(f.name, varValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!_cachedSuperFields.empty()) {
|
||||
for (f => v in _cachedSuperFields) {
|
||||
this.hset(f, v);
|
||||
}
|
||||
_cachedSuperFields.clear();
|
||||
}
|
||||
}
|
||||
|
||||
function buildSuperConstructor() {
|
||||
superConstructor = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||
createSuperClass(args);
|
||||
});
|
||||
}
|
||||
|
||||
private function createSuperClass(args:Array<Dynamic> = null) {
|
||||
if (args == null)
|
||||
args = [];
|
||||
|
||||
var extendString = new Printer().typeToString(__class.classDecl.extend);
|
||||
if (__class.pkg != null && extendString.indexOf(".") == -1) {
|
||||
extendString = __class.pkg.join(".") + "." + extendString;
|
||||
}
|
||||
|
||||
if (Interp.customClassExist(extendString)) {
|
||||
var abstractSuperClass:CustomClass = new CustomClass(Interp.getCustomClass(extendString), args, _cachedSuperFields, this.interp);
|
||||
superClass = abstractSuperClass;
|
||||
} else {
|
||||
var c = Type.resolveClass('${extendString}_HSX');
|
||||
if (c == null) {
|
||||
interp.error(ECustom("could not resolve super class: " + extendString));
|
||||
}
|
||||
if(_cachedSuperFields != null) {
|
||||
Reflect.setField(c, "__cachedFields", _cachedSuperFields); // Static field
|
||||
}
|
||||
|
||||
superClass = Type.createInstance(c, args);
|
||||
superClass.__customClass = this;
|
||||
superClass.__real_fields = Type.getInstanceFields(c);
|
||||
}
|
||||
}
|
||||
|
||||
function buildImports() {
|
||||
// TODO: implement Alias imports
|
||||
var i:Int = 0;
|
||||
for(_import in __class.imports) {
|
||||
var importedClass = _import.fullPath;
|
||||
|
||||
if(Interp.customClassExist(importedClass) && this.interp.importFailedCallback != null) {
|
||||
this.interp.importFailedCallback(importedClass.split("."));
|
||||
continue;
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
var e:Expr = {
|
||||
e: ExprDef.EImport(importedClass),
|
||||
pmin: 0,
|
||||
pmax: 0,
|
||||
origin: this.className,
|
||||
line: i
|
||||
};
|
||||
#else
|
||||
var e = Expr.EImport(importedClass);
|
||||
#end
|
||||
this.interp.expr(e);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public function callFunction(name:String, args:Array<Dynamic> = null):Dynamic {
|
||||
var r:Dynamic = null;
|
||||
|
||||
if (hasField(name)) {
|
||||
var fn = getFunction(name);
|
||||
var previousValues:Map<String, Dynamic> = [];
|
||||
var i = 0;
|
||||
for (a in fn.args) {
|
||||
var value:Dynamic = null;
|
||||
|
||||
if (args != null && i < args.length) {
|
||||
value = args[i];
|
||||
} else if (a.value != null) {
|
||||
value = interp.expr(a.value);
|
||||
}
|
||||
// NOTE: We assign these as variables rather than locals because those get wiped when we enter the function.
|
||||
if (interp.variables.exists(a.name)) {
|
||||
previousValues.set(a.name, interp.variables.get(a.name));
|
||||
}
|
||||
interp.variables.set(a.name, value);
|
||||
i++;
|
||||
}
|
||||
try{
|
||||
r = interp.execute(fn.body);
|
||||
}
|
||||
catch(e:hscript.Expr.Error){
|
||||
// A script error occurred while executing the script function.
|
||||
// Purge the function from the cache so it is not called again.
|
||||
purgeFunction(name);
|
||||
}
|
||||
|
||||
for (a in fn.args) {
|
||||
if (previousValues.exists(a.name)) {
|
||||
interp.variables.set(a.name, previousValues.get(a.name));
|
||||
} else {
|
||||
interp.variables.remove(a.name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var fixedArgs = [];
|
||||
// OVERRIDE CHANGE: Use _HX_SUPER__ when calling superclass
|
||||
var fixedName = '_HX_SUPER__${name}';
|
||||
for (a in args) {
|
||||
if ((a is CustomClass)) {
|
||||
fixedArgs.push(cast(a, CustomClass).superClass);
|
||||
} else {
|
||||
fixedArgs.push(a);
|
||||
}
|
||||
}
|
||||
var superFn = Reflect.field(superClass, fixedName);
|
||||
if(superFn == null) {
|
||||
this.interp.error(ECustom('Error while calling function super.${name}(): EInvalidAccess'
|
||||
+ '\n'
|
||||
+ 'InvalidAccess error: Super function "${name}" does not exist! Define it or call the correct superclass function.'));
|
||||
}
|
||||
r = Reflect.callMethod(superClass, superFn, fixedArgs);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private function hasField(name:String):Bool {
|
||||
return _cachedFieldDecls != null ? _cachedFieldDecls.exists(name) : false;
|
||||
}
|
||||
|
||||
private function getField(name:String):FieldDecl {
|
||||
return _cachedFieldDecls != null ? _cachedFieldDecls.get(name) : null;
|
||||
}
|
||||
|
||||
private function hasVar(name:String):Bool {
|
||||
return _cachedVarDecls != null ? _cachedVarDecls.exists(name) : false;
|
||||
}
|
||||
|
||||
private function getVar(name:String):VarDecl {
|
||||
return _cachedVarDecls != null ? _cachedVarDecls.get(name): null;
|
||||
}
|
||||
|
||||
private function hasFunction(name:String):Bool {
|
||||
return _cachedFunctionDecls != null ? _cachedFunctionDecls.exists(name) : false;
|
||||
}
|
||||
|
||||
private function getFunction(name:String):FunctionDecl {
|
||||
return _cachedFunctionDecls != null ? _cachedFunctionDecls.get(name) : null;
|
||||
}
|
||||
|
||||
private function cacheSuperField(name:String, value:Dynamic) {
|
||||
if(_cachedSuperFields != null) {
|
||||
_cachedSuperFields.set(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
var __superClassFieldList:Array<String> = null;
|
||||
|
||||
public function superHasField(name:String):Bool {
|
||||
if(superClass == null) return false;
|
||||
|
||||
// Reflect.hasField(this, name) is REALLY expensive so we use a cache.
|
||||
if(__superClassFieldList == null) {
|
||||
__superClassFieldList = Reflect.fields(superClass).concat(Type.getInstanceFields(Type.getClass(superClass)));
|
||||
}
|
||||
|
||||
return __superClassFieldList.indexOf(name) != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a function from the cache.
|
||||
* This is useful when a function is broken and needs to be skipped.
|
||||
* @param name The name of the function to remove from the cache.
|
||||
*/
|
||||
private function purgeFunction(name:String):Void {
|
||||
if (_cachedFunctionDecls != null) {
|
||||
_cachedFunctionDecls.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Access fields
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
return resolveField(name);
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
switch (name) {
|
||||
default:
|
||||
if (hasVar(name)) {
|
||||
if(__allowSetGet && hasFunction('set_${name}'))
|
||||
return __callSetter(name, val);
|
||||
|
||||
this.interp.variables.set(name, val);
|
||||
}
|
||||
else if (this.superClass != null) {
|
||||
if(Type.getClass(this.superClass) == null) {
|
||||
// Anonymous structure
|
||||
if(Reflect.hasField(this.superClass, name)) {
|
||||
Reflect.setField(this.superClass, name, val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
else if (this.superClass is CustomClass) {
|
||||
var superCustomClass:CustomClass = cast(this.superClass, CustomClass);
|
||||
try {
|
||||
superCustomClass.__allowSetGet = this.__allowSetGet;
|
||||
return superCustomClass.hset(name, val);
|
||||
} catch (e:Dynamic) {}
|
||||
}
|
||||
else if(superHasField(name)){
|
||||
if(__allowSetGet)
|
||||
Reflect.setProperty(this.superClass, name, val);
|
||||
else
|
||||
Reflect.setField(this.superClass, name, val);
|
||||
}
|
||||
else {
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '"
|
||||
+ Type.getClassName(Type.getClass(this.superClass)) + "'";
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "'";
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('get_${name}');
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('set_${name}', [val]);
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
private function resolveField(name:String):Dynamic {
|
||||
switch (name) {
|
||||
case "superClass":
|
||||
return this.superClass;
|
||||
case "createSuperClass":
|
||||
return this.createSuperClass;
|
||||
case "hasFunction":
|
||||
return this.hasFunction;
|
||||
case "callFunction":
|
||||
return this.callFunction;
|
||||
default:
|
||||
if (hasFunction(name)) {
|
||||
var fn:Function = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||
return this.callFunction(name, args);
|
||||
});
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
if (hasVar(name)) {
|
||||
var value:Dynamic = null;
|
||||
|
||||
if(__allowSetGet && hasFunction('get_${name}')) {
|
||||
value = __callGetter(name);
|
||||
}
|
||||
else if (this.interp.variables.exists(name)) {
|
||||
value = this.interp.variables.get(name);
|
||||
} else {
|
||||
var v = getVar(name);
|
||||
|
||||
if (v.expr != null) {
|
||||
value = this.interp.expr(v.expr);
|
||||
this.interp.variables.set(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
if (this.superClass != null) {
|
||||
if (Type.getClass(this.superClass) == null) {
|
||||
// Anonymous structure
|
||||
if (Reflect.hasField(this.superClass, name)) {
|
||||
return Reflect.field(this.superClass, name);
|
||||
} else {
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '"
|
||||
+ Type.getClassName(Type.getClass(this.superClass)) + "'";
|
||||
}
|
||||
}
|
||||
|
||||
if (this.superClass is CustomClass) {
|
||||
var superCustomClass:CustomClass = cast(this.superClass, CustomClass);
|
||||
try {
|
||||
superCustomClass.__allowSetGet = this.__allowSetGet;
|
||||
return superCustomClass.hget(name);
|
||||
} catch (e:Dynamic) {}
|
||||
}
|
||||
|
||||
var fields = Type.getInstanceFields(Type.getClass(this.superClass));
|
||||
if (fields.contains(name)) {
|
||||
return __allowSetGet ? Reflect.getProperty(this.superClass, name) : Reflect.field(this.superClass, name);
|
||||
} else {
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '"
|
||||
+ Type.getClassName(Type.getClass(this.superClass)) + "'";
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "'";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package hscript.customclass;
|
||||
|
||||
import hscript.customclass.utils.FunctionUtils;
|
||||
import haxe.Constraints.Function;
|
||||
import hscript.Expr.FieldDecl;
|
||||
import hscript.Expr.VarDecl;
|
||||
import hscript.Expr.FunctionDecl;
|
||||
|
||||
@:structInit
|
||||
class CustomClassDecl implements IHScriptCustomAccessBehaviour {
|
||||
public var classDecl:Expr.ClassDecl;
|
||||
public var imports:Map<String, CustomClassImport>;
|
||||
public var pkg:Null<Array<String>> = null;
|
||||
|
||||
public var staticInterp:Interp = new Interp();
|
||||
|
||||
var _cachedStaticFields:Map<String, FieldDecl> = [];
|
||||
var _cachedStaticFunctions:Map<String, FunctionDecl> = [];
|
||||
var _cachedStaticVariables:Map<String, VarDecl> = [];
|
||||
|
||||
public var __allowSetGet:Bool = true;
|
||||
|
||||
public function cacheFields() {
|
||||
for (f in classDecl.fields) {
|
||||
if (f.access.contains(AStatic)) {
|
||||
_cachedStaticFields.set(f.name, f);
|
||||
switch (f.kind) {
|
||||
case KFunction(fn):
|
||||
_cachedStaticFunctions.set(f.name, fn);
|
||||
case KVar(v):
|
||||
_cachedStaticVariables.set(f.name, v);
|
||||
if (v.expr != null) {
|
||||
var varValue = this.staticInterp.expr(v.expr);
|
||||
this.staticInterp.variables.set(f.name, varValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function callFunction(name:String, ?args:Array<Dynamic>) {
|
||||
var func:FunctionDecl = getFunction(name);
|
||||
|
||||
if(func != null) {
|
||||
return FunctionUtils.callStaticFunction(name, this, staticInterp, func, args != null ? args : []);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hasField(name:String):Bool {
|
||||
return _cachedStaticFields.exists(name);
|
||||
}
|
||||
|
||||
private function hasFunction(name:String) {
|
||||
return _cachedStaticFunctions.exists(name);
|
||||
}
|
||||
|
||||
private function getFunction(name:String):FunctionDecl {
|
||||
return _cachedStaticFunctions.get(name);
|
||||
}
|
||||
|
||||
private function hasVar(name:String):Bool {
|
||||
return _cachedStaticVariables.exists(name);
|
||||
}
|
||||
|
||||
private function getVar(name:String):Dynamic {
|
||||
var staticVar = _cachedStaticVariables.get(name);
|
||||
|
||||
if(staticVar != null) {
|
||||
var varValue:Dynamic = null;
|
||||
if(!this.staticInterp.variables.exists(name)) {
|
||||
if(staticVar.expr != null) {
|
||||
varValue = this.staticInterp.expr(staticVar.expr);
|
||||
this.staticInterp.variables.set(name, varValue);
|
||||
}
|
||||
}
|
||||
else {
|
||||
varValue = this.staticInterp.variables.get(name);
|
||||
}
|
||||
return varValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a function from the cache.
|
||||
* This is useful when a function is broken and needs to be skipped.
|
||||
* @param name The name of the function to remove from the cache.
|
||||
*/
|
||||
private function purgeFunction(name:String):Void {
|
||||
if (_cachedStaticFunctions != null) {
|
||||
_cachedStaticFunctions.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
var r:Dynamic = null;
|
||||
|
||||
if(hasVar(name)) {
|
||||
if(__allowSetGet && hasFunction('get_${name}'))
|
||||
r = __callGetter(name);
|
||||
else
|
||||
r = getVar(name);
|
||||
return r;
|
||||
}
|
||||
if(hasFunction(name)) {
|
||||
var fn:Function = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||
return this.callFunction(name, args);
|
||||
});
|
||||
return fn;
|
||||
}
|
||||
|
||||
throw "static field '" + name + "' does not exist in custom class '" + this.classDecl.name + "'";
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if (hasVar(name)) {
|
||||
if (__allowSetGet && hasFunction('set_${name}'))
|
||||
return __callSetter(name, val);
|
||||
else {
|
||||
this.staticInterp.variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
throw "static field '" + name + "' does not exist in custom class '" + this.classDecl.name + "'";
|
||||
}
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('get_${name}');
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('set_${name}', [val]);
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
typedef CustomClassImport = {
|
||||
var ?name:String;
|
||||
var ?pkg:Array<String>;
|
||||
var ?fullPath:String; // pkg.pkg.pkg.name
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package hscript.customclass.utils;
|
||||
|
||||
import hscript.Expr.FunctionDecl;
|
||||
|
||||
@:access(hscript.customclass.CustomClassDecl)
|
||||
class FunctionUtils {
|
||||
public static function callStaticFunction(name:String, classDecl:CustomClassDecl, interp:Interp, fn:FunctionDecl, args:Array<Dynamic> = null) {
|
||||
var r:Dynamic = null;
|
||||
|
||||
var previousValues:Map<String, Dynamic> = [];
|
||||
var i = 0;
|
||||
for (a in fn.args) {
|
||||
var value:Dynamic = null;
|
||||
|
||||
if (args != null && i < args.length) {
|
||||
value = args[i];
|
||||
} else if (a.value != null) {
|
||||
value = interp.expr(a.value);
|
||||
}
|
||||
// NOTE: We assign these as variables rather than locals because those get wiped when we enter the function.
|
||||
if (interp.variables.exists(a.name)) {
|
||||
previousValues.set(a.name, interp.variables.get(a.name));
|
||||
}
|
||||
interp.variables.set(a.name, value);
|
||||
i++;
|
||||
}
|
||||
try {
|
||||
r = interp.execute(fn.body);
|
||||
} catch (e:hscript.Expr.Error) {
|
||||
// A script error occurred while executing the script function.
|
||||
// Purge the function from the cache so it is not called again.
|
||||
classDecl.purgeFunction(name);
|
||||
interp.error(#if hscriptPos e.e #else e #end);
|
||||
}
|
||||
|
||||
for (a in fn.args) {
|
||||
if (previousValues.exists(a.name)) {
|
||||
interp.variables.set(a.name, previousValues.get(a.name));
|
||||
} else {
|
||||
interp.variables.remove(a.name);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -211,6 +211,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))
|
||||
@@ -254,6 +263,11 @@ class ClassExtendMacro {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
|
||||
return __customClass.callFunction(name, [$a{arguments}]);
|
||||
}
|
||||
|
||||
return super.$name($a{arguments});
|
||||
};
|
||||
} else {
|
||||
@@ -269,6 +283,12 @@ class ClassExtendMacro {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
|
||||
__customClass.callFunction(name, [$a{arguments}]);
|
||||
return;
|
||||
}
|
||||
|
||||
super.$name($a{arguments});
|
||||
};
|
||||
}
|
||||
@@ -339,6 +359,13 @@ class ClassExtendMacro {
|
||||
Utils.setupMetas(shadowClass, imports);
|
||||
Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect");
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__cachedFields",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: Map<String, Dynamic>),
|
||||
access: [APublic, AStatic]
|
||||
});
|
||||
|
||||
// Adding hscript getters and setters
|
||||
|
||||
shadowClass.fields.push({
|
||||
@@ -348,6 +375,13 @@ class ClassExtendMacro {
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__customClass",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: hscript.customclass.CustomClass),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__custom__variables",
|
||||
pos: Context.currentPos(),
|
||||
@@ -472,31 +506,38 @@ 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(__custom__variables != null) {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
}
|
||||
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(__custom__variables != null) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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(__custom__variables != null) {
|
||||
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)) {
|
||||
|
||||
if(__real_fields != null && __real_fields.contains(name)) {
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
@@ -504,17 +545,20 @@ 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(__custom__variables != null) {
|
||||
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)) {
|
||||
|
||||
if(__real_fields != null && __real_fields.contains(name)) {
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
__custom__variables.set(name, val);
|
||||
if(__custom__variables != null) __custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@@ -579,6 +623,32 @@ 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(__cachedFields != null) {
|
||||
for(k => v in __cachedFields) {
|
||||
Reflect.setProperty(this, k, v);
|
||||
trace(k);
|
||||
}
|
||||
__cachedFields = null;
|
||||
}
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
#else
|
||||
class ClassExtendMacro {
|
||||
|
||||
Reference in New Issue
Block a user