Compare commits
118
Commits
@@ -1,2 +1,4 @@
|
||||
/hscript.swf
|
||||
/release.zip
|
||||
dump/*
|
||||
tests/bin/*
|
||||
@@ -1,8 +1,31 @@
|
||||
hscript
|
||||
hscript-improved
|
||||
=======
|
||||
|
||||
[](https://travis-ci.org/HaxeFoundation/hscript)
|
||||
[](https://ci.appveyor.com/project/HaxeFoundation/hscript)
|
||||
- [Features](docs/FEATURES.md)
|
||||
|
||||
How to install
|
||||
```
|
||||
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" />
|
||||
```
|
||||
|
||||
or set this in build.hxml
|
||||
|
||||
```hxml
|
||||
-D CUSTOM_CLASSES
|
||||
```
|
||||
|
||||
Current Custom Class Limitations :
|
||||
|
||||
- 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.
|
||||
- You cannot create custom classes that extends a typed class (those ones that has `<T>`), this will be implemented in the future.
|
||||
|
||||
-----------
|
||||
|
||||
Parse and evalutate Haxe expressions.
|
||||
|
||||
@@ -100,4 +123,4 @@ Some other optional files :
|
||||
- `hscript.Macro` : convert Haxe macro into hscript Expr
|
||||
- `hscript.Printer` : convert hscript Expr to String
|
||||
- `hscript.Tools` : utility functions (map/iter)
|
||||
|
||||
|
||||
|
||||
@@ -26,13 +26,8 @@ class TestHScript extends TestCase {
|
||||
assertScript("0",0);
|
||||
assertScript("0xFF", 255);
|
||||
#if !(php || python)
|
||||
#if haxe3
|
||||
assertScript("0xBFFFFFFF", 0xBFFFFFFF);
|
||||
assertScript("0x7FFFFFFF", 0x7FFFFFFF);
|
||||
#elseif !neko
|
||||
assertScript("n(0xBFFFFFFF)", 0xBFFFFFFF, { n : haxe.Int32.toNativeInt });
|
||||
assertScript("n(0x7FFFFFFF)", 0x7FFFFFFF, { n : haxe.Int32.toNativeInt } );
|
||||
#end
|
||||
#end
|
||||
assertScript("-123",-123);
|
||||
assertScript("- 123",-123);
|
||||
@@ -81,11 +76,7 @@ class TestHScript extends TestCase {
|
||||
assertScript("var a = [1,[2,[3,[4,null]]]]; var t = 0; while( a != null ) { t += a[0]; a = a[1]; }; t",10);
|
||||
assertScript("var a = false; do { a = true; } while (!a); a;",true);
|
||||
assertScript("var t = 0; for( x in 1...10 ) t += x; t", 45);
|
||||
#if haxe3
|
||||
assertScript("var t = 0; for( x in new IntIterator(1,10) ) t +=x; t", 45);
|
||||
#else
|
||||
assertScript("var t = 0; for( x in new IntIter(1,10) ) t +=x; t", 45);
|
||||
#end
|
||||
assertScript("var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x",790);
|
||||
assertScript("var x = 1; var f = function(x) throw x; try f(55) catch( e : Dynamic ) e + x",56);
|
||||
assertScript("var i=2; if( true ) --i; i",1);
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# FEATURES
|
||||
|
||||
- Custom Classes
|
||||
- Final Classes
|
||||
- Static Classes
|
||||
- Allow for type check (`is`)
|
||||
- Allow extending other custom classes
|
||||
- Enums
|
||||
|
||||
```haxe
|
||||
enum TypeValue {
|
||||
NUMBER(n:Int);
|
||||
DECIMAL(d:Float, ?p:Int);
|
||||
CHARACTER(s:String);
|
||||
BOOLEAN(b:Bool);
|
||||
}
|
||||
|
||||
var type = TypeValue.DECIMAL(10.1234, 2);
|
||||
// You need to type the full enum field for each case
|
||||
// i.e. you can't type the enum field directly (limitation for now)
|
||||
switch(type) {
|
||||
case TypeValue.NUMBER(number):
|
||||
trace("number: " + number);
|
||||
case TypeValue.DECIMAL(decimal, precision):
|
||||
if(precision != null)
|
||||
trace("decimal: " + decimal + " | rounded decimal: " + roundDecimal(decimal, precision));
|
||||
else
|
||||
trace("decimal: " + decimal);
|
||||
case TypeValue.CHARACTER(char):
|
||||
trace("character: " + char);
|
||||
default:
|
||||
trace("unknown type");
|
||||
}
|
||||
|
||||
function roundDecimal(Value:Float, Precision:Int) {
|
||||
var mult:Float = Math.pow(10, Precision);
|
||||
return Math.fround(Value * mult) / mult;
|
||||
}
|
||||
```
|
||||
|
||||
- Enum matching with arguments for switch statements (for real and scripted enums)
|
||||
- Property Fields (`(get, set)` variables)
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
|
||||
- `@:isVar` metadata support
|
||||
|
||||
- Static extension (`using`)
|
||||
|
||||
```haxe
|
||||
using StringTools;
|
||||
|
||||
class IntExtender {
|
||||
static public function triple(i:Int) {
|
||||
return i * 3;
|
||||
}
|
||||
}
|
||||
|
||||
// need to create/import the custom class
|
||||
// before setting the extension (limitation for now)
|
||||
using IntExtender;
|
||||
|
||||
var str = " Hello World! ";
|
||||
trace(str.trim()); // "Hello World!"
|
||||
trace(12.triple()); // 36
|
||||
```
|
||||
|
||||
- Support for real and custom classes
|
||||
- Misc.
|
||||
- Allow using type parameters for creating objects (i.e. `var a = new TypedObject<Int>();`)
|
||||
- Allow `package` declaration. Ignored by the interpreter.
|
||||
+3
-1
@@ -1,2 +1,4 @@
|
||||
--macro keep('IntIterator')
|
||||
--macro hscript.UsingHandler.init()
|
||||
--macro hscript.macros.AbstractHandler.init()
|
||||
--macro hscript.macros.ClassExtendMacro.init()
|
||||
--macro hscript.macros.CustomClassMacro.init()
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "hscript",
|
||||
"name": "hscript-improved",
|
||||
"url": "https://github.com/HaxeFoundation/hscript",
|
||||
"license": "MIT",
|
||||
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
|
||||
|
||||
+7
-7
@@ -153,11 +153,11 @@ class Async {
|
||||
}
|
||||
|
||||
inline function fun(arg:String, e, ?name) {
|
||||
return mk(EFunction([{ name : arg, t : null }], e, name), e);
|
||||
return mk(EFunction([{ name : arg, t : null, opt: false, value: null }], e, name), e);
|
||||
}
|
||||
|
||||
inline function funs(arg:Array<String>, e, ?name) {
|
||||
return mk(EFunction([for( a in arg ) { name : a, t : null }], e, name), e);
|
||||
return mk(EFunction([for( a in arg ) { name : a, t : null, opt: false, value: null }], e, name), e);
|
||||
}
|
||||
|
||||
inline function block(arr:Array<Expr>, e) {
|
||||
@@ -260,7 +260,7 @@ class Async {
|
||||
defineVar(name, Defined);
|
||||
for( a in args )
|
||||
defineVar(a.name, Defined);
|
||||
args.unshift( { name : "_onEnd", t : null } );
|
||||
args.unshift( { name : "_onEnd", t : null, opt: false, value: null } );
|
||||
var frest = ident("_onEnd",e);
|
||||
var oldFun = currentFun;
|
||||
currentFun = name;
|
||||
@@ -417,10 +417,10 @@ class Async {
|
||||
if( currentLoop == null ) throw "Continue outside loop";
|
||||
return block([retNull(currentLoop, e), mk(EReturn(),e)], e);
|
||||
case ESwitch(v, cases, def):
|
||||
var cases = [for( c in cases ) { values : c.values, expr : toCps(c.expr, rest, exit) } ];
|
||||
return toCps(v, mk(EFunction([ { name : "_c", t : null } ], mk(ESwitch(ident("_c",v), cases, def == null ? retNull(rest) : toCps(def, rest, exit)),e)),e), exit );
|
||||
var cases:Array<SwitchCase> = [for( c in cases ) { values : c.values, expr : toCps(c.expr, rest, exit) } ];
|
||||
return toCps(v, mk(EFunction([ { name : "_c", t : null, opt: false, value: null } ], mk(ESwitch(ident("_c",v), cases, def == null ? retNull(rest) : toCps(def, rest, exit)),e)),e), exit );
|
||||
case EThrow(v):
|
||||
return toCps(v, mk(EFunction([ { name : "_v", t : null } ], mk(EThrow(v),v)), v), exit);
|
||||
return toCps(v, mk(EFunction([ { name : "_v", t : null, opt: false, value: null } ], mk(EThrow(v),v)), v), exit);
|
||||
case EMeta(name,_,e) if( name.charCodeAt(0) == ":".code ): // ignore custom ":" metadata
|
||||
return toCps(e, rest, exit);
|
||||
//case EDoWhile(_), ETry(_), ECall(_):
|
||||
@@ -436,7 +436,7 @@ class AsyncInterp extends Interp {
|
||||
|
||||
public function setContext( api : Dynamic ) {
|
||||
|
||||
var funs = new Array();
|
||||
var funs = [];
|
||||
for( v in variables.keys() )
|
||||
if( Reflect.isFunction(variables.get(v)) )
|
||||
funs.push({ v : v, obj : null });
|
||||
|
||||
+18
-26
@@ -27,7 +27,7 @@ class Bytes {
|
||||
var bin : haxe.io.Bytes;
|
||||
var bout : haxe.io.BytesBuffer;
|
||||
var pin : Int;
|
||||
var hstrings : #if haxe3 Map<String,Int> #else Hash<Int> #end;
|
||||
var hstrings : Map<String,Int>;
|
||||
var strings : Array<String>;
|
||||
var nstrings : Int;
|
||||
|
||||
@@ -35,7 +35,7 @@ class Bytes {
|
||||
this.bin = bin;
|
||||
pin = 0;
|
||||
bout = new haxe.io.BytesBuffer();
|
||||
hstrings = #if haxe3 new Map() #else new Hash() #end;
|
||||
hstrings = new Map();
|
||||
strings = [null];
|
||||
nstrings = 1;
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class Bytes {
|
||||
var vid = hstrings.get(v);
|
||||
if( vid == null ) {
|
||||
if( nstrings == 256 ) {
|
||||
hstrings = #if haxe3 new Map() #else new Hash() #end;
|
||||
hstrings = new Map();
|
||||
nstrings = 1;
|
||||
}
|
||||
hstrings.set(v,nstrings);
|
||||
@@ -85,15 +85,6 @@ class Bytes {
|
||||
bout.addByte(1);
|
||||
doEncodeInt(v);
|
||||
}
|
||||
#if !haxe3
|
||||
case CInt32(v):
|
||||
bout.addByte(4);
|
||||
var mid = haxe.Int32.toInt(haxe.Int32.and(v,haxe.Int32.ofInt(0xFFFFFF)));
|
||||
bout.addByte(mid & 0xFF);
|
||||
bout.addByte((mid >> 8) & 0xFF);
|
||||
bout.addByte(mid >> 16);
|
||||
bout.addByte(haxe.Int32.toInt(haxe.Int32.ushr(v, 24)));
|
||||
#end
|
||||
case CFloat(f):
|
||||
bout.addByte(2);
|
||||
doEncodeString(Std.string(f));
|
||||
@@ -120,13 +111,6 @@ class Bytes {
|
||||
CFloat( Std.parseFloat(doDecodeString()) );
|
||||
case 3:
|
||||
CString( doDecodeString() );
|
||||
#if !haxe3
|
||||
case 4:
|
||||
var i = bin.get(pin) | (bin.get(pin+1) << 8) | (bin.get(pin+2) << 16);
|
||||
var j = bin.get(pin+3);
|
||||
pin += 4;
|
||||
CInt32(haxe.Int32.or(haxe.Int32.ofInt(i), haxe.Int32.shl(haxe.Int32.ofInt(j), 24)));
|
||||
#end
|
||||
default:
|
||||
throw "Invalid code "+bin.get(pin-1);
|
||||
}
|
||||
@@ -140,10 +124,18 @@ class Bytes {
|
||||
#end
|
||||
bout.addByte(Type.enumIndex(e));
|
||||
switch( e ) {
|
||||
case EPackage(n):
|
||||
// TODO
|
||||
case EImport(c):
|
||||
// TODO
|
||||
case EClass(_, _, _, _):
|
||||
// TODO
|
||||
case EEnum(en):
|
||||
// TODO
|
||||
case ECast(e, _):
|
||||
// TODO
|
||||
case ERegex(e, f):
|
||||
// TODO
|
||||
case EConst(c):
|
||||
doEncodeConst(c);
|
||||
case EIdent(v):
|
||||
@@ -278,7 +270,7 @@ class Bytes {
|
||||
case 3:
|
||||
EParent(doDecode());
|
||||
case 4:
|
||||
var a = new Array();
|
||||
var a = [];
|
||||
for( i in 0...bin.get(pin++) )
|
||||
a.push(doDecode());
|
||||
EBlock(a);
|
||||
@@ -295,7 +287,7 @@ class Bytes {
|
||||
EUnop(op,prefix,doDecode());
|
||||
case 8:
|
||||
var e = doDecode();
|
||||
var params = new Array();
|
||||
var params = [];
|
||||
for( i in 0...bin.get(pin++) )
|
||||
params.push(doDecode());
|
||||
ECall(e,params);
|
||||
@@ -317,7 +309,7 @@ class Bytes {
|
||||
case 14:
|
||||
var params = new Array<Argument>();
|
||||
for( i in 0...bin.get(pin++) )
|
||||
params.push({ name : doDecodeString() });
|
||||
params.push({ name : doDecodeString(), opt: false, value: null, t: null });
|
||||
var e = doDecode();
|
||||
var name = doDecodeString();
|
||||
EFunction(params,e,(name == "") ? null: name);
|
||||
@@ -327,13 +319,13 @@ class Bytes {
|
||||
var e = doDecode();
|
||||
EArray(e,doDecode());
|
||||
case 17:
|
||||
var el = new Array();
|
||||
var el = [];
|
||||
for( i in 0...bin.get(pin++) )
|
||||
el.push(doDecode());
|
||||
EArrayDecl(el);
|
||||
case 18:
|
||||
var cl = doDecodeString();
|
||||
var el = new Array();
|
||||
var el = [];
|
||||
for( i in 0...bin.get(pin++) )
|
||||
el.push(doDecode());
|
||||
ENew(cl,el);
|
||||
@@ -344,7 +336,7 @@ class Bytes {
|
||||
var v = doDecodeString();
|
||||
ETry(e,v,null,doDecode());
|
||||
case 21:
|
||||
var fl = new Array();
|
||||
var fl:Array<ObjectField> = [];
|
||||
for( i in 0...bin.get(pin++) ) {
|
||||
var name = doDecodeString();
|
||||
var e = doDecode();
|
||||
@@ -358,7 +350,7 @@ class Bytes {
|
||||
ETernary(cond, e1, e2);
|
||||
case 23:
|
||||
var e = doDecode();
|
||||
var cases = [];
|
||||
var cases:Array<SwitchCase> = [];
|
||||
while( true ) {
|
||||
var v = doDecode();
|
||||
if( v == null ) break;
|
||||
|
||||
+5
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1233,9 +1236,10 @@ class Checker {
|
||||
mergeType( typeExpr(defaultExpr, withType), defaultExpr);
|
||||
return withType == NoValue ? TVoid : tmin == null ? makeMono() : tmin;
|
||||
case ENew(cl, params):
|
||||
default:
|
||||
}
|
||||
error("Don't know how to type "+edef(expr).getName(), expr);
|
||||
return TDynamic;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package hscript;
|
||||
|
||||
class Config {
|
||||
// Runs support for custom classes in these
|
||||
public static final ALLOWED_CUSTOM_CLASSES = [
|
||||
"flixel",
|
||||
];
|
||||
|
||||
// Runs support for abstract support in these
|
||||
public static final ALLOWED_ABSTRACT_AND_ENUM = [
|
||||
"flixel",
|
||||
"openfl.display.BlendMode",
|
||||
];
|
||||
|
||||
// Incase any of your files fail
|
||||
// These are the module names
|
||||
public static final DISALLOW_CUSTOM_CLASSES = [
|
||||
|
||||
];
|
||||
|
||||
public static final DISALLOW_ABSTRACT_AND_ENUM = [
|
||||
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
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 => v in __class.__interp.variables) {
|
||||
if(f == 'new') continue;
|
||||
if (!__interp.variables.exists(f))
|
||||
__interp.variables.set(f, v);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
__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);
|
||||
|
||||
__superClass = Type.createInstance(__class.cl, args);
|
||||
var disallowCopy:Array<String> = {
|
||||
var fieldMap:Map<String, String> = []; // Prevent duplicate values
|
||||
for(f in Reflect.fields(__superClass).concat(Type.getInstanceFields(Type.getClass(__superClass))))
|
||||
fieldMap.set(f, f);
|
||||
|
||||
fieldMap.array();
|
||||
}
|
||||
this.__real_fields = disallowCopy;
|
||||
@:privateAccess __interp.__instanceFields = __interp.__instanceFields.concat(disallowCopy);
|
||||
__superClass.__real_fields = this.__real_fields;
|
||||
__superClass.__class__fields = this.__class__fields;
|
||||
__superClass.__interp = this.__interp;
|
||||
}
|
||||
}
|
||||
|
||||
public function call(name:String, ?args:Array<Dynamic>, ?toSuper:Bool = false):Dynamic {
|
||||
var superFnName:Null<String> = toSuper ? '_HX_SUPER__$name' : null;
|
||||
var fn:Dynamic = {
|
||||
if(toSuper && __interp.variables.exists(superFnName)) {
|
||||
__interp.variables.get(superFnName);
|
||||
}
|
||||
else
|
||||
__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;
|
||||
}
|
||||
+155
-66
@@ -1,75 +1,152 @@
|
||||
package hscript;
|
||||
|
||||
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 function new(ogInterp:Interp, name:String, fields:Array<Expr>, ?extend:String, ?interfaces:Array<String>) {
|
||||
public var cl:Dynamic;
|
||||
|
||||
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;
|
||||
|
||||
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 {
|
||||
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 cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX');
|
||||
var _class = Type.createInstance(cl, args);
|
||||
for(f => v in ogInterp.variables)
|
||||
if(!__interp.variables.exists(f))
|
||||
__interp.variables.set(f, v);
|
||||
|
||||
var __capturedLocals = ogInterp.duplicate(ogInterp.locals);
|
||||
var capturedLocals:Map<String, {r:Dynamic, depth:Int}> = [];
|
||||
for(k=>e in __capturedLocals)
|
||||
if (e != null && e.depth <= 0)
|
||||
capturedLocals.set(k, e);
|
||||
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:
|
||||
}
|
||||
|
||||
var disallowCopy = Type.getInstanceFields(cl);
|
||||
|
||||
//trace("Locals");
|
||||
for (key => value in capturedLocals) {
|
||||
if(!disallowCopy.contains(key)) {
|
||||
interp.locals.set(key, {r: value, depth: -1});
|
||||
//trace(key, value);
|
||||
if(staticField && validField) {
|
||||
__interp.exprReturn(e);
|
||||
__staticFields.push(fieldName);
|
||||
fields.remove(e);
|
||||
}
|
||||
}
|
||||
//trace("Variables");
|
||||
for (key => value in ogInterp.variables) {
|
||||
if(!disallowCopy.contains(key)) {
|
||||
interp.variables.set(key, value);
|
||||
//trace(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public function hnew(args:Array<Dynamic>):Dynamic
|
||||
return new CustomClass(this, args);
|
||||
|
||||
@: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;
|
||||
}
|
||||
|
||||
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(name == 'new') {
|
||||
var __constructor = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||
return this.hnew(args);
|
||||
});
|
||||
return __constructor;
|
||||
}
|
||||
|
||||
if(hasField(name)) {
|
||||
return getField(name);
|
||||
}
|
||||
throw "field '"+ name+ "' does not exist in class '"+ this.name+ "'";
|
||||
return null;
|
||||
}
|
||||
|
||||
for(expr in fields) {
|
||||
@:privateAccess
|
||||
interp.exprReturn(expr);
|
||||
}
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if(hasField(name))
|
||||
return setField(name, val);
|
||||
|
||||
interp.variables.set("super", staticHandler);
|
||||
throw "field '"+ name+ "' does not exist in class '"+ this.name+ "'";
|
||||
return null;
|
||||
}
|
||||
|
||||
_class.__interp = interp;
|
||||
interp.scriptObject = _class;
|
||||
// UNUSED
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
return null;
|
||||
}
|
||||
|
||||
var newFunc = interp.variables.get("new");
|
||||
if(newFunc != null) {
|
||||
Reflect.callMethod(null, newFunc, args);
|
||||
}
|
||||
|
||||
for(variable => value in interp.variables) {
|
||||
if(variable == "this") continue;
|
||||
}
|
||||
|
||||
return _class;
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
@@ -77,29 +154,41 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateClass implements IHScriptCustomBehaviour {
|
||||
|
||||
/**
|
||||
* This is for backwards compatibility with old hscript-improved, since some scripts use it
|
||||
**/
|
||||
@:dox(hide)
|
||||
@:keep
|
||||
class TemplateClass implements IHScriptCustomBehaviour implements IHScriptCustomAccessBehaviour {
|
||||
public var __interp:Interp;
|
||||
public var __allowSetGet:Bool = true;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if(this.__interp.variables.exists("set_" + name)) {
|
||||
return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function
|
||||
}
|
||||
if (this.__interp.variables.exists(name)) {
|
||||
this.__interp.variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
Reflect.setProperty(this, name, val);
|
||||
return Reflect.field(this, name);
|
||||
var variables = __interp.variables;
|
||||
if(__allowSetGet && variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
public function hget(name:String):Dynamic {
|
||||
if(this.__interp.variables.exists("get_" + name))
|
||||
return this.__interp.variables.get("get_" + name)();
|
||||
if (this.__interp.variables.exists(name))
|
||||
return this.__interp.variables.get(name);
|
||||
return Reflect.getProperty(this, name);
|
||||
var variables = __interp.variables;
|
||||
if(__allowSetGet && variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
return variables.get(name);
|
||||
}
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var v = __interp.variables.get("get_" + name)();
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var v = __interp.variables.get("set_" + name)(val);
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
class StaticHandler {
|
||||
public function new() {}
|
||||
}
|
||||
+90
-31
@@ -21,22 +21,30 @@
|
||||
*/
|
||||
package hscript;
|
||||
|
||||
typedef Int8 = #if cpp cpp.Int8 #elseif java java.Int8 #elseif cs cs.Int8 #else Int #end;
|
||||
typedef Int16 = #if cpp cpp.Int16 #elseif java java.Int16 #elseif cs cs.Int16 #else Int #end;
|
||||
typedef Int32 = #if cpp cpp.Int32 #else Int #end;
|
||||
typedef Int64 = #if cpp cpp.Int64 #elseif java java.Int64 #elseif cs cs.Int64 #else Int #end;
|
||||
|
||||
typedef UInt8 = #if cpp cpp.UInt8 #elseif cs cs.UInt8 #else Int #end;
|
||||
typedef UInt16 = #if cpp cpp.UInt16 #elseif cs cs.UInt16 #else Int #end;
|
||||
typedef UInt32 = #if cpp cpp.UInt32 #else Int #end;
|
||||
typedef UInt64 = #if cpp cpp.UInt64 #else Int #end;
|
||||
|
||||
enum Const {
|
||||
CInt( v : Int );
|
||||
CFloat( f : Float );
|
||||
CString( s : String );
|
||||
#if !haxe3
|
||||
CInt32( v : haxe.Int32 );
|
||||
#end
|
||||
CString( s : String, ?i : Bool );
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
typedef Expr = {
|
||||
var e : ExprDef;
|
||||
var pmin : Int;
|
||||
var pmax : Int;
|
||||
var origin : String;
|
||||
var line : Int;
|
||||
@:structInit
|
||||
final class Expr {
|
||||
public var e : ExprDef;
|
||||
public var pmin : Int;
|
||||
public var pmax : Int;
|
||||
public var origin : String;
|
||||
public var line : Int;
|
||||
}
|
||||
enum ExprDef {
|
||||
#else
|
||||
@@ -45,7 +53,7 @@ enum Expr {
|
||||
#end
|
||||
EConst( c : Const );
|
||||
EIdent( v : String );
|
||||
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : 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 );
|
||||
@@ -54,30 +62,70 @@ enum Expr {
|
||||
ECall( e : Expr, params : Array<Expr> );
|
||||
EIf( cond : Expr, e1 : Expr, ?e2 : Expr );
|
||||
EWhile( cond : Expr, e : Expr );
|
||||
EFor( v : String, it : Expr, e : Expr );
|
||||
EFor( v : String, it : Expr, e : Expr, ?ithv: String);
|
||||
EBreak;
|
||||
EContinue;
|
||||
EFunction( args : Array<Argument>, e : Expr, ?name : String, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool );
|
||||
EFunction( args : Array<Argument>, e : Expr, ?name : String, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool );
|
||||
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<{ name : String, e : Expr }> );
|
||||
EObject( fl : Array<ObjectField> );
|
||||
ETernary( cond : Expr, e1 : Expr, e2 : Expr );
|
||||
ESwitch( e : Expr, cases : Array<{ values : Array<Expr>, expr : Expr }>, ?defaultExpr : Expr );
|
||||
ESwitch( e : Expr, cases : Array<SwitchCase>, ?defaultExpr : Expr );
|
||||
EDoWhile( cond : Expr, e : Expr);
|
||||
EMeta( name : String, args : Array<Expr>, e : Expr );
|
||||
ECheckType( e : Expr, t : CType );
|
||||
|
||||
EImport( c : String, ?asname:String );
|
||||
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String> );
|
||||
EPackage( ?n: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);
|
||||
ERegex(e:String, flags:String);
|
||||
}
|
||||
|
||||
typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
|
||||
@:structInit
|
||||
final class ObjectField {
|
||||
public var name : String;
|
||||
public var e : Expr;
|
||||
}
|
||||
|
||||
typedef Metadata = Array<{ name : String, params : Array<Expr> }>;
|
||||
@:structInit
|
||||
final class SwitchCase {
|
||||
public var values : Array<Expr>;
|
||||
public var expr : Expr;
|
||||
}
|
||||
|
||||
@:structInit
|
||||
final class Argument {
|
||||
public var name : String;
|
||||
public var t : CType;
|
||||
public var opt : Bool;
|
||||
public var value : Expr;
|
||||
}
|
||||
|
||||
@:structInit
|
||||
final class MetadataEntry {
|
||||
public var name : String;
|
||||
public var params : Array<Expr>;
|
||||
}
|
||||
|
||||
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> );
|
||||
@@ -86,6 +134,7 @@ enum CType {
|
||||
CTParent( t : CType );
|
||||
CTOpt( t : CType );
|
||||
CTNamed( n : String, t : CType );
|
||||
CTExpr( e : Expr ); // for type parameters only
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
@@ -157,13 +206,22 @@ typedef FieldDecl = {
|
||||
var access : Array<FieldAccess>;
|
||||
}
|
||||
|
||||
enum FieldAccess {
|
||||
APublic;
|
||||
APrivate;
|
||||
AInline;
|
||||
AOverride;
|
||||
AStatic;
|
||||
AMacro;
|
||||
enum abstract FieldAccess(UInt8) {
|
||||
var APublic;
|
||||
var APrivate;
|
||||
var AInline;
|
||||
var AOverride;
|
||||
var AStatic;
|
||||
var AMacro;
|
||||
}
|
||||
|
||||
enum abstract FieldPropertyAccess(UInt8) {
|
||||
var ADefault;
|
||||
var ANull;
|
||||
var AGet;
|
||||
var ASet;
|
||||
var ADynamic;
|
||||
var ANever;
|
||||
}
|
||||
|
||||
enum FieldKind {
|
||||
@@ -171,10 +229,11 @@ enum FieldKind {
|
||||
KVar( v : VarDecl );
|
||||
}
|
||||
|
||||
typedef FunctionDecl = {
|
||||
var args : Array<Argument>;
|
||||
var expr : Expr;
|
||||
var ret : Null<CType>;
|
||||
@:structInit
|
||||
final class FunctionDecl {
|
||||
public var args : Array<Argument>;
|
||||
public var body : Expr;
|
||||
public var ret : Null<CType>;
|
||||
}
|
||||
|
||||
typedef 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;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package hscript;
|
||||
|
||||
/**
|
||||
* Same Interface as IHScriptCustomBehaviour but for Property.
|
||||
*/
|
||||
interface IHScriptCustomAccessBehaviour extends IHScriptCustomBehaviour {
|
||||
var __allowSetGet:Bool;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package hscript;
|
||||
|
||||
/**
|
||||
* Special Interface to make a class usable for Custom Classes.
|
||||
*/
|
||||
interface IHScriptCustomClassBehaviour extends IHScriptCustomAccessBehaviour{
|
||||
public var __interp:Interp;
|
||||
public var __real_fields:Array<String>;
|
||||
public var __class__fields:Array<String>;
|
||||
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package hscript;
|
||||
|
||||
/**
|
||||
* Special Interface for handling new instances of an object.
|
||||
*/
|
||||
interface IHScriptCustomConstructor {
|
||||
public function hnew(args:Array<Dynamic>):Dynamic;
|
||||
}
|
||||
+952
-318
File diff suppressed because it is too large
Load Diff
+8
-38
@@ -29,23 +29,13 @@ import haxe.macro.Expr;
|
||||
class Macro {
|
||||
|
||||
var p : Position;
|
||||
#if haxe3
|
||||
var binops : Map<String,Binop>;
|
||||
var unops : Map<String,Unop>;
|
||||
#else
|
||||
var binops : Hash<Binop>;
|
||||
var unops : Hash<Unop>;
|
||||
#end
|
||||
|
||||
public function new(pos) {
|
||||
p = pos;
|
||||
#if haxe3
|
||||
binops = new Map();
|
||||
unops = new Map();
|
||||
#else
|
||||
binops = new Hash();
|
||||
unops = new Hash();
|
||||
#end
|
||||
for( c in Type.getEnumConstructs(Binop) ) {
|
||||
if( c == "OpAssignOp" ) continue;
|
||||
var op = Type.createEnum(Binop, c);
|
||||
@@ -73,9 +63,7 @@ class Macro {
|
||||
case OpMod: assign = true; "%";
|
||||
case OpAssignOp(_): "";
|
||||
case OpInterval: "...";
|
||||
#if haxe3
|
||||
case OpArrow: "=>";
|
||||
#end
|
||||
#if (haxe_ver >= 4)
|
||||
case OpIn: "in";
|
||||
#end
|
||||
@@ -102,15 +90,8 @@ class Macro {
|
||||
}
|
||||
}
|
||||
|
||||
#if !haxe3
|
||||
function isType(v:String) {
|
||||
var c0 = v.charCodeAt(0);
|
||||
return c0 >= 'A'.code && c0 <= 'Z'.code;
|
||||
}
|
||||
#end
|
||||
|
||||
function map < T, R > ( a : Array<T>, f : T -> R ) : Array<R> {
|
||||
var b = new Array();
|
||||
var b = [];
|
||||
for( x in a )
|
||||
b.push(f(x));
|
||||
return b;
|
||||
@@ -146,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";
|
||||
};
|
||||
}
|
||||
|
||||
@@ -156,17 +139,9 @@ class Macro {
|
||||
case CInt(v): CInt(Std.string(v));
|
||||
case CFloat(f): CFloat(Std.string(f));
|
||||
case CString(s): CString(s);
|
||||
#if !haxe3
|
||||
case CInt32(v): CInt(Std.string(v));
|
||||
#end
|
||||
});
|
||||
case EIdent(v):
|
||||
#if !haxe3
|
||||
if( isType(v) )
|
||||
EConst(CType(v));
|
||||
else
|
||||
#end
|
||||
EConst(CIdent(v));
|
||||
EConst(CIdent(v));
|
||||
case EVar(n, t, e):
|
||||
EVars([ { name : n, expr : if( e == null ) null else convert(e), type : if( t == null ) null else convertType(t) } ]);
|
||||
case EParent(e):
|
||||
@@ -174,12 +149,7 @@ class Macro {
|
||||
case EBlock(el):
|
||||
EBlock(map(el,convert));
|
||||
case EField(e, f):
|
||||
#if !haxe3
|
||||
if( isType(f) )
|
||||
EType(convert(e), f);
|
||||
else
|
||||
#end
|
||||
EField(convert(e), f);
|
||||
EField(convert(e), f);
|
||||
case EBinop(op, e1, e2):
|
||||
var b = binops.get(op);
|
||||
if( b == null ) throw EInvalidOp(op);
|
||||
@@ -200,11 +170,9 @@ class Macro {
|
||||
#if (haxe_ver >= 4)
|
||||
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
||||
EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
|
||||
#elseif (haxe_211 || haxe3)
|
||||
#else
|
||||
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
||||
EFor({ expr : EIn({ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
|
||||
#else
|
||||
EFor(v, convert(it), convert(efor));
|
||||
#end
|
||||
case EBreak:
|
||||
EBreak;
|
||||
@@ -252,6 +220,8 @@ class Macro {
|
||||
EMeta({ name : m, params : params == null ? [] : [for( p in params ) convert(p)], pos : mpos }, convert(esub));
|
||||
case ECheckType(e, t):
|
||||
ECheckType(convert(e), convertType(t));
|
||||
default:
|
||||
null;
|
||||
}, pos : #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end }
|
||||
}
|
||||
|
||||
|
||||
+659
-168
File diff suppressed because it is too large
Load Diff
+116
-20
@@ -30,23 +30,23 @@ class Printer {
|
||||
public function new() {
|
||||
}
|
||||
|
||||
public function exprToString( e : Expr ) {
|
||||
public function exprToString( e : Expr ):String {
|
||||
buf = new StringBuf();
|
||||
tabs = "";
|
||||
expr(e);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public function typeToString( t : CType ) {
|
||||
public function typeToString( t : CType ):String {
|
||||
buf = new StringBuf();
|
||||
tabs = "";
|
||||
type(t);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
inline function add<T>(s:T) buf.add(s);
|
||||
inline function add<T>(s:T):Void buf.add(s);
|
||||
|
||||
function type( t : CType ) {
|
||||
function type( t : CType ):Void {
|
||||
switch( t ) {
|
||||
case CTOpt(t):
|
||||
add('?');
|
||||
@@ -98,41 +98,97 @@ class Printer {
|
||||
add("(");
|
||||
type(t);
|
||||
add(")");
|
||||
case CTExpr(e):
|
||||
expr(e);
|
||||
}
|
||||
}
|
||||
|
||||
function addType( t : CType ) {
|
||||
function addType( t : CType ):Void {
|
||||
if( t != null ) {
|
||||
add(" : ");
|
||||
type(t);
|
||||
}
|
||||
}
|
||||
|
||||
function expr( e : Expr ) {
|
||||
function expr( e : Expr ):Void {
|
||||
if( e == null ) {
|
||||
add("??NULL??");
|
||||
return;
|
||||
}
|
||||
switch( #if hscriptPos e.e #else e #end ) {
|
||||
case EImport(c, n):
|
||||
add("import " + c);
|
||||
switch(Tools.expr(e)) {
|
||||
case EPackage(n):
|
||||
add('package');
|
||||
if(n != null)
|
||||
add(' $n');
|
||||
add(';\n');
|
||||
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 ERegex(e, f):
|
||||
add('~/$e/$f');
|
||||
add(';\n');
|
||||
case EConst(c):
|
||||
switch( c ) {
|
||||
case CInt(i): add(i);
|
||||
@@ -141,8 +197,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 +265,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:
|
||||
@@ -217,8 +300,11 @@ class Printer {
|
||||
add(" while ( ");
|
||||
expr(cond);
|
||||
add(" )");
|
||||
case EFor(v, it, e):
|
||||
add("for( "+v+" in ");
|
||||
case EFor(v, it, e, ithv):
|
||||
if(ithv != null)
|
||||
add("for( "+ithv+" => "+v+" in ");
|
||||
else
|
||||
add("for( "+v+" in ");
|
||||
expr(it);
|
||||
add(" ) ");
|
||||
expr(e);
|
||||
@@ -261,8 +347,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(", ");
|
||||
@@ -344,11 +440,11 @@ class Printer {
|
||||
}
|
||||
}
|
||||
|
||||
public static function toString( e : Expr ) {
|
||||
public static function toString( e : Expr ):String {
|
||||
return new Printer().exprToString(e);
|
||||
}
|
||||
|
||||
public static function errorToString( e : Expr.Error ) {
|
||||
public static function errorToString( e : Expr.Error ):String {
|
||||
var message = switch( #if hscriptPos e.e #else e #end ) {
|
||||
case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")";
|
||||
case EUnexpected(s): "Unexpected token: \""+s+"\"";
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
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 {
|
||||
private static inline var GET = 'get_';
|
||||
private static inline var SET = 'set_';
|
||||
|
||||
public var r:Dynamic;
|
||||
public var getter:FieldPropertyAccess;
|
||||
public var setter:FieldPropertyAccess;
|
||||
|
||||
public var isStatic(get, never):Bool;
|
||||
function get_isStatic() {
|
||||
return __isStatic && interp.allowStaticVariables;
|
||||
}
|
||||
|
||||
var isVar:Bool;
|
||||
var interp:Interp;
|
||||
|
||||
public function new(r:Dynamic, getter:FieldPropertyAccess, setter:FieldPropertyAccess, isVar:Bool, isStatic:Bool, interp:Interp) {
|
||||
this.r = r;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
this.isVar = isVar;
|
||||
this.__isStatic = isStatic;
|
||||
this.interp = interp;
|
||||
}
|
||||
|
||||
var __allowReadAccess:Bool = false;
|
||||
var __allowWriteAccess:Bool = false;
|
||||
var __allowSetGet:Null<Bool> = null;
|
||||
final __isStatic:Bool = false;
|
||||
|
||||
public function callGetter(name:String) {
|
||||
switch (getter) {
|
||||
case AGet | ADynamic:
|
||||
var fName:String = '$GET$name';
|
||||
if (!__allowReadAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
|
||||
if (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 (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 = isStatic ? interp.staticVariables.get(f) : 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;
|
||||
}
|
||||
|
||||
private function varExists(n:String) {
|
||||
return isStatic ? interp.staticVariables.exists(n) : interp.variables.exists(n);
|
||||
}
|
||||
}
|
||||
+36
-6
@@ -24,7 +24,7 @@ import hscript.Expr;
|
||||
|
||||
class Tools {
|
||||
|
||||
public static function iter( e : Expr, f : Expr -> Void ) {
|
||||
public static function iter( e : Expr, f : Expr -> Void ):Void {
|
||||
switch( expr(e) ) {
|
||||
case EConst(_), EIdent(_):
|
||||
case EImport(c): f(e);
|
||||
@@ -59,14 +59,14 @@ 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:
|
||||
}
|
||||
}
|
||||
|
||||
public static function map( e : Expr, f : Expr -> Expr ) {
|
||||
public static function map( e : Expr, f : Expr -> Expr ):Expr {
|
||||
var edef = switch( expr(e) ) {
|
||||
case EConst(_), EIdent(_), EBreak, EContinue: expr(e);
|
||||
case EVar(n, t, e): EVar(n, t, if( e != null ) f(e) else null);
|
||||
case EVar(n, t, e, isPublic, isStatic, isPrivate): EVar(n, t, if( e != null ) f(e) else null, isPublic, isStatic, isPrivate);
|
||||
case EParent(e): EParent(f(e));
|
||||
case EBlock(el): EBlock([for( e in el ) f(e)]);
|
||||
case EField(e, fi): EField(f(e),fi);
|
||||
@@ -77,7 +77,7 @@ class Tools {
|
||||
case EWhile(c, e): EWhile(f(c),f(e));
|
||||
case EDoWhile(c, e): EDoWhile(f(c),f(e));
|
||||
case EFor(v, it, e): EFor(v, f(it), f(e));
|
||||
case EFunction(args, e, name, t): EFunction(args, f(e), name, t);
|
||||
case EFunction(args, e, name, t, isPublic, isStatic, isOverride, isPrivate): EFunction(args, f(e), name, t, isPublic, isStatic, isOverride, isPrivate);
|
||||
case EReturn(e): EReturn(if( e != null ) f(e) else null);
|
||||
case EArray(e, i): EArray(f(e),f(i));
|
||||
case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -103,7 +104,7 @@ class Tools {
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function mk( e : ExprDef, p : Expr ) {
|
||||
public static inline function mk( e : ExprDef, p : Expr ):Expr {
|
||||
#if hscriptPos
|
||||
return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line };
|
||||
#else
|
||||
@@ -111,4 +112,33 @@ 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 function isUppercase(s:String) {
|
||||
if(s.length == 0) return false;
|
||||
var c:Int = StringTools.fastCodeAt(s, 0);
|
||||
if(StringTools.isEof(c)) return false; // Just in case :3
|
||||
return c >= 65 && c <= 90; // A-Z
|
||||
}
|
||||
|
||||
public static inline function isCustomAbstract(obj:Dynamic):Bool
|
||||
return obj != null && obj is IHScriptAbstractBehaviour;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package hscript.macros;
|
||||
|
||||
#if macro
|
||||
import Type.ValueType;
|
||||
import haxe.macro.ComplexTypeTools;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Printer;
|
||||
import haxe.macro.Compiler;
|
||||
|
||||
using StringTools;
|
||||
|
||||
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.AbstractHandler.build())');
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
public static function build():Array<Field> {
|
||||
var fields = Context.getBuildFields();
|
||||
var clRef = Context.getLocalClass();
|
||||
if (clRef == null) return fields;
|
||||
var cl = clRef.get();
|
||||
|
||||
if (/* cl.name.startsWith("Flx") && */ cl.name.endsWith("_Impl_") && cl.params.length <= 0 && !cl.meta.has(":multiType") && !cl.name.contains("_HSC")) {
|
||||
var metas = cl.meta.get();
|
||||
|
||||
var trimEnum = cl.name.substr(0, cl.name.length - 6);
|
||||
|
||||
var key = cl.module;
|
||||
var fkey = cl.module + "." + trimEnum;
|
||||
if(key == "lime.system.Locale") return fields; // Error: Unknown identifier : currentLocale, Due to Func
|
||||
if(key == "cpp.Function") return fields; // Error: Unknown identifier : nativeGetProcAddress, Due to Func
|
||||
if(key == "haxe.ds.Vector") return fields; // Error: haxe.ds._Vector.VectorData<blit.T> has no field blit, Due to Func
|
||||
if(key == "haxe.display.Display") return fields; // Error: haxe.display.DisplayItemKind<haxe.display.DisplayLiteral<Dynamic>> has no field Null, Due to Func
|
||||
if(key == "cpp.Callable") return fields; // Error: cpp.Function.fromStaticFunction must be called on static function, Due to Func
|
||||
if(key == "haxe.display.JsonAnonStatusKind") return fields; // Error: cannot initialize a variable of type 'char *' with an rvalue of type 'const char *', Due to Func
|
||||
if(key == "cpp.CharStar") return fields; // Error: cannot initialize a variable of type 'char *' with an rvalue of type 'const char *', Due to Func
|
||||
if(Config.DISALLOW_ABSTRACT_AND_ENUM.contains(cl.module) || Config.DISALLOW_ABSTRACT_AND_ENUM.contains(fkey)) return fields;
|
||||
if(cl.module.contains("_")) return fields; // Weird issue, sorry
|
||||
|
||||
var shadowClass = macro class {
|
||||
|
||||
};
|
||||
shadowClass.kind = TDClass();
|
||||
shadowClass.params = switch(cl.params.length) {
|
||||
case 0:
|
||||
null;
|
||||
case 1:
|
||||
[{
|
||||
name: "T",
|
||||
}];
|
||||
default:
|
||||
[for(k=>e in cl.params) {
|
||||
name: "T" + Std.int(k+1)
|
||||
}];
|
||||
};
|
||||
shadowClass.name = '${cl.name.substr(0, cl.name.length - 6)}_HSC';
|
||||
|
||||
var imports = Context.getLocalImports().copy();
|
||||
Utils.setupMetas(shadowClass, imports);
|
||||
//trace(cl.module);
|
||||
|
||||
for(f in fields)
|
||||
switch(f.kind) {
|
||||
case FFun(fun):
|
||||
if (f.access.contains(AStatic)) {
|
||||
if (fun.expr != null) {
|
||||
fun.expr = macro @:privateAccess $e{fun.expr};
|
||||
shadowClass.fields.push(f);
|
||||
|
||||
/*var trimEnum = cl.name.substr(0, cl.name.length - 6);
|
||||
|
||||
var returns:Bool = !fun.ret.match(TPath({name: "Void"}));
|
||||
|
||||
var name = f.name;
|
||||
|
||||
var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}];
|
||||
|
||||
var expr:Expr = Context.parse('${returns?"return " : ""} $trimEnum.$name(${[for(a in fun.args) a.name].join(", ")})', f.pos);
|
||||
|
||||
var func:Function = {
|
||||
ret: fun.ret,
|
||||
params: fun.params.copy(),
|
||||
expr: expr,
|
||||
args: fun.args.copy()
|
||||
};
|
||||
|
||||
var field:Field = {
|
||||
pos: f.pos,
|
||||
name: f.name,
|
||||
meta: f.meta,
|
||||
kind: FFun(func),
|
||||
doc: null,//f.doc,
|
||||
access: [APublic, AStatic]
|
||||
}
|
||||
shadowClass.fields.push(field);*/
|
||||
}
|
||||
}
|
||||
case FProp(get, set, t, e):
|
||||
if (get == "default" && (set == "never" || set == "null")) {
|
||||
shadowClass.fields.push(f);
|
||||
}
|
||||
case FVar(t, e):
|
||||
if (f.access.contains(AStatic) || cl.meta.has(":enum") || f.name.toUpperCase() == f.name) {
|
||||
var name:String = f.name;
|
||||
var enumType:String = cl.name;
|
||||
var pack = cl.module.split(".");
|
||||
|
||||
//trace(pack, cl.name, name, cl.module);
|
||||
|
||||
if(pack[pack.length - 1] == trimEnum)
|
||||
pack.pop();
|
||||
|
||||
var complexType:ComplexType = t;
|
||||
if(complexType == null && e != null) {
|
||||
complexType = switch(e.expr) {
|
||||
case EConst(CRegexp(_)): TPath({ name: "EReg", pack: [] });
|
||||
|
||||
default: null;
|
||||
}
|
||||
}
|
||||
if(complexType == null) {
|
||||
complexType = TPath({
|
||||
name: trimEnum,
|
||||
pack: [],//pack
|
||||
});
|
||||
}
|
||||
|
||||
var code = Context.parse('@:privateAccess ($trimEnum.$name)', f.pos); // '${pack.join(".")}.${trimEnum}.$name'
|
||||
|
||||
var field:Field = {
|
||||
pos: f.pos,
|
||||
name: f.name,
|
||||
meta: f.meta,
|
||||
kind: FVar(null, code),
|
||||
doc: f.doc,
|
||||
access: [APublic, AStatic]
|
||||
}
|
||||
|
||||
shadowClass.fields.push(field);
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
//var printer = new Printer();
|
||||
//for(field in shadowClass.fields)
|
||||
// trace(printer.printField(field));
|
||||
|
||||
Context.defineModule(cl.module, [shadowClass], imports);
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,709 @@
|
||||
package hscript.macros;
|
||||
|
||||
#if macro
|
||||
import haxe.macro.Type.ClassType;
|
||||
import Type.ValueType;
|
||||
import haxe.macro.Expr.Function;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Type.MetaAccess;
|
||||
import haxe.macro.Type.FieldKind;
|
||||
import haxe.macro.Type.ClassField;
|
||||
import haxe.macro.Type.VarAccess;
|
||||
import haxe.macro.*;
|
||||
|
||||
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";
|
||||
|
||||
public static var unallowedMetas:Array<String> = [":bitmap", ":noCustomClass", ":generic"];
|
||||
|
||||
public static var modifiedClasses:Array<String> = [];
|
||||
|
||||
public static function init() {
|
||||
#if !display
|
||||
#if CUSTOM_CLASSES
|
||||
if(Context.defined("display")) return;
|
||||
/*
|
||||
for(apply in Config.ALLOWED_CUSTOM_CLASSES) {
|
||||
Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.ClassExtendMacro.build())");
|
||||
}
|
||||
*/
|
||||
//Context.onAfterTyping(buildTyped);
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
public static function build():Array<Field> {
|
||||
var fields = Context.getBuildFields();
|
||||
var clRef = Context.getLocalClass();
|
||||
if (clRef == null) return fields;
|
||||
var cl = clRef.get();
|
||||
|
||||
if (cl.isAbstract || cl.isExtern || cl.isFinal || cl.isInterface) return fields;
|
||||
if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith(CLASS_SUFFIX) && !cl.name.endsWith("_HSC")) {
|
||||
var metas = cl.meta.get();
|
||||
|
||||
for(m in metas)
|
||||
if (unallowedMetas.contains(m.name))
|
||||
return fields;
|
||||
|
||||
if(cl.params.length > 0)
|
||||
return fields;
|
||||
|
||||
var key = cl.module;
|
||||
var fkey = cl.module + "." + cl.name;
|
||||
if(key == "sys.thread.FixedThreadPool") return fields; // Error: Type name sys.thread.Worker_HSX is redefined from module sys.thread.FixedThreadPool
|
||||
if(key == "StdTypes") return fields; // Error: Cant extend basic class
|
||||
if(key == "Xml") return fields; // Error: Cant extend basic class
|
||||
if(key == "Date") return fields; // Error: Cant extend basic class
|
||||
if(key == "away3d.tools.commands.Mirror") return fields; // Error: Unknown identifier
|
||||
if(key == "away3d.tools.commands.SphereMaker") return fields; // Error: Unknown identifier
|
||||
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
|
||||
|
||||
var superFields = [];
|
||||
if(false && cl.superClass != null) {
|
||||
var _superFields = cl.superClass.t.get().fields.get();
|
||||
_superFields = []; // Comment to enable super support, (broken)
|
||||
|
||||
function convertField(field:ClassField) {
|
||||
try {
|
||||
var nfield = FixedTypeTools.toSimpleField(field);
|
||||
switch ([field.kind, field.type]) {
|
||||
case [FMethod(kind), TFun(args, ret)]:
|
||||
if(kind == MethInline)
|
||||
nfield.access.push(AInline);
|
||||
if(kind == MethDynamic)
|
||||
nfield.access.push(ADynamic);
|
||||
default:
|
||||
}
|
||||
|
||||
switch(nfield.kind) {
|
||||
case FFun(fun):
|
||||
if (fun.params != null && fun.params.length > 0)
|
||||
return null;
|
||||
|
||||
//sfun.ret = Utils.fixStdTypes(fun.ret);
|
||||
|
||||
var metas = nfield.meta;
|
||||
var defaultValues:Map<String, Dynamic> = [];
|
||||
var defaultEntry = null;
|
||||
var isGeneric = false;
|
||||
for(m in metas) {
|
||||
if(m.name == ":value") {
|
||||
defaultEntry = m;
|
||||
switch(m.params[0].expr) {
|
||||
case EObjectDecl(fields):
|
||||
for(fil in fields)
|
||||
defaultValues[fil.field] = fil.expr;
|
||||
default:
|
||||
}
|
||||
}
|
||||
if(m.name == ":generic")
|
||||
isGeneric = true;
|
||||
}
|
||||
if(isGeneric) return null;
|
||||
|
||||
if(defaultEntry != null)
|
||||
metas.remove(defaultEntry);
|
||||
|
||||
for(arg in fun.args) {
|
||||
var opt = false;
|
||||
if(defaultValues.exists(arg.name)) {
|
||||
arg.value = defaultValues[arg.name];
|
||||
arg.opt = false;
|
||||
}
|
||||
|
||||
arg.type = null;//Utils.fixStdTypes(arg.type);
|
||||
|
||||
//if(arg.opt) {
|
||||
// if(arg.type.getParameters()[0].name != "Null")
|
||||
// arg.type = TPath({name: "Null", params: [TPType(arg.type)], pack: []});//macro {Null<Dynamic>};
|
||||
//}
|
||||
}
|
||||
|
||||
trace(nfield.name);
|
||||
default:
|
||||
}
|
||||
return nfield;
|
||||
} catch(e) {
|
||||
trace(field.name, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var didPrint = false;
|
||||
|
||||
var fieldNames = [for(f in fields) f.name];
|
||||
|
||||
/*for(field in _superFields) {
|
||||
if(fieldNames.contains(field.name))
|
||||
continue;
|
||||
|
||||
if(!field.kind.match(FMethod(_))) // only catch methods
|
||||
continue;
|
||||
|
||||
if(field.name.startsWith("get_")) {
|
||||
var access = FixedTypeTools.getAccess(field);
|
||||
if(access.contains(AInline) || access.contains(AFinal) || field.isFinal)
|
||||
continue;
|
||||
var name = field.name;
|
||||
superFields.push({
|
||||
name: field.name,
|
||||
pos: field.pos,
|
||||
kind: FFun({
|
||||
ret: null,
|
||||
params: [],
|
||||
expr: macro {
|
||||
return super.$name();
|
||||
},
|
||||
args: []
|
||||
}),
|
||||
access: access,
|
||||
meta: field.meta.get(),
|
||||
});
|
||||
//var f = convertField(field);
|
||||
//if(f != null)
|
||||
// superFields.push(f);
|
||||
if(field.name == "get_bgColor") {
|
||||
if(!didPrint) {
|
||||
trace(cl.name);
|
||||
didPrint = true;
|
||||
}
|
||||
trace("> " + field.name + " : " + access, field);
|
||||
}
|
||||
}
|
||||
|
||||
}*/
|
||||
|
||||
// want to get this working
|
||||
/*for(field in _superFields) {
|
||||
if(fieldNames.contains(field.name))
|
||||
continue;
|
||||
|
||||
if(!field.kind.match(FMethod(_))) // only catch methods
|
||||
continue;
|
||||
|
||||
var f = convertField(field);
|
||||
if(f != null)
|
||||
superFields.push(f);
|
||||
}*/
|
||||
//superFields = [];
|
||||
}
|
||||
|
||||
var shadowClass = macro class {
|
||||
|
||||
};
|
||||
|
||||
var definedFields:Array<String> = [];
|
||||
|
||||
//trace(getModuleName(cl));
|
||||
|
||||
var hasNew = false;
|
||||
|
||||
for(_field in [fields.copy(), superFields.copy()])
|
||||
for(f in _field) {
|
||||
if (f == null)
|
||||
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))
|
||||
continue;
|
||||
if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern) || f.access.contains(AInline) || f.access.contains(AFinal))
|
||||
continue;
|
||||
|
||||
if(f.name == "hget" || f.name == "hset") continue; // sorry, no overwriting the hget and hset in custom classes, yet
|
||||
if(definedFields.contains(f.name)) continue; // no duplicate fields
|
||||
|
||||
for(m in f.meta)
|
||||
if (unallowedMetas.contains(m.name))
|
||||
continue;
|
||||
|
||||
switch(f.kind) {
|
||||
case FFun(fun):
|
||||
if (fun == null)
|
||||
continue;
|
||||
if (fun.params != null && fun.params.length > 0) // TODO: Support for this maybe?
|
||||
continue;
|
||||
|
||||
if(fun.params == null)
|
||||
fun.params = [];
|
||||
|
||||
var overrideExpr:Expr;
|
||||
var returns:Bool = !fun.ret.match(TPath({name: "Void"}));
|
||||
|
||||
var name = f.name;
|
||||
|
||||
var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}];
|
||||
|
||||
if (returns) {
|
||||
overrideExpr = macro {
|
||||
var name:String = $v{name};
|
||||
|
||||
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 (__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});
|
||||
};
|
||||
}
|
||||
|
||||
var superFuncExpr:Expr = returns ? {
|
||||
macro return super.$name($a{arguments});
|
||||
} : {
|
||||
macro super.$name($a{arguments});
|
||||
};
|
||||
|
||||
var func:Function = {
|
||||
ret: fun.ret,
|
||||
params: fun.params.copy(),
|
||||
expr: overrideExpr,
|
||||
args: fun.args.copy()
|
||||
};
|
||||
|
||||
var overrideField:Field = {
|
||||
name: f.name,
|
||||
access: f.access.copy(),
|
||||
kind: FFun(func),
|
||||
pos: Context.currentPos(),
|
||||
doc: f.doc,
|
||||
meta: f.meta.copy()
|
||||
};
|
||||
|
||||
if (!overrideField.access.contains(AOverride))
|
||||
overrideField.access.push(AOverride);
|
||||
|
||||
var superField:Field = {
|
||||
name: '$FUNC_PREFIX${f.name}',
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
ret: fun.ret,
|
||||
params: fun.params.copy(),
|
||||
expr: superFuncExpr,
|
||||
args: fun.args.copy()
|
||||
}),
|
||||
access: f.access.copy()
|
||||
};
|
||||
if (superField.access.contains(AOverride))
|
||||
superField.access.remove(AOverride);
|
||||
shadowClass.fields.push(overrideField);
|
||||
shadowClass.fields.push(superField);
|
||||
definedFields.push(f.name);
|
||||
default:
|
||||
// fuck off >:(
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var totalFields = definedFields.length;
|
||||
|
||||
if(totalFields == 0 && !hasNew) {
|
||||
//Sys.println(cl.pack.join(".") + "." + cl.name + ", " + totalFields);
|
||||
return fields;
|
||||
}
|
||||
|
||||
shadowClass.kind = TDClass({
|
||||
pack: cl.pack.copy(),
|
||||
name: cl.name
|
||||
}, [
|
||||
{name: "IHScriptCustomClassBehaviour", pack: ["hscript"]}
|
||||
], false, true, false);
|
||||
shadowClass.name = '${cl.name}$CLASS_SUFFIX';
|
||||
var imports = Context.getLocalImports().copy();
|
||||
Utils.setupMetas(shadowClass, imports);
|
||||
Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect");
|
||||
|
||||
// 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(),
|
||||
kind: FVar(macro: Bool, macro true),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__real_fields",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: Array<String>),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__class__fields",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: Array<String>),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__callGetter",
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: macro {
|
||||
return null;
|
||||
},
|
||||
args: [
|
||||
{
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: String
|
||||
}
|
||||
]
|
||||
}),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__callSetter",
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: macro {
|
||||
return null;
|
||||
},
|
||||
args: [
|
||||
{
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: String
|
||||
},
|
||||
{
|
||||
name: "val",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: Dynamic
|
||||
}
|
||||
]
|
||||
}),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
// Todo: make it possible to override
|
||||
if(cl.name == "FunkinShader" || cl.name == "CustomShader" || cl.name == "MultiThreadedScript") {
|
||||
Context.defineModule(cl.module, [shadowClass], imports);
|
||||
return fields;
|
||||
}
|
||||
|
||||
var hasHgetInSuper = false;
|
||||
var hasHsetInSuper = false;
|
||||
|
||||
if(cl.name == "CustomShader") {
|
||||
hasHgetInSuper = hasHsetInSuper = true;
|
||||
}
|
||||
|
||||
// TODO: somehow check the super super class
|
||||
for(_field in [fields.copy(), superFields.copy()])
|
||||
for(f in _field) {
|
||||
if (f.name == "new")
|
||||
continue;
|
||||
if (f.name.startsWith(FUNC_PREFIX))
|
||||
continue;
|
||||
if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern))
|
||||
continue;
|
||||
|
||||
switch(f.kind) {
|
||||
case FFun(fun):
|
||||
if (fun.params != null && fun.params.length > 0)
|
||||
continue;
|
||||
|
||||
if(!hasHgetInSuper)
|
||||
hasHgetInSuper = f.name == "hget";
|
||||
if(!hasHsetInSuper)
|
||||
hasHsetInSuper = f.name == "hset";
|
||||
|
||||
if(hasHgetInSuper && hasHsetInSuper)
|
||||
break;
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var hgetField = if(hasHgetInSuper) {
|
||||
macro {
|
||||
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 (__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 (__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);
|
||||
}
|
||||
return super.hset(name, val);
|
||||
}
|
||||
} else {
|
||||
macro {
|
||||
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);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
//if(hasHsetInSuper || hasHgetInSuper) return fields;
|
||||
|
||||
//trace(cl.name);
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "hset",
|
||||
pos: Context.currentPos(),
|
||||
access: hasHsetInSuper ? [AOverride, APublic] : [APublic],
|
||||
kind: FFun({
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: hsetField,
|
||||
args: [
|
||||
{
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: String
|
||||
},
|
||||
{
|
||||
name: "val",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: Dynamic
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "hget",
|
||||
pos: Context.currentPos(),
|
||||
access: hasHgetInSuper ? [AOverride, APublic] : [APublic],
|
||||
kind: FFun({
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: hgetField,
|
||||
args: [
|
||||
{
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: String
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
/*var p = new Printer();
|
||||
var aa = p.printTypeDefinition(shadowClass);
|
||||
if(aa.length < 5024)
|
||||
trace(aa);
|
||||
if(aa.indexOf("pack") >= 0)
|
||||
if(cl.name == "FunkinShader")*/
|
||||
|
||||
Context.defineModule(cl.module, [shadowClass], imports);
|
||||
}
|
||||
|
||||
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 {
|
||||
public var usedClass:Class<Dynamic>;
|
||||
public var className:String;
|
||||
}
|
||||
#end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,529 @@
|
||||
package hscript.macros;
|
||||
|
||||
/*
|
||||
* Copyright (C)2005-2019 Haxe Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Type;
|
||||
import Type as StdType;
|
||||
|
||||
using Lambda;
|
||||
|
||||
/**
|
||||
This class provides some utility methods to work with types. It is
|
||||
best used through 'using haxe.macro.TypeTools' syntax and then provides
|
||||
additional methods on haxe.macro.Type instances.
|
||||
**/
|
||||
#if hl
|
||||
@:hlNative("macro")
|
||||
#end
|
||||
class FixedTypeTools {
|
||||
static function nullable(complexType:ComplexType):ComplexType
|
||||
return macro:Null<$complexType>;
|
||||
|
||||
public static function toField(cf:ClassField):Field {
|
||||
function varAccessToString(va:VarAccess, getOrSet:String):String {
|
||||
return {
|
||||
switch (va) {
|
||||
case AccNormal | AccCtor: "default";
|
||||
case AccNo: "null";
|
||||
case AccNever: "never";
|
||||
case AccResolve: throw "Invalid " + StdType.enumConstructor(cf.type) + " in varAccessToString";
|
||||
case AccCall: getOrSet;
|
||||
case AccInline: "default";
|
||||
case AccRequire(_, _): "default";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var access = cf.isPublic ? [APublic] : [APrivate];
|
||||
if (cf.meta.has(":final")) {
|
||||
access.push(AFinal);
|
||||
}
|
||||
|
||||
if (cf.params.length != 0)
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " has more than 0 params";
|
||||
|
||||
return {
|
||||
name: cf.name,
|
||||
doc: cf.doc,
|
||||
access: access,
|
||||
kind: switch ([cf.kind, cf.type]) {
|
||||
case [FVar(read, write), ret]:
|
||||
FProp(varAccessToString(read, "get"), varAccessToString(write, "set"), toComplexType(ret), null);
|
||||
case [FMethod(_), TFun(args, ret)]:
|
||||
Sys.println("Converting " + cf.name);
|
||||
FFun({
|
||||
args: [
|
||||
for (a in args)
|
||||
{
|
||||
name: a.name,
|
||||
opt: a.opt,
|
||||
type: toComplexType(a.t),
|
||||
}
|
||||
],
|
||||
ret: toComplexType(ret),
|
||||
expr: null,
|
||||
});
|
||||
case [FMethod(_), TLazy(f)]:
|
||||
Sys.println("Converting lazy " + cf.name + " in " + cf.pos);
|
||||
switch(f()) {
|
||||
case TFun(args, ret):
|
||||
FFun({
|
||||
args: [
|
||||
for (a in args)
|
||||
{
|
||||
name: a.name,
|
||||
opt: a.opt,
|
||||
type: toComplexType(a.t),
|
||||
}
|
||||
],
|
||||
ret: toComplexType(ret),
|
||||
expr: null,
|
||||
});
|
||||
default:
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
|
||||
}
|
||||
default:
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
|
||||
},
|
||||
pos: cf.pos,
|
||||
meta: cf.meta.get(),
|
||||
}
|
||||
}
|
||||
|
||||
public static function getAccess(cf:ClassField):Array<Access> {
|
||||
var access = cf.isPublic ? [APublic] : [APrivate];
|
||||
if (cf.meta.has(":final") || cf.isFinal) {
|
||||
access.push(AFinal);
|
||||
}
|
||||
switch ([cf.kind, cf.type]) {
|
||||
case [FMethod(kind), TFun(_, _)] | [FMethod(kind), TLazy(_)]:
|
||||
if(kind == MethInline)
|
||||
access.push(AInline);
|
||||
if(kind == MethDynamic)
|
||||
access.push(ADynamic);
|
||||
default:
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
public static function toSimpleField(cf:ClassField):Field {
|
||||
function varAccessToString(va:VarAccess, getOrSet:String):String {
|
||||
return {
|
||||
switch (va) {
|
||||
case AccNormal | AccCtor: "default";
|
||||
case AccNo: "null";
|
||||
case AccNever: "never";
|
||||
case AccResolve: throw "Invalid " + StdType.enumConstructor(cf.type) + " in varAccessToString";
|
||||
case AccCall: getOrSet;
|
||||
case AccInline: "default";
|
||||
case AccRequire(_, _): "default";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var access = cf.isPublic ? [APublic] : [APrivate];
|
||||
if (cf.meta.has(":final")) {
|
||||
access.push(AFinal);
|
||||
}
|
||||
|
||||
if (cf.params.length != 0)
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " has more than 0 params";
|
||||
|
||||
return {
|
||||
name: cf.name,
|
||||
doc: cf.doc,
|
||||
access: access,
|
||||
kind: switch ([cf.kind, cf.type]) {
|
||||
case [FVar(read, write), ret]:
|
||||
FProp(varAccessToString(read, "get"), varAccessToString(write, "set"), toComplexType(ret), null);
|
||||
case [FMethod(_), TFun(args, ret)]:
|
||||
Sys.println("Converting " + cf.name);
|
||||
FFun({
|
||||
args: [
|
||||
for (a in args)
|
||||
{
|
||||
name: a.name,
|
||||
opt: a.opt,
|
||||
type: null,//toComplexType(a.t),
|
||||
}
|
||||
],
|
||||
ret: null,//toComplexType(ret),
|
||||
expr: null,
|
||||
});
|
||||
case [FMethod(_), TLazy(f)]:
|
||||
Sys.println("Converting lazy " + cf.name + " in " + cf.pos);
|
||||
switch(f()) {
|
||||
case TFun(args, ret):
|
||||
FFun({
|
||||
args: [
|
||||
for (a in args)
|
||||
{
|
||||
name: a.name,
|
||||
opt: a.opt,
|
||||
type: null,//toComplexType(a.t),
|
||||
}
|
||||
],
|
||||
ret: null,//toComplexType(ret),
|
||||
expr: null,
|
||||
});
|
||||
default:
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
|
||||
}
|
||||
default:
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
|
||||
},
|
||||
pos: cf.pos,
|
||||
meta: cf.meta.get(),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a syntax-level type corresponding to Type `t`.
|
||||
|
||||
This function is mostly inverse to `ComplexTypeTools.toType`, but may
|
||||
lose some information on types that do not have a corresponding syntax
|
||||
version, such as monomorphs. In these cases, the result is null.
|
||||
|
||||
If `t` is null, an internal exception is thrown.
|
||||
**/
|
||||
public static function toComplexType(type:Null<Type>):Null<ComplexType>
|
||||
return {
|
||||
#if macro
|
||||
Context.toComplexType(type);
|
||||
#else
|
||||
switch (type) {
|
||||
case null:
|
||||
null;
|
||||
case TMono(_.get() => t):
|
||||
t == null ? null : toComplexType(t);
|
||||
case TEnum(_.get() => baseType, params):
|
||||
TPath(toTypePath(baseType, params));
|
||||
case TInst(_.get() => classType, params):
|
||||
switch (classType.kind) {
|
||||
case KTypeParameter(_):
|
||||
TPath({
|
||||
name: classType.name,
|
||||
pack: [],
|
||||
});
|
||||
default:
|
||||
TPath(toTypePath(classType, params));
|
||||
}
|
||||
case TType(_.get() => baseType, params):
|
||||
TPath(toTypePath(baseType, params));
|
||||
case TFun(args, ret):
|
||||
TFunction([for (a in args) a.opt ? nullable(toComplexType(a.t)) : toComplexType(a.t)], toComplexType(ret));
|
||||
case TAnonymous(_.get() => {fields: fields}):
|
||||
TAnonymous([for (cf in fields) toField(cf)]);
|
||||
case TDynamic(t):
|
||||
if (t == null) {
|
||||
macro:Dynamic;
|
||||
} else {
|
||||
var ct = toComplexType(t);
|
||||
macro:Dynamic<$ct>;
|
||||
}
|
||||
case TLazy(f):
|
||||
toComplexType(f());
|
||||
case TAbstract(_.get() => baseType, params):
|
||||
TPath(toTypePath(baseType, params));
|
||||
default:
|
||||
throw "Invalid type";
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
static function toTypeParam(type:Type):TypeParam
|
||||
return {
|
||||
switch (type) {
|
||||
case TInst(_.get() => {kind: KExpr(e)}, _): TPExpr(e);
|
||||
case _: TPType(toComplexType(type));
|
||||
}
|
||||
}
|
||||
|
||||
static function toTypePath(baseType:BaseType, params:Array<Type>):TypePath
|
||||
return {
|
||||
var module = baseType.module;
|
||||
{
|
||||
pack: baseType.pack,
|
||||
name: module.substring(module.lastIndexOf(".") + 1),
|
||||
sub: baseType.name,
|
||||
params: [for (t in params) toTypeParam(t)],
|
||||
}
|
||||
}
|
||||
|
||||
#if macro
|
||||
/**
|
||||
Follows all typedefs of `t` to reach the actual type.
|
||||
|
||||
If `once` is true, this function does not call itself recursively,
|
||||
otherwise it does. This can be useful in cases where intermediate
|
||||
typedefs might be of interest.
|
||||
|
||||
Affected types are monomorphs `TMono` and typedefs `TType(t,pl)`.
|
||||
|
||||
If `t` is null, an internal exception is thrown.
|
||||
|
||||
Usage example with monomorphs:
|
||||
var t = Context.typeof(macro null); // TMono(<mono>)
|
||||
var ts = Context.typeof(macro "foo"); //TInst(String,[])
|
||||
Context.unify(t, ts);
|
||||
trace(t); // TMono(<mono>)
|
||||
trace(t.follow()); //TInst(String,[])
|
||||
|
||||
Usage example with typedefs:
|
||||
var t = Context.typeof(macro ("foo" :MyString)); // typedef MyString = String
|
||||
trace(t); // TType(MyString,[])
|
||||
trace(t.follow()); //TInst(String,[])
|
||||
**/
|
||||
static public inline function follow(t:Type, ?once:Bool):Type
|
||||
return Context.follow(t, once);
|
||||
|
||||
/**
|
||||
Like `follow`, follows all typedefs of `t` to reach the actual type.
|
||||
|
||||
Will however follow also abstracts to their underlying implementation,
|
||||
if they are not a @:coreType abstract
|
||||
|
||||
If `t` is null, an internal exception is thrown.
|
||||
|
||||
Usage example:
|
||||
var t = Context.typeof(macro new Map<String, String>());
|
||||
trace(t); // TAbstract(Map,[TInst(String,[]),TInst(String,[])])
|
||||
trace(t.followWithAbstracts()); // TInst(haxe.ds.StringMap, [TInst(String,[])])
|
||||
**/
|
||||
static public inline function followWithAbstracts(t:Type, once:Bool = false):Type
|
||||
return Context.followWithAbstracts(t, once);
|
||||
|
||||
/**
|
||||
Returns true if `t1` and `t2` unify, false otherwise.
|
||||
**/
|
||||
static public inline function unify(t1:Type, t2:Type):Bool
|
||||
return Context.unify(t1, t2);
|
||||
|
||||
/**
|
||||
Tries to extract the class instance stored inside `t`.
|
||||
|
||||
If `t` is a class instance `TInst(c,pl)`, c is returned.
|
||||
|
||||
If `t` is of a different type, an exception of type String is thrown.
|
||||
|
||||
If `t` is null, the result is null.
|
||||
**/
|
||||
static public function getClass(t:Type)
|
||||
return t == null ? null : switch (follow(t)) {
|
||||
case TInst(c, _): c.get();
|
||||
case _: throw "Class instance expected";
|
||||
}
|
||||
|
||||
/**
|
||||
Tries to extract the enum instance stored inside `t`.
|
||||
|
||||
If `t` is an enum instance `TEnum(e,pl)`, e is returned.
|
||||
|
||||
If `t` is of a different type, an exception of type String is thrown.
|
||||
|
||||
If `t` is null, the result is null.
|
||||
**/
|
||||
static public function getEnum(t:Type)
|
||||
return t == null ? null : switch (follow(t)) {
|
||||
case TEnum(e, _): e.get();
|
||||
case _: throw "Enum instance expected";
|
||||
}
|
||||
|
||||
/**
|
||||
Applies the type parameters `typeParameters` to type `t` with the given
|
||||
types `concreteTypes`.
|
||||
|
||||
This function replaces occurrences of type parameters in `t` if they are
|
||||
part of `typeParameters`. The array index of such a type parameter is
|
||||
then used to lookup the concrete type in `concreteTypes`.
|
||||
|
||||
If `typeParameters.length` is not equal to `concreteTypes.length`, an
|
||||
exception of type `String` is thrown.
|
||||
|
||||
If `typeParameters.length` is 0, `t` is returned unchanged.
|
||||
|
||||
If either argument is `null`, the result is unspecified.
|
||||
**/
|
||||
static public function applyTypeParameters(t:Type, typeParameters:Array<TypeParameter>, concreteTypes:Array<Type>):Type {
|
||||
if (typeParameters.length != concreteTypes.length)
|
||||
throw 'Incompatible arguments: ${typeParameters.length} type parameters and ${concreteTypes.length} concrete types';
|
||||
else if (typeParameters.length == 0)
|
||||
return t;
|
||||
#if (neko || eval)
|
||||
return @:privateAccess Context.load("apply_params", 3)(typeParameters, concreteTypes, t);
|
||||
#else
|
||||
return applyParams(typeParameters, concreteTypes, t);
|
||||
#end
|
||||
}
|
||||
|
||||
#if !neko
|
||||
private static function applyParams(typeParameters:Array<TypeParameter>, concreteTypes:Array<Type>, t:Type):Type {
|
||||
return null;
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Transforms `t` by calling `f` on each of its subtypes.
|
||||
|
||||
If `t` is a compound type, `f` is called on each of its components.
|
||||
|
||||
Otherwise `t` is returned unchanged.
|
||||
|
||||
The following types are considered compound:
|
||||
- TInst, TEnum, TType and TAbstract with type parameters
|
||||
- TFun
|
||||
- TAnonymous
|
||||
|
||||
If `t` or `f` are null, the result is unspecified.
|
||||
**/
|
||||
static public function map(t:Type, f:Type->Type):Type {
|
||||
return switch (t) {
|
||||
case TMono(tm):
|
||||
switch (tm.get()) {
|
||||
case null: t;
|
||||
case var t: f(t);
|
||||
}
|
||||
case TEnum(_, []) | TInst(_, []) | TType(_, []):
|
||||
t;
|
||||
case TEnum(en, tl):
|
||||
TEnum(en, tl.map(f));
|
||||
case TInst(cl, tl):
|
||||
TInst(cl, tl.map(f));
|
||||
case TType(t2, tl):
|
||||
TType(t2, tl.map(f));
|
||||
case TAbstract(a, tl):
|
||||
TAbstract(a, tl.map(f));
|
||||
case TFun(args, ret):
|
||||
TFun(args.map(function(arg) return {
|
||||
name: arg.name,
|
||||
opt: arg.opt,
|
||||
t: f(arg.t)
|
||||
}), f(ret));
|
||||
case TAnonymous(an):
|
||||
TAnonymous(@:privateAccess Context.load("map_anon_ref", 2)(an, f));
|
||||
case TDynamic(t2):
|
||||
t == t2 ? t : TDynamic(f(t2));
|
||||
case TLazy(ft):
|
||||
var ft = ft();
|
||||
var ft2 = f(ft);
|
||||
ft == ft2 ? t : ft2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Calls function `f` on each component of type `t`.
|
||||
|
||||
If `t` is not a compound type, this operation has no effect.
|
||||
|
||||
The following types are considered compound:
|
||||
- TInst, TEnum, TType and TAbstract with type parameters
|
||||
- TFun
|
||||
- TAnonymous
|
||||
|
||||
If `t` or `f` are null, the result is unspecified.
|
||||
**/
|
||||
static public function iter(t:Type, f:Type->Void):Void {
|
||||
switch (t) {
|
||||
case TMono(tm):
|
||||
var t = tm.get();
|
||||
if (t != null)
|
||||
f(t);
|
||||
case TEnum(_, tl) | TInst(_, tl) | TType(_, tl) | TAbstract(_, tl):
|
||||
for (t in tl)
|
||||
f(t);
|
||||
case TDynamic(t2):
|
||||
if (t != t2)
|
||||
f(t2);
|
||||
case TLazy(ft):
|
||||
f(ft());
|
||||
case TAnonymous(an):
|
||||
for (field in an.get().fields)
|
||||
f(field.type);
|
||||
case TFun(args, ret):
|
||||
for (arg in args)
|
||||
f(arg.t);
|
||||
f(ret);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Converts type `t` to a human-readable String representation.
|
||||
**/
|
||||
static public function toString(t:Type):String {
|
||||
#if (neko || eval)
|
||||
return @:privateAccess Context.load("s_type", 1)(t);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Changes the name of the variable in the typed expression.
|
||||
**/
|
||||
static public function setVarName(t:TVar, name:String) {
|
||||
@:privateAccess Context.load("set_var_name", 2)(t, name);
|
||||
}
|
||||
|
||||
/**
|
||||
Converts type `t` to `ModuleType`.
|
||||
**/
|
||||
static public function toModuleType(t:Type):ModuleType {
|
||||
#if (neko || eval)
|
||||
return @:privateAccess Context.load("type_to_module_type", 1)(t);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a type from the `ModuleType` argument.
|
||||
**/
|
||||
static public function fromModuleType(mt:ModuleType):Type {
|
||||
#if (neko || eval)
|
||||
return @:privateAccess Context.load("module_type_to_type", 1)(mt);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Resolves the field named `name` on class `c`.
|
||||
|
||||
If `isStatic` is true, the classes' static fields are checked. Otherwise
|
||||
the classes' member fields are checked.
|
||||
|
||||
If the field is found, it is returned. Otherwise if `c` has a super
|
||||
class, `findField` recursively checks that super class. Otherwise null
|
||||
is returned.
|
||||
|
||||
If any argument is null, the result is unspecified.
|
||||
**/
|
||||
static public function findField(c:ClassType, name:String, isStatic:Bool = false):Null<ClassField> {
|
||||
var field = (isStatic ? c.statics : c.fields).get().find(function(field) return field.name == name);
|
||||
return if (field != null) field; else if (c.superClass != null) findField(c.superClass.t.get(), name, isStatic); else null;
|
||||
}
|
||||
}
|
||||
@@ -1,107 +1,20 @@
|
||||
package hscript;
|
||||
package hscript.macros;
|
||||
|
||||
import Type.ValueType;
|
||||
import haxe.macro.ComplexTypeTools;
|
||||
#if macro
|
||||
import haxe.macro.Type.ClassType;
|
||||
import Type.ValueType;
|
||||
import haxe.macro.Expr.Function;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Printer;
|
||||
import haxe.macro.Compiler;
|
||||
import haxe.macro.Type.MetaAccess;
|
||||
import haxe.macro.Type.FieldKind;
|
||||
import haxe.macro.Type.ClassField;
|
||||
import haxe.macro.Type.VarAccess;
|
||||
import haxe.macro.*;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class UsingHandler {
|
||||
public var usedClass:Class<Dynamic>;
|
||||
public var className:String;
|
||||
|
||||
public function new(className:String, usedClass:Class<Dynamic>) {
|
||||
this.className = className;
|
||||
this.usedClass = usedClass;
|
||||
}
|
||||
|
||||
public static function init() {
|
||||
Compiler.addGlobalMetadata('flixel', '@:build(hscript.UsingHandler.build())');
|
||||
Compiler.addGlobalMetadata('openfl.display.BlendMode', '@:build(hscript.UsingHandler.build())');
|
||||
}
|
||||
|
||||
public static function build():Array<Field> {
|
||||
var fields = Context.getBuildFields();
|
||||
var clRef = Context.getLocalClass();
|
||||
if (clRef == null) return fields;
|
||||
var cl = clRef.get();
|
||||
|
||||
if (/* cl.name.startsWith("Flx") && */ cl.name.endsWith("_Impl_") && cl.params.length <= 0 && !cl.meta.has(":multiType")) {
|
||||
var metas = cl.meta.get();
|
||||
|
||||
var shadowClass = macro class {
|
||||
|
||||
};
|
||||
shadowClass.kind = TDClass();
|
||||
shadowClass.params = switch(cl.params.length) {
|
||||
case 0:
|
||||
null;
|
||||
case 1:
|
||||
[
|
||||
{
|
||||
name: "T",
|
||||
}
|
||||
];
|
||||
default:
|
||||
[for(k=>e in cl.params) {
|
||||
name: "T" + Std.int(k+1)
|
||||
}];
|
||||
};
|
||||
shadowClass.name = '${cl.name.substr(0, cl.name.length - 6)}_HSC';
|
||||
|
||||
var imports = Context.getLocalImports().copy();
|
||||
setupMetas(shadowClass, imports);
|
||||
|
||||
for(f in fields)
|
||||
switch(f.kind) {
|
||||
case FFun(fun):
|
||||
if (f.access.contains(AStatic)) {
|
||||
if (fun.expr != null)
|
||||
shadowClass.fields.push(f);
|
||||
}
|
||||
case FProp(get, set, t, e):
|
||||
if (get == "default" && (set == "never" || set == "null"))
|
||||
shadowClass.fields.push(f);
|
||||
case FVar(t, e):
|
||||
if (f.access.contains(AStatic) || cl.meta.has(":enum") || f.name.toUpperCase() == f.name) {
|
||||
var name:String = f.name;
|
||||
var enumType:String = cl.name;
|
||||
var pack = cl.module.split(".");
|
||||
pack.pop();
|
||||
var complexType:ComplexType = t != null ? t : (name.contains("REGEX") ? TPath({
|
||||
name: "EReg",
|
||||
pack: []
|
||||
}) : TPath({
|
||||
name: cl.name.substr(0, cl.name.length - 6),
|
||||
pack: pack}));
|
||||
var field:Field = {
|
||||
pos: f.pos,
|
||||
name: f.name,
|
||||
meta: f.meta,
|
||||
kind: FVar(complexType, {
|
||||
pos: Context.currentPos(),
|
||||
expr: ECast(e, complexType)
|
||||
}),
|
||||
doc: f.doc,
|
||||
access: [APublic, AStatic]
|
||||
}
|
||||
|
||||
shadowClass.fields.push(field);
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
Context.defineModule(cl.module + "_HSC", [shadowClass], imports);
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
static function fixStdTypes(type:ComplexType) {
|
||||
class Utils {
|
||||
public static function fixStdTypes(type:ComplexType) {
|
||||
switch(type) {
|
||||
case TPath({name: "StdTypes"}):
|
||||
var a:TypePath = type.getParameters()[0];
|
||||
@@ -113,16 +26,9 @@ class UsingHandler {
|
||||
}
|
||||
|
||||
public static function setupMetas(shadowClass:TypeDefinition, imports) {
|
||||
shadowClass.meta = [{
|
||||
name: ':dox',
|
||||
pos: Context.currentPos(),
|
||||
params: [
|
||||
{
|
||||
expr: EConst(CIdent("hide")),
|
||||
pos: Context.currentPos()
|
||||
}
|
||||
]
|
||||
}];
|
||||
shadowClass.meta = [];
|
||||
shadowClass.meta.push({name: ":dox", params: [macro hide], pos: Context.currentPos()});
|
||||
shadowClass.meta.push({name: ":noCompletion", params: [], pos: Context.currentPos()});
|
||||
var module = Context.getModule(Context.getLocalModule());
|
||||
for(t in module) {
|
||||
switch(t) {
|
||||
@@ -300,9 +206,4 @@ class UsingHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
class UsingHandler {
|
||||
public var usedClass:Class<Dynamic>;
|
||||
public var className:String;
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,184 @@
|
||||
package hscript.utils;
|
||||
|
||||
#if cpp
|
||||
import cpp.ObjectType;
|
||||
#end
|
||||
|
||||
@:analyzer(ignore)
|
||||
class UnsafeReflect {
|
||||
public #if !cpp inline #end static function hasField(o:Dynamic, field:String):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
return o.__HasField(field);
|
||||
}
|
||||
#else
|
||||
return Reflect.hasField(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function field(o:Dynamic, field:String):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
return o.__Field(field, untyped __cpp__("::hx::paccNever"));
|
||||
}
|
||||
#else
|
||||
return Reflect.field(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function setField(o:Dynamic, field:String, value:Dynamic):Void {
|
||||
#if cpp
|
||||
untyped {
|
||||
o.__SetField(field, value, untyped __cpp__("::hx::paccNever"));
|
||||
}
|
||||
#else
|
||||
return Reflect.setField(o, field, value);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function getProperty(o:Dynamic, field:String):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
return o.__Field(field, untyped __cpp__("::hx::paccAlways"));
|
||||
}
|
||||
#else
|
||||
return Reflect.getProperty(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function setProperty(o:Dynamic, field:String, value:Dynamic):Void {
|
||||
#if cpp
|
||||
untyped {
|
||||
o.__SetField(field, value, untyped __cpp__("::hx::paccAlways"));
|
||||
}
|
||||
#else
|
||||
Reflect.setProperty(o, field, value);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function callFieldUnsafe(o:Dynamic, field:String, args:Array<Dynamic>):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
var func:Dynamic = o.__Field(field, untyped __cpp__("::hx::paccDynamic"));
|
||||
untyped func.__SetThis(o);
|
||||
return untyped func.__Run(args);
|
||||
}
|
||||
#else
|
||||
return Reflect.callMethod(o, Reflect.field(o, field), args);
|
||||
#end
|
||||
}
|
||||
|
||||
public inline static function callMethod(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||
return Reflect.callMethod(o, func, args);
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function callMethodSafe(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
if (func == null)
|
||||
throw cpp.ErrorConstants.nullFunctionPointer;
|
||||
untyped func.__SetThis(o);
|
||||
return untyped func.__Run(args);
|
||||
}
|
||||
#else
|
||||
return Reflect.callMethod(o, func, args);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function callMethodUnsafe(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
untyped func.__SetThis(o);
|
||||
return untyped func.__Run(args);
|
||||
}
|
||||
#else
|
||||
return Reflect.callMethod(o, func, args);
|
||||
#end
|
||||
}
|
||||
|
||||
public inline static function fields(o:Dynamic):Array<String>
|
||||
return Reflect.fields(o);
|
||||
/*untyped {
|
||||
if (o == null)
|
||||
return new Array();
|
||||
var a:Array<String> = [];
|
||||
o.__GetFields(a);
|
||||
return a;
|
||||
}*/
|
||||
|
||||
public #if !cpp inline #end static function isFunction(f:Dynamic):Bool
|
||||
#if cpp
|
||||
untyped {
|
||||
return f.__GetType() == ObjectType.vtFunction;
|
||||
}
|
||||
#else
|
||||
return Reflect.isFunction(f);
|
||||
#end
|
||||
|
||||
public inline static function compare<T>(a:T, b:T):Int {
|
||||
return Reflect.compare(a, b);
|
||||
//return (a == b) ? 0 : (((a : Dynamic) > (b : Dynamic)) ? 1 : -1);
|
||||
}
|
||||
|
||||
public inline static function compareMethods(f1:Dynamic, f2:Dynamic):Bool {
|
||||
return Reflect.compareMethods(f1, f2);
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function isObject(v:Dynamic):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
var t:Int = v.__GetType();
|
||||
return t == ObjectType.vtObject || t == ObjectType.vtClass || t == ObjectType.vtString || t == ObjectType.vtArray;
|
||||
}
|
||||
#else
|
||||
return Reflect.isObject(v);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function isEnumValue(v:Dynamic):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
return v.__GetType() == ObjectType.vtEnum;
|
||||
}
|
||||
#else
|
||||
return Reflect.isEnumValue(v);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function deleteField(o:Dynamic, field:String):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
return untyped __global__.__hxcpp_anon_remove(o, field);
|
||||
}
|
||||
#else
|
||||
return Reflect.deleteField(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function copy<T>(o:Null<T>):Null<T> {
|
||||
#if cpp
|
||||
if (o == null)
|
||||
return null;
|
||||
var t:Int = untyped o.__GetType();
|
||||
if (t == ObjectType.vtString)
|
||||
return o;
|
||||
if (t == ObjectType.vtArray)
|
||||
return untyped o.__Field("copy", untyped __cpp__("::hx::paccDynamic"))();
|
||||
var o2:Dynamic = {};
|
||||
for (f in UnsafeReflect.fields(o))
|
||||
UnsafeReflect.setField(o2, f, UnsafeReflect.field(o, f));
|
||||
return o2;
|
||||
#else
|
||||
return Reflect.copy(o);
|
||||
#end
|
||||
}
|
||||
|
||||
@:overload(function(f:Array<Dynamic>->Void):Dynamic {})
|
||||
public static function makeVarArgs(f:Array<Dynamic>->Dynamic):Dynamic {
|
||||
#if cpp
|
||||
return untyped __global__.__hxcpp_create_var_args(f);
|
||||
#else
|
||||
return inline Reflect.makeVarArgs(f);
|
||||
#end
|
||||
}
|
||||
}
|
||||
@@ -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