Compare commits
63
Commits
+1
-1
@@ -1,4 +1,4 @@
|
||||
/hscript.swf
|
||||
/release.zip
|
||||
|
||||
dump/*
|
||||
tests/bin/*
|
||||
@@ -3,7 +3,7 @@ hscript-improved
|
||||
|
||||
How to install
|
||||
```
|
||||
haxelib git hscript-improved https://github.com/FNF-CNE-Devs/hscript-improved.git
|
||||
haxelib git hscript-improved https://github.com/CodenameCrew/hscript-improved.git
|
||||
```
|
||||
|
||||
To enable custom classes support you have to do this in project.xml
|
||||
|
||||
+5
-9
@@ -26,18 +26,17 @@ 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);
|
||||
assertScript("1.546",1.546);
|
||||
assertScript(".545",.545);
|
||||
assertScript("1e5",100000);
|
||||
assertScript("1.2e2",120);
|
||||
assertScript("100e-2",1);
|
||||
assertScript("1.2e-1",0.12);
|
||||
assertScript("'bla'","bla");
|
||||
assertScript("null",null);
|
||||
assertScript("true",true);
|
||||
@@ -58,6 +57,7 @@ class TestHScript extends TestCase {
|
||||
assertScript("3 * 2 // + 5 \n + 6",12);
|
||||
assertScript("3 /* 2\n */ + 5",8);
|
||||
assertScript("[55,66,77][1]",66);
|
||||
assertScript("[11,22,33,][2]", 33);
|
||||
assertScript("var a = [55]; a[0] *= 2; a[0]",110);
|
||||
assertScript("x",55,{ x : 55 });
|
||||
assertScript("var y = 33; y",33);
|
||||
@@ -81,11 +81,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);
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
--macro keep('IntIterator')
|
||||
--macro hscript.macros.UsingHandler.init()
|
||||
--macro hscript.macros.AbstractHandler.init()
|
||||
--macro hscript.macros.UsingMacro.init()
|
||||
--macro hscript.macros.ClassExtendMacro.init()
|
||||
|
||||
+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 });
|
||||
|
||||
+17
-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,17 @@ class Bytes {
|
||||
#end
|
||||
bout.addByte(Type.enumIndex(e));
|
||||
switch( e ) {
|
||||
case EIgnore(_):
|
||||
case EImport(c):
|
||||
// TODO
|
||||
case EClass(_, _, _, _):
|
||||
// TODO
|
||||
case ERedirect(_, _):
|
||||
// TODO
|
||||
case EUsing(name):
|
||||
// TODO
|
||||
case EEnum(name, fields):
|
||||
// TODO
|
||||
case EConst(c):
|
||||
doEncodeConst(c);
|
||||
case EIdent(v):
|
||||
@@ -278,7 +269,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 +286,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 +308,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 +318,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 +335,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 +349,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;
|
||||
|
||||
@@ -12,6 +12,11 @@ class Config {
|
||||
"openfl.display.BlendMode",
|
||||
];
|
||||
|
||||
// Runs support for using in specific classes.
|
||||
public static final ALLOWED_USING = [
|
||||
|
||||
];
|
||||
|
||||
// Incase any of your files fail
|
||||
// These are the module names
|
||||
public static final DISALLOW_CUSTOM_CLASSES = [
|
||||
@@ -21,4 +26,8 @@ class Config {
|
||||
public static final DISALLOW_ABSTRACT_AND_ENUM = [
|
||||
|
||||
];
|
||||
|
||||
public static final DISALLOW_USING = [
|
||||
|
||||
];
|
||||
}
|
||||
+133
-34
@@ -4,6 +4,7 @@ import hscript.Interp.DeclaredVar;
|
||||
import hscript.utils.UnsafeReflect;
|
||||
using StringTools;
|
||||
|
||||
@:deprecated('Switch to new Custom Class instead')
|
||||
class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
public static var staticHandler = new StaticHandler();
|
||||
|
||||
@@ -22,84 +23,131 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
this.extend = extend;
|
||||
this.interfaces = interfaces;
|
||||
|
||||
this.cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX');
|
||||
//this.cl = extend == null ? CustomTemplateClass : Type.resolveClass('${extend}_HSX');
|
||||
if(cl == null)
|
||||
ogInterp.error(EInvalidClass(extend));
|
||||
}
|
||||
|
||||
public function hnew(args:Array<Dynamic>):Dynamic {
|
||||
// TODO: clean this up, it sucks, i hate it
|
||||
// TODO: make static vars work correctly
|
||||
var interp = new Interp();
|
||||
|
||||
interp.errorHandler = ogInterp.errorHandler;
|
||||
|
||||
var _class:IHScriptCustomClassBehaviour = Type.createInstance(cl, args);
|
||||
|
||||
var __capturedLocals = ogInterp.duplicate(ogInterp.locals);
|
||||
var capturedLocals:Map<String, DeclaredVar> = [];
|
||||
for(k=>e in __capturedLocals)
|
||||
if (e != null && e.depth <= 0)
|
||||
capturedLocals.set(k, e);
|
||||
//var __capturedLocals = ogInterp.duplicate(ogInterp.locals);
|
||||
//var capturedLocals:Map<String, DeclaredVar> = [];
|
||||
//for(k=>e in __capturedLocals)
|
||||
// if (e != null && e.depth <= 0)
|
||||
// capturedLocals.set(k, e);
|
||||
|
||||
var disallowCopy = Type.getInstanceFields(cl);
|
||||
|
||||
for (key => value in capturedLocals) {
|
||||
if(!disallowCopy.contains(key)) {
|
||||
interp.locals.set(key, {r: value, depth: -1});
|
||||
}
|
||||
}
|
||||
_class.__real_fields = disallowCopy;
|
||||
|
||||
// todo: make it so you can use variables from the same scope as where the class was defined
|
||||
|
||||
//for (key => value in capturedLocals) {
|
||||
// if(!disallowCopy.contains(key)) {
|
||||
// interp.locals.set(key, {r: value, depth: -1});
|
||||
// }
|
||||
//}
|
||||
for (key => value in ogInterp.variables) {
|
||||
if(!disallowCopy.contains(key)) {
|
||||
interp.variables.set(key, value);
|
||||
}
|
||||
}
|
||||
for(key => value in ogInterp.customClasses) {
|
||||
if(!disallowCopy.contains(key)) {
|
||||
interp.customClasses.set(key, value);
|
||||
}
|
||||
}
|
||||
// todo: clone static vars, but make it so setting it only sets it on the class
|
||||
// todo: clone public vars
|
||||
|
||||
var comparisonMap = new Map();
|
||||
//trace("Before: " + [for(key => value in interp.variables) key]);
|
||||
|
||||
interp.variables.set("super", staticHandler);
|
||||
|
||||
var comparisonMap:Map<String, Dynamic> = [];
|
||||
for(key => value in interp.variables) {
|
||||
comparisonMap.set(key, value);
|
||||
}
|
||||
|
||||
_class.__custom__variables = interp.variables;
|
||||
//_class.__custom__variables = interp.variables;
|
||||
|
||||
//trace(fields);
|
||||
|
||||
for(expr in fields) {
|
||||
@:privateAccess
|
||||
interp.exprReturn(expr);
|
||||
}
|
||||
|
||||
interp.variables.set("super", staticHandler);
|
||||
//trace("After: " + [for(key => value in interp.variables) key]);
|
||||
|
||||
_class.__interp = interp;
|
||||
interp.scriptObject = _class;
|
||||
// get only variables that were not set before
|
||||
var classVariables = [for(key => value in interp.variables) if(!comparisonMap.exists(key) || comparisonMap[key] != value) key => value];
|
||||
for(variable => value in classVariables) {
|
||||
if(variable == "this" || variable == "super") continue;
|
||||
@:privateAccess
|
||||
if(!interp.__instanceFields.contains(variable)) {
|
||||
interp.__instanceFields.push(variable);
|
||||
}
|
||||
}
|
||||
var classVariables = [
|
||||
for(key => value in interp.variables)
|
||||
if(!comparisonMap.exists(key) || comparisonMap[key] != value)
|
||||
key => value
|
||||
];
|
||||
//for(variable => value in classVariables) {
|
||||
// if(variable == "this" || variable == "super" || variable == "new") continue;
|
||||
// @:privateAccess
|
||||
// if(!interp.__instanceFields.contains(variable)) {
|
||||
// interp.__instanceFields.push(variable);
|
||||
// }
|
||||
//}
|
||||
|
||||
//trace([for(key => value in classVariables) key]);
|
||||
//_class.__class__fields = [for(key => value in classVariables) key];
|
||||
|
||||
//trace(_class.__class__fields);
|
||||
//@:privateAccess
|
||||
//trace(interp.__instanceFields);
|
||||
|
||||
_class.__allowSetGet = false;
|
||||
//_class.__interp = interp;
|
||||
//_class.__allowSetGet = false;
|
||||
interp.scriptObject = _class;
|
||||
|
||||
for(variable => value in interp.variables) {
|
||||
if(variable == "this") continue;
|
||||
if(variable == "this" || variable == "super" || variable == "new") continue;
|
||||
|
||||
if(variable.startsWith("set_") || variable.startsWith("get_")) {
|
||||
_class.__allowSetGet = true;
|
||||
//_class.__allowSetGet = true;
|
||||
}
|
||||
}
|
||||
|
||||
var newFunc = interp.variables.get("new");
|
||||
if(newFunc != null) {
|
||||
var comparisonMap:Map<String, Dynamic> = [];
|
||||
for(key => value in interp.variables) {
|
||||
comparisonMap.set(key, value);
|
||||
}
|
||||
|
||||
UnsafeReflect.callMethodUnsafe(null, newFunc, args);
|
||||
|
||||
// get only variables that were not set before
|
||||
var classVariables = [
|
||||
for(key => value in interp.variables)
|
||||
if(!comparisonMap.exists(key) || comparisonMap[key] != value)
|
||||
key => value
|
||||
];
|
||||
for(variable => value in classVariables) {
|
||||
if(variable == "this" || variable == "super" || variable == "new") continue;
|
||||
@:privateAccess
|
||||
if(!interp.__instanceFields.contains(variable)) {
|
||||
interp.__instanceFields.push(variable);
|
||||
}
|
||||
/*
|
||||
if(!_class.__class__fields.contains(variable)) {
|
||||
_class.__class__fields.push(variable);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return _class;
|
||||
}
|
||||
|
||||
@@ -107,11 +155,14 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomBehaviour {
|
||||
/*
|
||||
class CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomAccessBehaviour {
|
||||
public var __interp:Interp;
|
||||
public var __customClass:hscript.customclass.CustomClass;
|
||||
public var __allowSetGet:Bool = true;
|
||||
public var __custom__variables:Map<String, Dynamic>;
|
||||
public var __real_fields:Array<String>;
|
||||
public var __class__fields:Array<String>;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
@@ -120,14 +171,19 @@ class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptC
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
if(__real_fields.contains(name)) {
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
public function hget(name:String):Dynamic {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
|
||||
return UnsafeReflect.getProperty(this, name);
|
||||
}
|
||||
|
||||
@@ -146,6 +202,49 @@ class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptC
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 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 var __allowPrivateAccess:Bool = true;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
/*
|
||||
final class StaticHandler {
|
||||
public function new() {}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
+65
-22
@@ -21,13 +21,13 @@
|
||||
*/
|
||||
package hscript;
|
||||
|
||||
typedef Int8 = #if cpp cpp.Int8 #else Int #end;
|
||||
typedef Int16 = #if cpp cpp.Int16 #else Int #end;
|
||||
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 #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 #else Int #end;
|
||||
typedef UInt16 = #if cpp cpp.UInt16 #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;
|
||||
|
||||
@@ -35,9 +35,6 @@ enum Const {
|
||||
CInt( v : Int );
|
||||
CFloat( f : Float );
|
||||
CString( s : String );
|
||||
#if !haxe3
|
||||
CInt32( v : haxe.Int32 );
|
||||
#end
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
@@ -54,9 +51,10 @@ enum ExprDef {
|
||||
typedef ExprDef = Expr;
|
||||
enum Expr {
|
||||
#end
|
||||
EIgnore(skip:Bool);
|
||||
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 );
|
||||
EParent( e : Expr );
|
||||
EBlock( e : Array<Expr> );
|
||||
EField( e : Expr, f : String , ?safe : Bool );
|
||||
@@ -68,14 +66,14 @@ enum 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> );
|
||||
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<SwitchCase>, ?defaultExpr : Expr );
|
||||
EDoWhile( cond : Expr, e : Expr);
|
||||
@@ -83,7 +81,16 @@ enum Expr {
|
||||
ECheckType( e : Expr, t : CType );
|
||||
|
||||
EImport( c : String, ?asname:String );
|
||||
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String> );
|
||||
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String>, ?isFinal:Bool, ?isPrivate:Bool );
|
||||
ERedirect( name:String, className:String, ?cl:Class<Dynamic> );
|
||||
EUsing(name:String);
|
||||
EEnum(name:String, fields:Array<EnumType>);
|
||||
}
|
||||
|
||||
@:structInit
|
||||
final class ObjectField {
|
||||
public var name : String;
|
||||
public var e : Expr;
|
||||
}
|
||||
|
||||
@:structInit
|
||||
@@ -92,9 +99,21 @@ final class SwitchCase {
|
||||
public var expr : Expr;
|
||||
}
|
||||
|
||||
typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
|
||||
@:structInit
|
||||
final class Argument {
|
||||
public var name : String;
|
||||
public var t : CType;
|
||||
public var opt : Bool;
|
||||
public var value : Expr;
|
||||
}
|
||||
|
||||
typedef Metadata = Array<{ name : String, params : Array<Expr> }>;
|
||||
@:structInit
|
||||
final class MetadataEntry {
|
||||
public var name : String;
|
||||
public var params : Array<Expr>;
|
||||
}
|
||||
|
||||
typedef Metadata = Array<MetadataEntry>;
|
||||
|
||||
enum CType {
|
||||
CTPath( path : Array<String>, ?params : Array<CType> );
|
||||
@@ -144,9 +163,10 @@ enum Error {
|
||||
|
||||
enum ModuleDecl {
|
||||
DPackage( path : Array<String> );
|
||||
DImport( path : Array<String>, ?everything : Bool );
|
||||
DClass( c : ClassDecl );
|
||||
DImport( path : Array<String>, ?everything : Bool, ?asname:String );
|
||||
DUsing( path : Array<String>);
|
||||
DTypedef( c : TypeDecl );
|
||||
DClass( c : ClassDecl );
|
||||
}
|
||||
|
||||
typedef ModuleType = {
|
||||
@@ -188,15 +208,38 @@ 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 = {
|
||||
var get : Null<String>;
|
||||
var set : Null<String>;
|
||||
var get : FieldPropertyAccess;
|
||||
var set : FieldPropertyAccess;
|
||||
var isFinal : Bool;
|
||||
var expr : Null<Expr>;
|
||||
var type : Null<CType>;
|
||||
}
|
||||
|
||||
typedef TypeParamDecl = {};
|
||||
|
||||
enum EnumType {
|
||||
ESimple(name: String);
|
||||
EConstructor(name: String, args: Array<Argument>);
|
||||
}
|
||||
|
||||
enum abstract FieldPropertyAccess(UInt8) {
|
||||
var ADefault;
|
||||
var ANull;
|
||||
var AGet;
|
||||
var ASet;
|
||||
var ADynamic;
|
||||
var ANever;
|
||||
}
|
||||
|
||||
enum AccessContext {
|
||||
CInner(cl:String);
|
||||
COuter;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package hscript;
|
||||
|
||||
interface IHScriptCustomAccessBehaviour {
|
||||
var __allowSetGet:Bool;
|
||||
var __allowPrivateAccess:Bool;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic;
|
||||
public function hget(name:String):Dynamic;
|
||||
|
||||
public function __callGetter(name:String):Dynamic;
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic;
|
||||
}
|
||||
@@ -1,14 +1,6 @@
|
||||
package hscript;
|
||||
|
||||
interface IHScriptCustomClassBehaviour {
|
||||
public var __interp:Interp;
|
||||
public var __custom__variables:Map<String, Dynamic>;
|
||||
public var __allowSetGet:Bool;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic;
|
||||
public function hget(name:String):Dynamic;
|
||||
|
||||
public function __callGetter(name:String):Dynamic;
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic;
|
||||
public var __customClass:hscript.customclass.CustomClass;
|
||||
public var __real_fields:Array<String>;
|
||||
}
|
||||
+1105
-209
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
package hscript;
|
||||
|
||||
class InterpConfig {
|
||||
// Automatic import redirect for certain classes
|
||||
public static final IMPORT_REDIRECTS = [
|
||||
"Type" => "hscript.proxy.ProxyType"
|
||||
];
|
||||
|
||||
// Incase an import fails
|
||||
// These are the module names
|
||||
@:unreflective
|
||||
public static final DISALLOW_IMPORT = [
|
||||
"Type"
|
||||
];
|
||||
}
|
||||
+4
-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;
|
||||
@@ -156,17 +137,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 +147,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 +168,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;
|
||||
|
||||
+493
-160
File diff suppressed because it is too large
Load Diff
+46
-9
@@ -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('?');
|
||||
@@ -101,19 +101,20 @@ class Printer {
|
||||
}
|
||||
}
|
||||
|
||||
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 ) {
|
||||
switch( Tools.expr(e) ) {
|
||||
case EIgnore(_):
|
||||
case EImport(c, n):
|
||||
add("import " + c);
|
||||
if(n != null)
|
||||
@@ -133,6 +134,8 @@ class Printer {
|
||||
|
||||
tabs = tabs.substr(1);
|
||||
add("}");
|
||||
case ERedirect(n, cl):
|
||||
add('typedef $n = $cl');
|
||||
case EConst(c):
|
||||
switch( c ) {
|
||||
case CInt(i): add(i);
|
||||
@@ -344,14 +347,48 @@ class Printer {
|
||||
add(" : ");
|
||||
addType(t);
|
||||
add(")");
|
||||
case EUsing(name):
|
||||
add("using ");
|
||||
add(name);
|
||||
case EEnum(name, params):
|
||||
if (params.length == 0) {
|
||||
add("enum " + name + " {}");
|
||||
return;
|
||||
}
|
||||
add("enum " + name + " {\n");
|
||||
tabs += "\t";
|
||||
for (p in params) {
|
||||
add(tabs);
|
||||
switch p {
|
||||
case EConstructor(name, args):
|
||||
add(name);
|
||||
add("(");
|
||||
for (a in args)
|
||||
addArgument(a);
|
||||
add(")");
|
||||
case ESimple(name):
|
||||
add(name);
|
||||
}
|
||||
add(";\n");
|
||||
}
|
||||
tabs = tabs.substr(1);
|
||||
add(tabs);
|
||||
add("}");
|
||||
}
|
||||
}
|
||||
|
||||
public static function toString( e : Expr ) {
|
||||
function addArgument(a: Argument) {
|
||||
if (a.opt)
|
||||
add("?");
|
||||
add(a.name);
|
||||
addType(a.t);
|
||||
}
|
||||
|
||||
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+"\"";
|
||||
|
||||
+152
-6
@@ -22,9 +22,11 @@
|
||||
package hscript;
|
||||
import hscript.Expr;
|
||||
|
||||
using StringTools;
|
||||
|
||||
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 +61,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 +79,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 +93,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: #if hscriptPos e.e #else e #end;
|
||||
}
|
||||
return mk(edef, e);
|
||||
}
|
||||
@@ -103,7 +106,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 +114,147 @@ class Tools {
|
||||
#end
|
||||
}
|
||||
|
||||
@:access(hscript.Interp)
|
||||
public static function clearClasses() {
|
||||
#if CUSTOM_CLASSES
|
||||
Interp._customClasses.clear();
|
||||
Interp._customClassAliases.clear();
|
||||
#end
|
||||
}
|
||||
|
||||
public static function removeInnerClass(name: String): String {
|
||||
var ll = name.lastIndexOf(".");
|
||||
if (name.indexOf(".") != ll) { // checks if there are 2 or more dots
|
||||
// wtf did i make -neo
|
||||
// this is awesome -crow
|
||||
return name.substr(0, name.lastIndexOf(".", ll - 1)) + "." + name.substr(ll + 1);
|
||||
}
|
||||
// only one dot or no dots (will work since -1 + 1 = 0)
|
||||
return name.substr(ll + 1);
|
||||
}
|
||||
|
||||
// TODO: maybe use this function for import since also check for innerclasses
|
||||
public static function getClass(name: String): Dynamic {
|
||||
var splitClassName = [for (e in name.split(".")) e.trim()];
|
||||
var realClassName = splitClassName.join(".");
|
||||
|
||||
var c: Dynamic = Type.resolveClass(realClassName);
|
||||
if (c == null) // try importing as enum
|
||||
try
|
||||
c = Type.resolveEnum(name);
|
||||
|
||||
if (c == null) {
|
||||
// lastly try removing any inner class from it
|
||||
// this allows you to import stuff like
|
||||
// flixel.text.FlxText.FlxTextBorderStyle
|
||||
// without the script crashing immediately
|
||||
var className = removeInnerClass(realClassName);
|
||||
if (className != name) {
|
||||
c = Type.resolveClass(className);
|
||||
if (c == null)
|
||||
c = Type.resolveEnum(className);
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
public static function isIterable(v: Dynamic): Bool {
|
||||
// TODO: test for php and lua, they might have issues with this check
|
||||
return v != null && v.iterator != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE INLINE ON THIS FUNCTION
|
||||
**/
|
||||
public static function argCount(func: haxe.Constraints.Function): Int {
|
||||
#if cpp
|
||||
return untyped __cpp__("{0}->__ArgCount()", func);
|
||||
#elseif js
|
||||
return untyped js.Syntax.code("{0}.length", func);
|
||||
#else
|
||||
return -1;
|
||||
#end
|
||||
}
|
||||
|
||||
public static function getClassDeclFields(classDecl:hscript.Expr.ClassDecl, onlyStatic:Bool = false):Array<FieldDecl> {
|
||||
var fields = classDecl.fields.filter(function(field:FieldDecl) {
|
||||
var isStatic = field.access.contains(AStatic);
|
||||
return onlyStatic ? isStatic : !isStatic;
|
||||
});
|
||||
return fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/hscript/Tools.hx#L217
|
||||
*/
|
||||
class EnumValue {
|
||||
public var enumName: String;
|
||||
public var name: String;
|
||||
public var index: Int;
|
||||
public var args: Array<Dynamic>;
|
||||
public var enumParent:Dynamic;
|
||||
|
||||
public function new(enumName: String, name: String, index: Int, enumParent:Dynamic, ?args: Array<Dynamic>) {
|
||||
this.enumName = enumName;
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
this.enumParent = enumParent;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public function toString(): String {
|
||||
if (args == null)
|
||||
return enumName + "." + name;
|
||||
return enumName + "." + name + "(" + [for (arg in args) arg].join(", ") + ")";
|
||||
}
|
||||
|
||||
public inline function getEnumName(): String
|
||||
return this.enumName;
|
||||
|
||||
public inline function getConstructorArgs(): Array<Dynamic>
|
||||
return this.args != null ? this.args : [];
|
||||
|
||||
public function compare(other: EnumValue): Bool {
|
||||
if (enumName != other.enumName || name != other.name)
|
||||
return false;
|
||||
if (args == null && other.args == null)
|
||||
return true;
|
||||
if (args == null || other.args == null)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
class HScriptEnum implements IHScriptCustomBehaviour {
|
||||
public var enumValues(default, null) = {};
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function setEnum(name:String, enumValue:Dynamic) {
|
||||
Reflect.setField(enumValues, name, enumValue);
|
||||
}
|
||||
|
||||
public function getEnumValue(name:String):Null<Dynamic> {
|
||||
if(Reflect.hasField(enumValues, name)) return Reflect.field(enumValues, name);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
return getEnumValue(name);
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,512 @@
|
||||
package hscript.customclass;
|
||||
|
||||
import hscript.utils.UnsafeReflect;
|
||||
import haxe.Constraints.Function;
|
||||
import hscript.Expr;
|
||||
import hscript.Expr.FieldPropertyAccess;
|
||||
import hscript.Expr.VarDecl;
|
||||
import hscript.Expr.FunctionDecl;
|
||||
import hscript.Expr.FieldDecl;
|
||||
|
||||
using Lambda;
|
||||
using StringTools;
|
||||
|
||||
/**
|
||||
* The Custom Class core.
|
||||
*
|
||||
* Provides handlers for custom classes.
|
||||
*
|
||||
* Rest in peace, CustomClassHandler.hx
|
||||
*
|
||||
* Based on Polymod Hscript class system
|
||||
* @see https://github.com/larsiusprime/polymod/tree/master/polymod/hscript/_internal
|
||||
*/
|
||||
@:access(hscript.customclass.CustomClassDecl)
|
||||
class CustomClass implements IHScriptCustomAccessBehaviour {
|
||||
public var interp:Interp;
|
||||
|
||||
public var superClass:Dynamic;
|
||||
public var superConstructor(default, null):Dynamic;
|
||||
|
||||
public var superIsCustomClass(get, never):Bool;
|
||||
private function get_superIsCustomClass():Bool
|
||||
return (superClass != null && superClass is CustomClass);
|
||||
|
||||
public var className(get, never):String;
|
||||
private function get_className():String {
|
||||
return __class.toString();
|
||||
}
|
||||
|
||||
private var __class:CustomClassDecl;
|
||||
private var __cachedSuperFields:Null<Map<String, Dynamic>> = null;
|
||||
|
||||
private var __cachedFieldDecls:Map<String, FieldDecl> = [];
|
||||
private var __cachedFunctionDecls:Map<String, FunctionDecl> = [];
|
||||
private var __cachedVarDecls:Map<String, VarDecl> = [];
|
||||
|
||||
public var accessContext:AccessContext = COuter;
|
||||
public var __allowSetGet:Bool = false;
|
||||
public var __allowPrivateAccess:Bool = false;
|
||||
|
||||
private var isInline(default, null):Bool = false;
|
||||
private var ogVariables(default, null):Map<String, Dynamic>;
|
||||
private var initializing(default, null):Bool = false; // Allows final variables to be initialized
|
||||
|
||||
public function new(__class:CustomClassDecl, args:Array<Dynamic>, ?extendFieldDecl:Map<String, Dynamic>, ?ogInterp:Interp, ?callNew:Bool = true) {
|
||||
this.__class = __class;
|
||||
this.interp = new Interp(this);
|
||||
|
||||
if (ogInterp != null) {
|
||||
interp.importFailedCallback = ogInterp.importFailedCallback;
|
||||
interp.errorHandler = ogInterp.errorHandler;
|
||||
interp.allowStaticVariables = ogInterp.allowStaticVariables;
|
||||
interp.staticVariables = ogInterp.staticVariables;
|
||||
// todo: make it so you can use variables from the same scope as where the class was defined
|
||||
if(__class.isInline != null && __class.isInline) {
|
||||
isInline = __class.isInline;
|
||||
ogVariables = ogInterp.variables;
|
||||
interp.allowPublicVariables = __class.staticInterp.allowPublicVariables;
|
||||
interp.publicVariables = __class.staticInterp.publicVariables;
|
||||
}
|
||||
}
|
||||
|
||||
buildImports();
|
||||
buildUsings();
|
||||
|
||||
if (extendFieldDecl != null)
|
||||
__cachedSuperFields = extendFieldDecl;
|
||||
|
||||
buildClass();
|
||||
|
||||
if (hasFunction('new') && callNew) {
|
||||
buildSuperConstructor();
|
||||
initializing = true;
|
||||
callFunction('new', args);
|
||||
initializing = false;
|
||||
|
||||
if (this.superClass == null && this.__class.classDecl.extend != null)
|
||||
this.interp.error(ECustom("super() not called"));
|
||||
} else if (__class.classDecl.extend != null) {
|
||||
createSuperClass(args);
|
||||
}
|
||||
}
|
||||
|
||||
function buildClass() {
|
||||
if (__cachedSuperFields == null)
|
||||
__cachedSuperFields = [];
|
||||
|
||||
for (f in __class.classDecl.fields) {
|
||||
if (f.access.contains(AStatic))
|
||||
continue; // Skip static field. It's handled by CustomClassDecl.hx
|
||||
__cachedFieldDecls.set(f.name, f);
|
||||
switch (f.kind) {
|
||||
case KFunction(fn):
|
||||
__cachedFunctionDecls.set(f.name, fn);
|
||||
#if hscriptPos
|
||||
var fexpr:Expr = {
|
||||
e: ExprDef.EFunction(fn.args, fn.body, f.name, fn.ret, false, false),
|
||||
pmin: fn.body.pmin,
|
||||
pmax: fn.body.pmax,
|
||||
line: fn.body.line,
|
||||
origin: fn.body.origin
|
||||
};
|
||||
#else
|
||||
var fexpr = Expr.EFunction(fn.args, fn.body, f.name, fn.ret, false, false);
|
||||
#end
|
||||
var func:Function = this.interp.expr(fexpr);
|
||||
this.interp.variables.set(f.name, func);
|
||||
case KVar(v):
|
||||
if(v.get != ADefault || v.set != ADefault)
|
||||
__allowSetGet = true;
|
||||
__cachedVarDecls.set(f.name, v);
|
||||
if (v.expr != null) {
|
||||
var varValue = this.interp.expr(v.expr);
|
||||
this.interp.variables.set(f.name, varValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!__cachedSuperFields.empty()) {
|
||||
for (f => v in __cachedSuperFields) {
|
||||
this.hset(f, v);
|
||||
}
|
||||
__cachedSuperFields.clear();
|
||||
}
|
||||
}
|
||||
|
||||
function buildSuperConstructor() {
|
||||
superConstructor = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||
createSuperClass(args);
|
||||
});
|
||||
}
|
||||
|
||||
private function createSuperClass(?args:Array<Dynamic>) {
|
||||
if (args == null)
|
||||
args = [];
|
||||
|
||||
if(__class.superClassDecl is CustomClassDecl)
|
||||
superClass = new CustomClass(__class.superClassDecl, args, __cachedSuperFields, this.interp);
|
||||
else {
|
||||
if (__cachedSuperFields != null) {
|
||||
Reflect.setField(__class.superClassDecl, "__cachedFields", __cachedSuperFields); // Static field
|
||||
}
|
||||
|
||||
var disallowCopy = Type.getInstanceFields(__class.superClassDecl);
|
||||
|
||||
superClass = Type.createInstance(__class.superClassDecl, args);
|
||||
superClass.__customClass = this;
|
||||
superClass.__real_fields = disallowCopy;
|
||||
|
||||
if(isInline) {
|
||||
for(s => v in ogVariables)
|
||||
if(!disallowCopy.contains(s))
|
||||
interp.variables.set(s, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildImports() {
|
||||
var i:Int = 0;
|
||||
for (_import in __class.imports) {
|
||||
var importedClass = _import.fullPath;
|
||||
var importAlias = _import.as;
|
||||
|
||||
if (Interp.customClassExist(importedClass) && this.interp.importFailedCallback != null) {
|
||||
this.interp.importFailedCallback(importedClass.split("."), importAlias);
|
||||
continue;
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
var e:Expr = {
|
||||
e: ExprDef.EImport(importedClass, importAlias),
|
||||
pmin: 0,
|
||||
pmax: 0,
|
||||
origin: this.className,
|
||||
line: i
|
||||
};
|
||||
#else
|
||||
var e = Expr.EImport(importedClass, importAlias);
|
||||
#end
|
||||
this.interp.expr(e);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
inline function buildUsings() {
|
||||
for (us in __class.usings) {
|
||||
@:privateAccess this.interp.useUsing(us);
|
||||
}
|
||||
}
|
||||
|
||||
public function callFunction(name:String, ?args:Array<Dynamic>):Dynamic {
|
||||
var r:Dynamic = null;
|
||||
|
||||
if (hasField(name)) {
|
||||
var fn = getFunction(name);
|
||||
try {
|
||||
if (fn == null)
|
||||
interp.error(ECustom('${name} is not a function'));
|
||||
|
||||
r = UnsafeReflect.callMethodUnsafe(null, fn, args == null ? [] : args);
|
||||
} catch (e:hscript.Expr.Error) {
|
||||
// A script error occurred while executing the custom class function.
|
||||
// Purge the function from the cache so it is not called again.
|
||||
purgeFunction(name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var fixedArgs = [];
|
||||
for (a in args) {
|
||||
if ((a is CustomClass)) {
|
||||
var customClass:CustomClass = cast(a, CustomClass);
|
||||
fixedArgs.push(customClass.superClass != null ? customClass.getSuperclass() : customClass);
|
||||
} else {
|
||||
fixedArgs.push(a);
|
||||
}
|
||||
}
|
||||
var superFn:Function = null;
|
||||
if(superClass is CustomClass) {
|
||||
superFn = cast(superClass, CustomClass).hget(name);
|
||||
}
|
||||
else {
|
||||
var fixedName = '_HX_SUPER__${name}';
|
||||
superFn = Reflect.field(superClass, fixedName);
|
||||
}
|
||||
|
||||
if (superFn == null || !Reflect.isFunction(superFn)) {
|
||||
this.interp.error(ECustom('Error while calling function super.${name}(): EInvalidAccess'
|
||||
+ '\n'
|
||||
+ 'InvalidAccess error: Super function "${name}" does not exist! Define it or call the correct superclass function.'));
|
||||
}
|
||||
r = Reflect.callMethod((superClass is CustomClass) ? null : superClass, superFn, fixedArgs);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Field check
|
||||
|
||||
private function hasField(name:String):Bool {
|
||||
return __cachedFieldDecls.exists(name);
|
||||
}
|
||||
|
||||
private function getField(name:String):FieldDecl {
|
||||
return __cachedFieldDecls != null ? __cachedFieldDecls.get(name) : null;
|
||||
}
|
||||
|
||||
private function hasVar(name:String):Bool {
|
||||
return __cachedVarDecls.exists(name);
|
||||
}
|
||||
|
||||
private function getVar(name:String):VarDecl {
|
||||
return __cachedVarDecls.get(name);
|
||||
}
|
||||
|
||||
private function hasFunction(name:String):Bool {
|
||||
return __cachedFunctionDecls.exists(name);
|
||||
}
|
||||
|
||||
private function getFunction(name:String):Function {
|
||||
var fn = this.interp.variables.get(name);
|
||||
return Reflect.isFunction(fn) ? fn : null;
|
||||
}
|
||||
|
||||
// SuperClass field check
|
||||
|
||||
private function cacheSuperField(name:String, value:Dynamic) {
|
||||
if (__cachedSuperFields != null) {
|
||||
__cachedSuperFields.set(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
var __superClassFieldList:Array<String> = null;
|
||||
|
||||
public function superHasField(name:String):Bool {
|
||||
if (superClass == null)
|
||||
return false;
|
||||
|
||||
// Reflect.hasField(this, name) is REALLY expensive so we use a cache.
|
||||
if (__superClassFieldList == null) {
|
||||
__superClassFieldList = [];
|
||||
|
||||
if(superClass is CustomClass) {
|
||||
var cls:Null<Dynamic> = superClass;
|
||||
while(cls != null && cls is CustomClass) {
|
||||
var currentClass = cast(cls, CustomClass);
|
||||
var fields = [for(f in currentClass.__cachedFieldDecls.keys()) f];
|
||||
__superClassFieldList.concat(fields);
|
||||
|
||||
var next = currentClass.superClass;
|
||||
if(next == null)
|
||||
break;
|
||||
cls = next;
|
||||
}
|
||||
|
||||
// The last fetched class is a real class
|
||||
if(!(cls is CustomClass))
|
||||
__superClassFieldList.concat(Reflect.fields(cls).concat(Type.getInstanceFields(Type.getClass(cls))));
|
||||
}
|
||||
else {
|
||||
var realFields = Reflect.fields(superClass).concat(Type.getInstanceFields(Type.getClass(superClass)));
|
||||
__superClassFieldList.concat(realFields);
|
||||
}
|
||||
}
|
||||
|
||||
return __superClassFieldList.indexOf(name) != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a function from the cache.
|
||||
* This is useful when a function is broken and needs to be skipped.
|
||||
* @param name The name of the function to remove from the cache.
|
||||
*/
|
||||
private function purgeFunction(name:String):Void {
|
||||
if (__cachedFunctionDecls != null) {
|
||||
__cachedFunctionDecls.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Access fields
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
switch (name) {
|
||||
case "superClass": return this.superClass;
|
||||
case "createSuperClass": return this.createSuperClass;
|
||||
case "hasFunction": return this.hasFunction;
|
||||
case "callFunction": return this.callFunction;
|
||||
default:
|
||||
if (hasFunction(name)) {
|
||||
var fn:Function = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||
return this.callFunction(name, args);
|
||||
});
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
if (hasVar(name)) {
|
||||
var v = getVar(name);
|
||||
var getter = v.get;
|
||||
var isInner = switch (accessContext) {
|
||||
case CInner(cl): return cl == this.className;
|
||||
default: false;
|
||||
}
|
||||
|
||||
var value:Dynamic = null;
|
||||
|
||||
if(getter == ANever || getter == ANull && (!isInner || !__allowPrivateAccess))
|
||||
throw 'field $name cannot be accessed for reading';
|
||||
|
||||
if (__allowSetGet && getter == AGet) {
|
||||
if (hasFunction('get_$name'))
|
||||
value = __callGetter(name);
|
||||
else
|
||||
throw 'Method get_$name required by property $name is missing';
|
||||
}
|
||||
else if (this.interp.variables.exists(name))
|
||||
value = this.interp.variables.get(name);
|
||||
else {
|
||||
if (v.expr != null) {
|
||||
value = this.interp.expr(v.expr);
|
||||
this.interp.variables.set(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
if (this.superClass != null) {
|
||||
if (superHasField(name)) {
|
||||
// Anonymous structure as a super class???
|
||||
if (Type.getClass(this.superClass) == null) {
|
||||
// Anonymous structure
|
||||
if (Reflect.hasField(this.superClass, name))
|
||||
return Reflect.field(this.superClass, name);
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '[ANONYMOUS STRUCTURE]'";
|
||||
}
|
||||
|
||||
if (this.superClass is CustomClass) {
|
||||
var superCustomClass:CustomClass = cast(this.superClass, CustomClass);
|
||||
|
||||
superCustomClass.__allowSetGet = this.__allowSetGet;
|
||||
superCustomClass.__allowPrivateAccess = this.__allowPrivateAccess;
|
||||
superCustomClass.accessContext = switch (accessContext) {
|
||||
case CInner(_): CInner(superCustomClass.className);
|
||||
default: COuter;
|
||||
}
|
||||
return superCustomClass.hget(name);
|
||||
}
|
||||
|
||||
// Real Class
|
||||
if (__allowSetGet)
|
||||
Reflect.getProperty(this.superClass, name);
|
||||
else
|
||||
Reflect.field(this.superClass, name);
|
||||
}
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'";
|
||||
}
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "'";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
switch (name) {
|
||||
default:
|
||||
if (hasVar(name)) {
|
||||
var v = getVar(name);
|
||||
var setter = v.set;
|
||||
var isInner = switch (accessContext) {
|
||||
case CInner(cl): return cl == this.className;
|
||||
default: false;
|
||||
}
|
||||
|
||||
if (setter == ANever || setter == ANull && (!isInner || !__allowPrivateAccess) || (v.isFinal && !initializing))
|
||||
throw 'field $name cannot be accessed for writing';
|
||||
|
||||
if (__allowSetGet && setter == ASet) {
|
||||
if (hasFunction('set_$name'))
|
||||
return __callSetter(name, val);
|
||||
else
|
||||
throw 'Method set_$name required by property $name is missing';
|
||||
}
|
||||
|
||||
this.interp.variables.set(name, val);
|
||||
}
|
||||
else if (this.superClass != null) {
|
||||
if (superHasField(name)) {
|
||||
if (Type.getClass(this.superClass) == null) {
|
||||
// Anonymous structure
|
||||
if (Reflect.hasField(this.superClass, name)) {
|
||||
Reflect.setField(this.superClass, name, val);
|
||||
return val;
|
||||
}
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '[ANONYMOUS STRUCTURE]'";
|
||||
}
|
||||
|
||||
if (this.superClass is CustomClass) {
|
||||
var superCustomClass:CustomClass = cast(this.superClass, CustomClass);
|
||||
|
||||
superCustomClass.__allowSetGet = this.__allowSetGet;
|
||||
superCustomClass.__allowPrivateAccess = this.__allowPrivateAccess;
|
||||
superCustomClass.accessContext = switch (accessContext) {
|
||||
case CInner(_): CInner(superCustomClass.className);
|
||||
default: COuter;
|
||||
}
|
||||
return superCustomClass.hset(name, val);
|
||||
}
|
||||
// Real Class
|
||||
|
||||
if (__allowSetGet)
|
||||
Reflect.setProperty(this.superClass, name, val);
|
||||
else
|
||||
Reflect.setField(this.superClass, name, val);
|
||||
}
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'";
|
||||
}
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "'";
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('get_$name');
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('set_$name', [val]);
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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():Null<Dynamic> {
|
||||
var cls:Null<Dynamic> = this.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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
package hscript.customclass;
|
||||
|
||||
import hscript.Expr.AccessContext;
|
||||
import hscript.proxy.ProxyType;
|
||||
import hscript.customclass.utils.FunctionUtils;
|
||||
import haxe.Constraints.Function;
|
||||
import hscript.Expr.FieldDecl;
|
||||
import hscript.Expr.VarDecl;
|
||||
import hscript.Expr.FunctionDecl;
|
||||
|
||||
/**
|
||||
* The ACTUAL "StaticHandler"
|
||||
* @author Jamextreme140
|
||||
*/
|
||||
@:access(hscript.Interp)
|
||||
@:structInit
|
||||
class CustomClassDecl implements IHScriptCustomAccessBehaviour {
|
||||
public var classDecl:Expr.ClassDecl; //This holds the class instantiation info
|
||||
public var imports:Map<String, CustomClassImport>;
|
||||
public var usings:Array<String>;
|
||||
public var pkg:Null<Array<String>> = null;
|
||||
public var ogInterp:Null<Interp> = null;
|
||||
public var isInline:Null<Bool> = null;
|
||||
|
||||
public var staticInterp:Interp = new Interp();
|
||||
|
||||
public var superClassDecl(default, null):Dynamic = null; //This holds the super class reference.
|
||||
|
||||
var _cachedStaticFields:Map<String, FieldDecl> = [];
|
||||
var _cachedStaticFunctions:Map<String, FunctionDecl> = [];
|
||||
var _cachedStaticVariables:Map<String, VarDecl> = [];
|
||||
|
||||
public var __allowSetGet:Bool = true;
|
||||
|
||||
public var __allowPrivateAccess:Bool = false;
|
||||
|
||||
public var accessContext:AccessContext = COuter;
|
||||
|
||||
public final name:String;
|
||||
|
||||
public function new(classDecl:Expr.ClassDecl, imports:Map<String, CustomClassImport>, usings:Array<String>, ?pkg:Array<String>, ?ogInterp:Interp, ?isInline:Bool) {
|
||||
this.classDecl = classDecl;
|
||||
this.imports = imports;
|
||||
this.usings = usings;
|
||||
this.pkg = pkg;
|
||||
this.ogInterp = ogInterp;
|
||||
this.isInline = isInline;
|
||||
|
||||
// Cache the classname
|
||||
var pkg = pkg != null ? '${pkg.join(".")}.' : "";
|
||||
var className = classDecl.name;
|
||||
this.name = '$pkg$className';
|
||||
|
||||
if(ogInterp != null) {
|
||||
staticInterp.importFailedCallback = ogInterp.importFailedCallback;
|
||||
staticInterp.errorHandler = ogInterp.errorHandler;
|
||||
staticInterp.allowStaticVariables = ogInterp.allowStaticVariables;
|
||||
staticInterp.staticVariables = ogInterp.staticVariables;
|
||||
|
||||
if(isInline != null && isInline) {
|
||||
// uses public variables from the same scope as where the class was defined
|
||||
staticInterp.variables = ogInterp.variables;
|
||||
staticInterp.allowPublicVariables = ogInterp.allowPublicVariables;
|
||||
staticInterp.publicVariables = ogInterp.publicVariables;
|
||||
}
|
||||
}
|
||||
|
||||
cacheImports();
|
||||
processUsings();
|
||||
cacheFields();
|
||||
if(classDecl.extend != null)
|
||||
buildSuperClass();
|
||||
}
|
||||
|
||||
function cacheImports() {
|
||||
// This will make imported classes available for Static Functions
|
||||
var i:Int = 0;
|
||||
for(s => imp in imports) {
|
||||
var importedClass = imp.fullPath;
|
||||
var importAlias = imp.as;
|
||||
|
||||
if(this.staticInterp.variables.exists(imp.name)) continue; // class is already imported
|
||||
|
||||
if (Interp.customClassExist(importedClass) && this.staticInterp.importFailedCallback != null) {
|
||||
this.staticInterp.importFailedCallback(importedClass.split("."), importAlias);
|
||||
continue;
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
var e:Expr = {
|
||||
e: ExprDef.EImport(importedClass, importAlias),
|
||||
pmin: 0,
|
||||
pmax: 0,
|
||||
origin: this.classDecl.name,
|
||||
line: i
|
||||
};
|
||||
i++;
|
||||
#else
|
||||
var e = Expr.EImport(importedClass, importAlias);
|
||||
#end
|
||||
this.staticInterp.expr(e);
|
||||
}
|
||||
}
|
||||
|
||||
function cacheFields() {
|
||||
for (f in classDecl.fields) {
|
||||
if (f.access.contains(AStatic)) {
|
||||
_cachedStaticFields.set(f.name, f);
|
||||
switch (f.kind) {
|
||||
case KFunction(fn):
|
||||
_cachedStaticFunctions.set(f.name, fn);
|
||||
#if hscriptPos
|
||||
var fexpr:Expr = {
|
||||
e: ExprDef.EFunction(fn.args, fn.body, f.name, fn.ret, false, false),
|
||||
pmin: fn.body.pmin,
|
||||
pmax: fn.body.pmax,
|
||||
line: fn.body.line,
|
||||
origin: fn.body.origin
|
||||
};
|
||||
#else
|
||||
var fexpr = Expr.EFunction(fn.args, fn.body, f.name, fn.ret, false, false);
|
||||
#end
|
||||
var func:Function = this.staticInterp.expr(fexpr);
|
||||
this.staticInterp.variables.set(f.name, func);
|
||||
case KVar(v):
|
||||
if(v.get != ADefault || v.set != ADefault)
|
||||
__allowSetGet = true;
|
||||
_cachedStaticVariables.set(f.name, v);
|
||||
if (v.expr != null) {
|
||||
var varValue = this.staticInterp.expr(v.expr);
|
||||
this.staticInterp.variables.set(f.name, varValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processUsings() {
|
||||
for(us in usings) {
|
||||
this.staticInterp.useUsing(us);
|
||||
}
|
||||
}
|
||||
|
||||
function buildSuperClass() {
|
||||
var extendString = new Printer().typeToString(classDecl.extend);
|
||||
if (this.pkg != null && extendString.indexOf(".") == -1) {
|
||||
extendString = this.pkg.join(".") + "." + extendString;
|
||||
}
|
||||
|
||||
var cls:Dynamic = Type.resolveClass('${extendString}_HSX');
|
||||
if(cls == null)
|
||||
cls = ProxyType.resolveClass(extendString);
|
||||
|
||||
superClassDecl = cls;
|
||||
|
||||
if(superClassDecl == null)
|
||||
staticInterp.error(ECustom("could not resolve super class: " + extendString));
|
||||
}
|
||||
|
||||
public function callFunction(name:String, ?args:Array<Dynamic>):Dynamic {
|
||||
var func:Function = getFunction(name);
|
||||
|
||||
return FunctionUtils.callStaticFunction(name, this, staticInterp, func, args != null ? args : []);
|
||||
}
|
||||
|
||||
public function hasField(name:String):Bool {
|
||||
return _cachedStaticFields.exists(name);
|
||||
}
|
||||
|
||||
private function hasFunction(name:String) {
|
||||
return _cachedStaticFunctions.exists(name);
|
||||
}
|
||||
|
||||
private function getFunction(name:String):Function {
|
||||
var fn = this.staticInterp.variables.get(name);
|
||||
return Reflect.isFunction(fn) ? fn : null;
|
||||
}
|
||||
|
||||
private function hasVar(name:String):Bool {
|
||||
return _cachedStaticVariables.exists(name);
|
||||
}
|
||||
|
||||
private function getVar(name:String):VarDecl {
|
||||
return _cachedStaticVariables.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a function from the cache.
|
||||
* This is useful when a function is broken and needs to be skipped.
|
||||
* @param name The name of the function to remove from the cache.
|
||||
*/
|
||||
private function purgeFunction(name:String):Void {
|
||||
if (_cachedStaticFunctions != null) {
|
||||
_cachedStaticFunctions.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
if(hasVar(name)) {
|
||||
var v = getVar(name);
|
||||
var getter = v.get;
|
||||
var isInner = switch (accessContext) {
|
||||
case CInner(cl): return cl == this.toString();
|
||||
default: false;
|
||||
}
|
||||
|
||||
var r:Dynamic = null;
|
||||
|
||||
if (getter == ANever || getter == ANull && (!isInner || !__allowPrivateAccess))
|
||||
throw 'field $name cannot be accessed for reading';
|
||||
|
||||
if(__allowSetGet && getter == AGet){
|
||||
if(hasFunction('get_$name'))
|
||||
r = __callGetter(name);
|
||||
else
|
||||
throw 'Method get_$name required by property $name is missing';
|
||||
}
|
||||
else if (this.staticInterp.variables.exists(name))
|
||||
r = this.staticInterp.variables.get(name);
|
||||
else {
|
||||
if(v.expr != null) {
|
||||
r = this.staticInterp.expr(v.expr);
|
||||
this.staticInterp.variables.set(name, r);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
if(hasFunction(name)) {
|
||||
// TODO: optimize this
|
||||
var fn:Function = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||
return this.callFunction(name, args);
|
||||
});
|
||||
return fn;
|
||||
}
|
||||
|
||||
throw "static field '" + name + "' does not exist in custom class '" + this.classDecl.name + "'";
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if (hasVar(name)) {
|
||||
var v = getVar(name);
|
||||
var setter = v.set;
|
||||
var isInner = switch (accessContext) {
|
||||
case CInner(cl): return cl == this.toString();
|
||||
default: false;
|
||||
}
|
||||
|
||||
if (setter == ANever || setter == ANull && (!isInner || !__allowPrivateAccess) || v.isFinal)
|
||||
throw 'field $name cannot be accessed for writing';
|
||||
|
||||
if (__allowSetGet && setter == ASet) {
|
||||
if (hasFunction('set_$name'))
|
||||
return __callSetter(name, val);
|
||||
else
|
||||
throw 'Method set_$name required by property $name is missing';
|
||||
}
|
||||
else {
|
||||
this.staticInterp.variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
throw "static field '" + name + "' does not exist in custom class '" + this.classDecl.name + "'";
|
||||
}
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('get_${name}');
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('set_${name}', [val]);
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
typedef CustomClassImport = {
|
||||
var ?name:String;
|
||||
var ?pkg:Array<String>;
|
||||
var ?fullPath:String; // pkg.pkg.pkg.name
|
||||
var ?as:Null<String>; // import pkg.Name as OtherName
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package hscript.customclass.utils;
|
||||
|
||||
import hscript.utils.UnsafeReflect;
|
||||
import haxe.Constraints.Function;
|
||||
|
||||
@:access(hscript.customclass.CustomClassDecl)
|
||||
class FunctionUtils {
|
||||
public static inline function callStaticFunction(name:String, classDecl:CustomClassDecl, interp:Interp, fn:Function, args:Array<Dynamic> = null) {
|
||||
var r:Dynamic = null;
|
||||
|
||||
try {
|
||||
if (fn == null)
|
||||
interp.error(ECustom('${name} is not a function'));
|
||||
|
||||
r = UnsafeReflect.callMethodUnsafe(null, fn, args);
|
||||
} catch (e:hscript.Expr.Error) {
|
||||
// A script error occurred while executing the custom class function.
|
||||
// Purge the function from the cache so it is not called again.
|
||||
classDecl.purgeFunction(name);
|
||||
interp.error(#if hscriptPos e.e #else e #end);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,12 @@ import haxe.macro.Compiler;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class UsingHandler {
|
||||
class AbstractHandler {
|
||||
public static function init() {
|
||||
#if !display
|
||||
if(Context.defined("display")) return;
|
||||
for(apply in Config.ALLOWED_ABSTRACT_AND_ENUM) {
|
||||
Compiler.addGlobalMetadata(apply, '@:build(hscript.macros.UsingHandler.build())');
|
||||
Compiler.addGlobalMetadata(apply, '@:build(hscript.macros.AbstractHandler.build())');
|
||||
}
|
||||
#end
|
||||
}
|
||||
@@ -60,20 +60,19 @@ class ClassExtendMacro {
|
||||
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(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(cl.superClass != null) {
|
||||
if(false && cl.superClass != null) {
|
||||
var _superFields = cl.superClass.t.get().fields.get();
|
||||
_superFields = []; // Comment to enable super support, (broken)
|
||||
for(field in _superFields) {
|
||||
if(!field.kind.match(FMethod(_))) // only catch methods
|
||||
continue;
|
||||
|
||||
function convertField(field:ClassField) {
|
||||
try {
|
||||
var nfield = @:privateAccess TypeTools.toField(field);
|
||||
var nfield = FixedTypeTools.toSimpleField(field);
|
||||
switch ([field.kind, field.type]) {
|
||||
case [FMethod(kind), TFun(args, ret)]:
|
||||
if(kind == MethInline)
|
||||
@@ -86,9 +85,9 @@ class ClassExtendMacro {
|
||||
switch(nfield.kind) {
|
||||
case FFun(fun):
|
||||
if (fun.params != null && fun.params.length > 0)
|
||||
continue;
|
||||
return null;
|
||||
|
||||
fun.ret = Utils.fixStdTypes(fun.ret);
|
||||
//sfun.ret = Utils.fixStdTypes(fun.ret);
|
||||
|
||||
var metas = nfield.meta;
|
||||
var defaultValues:Map<String, Dynamic> = [];
|
||||
@@ -107,7 +106,7 @@ class ClassExtendMacro {
|
||||
if(m.name == ":generic")
|
||||
isGeneric = true;
|
||||
}
|
||||
if(isGeneric) continue;
|
||||
if(isGeneric) return null;
|
||||
|
||||
if(defaultEntry != null)
|
||||
metas.remove(defaultEntry);
|
||||
@@ -119,20 +118,80 @@ class ClassExtendMacro {
|
||||
arg.opt = false;
|
||||
}
|
||||
|
||||
arg.type = Utils.fixStdTypes(arg.type);
|
||||
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>};
|
||||
}
|
||||
//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:
|
||||
}
|
||||
superFields.push(nfield);
|
||||
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 = [];
|
||||
}
|
||||
|
||||
@@ -152,11 +211,20 @@ class ClassExtendMacro {
|
||||
continue;
|
||||
if (f.name == "new") {
|
||||
hasNew = true;
|
||||
switch (f.kind) {
|
||||
case FFun(fn):
|
||||
var constructor:Field = buildConstructor(fn.args);
|
||||
|
||||
shadowClass.fields.push(constructor);
|
||||
definedFields.push(f.name);
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (f.name.startsWith(FUNC_PREFIX))
|
||||
continue;
|
||||
if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern) || f.access.contains(AInline))
|
||||
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
|
||||
@@ -186,7 +254,7 @@ class ClassExtendMacro {
|
||||
if (returns) {
|
||||
overrideExpr = macro {
|
||||
var name:String = $v{name};
|
||||
|
||||
/*
|
||||
if (__custom__variables != null) {
|
||||
if(__custom__variables.exists(name)) {
|
||||
var v:Dynamic = null;
|
||||
@@ -195,12 +263,17 @@ class ClassExtendMacro {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
|
||||
return __customClass.callFunction(name, [$a{arguments}]);
|
||||
}
|
||||
|
||||
return super.$name($a{arguments});
|
||||
};
|
||||
} else {
|
||||
overrideExpr = macro {
|
||||
var name:String = $v{name};
|
||||
|
||||
/*
|
||||
if (__custom__variables != null) {
|
||||
if(__custom__variables.exists(name)) {
|
||||
var v:Dynamic = null;
|
||||
@@ -210,6 +283,12 @@ class ClassExtendMacro {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
|
||||
__customClass.callFunction(name, [$a{arguments}]);
|
||||
return;
|
||||
}
|
||||
|
||||
super.$name($a{arguments});
|
||||
};
|
||||
}
|
||||
@@ -280,44 +359,64 @@ class ClassExtendMacro {
|
||||
Utils.setupMetas(shadowClass, imports);
|
||||
Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect");
|
||||
|
||||
// Adding hscript getters and setters
|
||||
shadowClass.fields.push({
|
||||
name: "__cachedFields",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: Map<String, Dynamic>),
|
||||
access: [APublic, AStatic]
|
||||
});
|
||||
|
||||
// Adding hscript getters and setters
|
||||
/*
|
||||
shadowClass.fields.push({
|
||||
name: "__interp",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(TPath({
|
||||
pack: ['hscript'],
|
||||
name: 'Interp'
|
||||
})),
|
||||
kind: FVar(macro: hscript.Interp),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
*/
|
||||
shadowClass.fields.push({
|
||||
name: "__customClass",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: hscript.customclass.CustomClass),
|
||||
access: [APublic]
|
||||
});
|
||||
/*
|
||||
shadowClass.fields.push({
|
||||
name: "__custom__variables",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(TPath({
|
||||
pack: [],
|
||||
name: 'Map',
|
||||
params: [TPType(TPath({name: "String", pack: []})), TPType(TPath({name: "Dynamic", pack: []}))]
|
||||
})),
|
||||
kind: FVar(macro: Map<String, Dynamic>),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__allowSetGet",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(TPath({
|
||||
pack: [],
|
||||
name: 'Bool',
|
||||
}), macro true),
|
||||
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]
|
||||
});
|
||||
*/
|
||||
// Unneccessary since the get/set call is already handled.
|
||||
/*
|
||||
shadowClass.fields.push({
|
||||
name: "__callGetter",
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
ret: TPath({name: 'Dynamic', pack: []}),
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: macro {
|
||||
__allowSetGet = false;
|
||||
@@ -330,7 +429,7 @@ class ClassExtendMacro {
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: TPath({name: "String", pack: []})
|
||||
type: macro: String
|
||||
}
|
||||
]
|
||||
}),
|
||||
@@ -341,7 +440,7 @@ class ClassExtendMacro {
|
||||
name: "__callSetter",
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
ret: TPath({name: 'Dynamic', pack: []}),
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: macro {
|
||||
__allowSetGet = false;
|
||||
@@ -354,19 +453,19 @@ class ClassExtendMacro {
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: TPath({name: "String", pack: []})
|
||||
type: macro: String
|
||||
},
|
||||
{
|
||||
name: "val",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: TPath({name: "Dynamic", pack: []})
|
||||
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);
|
||||
@@ -409,42 +508,66 @@ class ClassExtendMacro {
|
||||
|
||||
var hgetField = if(hasHgetInSuper) {
|
||||
macro {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
/*
|
||||
if(__custom__variables != null) {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
}
|
||||
*/
|
||||
return super.hget(name);
|
||||
}
|
||||
} else {
|
||||
macro {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
/*
|
||||
if(__custom__variables != null) {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
}
|
||||
*/
|
||||
return UnsafeReflect.getProperty(this, name);
|
||||
}
|
||||
}
|
||||
|
||||
var hsetField = if(hasHsetInSuper) {
|
||||
macro {
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
/*
|
||||
if(__custom__variables != null) {
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return super.hset(this, name);
|
||||
*/
|
||||
if(__real_fields != null && __real_fields.contains(name)) {
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
return super.hset(name, val);
|
||||
}
|
||||
} else {
|
||||
macro {
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
/*
|
||||
if(__custom__variables != null) {
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
*/
|
||||
if(__real_fields != null && __real_fields.contains(name)) {
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
//if(__custom__variables != null) __custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,7 +580,7 @@ class ClassExtendMacro {
|
||||
pos: Context.currentPos(),
|
||||
access: hasHsetInSuper ? [AOverride, APublic] : [APublic],
|
||||
kind: FFun({
|
||||
ret: TPath({name: 'Dynamic', pack: []}),
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: hsetField,
|
||||
args: [
|
||||
@@ -465,13 +588,13 @@ class ClassExtendMacro {
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: TPath({name: "String", pack: []})
|
||||
type: macro: String
|
||||
},
|
||||
{
|
||||
name: "val",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: TPath({name: "Dynamic", pack: []})
|
||||
type: macro: Dynamic
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -482,7 +605,7 @@ class ClassExtendMacro {
|
||||
pos: Context.currentPos(),
|
||||
access: hasHgetInSuper ? [AOverride, APublic] : [APublic],
|
||||
kind: FFun({
|
||||
ret: TPath({name: 'Dynamic', pack: []}),
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: hgetField,
|
||||
args: [
|
||||
@@ -490,12 +613,12 @@ class ClassExtendMacro {
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: TPath({name: "String", pack: []})
|
||||
type: macro: String
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
/*var p = new Printer();
|
||||
var aa = p.printTypeDefinition(shadowClass);
|
||||
if(aa.length < 5024)
|
||||
@@ -508,6 +631,32 @@ class ClassExtendMacro {
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
static function buildConstructor(constArgs:Array<FunctionArg>):Field {
|
||||
var superCallArgs:Array<Expr> = [for (arg in constArgs) macro $i{arg.name}];
|
||||
|
||||
return {
|
||||
name: 'new',
|
||||
access: [APublic],
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
args: constArgs,
|
||||
expr: macro
|
||||
{
|
||||
// Call the super constructor with appropriate args
|
||||
super($a{superCallArgs});
|
||||
|
||||
if(__cachedFields != null) {
|
||||
for(k => v in __cachedFields) {
|
||||
Reflect.setProperty(this, k, v);
|
||||
trace(k);
|
||||
}
|
||||
__cachedFields = null;
|
||||
}
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
#else
|
||||
class ClassExtendMacro {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package hscript.macros;
|
||||
|
||||
#if macro
|
||||
import Type.ValueType;
|
||||
import haxe.macro.Compiler;
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
|
||||
using Lambda;
|
||||
using StringTools;
|
||||
using haxe.macro.Tools;
|
||||
|
||||
/**
|
||||
* Macro used for the `using Class;` keyword
|
||||
*
|
||||
* you can make classes be able to be used specifing the classes/packages on Config.hx!
|
||||
* or implementing the interface `hscript.utils.UsingClass`
|
||||
* ```haxe
|
||||
* public static final ALLOWED_USING = ["my.pack.VeryNiceTools"];
|
||||
* ```
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* ```haxe
|
||||
* package my.pack;
|
||||
* // @:usableEntry() // optional
|
||||
* // @:usableEntry(forceAny) // optional // forces the class to be called with any type
|
||||
* // @:usableEntry(onlyBasic) // optional // only basic types will be allowed
|
||||
* // @:usableEntry(onlyBasic, forceAny) // optional // only basic types will be allowed, and the class will be called with any type
|
||||
* class VeryNiceTools implements hscript.utils.UsingClass {}
|
||||
* ```
|
||||
*
|
||||
* @author NeeEoo
|
||||
* @see https://github.com/pisayesiwsi/hscript-iris/blob/master/crowplexus/iris/macro/UsingMacro.macro.hx
|
||||
**/
|
||||
class UsingMacro {
|
||||
public static inline final USING_PREFIX = "_HX_USING__";
|
||||
|
||||
public static var unallowedMetas:Array<String> = [":noUsing", ":noUse"];
|
||||
|
||||
public static function init() {
|
||||
#if !display
|
||||
if(Context.defined("display")) return;
|
||||
for(apply in Config.ALLOWED_USING) {
|
||||
Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.UsingMacro.build())");
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
public static function build() {
|
||||
var cls: haxe.macro.Type.ClassType = Context.getLocalClass().get();
|
||||
var fields = Context.getBuildFields();
|
||||
|
||||
var key = cls.module;
|
||||
var fkey = cls.module + "." + cls.name;
|
||||
var packName = (cls.pack.length > 0 ? cls.pack.join(".") + "." : "") + cls.name;
|
||||
|
||||
if(cls.meta.get().find(function(m) return m.name == ':usingProcessed') != null) return fields;
|
||||
if (Config.DISALLOW_USING.contains(key) || Config.DISALLOW_USING.contains(fkey) || Config.DISALLOW_USING.contains(packName))
|
||||
return fields;
|
||||
|
||||
var entryField = cls.meta.get().find(function(m) return m.name == ':usableEntry');
|
||||
var hasParams = entryField != null && entryField.params != null;
|
||||
var forceAny = false;
|
||||
var onlyBasic = false;
|
||||
if (hasParams) {
|
||||
for (i in 0...entryField.params.length) {
|
||||
if (entryField.params[i].expr.match(EConst(CIdent("onlyBasic"))))
|
||||
onlyBasic = true;
|
||||
if (entryField.params[i].expr.match(EConst(CIdent("forceAny"))))
|
||||
forceAny = true;
|
||||
}
|
||||
}
|
||||
|
||||
var data: Array<Array<String>> = [];
|
||||
|
||||
for (field in fields) {
|
||||
// functions marked with @:noUsing won't be able to be used by variables
|
||||
// also if you want it to be usable in source, but not in the script, use @:noUse
|
||||
for(m in field.meta)
|
||||
if(unallowedMetas.contains(m.name))
|
||||
continue;
|
||||
|
||||
// It's called "static extensions" for some reason
|
||||
if(!field.access.contains(AStatic))
|
||||
continue;
|
||||
|
||||
switch (field.kind) {
|
||||
default:
|
||||
case FFun(f):
|
||||
if (f.args.length == 0)
|
||||
continue;
|
||||
var arg = f.args[0];
|
||||
if (arg.type == null)
|
||||
continue;
|
||||
var type = arg.type;
|
||||
|
||||
var valueType: String = switch (type) {
|
||||
case TPath({name: "Int", pack: []}):
|
||||
"TInt";
|
||||
case TPath({name: "Float", pack: []}):
|
||||
"TFloat";
|
||||
case TPath({name: "Single", pack: []}):
|
||||
"TFloat";
|
||||
case TPath({name: "String", pack: []}):
|
||||
"TClass(String)";
|
||||
case TPath({name: "Bool", pack: []}):
|
||||
"TBool";
|
||||
case TPath({name: "Array", pack: []}):
|
||||
"TClass(Array)";
|
||||
case TPath({name: "Map", pack: []}):
|
||||
"TClass(haxe.Constraints.IMap)";
|
||||
case TPath({name: "Dynamic", pack: []}):
|
||||
null;
|
||||
case TPath({name: "Class", pack: []}):
|
||||
"TClass(null)"; // this feels wrong
|
||||
case TPath({name: "Enum", pack: []}):
|
||||
"TEnum(null)";
|
||||
// case TPath({name: "Void", pack: []}): "ValueType.TVoid";
|
||||
default:
|
||||
null; // null acts as a wildcard
|
||||
}
|
||||
|
||||
// MIGHT CRASH COMPILATION? YEAH, IT CRASH ON TYPE PARAMETERS
|
||||
if (!onlyBasic && valueType == null) {
|
||||
var rtype:haxe.macro.Type = null;
|
||||
|
||||
try {rtype = type.toType();} catch(e) {}
|
||||
|
||||
if(rtype != null) {
|
||||
switch (rtype) {
|
||||
case TInst(t, []):
|
||||
valueType = "TClass(" + t.toString() + ")";
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (forceAny) {
|
||||
valueType = null;
|
||||
}
|
||||
|
||||
data.push([field.name, valueType]);
|
||||
}
|
||||
}
|
||||
|
||||
if(data.length == 0)
|
||||
return fields;
|
||||
|
||||
fields.push({
|
||||
name: '$USING_PREFIX${packName.replace(".", "_")}',
|
||||
access: [APrivate, AStatic],
|
||||
kind: FVar(macro : Map<String, Type.ValueType>, {
|
||||
var arr: Array<Expr> = [];
|
||||
for (i in data)
|
||||
if (i[1] != null)
|
||||
arr.push(macro $v{i[0]} => ${Context.parse("Type.ValueType." + i[1], Context.currentPos())});
|
||||
else
|
||||
arr.push(macro $v{i[0]} => null);
|
||||
macro $a{arr};
|
||||
}),
|
||||
pos: cls.pos,
|
||||
});
|
||||
|
||||
//var printer = new haxe.macro.Printer();
|
||||
//trace(printer.printField(fields[fields.length - 1]));
|
||||
|
||||
cls.meta.add(':usingProcessed', [], cls.pos);
|
||||
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,364 @@
|
||||
package hscript.proxy;
|
||||
|
||||
import hscript.customclass.CustomClass;
|
||||
import hscript.customclass.CustomClassDecl;
|
||||
import hscript.Tools.HScriptEnum;
|
||||
import hscript.Tools.EnumValue as HScriptEnumValue;
|
||||
|
||||
abstract OneOfTwo<T1, T2>(Dynamic) from T1 from T2 to T1 to T2 {}
|
||||
|
||||
@:allow(hscript.customclass.CustomClass)
|
||||
@:allow(hscript.customclass.CustomClassDecl)
|
||||
class ProxyType {
|
||||
/**
|
||||
Returns the class of `o`, if `o` is a class instance.
|
||||
|
||||
If `o` is null or of a different type, null is returned.
|
||||
|
||||
In general, type parameter information cannot be obtained at runtime.
|
||||
**/
|
||||
inline static function getClass<T>(o:OneOfTwo<T, CustomClass>):Null<Dynamic> {
|
||||
if(o is CustomClass)
|
||||
@:privateAccess return cast(o, CustomClass).__class;
|
||||
return Type.getClass(cast o);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the enum of enum instance `o`.
|
||||
|
||||
An enum instance is the result of using an enum constructor. Given an
|
||||
`enum Color { Red; }`, `getEnum(Red)` returns `Enum<Color>`.
|
||||
|
||||
If `o` is null, null is returned.
|
||||
|
||||
In general, type parameter information cannot be obtained at runtime.
|
||||
**/
|
||||
inline static function getEnum(o:OneOfTwo<EnumValue, HScriptEnumValue>):Dynamic {
|
||||
// TODO: SUPPORT HSCRIPT ENUMS
|
||||
var isScripted:Bool = Std.isOfType(o, HScriptEnumValue);
|
||||
if(isScripted) {
|
||||
return cast(o, HScriptEnumValue).enumParent;
|
||||
}
|
||||
return Type.getEnum(cast o);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the super-class of class `c`.
|
||||
|
||||
If `c` has no super class, null is returned.
|
||||
|
||||
If `c` is null, the result is unspecified.
|
||||
|
||||
In general, type parameter information cannot be obtained at runtime.
|
||||
**/
|
||||
inline static function getSuperClass(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Dynamic {
|
||||
if(c is CustomClassDecl) {
|
||||
return cast(c, CustomClassDecl).superClassDecl;
|
||||
}
|
||||
return Type.getSuperClass(cast c);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the name of class `c`, including its path.
|
||||
|
||||
If `c` is inside a package, the package structure is returned dot-
|
||||
separated, with another dot separating the class name:
|
||||
`pack1.pack2.(...).packN.ClassName`
|
||||
If `c` is a sub-type of a Haxe module, that module is not part of the
|
||||
package structure.
|
||||
|
||||
If `c` has no package, the class name is returned.
|
||||
|
||||
If `c` is null, the result is unspecified.
|
||||
|
||||
The class name does not include any type parameters.
|
||||
**/
|
||||
inline static function getClassName(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):String {
|
||||
if(c is CustomClassDecl) {
|
||||
var cls:CustomClassDecl = cast c;
|
||||
var name = cls.classDecl.name;
|
||||
var pkg = cls.pkg != null ? '${cls.pkg.join(".")}.' : "";
|
||||
return '$pkg$name';
|
||||
}
|
||||
return Type.getClassName(cast c);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the name of enum `e`, including its path.
|
||||
|
||||
If `e` is inside a package, the package structure is returned dot-
|
||||
separated, with another dot separating the enum name:
|
||||
`pack1.pack2.(...).packN.EnumName`
|
||||
If `e` is a sub-type of a Haxe module, that module is not part of the
|
||||
package structure.
|
||||
|
||||
If `e` has no package, the enum name is returned.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
|
||||
The enum name does not include any type parameters.
|
||||
**/
|
||||
inline static function getEnumName(e:Enum<Dynamic>):String {
|
||||
var isScripted:Bool = Std.isOfType(e, HScriptEnumValue);
|
||||
if (isScripted)
|
||||
return cast(e, HScriptEnumValue).getEnumName();
|
||||
return Type.getEnumName(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Resolves a class by name.
|
||||
|
||||
If `name` is the path of an existing class, that class is returned.
|
||||
|
||||
Otherwise null is returned.
|
||||
|
||||
If `name` is null or the path to a different type, the result is
|
||||
unspecified.
|
||||
|
||||
The class name must not include any type parameters.
|
||||
**/
|
||||
inline static function resolveClass(name:String):Dynamic {
|
||||
if(Interp.customClassExist(name)) {
|
||||
return Interp.getCustomClass(name);
|
||||
}
|
||||
return Type.resolveClass(name);
|
||||
}
|
||||
|
||||
/**
|
||||
Resolves an enum by name.
|
||||
|
||||
If `name` is the path of an existing enum, that enum is returned.
|
||||
|
||||
Otherwise null is returned.
|
||||
|
||||
If `name` is null the result is unspecified.
|
||||
|
||||
If `name` is the path to a different type, null is returned.
|
||||
|
||||
The enum name must not include any type parameters.
|
||||
**/
|
||||
inline static function resolveEnum(name:String):Enum<Dynamic> {
|
||||
return Type.resolveEnum(name);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an instance of class `cl`, using `args` as arguments to the
|
||||
class constructor.
|
||||
|
||||
This function guarantees that the class constructor is called.
|
||||
|
||||
Default values of constructors arguments are not guaranteed to be
|
||||
taken into account.
|
||||
|
||||
If `cl` or `args` are null, or if the number of elements in `args` does
|
||||
not match the expected number of constructor arguments, or if any
|
||||
argument has an invalid type, or if `cl` has no own constructor, the
|
||||
result is unspecified.
|
||||
|
||||
In particular, default values of constructor arguments are not
|
||||
guaranteed to be taken into account.
|
||||
**/
|
||||
inline static function createInstance<T>(cl:OneOfTwo<Class<T>, CustomClassDecl>, args:Array<Dynamic>):T {
|
||||
// TODO: somehow pass the ogInterp argument without breaking the original functionality
|
||||
if(cl is CustomClassDecl) {
|
||||
return cast new CustomClass(cast cl, args);
|
||||
}
|
||||
return Type.createInstance(cast cl, args);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an instance of class `cl`.
|
||||
|
||||
This function guarantees that the class constructor is not called.
|
||||
|
||||
If `cl` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function createEmptyInstance<T>(cl:OneOfTwo<Class<T>, CustomClassDecl>):T {
|
||||
// TODO: same as above
|
||||
if(cl is CustomClassDecl)
|
||||
return cast new CustomClass(cast cl, [], null, null, false);
|
||||
return Type.createEmptyInstance(cast cl);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an instance of enum `e` by calling its constructor `constr` with
|
||||
arguments `params`.
|
||||
|
||||
If `e` or `constr` is null, or if enum `e` has no constructor named
|
||||
`constr`, or if the number of elements in `params` does not match the
|
||||
expected number of constructor arguments, or if any argument has an
|
||||
invalid type, the result is unspecified.
|
||||
**/
|
||||
inline static function createEnum<T>(e:OneOfTwo<Enum<T>, HScriptEnum>, constr:String, ?params:Array<Dynamic>):T {
|
||||
// TODO: SUPPORT HSCRIPT ENUMS
|
||||
var isScripted:Bool = Std.isOfType(e, HScriptEnum);
|
||||
if(isScripted) {
|
||||
var scriptEnum:HScriptEnum = cast e;
|
||||
var enumToCreate = scriptEnum.getEnumValue(constr);
|
||||
if(Reflect.isFunction(enumToCreate)) {
|
||||
return Reflect.callMethod(null, enumToCreate, params);
|
||||
}
|
||||
return enumToCreate;
|
||||
}
|
||||
return Type.createEnum(cast e, constr, params);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an instance of enum `e` by calling its constructor number
|
||||
`index` with arguments `params`.
|
||||
|
||||
The constructor indices are preserved from Haxe syntax, so the first
|
||||
declared is index 0, the next index 1 etc.
|
||||
|
||||
If `e` or `constr` is null, or if enum `e` has no constructor named
|
||||
`constr`, or if the number of elements in `params` does not match the
|
||||
expected number of constructor arguments, or if any argument has an
|
||||
invalid type, the result is unspecified.
|
||||
**/
|
||||
inline static function createEnumIndex<T>(e:Enum<T>, index:Int, ?params:Array<Dynamic>):T {
|
||||
// TODO: SUPPORT HSCRIPT ENUMS
|
||||
return Type.createEnumIndex(e, index, params);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a list of the instance fields of class `c`, including
|
||||
inherited fields.
|
||||
|
||||
This only includes fields which are known at compile-time. In
|
||||
particular, using `getInstanceFields(getClass(obj))` will not include
|
||||
any fields which were added to `obj` at runtime.
|
||||
|
||||
The order of the fields in the returned Array is unspecified.
|
||||
|
||||
If `c` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function getInstanceFields(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Array<String> {
|
||||
if(c is CustomClassDecl) {
|
||||
var cls:CustomClassDecl = cast c;
|
||||
return [for(f in Tools.getClassDeclFields(cls.classDecl)) f.name];
|
||||
}
|
||||
return Type.getInstanceFields(cast c);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a list of static fields of class `c`.
|
||||
|
||||
This does not include static fields of parent classes.
|
||||
|
||||
The order of the fields in the returned Array is unspecified.
|
||||
|
||||
If `c` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function getClassFields(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Array<String> {
|
||||
if(c is CustomClassDecl) {
|
||||
var cls:CustomClassDecl = cast c;
|
||||
return [for(f in Tools.getClassDeclFields(cls.classDecl, true)) f.name];
|
||||
}
|
||||
return Type.getClassFields(cast c);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a list of the names of all constructors of enum `e`.
|
||||
|
||||
The order of the constructor names in the returned Array is preserved
|
||||
from the original syntax.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function getEnumConstructs(e:OneOfTwo<Enum<Dynamic>, HScriptEnum>):Array<String> {
|
||||
// TODO: SUPPORT HSCRIPT ENUMS
|
||||
return Type.getEnumConstructs(cast e);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the runtime type of value `v`.
|
||||
|
||||
The result corresponds to the type `v` has at runtime, which may vary
|
||||
per platform. Assumptions regarding this should be minimized to avoid
|
||||
surprises.
|
||||
**/
|
||||
inline static function typeof(v:Dynamic):Type.ValueType {
|
||||
return Type.typeof(v);
|
||||
}
|
||||
|
||||
/**
|
||||
Recursively compares two enum instances `a` and `b` by value.
|
||||
|
||||
Unlike `a == b`, this function performs a deep equality check on the
|
||||
arguments of the constructors, if exists.
|
||||
|
||||
If `a` or `b` are null, the result is unspecified.
|
||||
**/
|
||||
inline static function enumEq(a:Dynamic, b:Dynamic):Bool {
|
||||
var isScripted:Bool = Std.isOfType(a, HScriptEnumValue) && Std.isOfType(b, HScriptEnumValue);
|
||||
if (isScripted)
|
||||
return cast(a, HScriptEnumValue).compare(cast(b, HScriptEnumValue));
|
||||
return Type.enumEq(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the constructor name of enum instance `e`.
|
||||
|
||||
The result String does not contain any constructor arguments.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function enumConstructor(e:Dynamic):String {
|
||||
if (Std.isOfType(e, HScriptEnumValue))
|
||||
return cast(e, HScriptEnumValue).name;
|
||||
return Type.enumConstructor(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a list of the constructor arguments of enum instance `e`.
|
||||
|
||||
If `e` has no arguments, the result is [].
|
||||
|
||||
Otherwise the result are the values that were used as arguments to `e`,
|
||||
in the order of their declaration.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function enumParameters(e:Dynamic):Array<Dynamic> {
|
||||
if (Std.isOfType(e, HScriptEnumValue))
|
||||
return cast(e, HScriptEnumValue).args;
|
||||
return Type.enumParameters(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the index of enum instance `e`.
|
||||
|
||||
This corresponds to the original syntactic position of `e`. The index of
|
||||
the first declared constructor is 0, the next one is 1 etc.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function enumIndex(e:Dynamic):Int {
|
||||
if (Std.isOfType(e, HScriptEnumValue))
|
||||
return cast(e, HScriptEnumValue).index;
|
||||
return Type.enumIndex(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a list of all constructors of enum `e` that require no
|
||||
arguments.
|
||||
|
||||
This may return the empty Array `[]` if all constructors of `e` require
|
||||
arguments.
|
||||
|
||||
Otherwise an instance of `e` constructed through each of its non-
|
||||
argument constructors is returned, in the order of the constructor
|
||||
declaration.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function allEnums<T>(e:OneOfTwo<Enum<T>, HScriptEnum>):Array<T> {
|
||||
// TODO: SUPPORT HSCRIPT ENUMS
|
||||
var isScripted:Bool = Std.isOfType(e, HScriptEnum);
|
||||
if(isScripted) {
|
||||
var hEnum:HScriptEnum = cast e;
|
||||
return [for(en in Reflect.fields(hEnum.enumValues)) Reflect.field(hEnum, en)];
|
||||
}
|
||||
return Type.allEnums(cast e);
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ class UnsafeReflect {
|
||||
o.__SetField(field, value, untyped __cpp__("::hx::paccAlways"));
|
||||
}
|
||||
#else
|
||||
Reflect.setProperty(o, field);
|
||||
Reflect.setProperty(o, field, value);
|
||||
#end
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class UnsafeReflect {
|
||||
return untyped func.__Run(args);
|
||||
}
|
||||
#else
|
||||
return Reflect.callMethod(o, func, args);
|
||||
return Reflect.callMethod(o, Reflect.field(o, field), args);
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package hscript.utils;
|
||||
|
||||
/**
|
||||
* This is used to mark classes that can be used with the `using` keyword.
|
||||
* You can also add @:usableEntry to your class.
|
||||
* If you wanna force the class to be called with any type, you can add @:usableEntry(forceAny)
|
||||
**/
|
||||
@:autoBuild(hscript.macros.UsingMacro.build())
|
||||
interface UsingClass {}
|
||||
@@ -0,0 +1,17 @@
|
||||
package hscript.utils;
|
||||
|
||||
typedef UsingCall = (o: Dynamic, f: String, args: Array<Dynamic>) -> Dynamic;
|
||||
|
||||
/**
|
||||
* Code based on Using system from "hscript-iris"
|
||||
* @see https://github.com/pisayesiwsi/hscript-iris/blob/master/crowplexus/iris/utils/UsingEntry.hx
|
||||
*/
|
||||
class UsingEntry {
|
||||
public var name: String;
|
||||
public var call: UsingCall;
|
||||
|
||||
public function new(name: String, call: UsingCall) {
|
||||
this.name = name;
|
||||
this.call = call;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package hscript.utils;
|
||||
|
||||
import hscript.utils.UsingEntry.UsingCall;
|
||||
import StringTools;
|
||||
import Lambda;
|
||||
|
||||
class UsingHandler {
|
||||
public static var usingEntries:Array<UsingEntry> = [
|
||||
new UsingEntry("StringTools", function(o: Dynamic, f: String, args: Array<Dynamic>): Dynamic {
|
||||
if (f == "isEof") // has @:noUsing
|
||||
return null;
|
||||
switch (Type.typeof(o)) {
|
||||
case TInt if (f == "hex"):
|
||||
return StringTools.hex(o, args[0]);
|
||||
case TClass(String):
|
||||
if (Reflect.hasField(StringTools, f)) {
|
||||
var field = Reflect.field(StringTools, f);
|
||||
if (Reflect.isFunction(field)) {
|
||||
return Reflect.callMethod(StringTools, field, [o].concat(args));
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
new UsingEntry("Lambda", function(o: Dynamic, f: String, args: Array<Dynamic>): Dynamic {
|
||||
if (Tools.isIterable(o)) {
|
||||
// TODO: Check if the values are Iterable<T>
|
||||
if (Reflect.hasField(Lambda, f)) {
|
||||
var field = Reflect.field(Lambda, f);
|
||||
if (Reflect.isFunction(field)) {
|
||||
return Reflect.callMethod(Lambda, field, [o].concat(args));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
];
|
||||
|
||||
public static function registerUsingGlobal(name: String, call:UsingCall):UsingEntry {
|
||||
var entry = new UsingEntry(name, call);
|
||||
usingEntries.push(entry);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user