326 lines
8.7 KiB
Haxe
326 lines
8.7 KiB
Haxe
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 __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;
|
|
__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);
|
|
}
|
|
}
|
|
__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 {
|
|
var fn:Dynamic = null;
|
|
if (!toSuper)
|
|
fn = __interp.variables.get(name);
|
|
else
|
|
fn = __interp.variables.get('_HX_SUPER__$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 (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 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;
|
|
}
|
|
|
|
public function toString():String
|
|
return className;
|
|
}
|