Compare commits
40
Commits
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
/hscript.swf
|
/hscript.swf
|
||||||
/release.zip
|
/release.zip
|
||||||
|
dump/*
|
||||||
tests/bin/*
|
tests/bin/*
|
||||||
@@ -3,7 +3,7 @@ hscript-improved
|
|||||||
|
|
||||||
How to install
|
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
|
To enable custom classes support you have to do this in project.xml
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ class TestHScript extends TestCase {
|
|||||||
assertScript("- 123",-123);
|
assertScript("- 123",-123);
|
||||||
assertScript("1.546",1.546);
|
assertScript("1.546",1.546);
|
||||||
assertScript(".545",.545);
|
assertScript(".545",.545);
|
||||||
|
assertScript("1e5",100000);
|
||||||
|
assertScript("1.2e2",120);
|
||||||
|
assertScript("100e-2",1);
|
||||||
|
assertScript("1.2e-1",0.12);
|
||||||
assertScript("'bla'","bla");
|
assertScript("'bla'","bla");
|
||||||
assertScript("null",null);
|
assertScript("null",null);
|
||||||
assertScript("true",true);
|
assertScript("true",true);
|
||||||
@@ -53,6 +57,7 @@ class TestHScript extends TestCase {
|
|||||||
assertScript("3 * 2 // + 5 \n + 6",12);
|
assertScript("3 * 2 // + 5 \n + 6",12);
|
||||||
assertScript("3 /* 2\n */ + 5",8);
|
assertScript("3 /* 2\n */ + 5",8);
|
||||||
assertScript("[55,66,77][1]",66);
|
assertScript("[55,66,77][1]",66);
|
||||||
|
assertScript("[11,22,33,][2]", 33);
|
||||||
assertScript("var a = [55]; a[0] *= 2; a[0]",110);
|
assertScript("var a = [55]; a[0] *= 2; a[0]",110);
|
||||||
assertScript("x",55,{ x : 55 });
|
assertScript("x",55,{ x : 55 });
|
||||||
assertScript("var y = 33; y",33);
|
assertScript("var y = 33; y",33);
|
||||||
|
|||||||
+2
-1
@@ -1,3 +1,4 @@
|
|||||||
--macro keep('IntIterator')
|
--macro keep('IntIterator')
|
||||||
--macro hscript.macros.UsingHandler.init()
|
--macro hscript.macros.AbstractHandler.init()
|
||||||
|
--macro hscript.macros.UsingMacro.init()
|
||||||
--macro hscript.macros.ClassExtendMacro.init()
|
--macro hscript.macros.ClassExtendMacro.init()
|
||||||
|
|||||||
@@ -124,10 +124,17 @@ class Bytes {
|
|||||||
#end
|
#end
|
||||||
bout.addByte(Type.enumIndex(e));
|
bout.addByte(Type.enumIndex(e));
|
||||||
switch( e ) {
|
switch( e ) {
|
||||||
|
case EIgnore(_):
|
||||||
case EImport(c):
|
case EImport(c):
|
||||||
// TODO
|
// TODO
|
||||||
case EClass(_, _, _, _):
|
case EClass(_, _, _, _):
|
||||||
// TODO
|
// TODO
|
||||||
|
case ERedirect(_, _):
|
||||||
|
// TODO
|
||||||
|
case EUsing(name):
|
||||||
|
// TODO
|
||||||
|
case EEnum(name, fields):
|
||||||
|
// TODO
|
||||||
case EConst(c):
|
case EConst(c):
|
||||||
doEncodeConst(c);
|
doEncodeConst(c);
|
||||||
case EIdent(v):
|
case EIdent(v):
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ class Config {
|
|||||||
"openfl.display.BlendMode",
|
"openfl.display.BlendMode",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Runs support for using in specific classes.
|
||||||
|
public static final ALLOWED_USING = [
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
// Incase any of your files fail
|
// Incase any of your files fail
|
||||||
// These are the module names
|
// These are the module names
|
||||||
public static final DISALLOW_CUSTOM_CLASSES = [
|
public static final DISALLOW_CUSTOM_CLASSES = [
|
||||||
@@ -21,4 +26,8 @@ class Config {
|
|||||||
public static final DISALLOW_ABSTRACT_AND_ENUM = [
|
public static final DISALLOW_ABSTRACT_AND_ENUM = [
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public static final DISALLOW_USING = [
|
||||||
|
|
||||||
|
];
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import hscript.Interp.DeclaredVar;
|
|||||||
import hscript.utils.UnsafeReflect;
|
import hscript.utils.UnsafeReflect;
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
|
||||||
|
@:deprecated('Switch to new Custom Class instead')
|
||||||
class CustomClassHandler implements IHScriptCustomConstructor {
|
class CustomClassHandler implements IHScriptCustomConstructor {
|
||||||
public static var staticHandler = new StaticHandler();
|
public static var staticHandler = new StaticHandler();
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
|||||||
this.extend = extend;
|
this.extend = extend;
|
||||||
this.interfaces = interfaces;
|
this.interfaces = interfaces;
|
||||||
|
|
||||||
this.cl = extend == null ? CustomTemplateClass : Type.resolveClass('${extend}_HSX');
|
//this.cl = extend == null ? CustomTemplateClass : Type.resolveClass('${extend}_HSX');
|
||||||
if(cl == null)
|
if(cl == null)
|
||||||
ogInterp.error(EInvalidClass(extend));
|
ogInterp.error(EInvalidClass(extend));
|
||||||
}
|
}
|
||||||
@@ -75,7 +76,7 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
|||||||
comparisonMap.set(key, value);
|
comparisonMap.set(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
_class.__custom__variables = interp.variables;
|
//_class.__custom__variables = interp.variables;
|
||||||
|
|
||||||
//trace(fields);
|
//trace(fields);
|
||||||
for(expr in fields) {
|
for(expr in fields) {
|
||||||
@@ -99,21 +100,21 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
_class.__class__fields = [for(key => value in classVariables) key];
|
//_class.__class__fields = [for(key => value in classVariables) key];
|
||||||
|
|
||||||
//trace(_class.__class__fields);
|
//trace(_class.__class__fields);
|
||||||
//@:privateAccess
|
//@:privateAccess
|
||||||
//trace(interp.__instanceFields);
|
//trace(interp.__instanceFields);
|
||||||
|
|
||||||
_class.__interp = interp;
|
//_class.__interp = interp;
|
||||||
_class.__allowSetGet = false;
|
//_class.__allowSetGet = false;
|
||||||
interp.scriptObject = _class;
|
interp.scriptObject = _class;
|
||||||
|
|
||||||
for(variable => value in interp.variables) {
|
for(variable => value in interp.variables) {
|
||||||
if(variable == "this" || variable == "super" || variable == "new") continue;
|
if(variable == "this" || variable == "super" || variable == "new") continue;
|
||||||
|
|
||||||
if(variable.startsWith("set_") || variable.startsWith("get_")) {
|
if(variable.startsWith("set_") || variable.startsWith("get_")) {
|
||||||
_class.__allowSetGet = true;
|
//_class.__allowSetGet = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,9 +139,11 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
|||||||
if(!interp.__instanceFields.contains(variable)) {
|
if(!interp.__instanceFields.contains(variable)) {
|
||||||
interp.__instanceFields.push(variable);
|
interp.__instanceFields.push(variable);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
if(!_class.__class__fields.contains(variable)) {
|
if(!_class.__class__fields.contains(variable)) {
|
||||||
_class.__class__fields.push(variable);
|
_class.__class__fields.push(variable);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,9 +155,10 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
class CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomAccessBehaviour {
|
class CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomAccessBehaviour {
|
||||||
public var __interp:Interp;
|
public var __interp:Interp;
|
||||||
|
public var __customClass:hscript.customclass.CustomClass;
|
||||||
public var __allowSetGet:Bool = true;
|
public var __allowSetGet:Bool = true;
|
||||||
public var __custom__variables:Map<String, Dynamic>;
|
public var __custom__variables:Map<String, Dynamic>;
|
||||||
public var __real_fields:Array<String>;
|
public var __real_fields:Array<String>;
|
||||||
@@ -198,15 +202,18 @@ class CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is for backwards compatibility with old hscript-improved, since some scripts use it
|
* This is for backwards compatibility with old hscript-improved, since some scripts use it
|
||||||
**/
|
*/
|
||||||
@:dox(hide)
|
@:dox(hide)
|
||||||
@:keep
|
@:keep
|
||||||
class TemplateClass implements IHScriptCustomBehaviour implements IHScriptCustomAccessBehaviour {
|
class TemplateClass implements IHScriptCustomBehaviour implements IHScriptCustomAccessBehaviour {
|
||||||
public var __interp:Interp;
|
public var __interp:Interp;
|
||||||
public var __allowSetGet:Bool = true;
|
public var __allowSetGet:Bool = true;
|
||||||
|
public var __allowPrivateAccess:Bool = true;
|
||||||
|
|
||||||
public function hset(name:String, val:Dynamic):Dynamic {
|
public function hset(name:String, val:Dynamic):Dynamic {
|
||||||
var variables = __interp.variables;
|
var variables = __interp.variables;
|
||||||
@@ -236,7 +243,8 @@ class TemplateClass implements IHScriptCustomBehaviour implements IHScriptCustom
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
final class StaticHandler {
|
final class StaticHandler {
|
||||||
public function new() {}
|
public function new() {}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
+32
-5
@@ -51,9 +51,10 @@ enum ExprDef {
|
|||||||
typedef ExprDef = Expr;
|
typedef ExprDef = Expr;
|
||||||
enum Expr {
|
enum Expr {
|
||||||
#end
|
#end
|
||||||
|
EIgnore(skip:Bool);
|
||||||
EConst( c : Const );
|
EConst( c : Const );
|
||||||
EIdent( v : String );
|
EIdent( v : String );
|
||||||
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool );
|
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool, ?get : FieldPropertyAccess, ?set : FieldPropertyAccess );
|
||||||
EParent( e : Expr );
|
EParent( e : Expr );
|
||||||
EBlock( e : Array<Expr> );
|
EBlock( e : Array<Expr> );
|
||||||
EField( e : Expr, f : String , ?safe : Bool );
|
EField( e : Expr, f : String , ?safe : Bool );
|
||||||
@@ -81,6 +82,9 @@ enum Expr {
|
|||||||
|
|
||||||
EImport( c : String, ?asname:String );
|
EImport( c : String, ?asname:String );
|
||||||
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String>, ?isFinal:Bool, ?isPrivate:Bool );
|
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
|
@:structInit
|
||||||
@@ -159,9 +163,10 @@ enum Error {
|
|||||||
|
|
||||||
enum ModuleDecl {
|
enum ModuleDecl {
|
||||||
DPackage( path : Array<String> );
|
DPackage( path : Array<String> );
|
||||||
DImport( path : Array<String>, ?everything : Bool );
|
DImport( path : Array<String>, ?everything : Bool, ?asname:String );
|
||||||
DClass( c : ClassDecl );
|
DUsing( path : Array<String>);
|
||||||
DTypedef( c : TypeDecl );
|
DTypedef( c : TypeDecl );
|
||||||
|
DClass( c : ClassDecl );
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef ModuleType = {
|
typedef ModuleType = {
|
||||||
@@ -211,8 +216,30 @@ final class FunctionDecl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef VarDecl = {
|
typedef VarDecl = {
|
||||||
var get : Null<String>;
|
var get : FieldPropertyAccess;
|
||||||
var set : Null<String>;
|
var set : FieldPropertyAccess;
|
||||||
|
var isFinal : Bool;
|
||||||
var expr : Null<Expr>;
|
var expr : Null<Expr>;
|
||||||
var type : Null<CType>;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package hscript;
|
|||||||
|
|
||||||
interface IHScriptCustomAccessBehaviour {
|
interface IHScriptCustomAccessBehaviour {
|
||||||
var __allowSetGet:Bool;
|
var __allowSetGet:Bool;
|
||||||
|
var __allowPrivateAccess:Bool;
|
||||||
|
|
||||||
public function hset(name:String, val:Dynamic):Dynamic;
|
public function hset(name:String, val:Dynamic):Dynamic;
|
||||||
public function hget(name:String):Dynamic;
|
public function hget(name:String):Dynamic;
|
||||||
|
|||||||
@@ -1,16 +1,6 @@
|
|||||||
package hscript;
|
package hscript;
|
||||||
|
|
||||||
interface IHScriptCustomClassBehaviour {
|
interface IHScriptCustomClassBehaviour {
|
||||||
public var __interp:Interp;
|
public var __customClass:hscript.customclass.CustomClass;
|
||||||
public var __custom__variables:Map<String, Dynamic>;
|
|
||||||
public var __allowSetGet:Bool;
|
|
||||||
public var __real_fields:Array<String>;
|
public var __real_fields:Array<String>;
|
||||||
public var __class__fields:Array<String>;
|
|
||||||
|
|
||||||
public function hset(name:String, val:Dynamic):Dynamic;
|
|
||||||
public function hget(name:String):Dynamic;
|
|
||||||
|
|
||||||
public function __callGetter(name:String):Dynamic;
|
|
||||||
|
|
||||||
public function __callSetter(name:String, val:Dynamic):Dynamic;
|
|
||||||
}
|
}
|
||||||
+892
-127
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"
|
||||||
|
];
|
||||||
|
}
|
||||||
+271
-21
@@ -22,6 +22,7 @@
|
|||||||
package hscript;
|
package hscript;
|
||||||
import haxe.rtti.Meta;
|
import haxe.rtti.Meta;
|
||||||
import hscript.Expr;
|
import hscript.Expr;
|
||||||
|
import hscript.Expr.FieldPropertyAccess;
|
||||||
|
|
||||||
enum Token {
|
enum Token {
|
||||||
TEof;
|
TEof;
|
||||||
@@ -86,6 +87,11 @@ class Parser {
|
|||||||
**/
|
**/
|
||||||
public var resumeErrors : Bool;
|
public var resumeErrors : Bool;
|
||||||
|
|
||||||
|
/*
|
||||||
|
package name, set when using "package;" in your script.
|
||||||
|
*/
|
||||||
|
public var packageName:String = null;
|
||||||
|
|
||||||
// implementation
|
// implementation
|
||||||
var input : String;
|
var input : String;
|
||||||
var readPos : Int;
|
var readPos : Int;
|
||||||
@@ -97,6 +103,8 @@ class Parser {
|
|||||||
|
|
||||||
var disableOrOp : Bool = false;
|
var disableOrOp : Bool = false;
|
||||||
|
|
||||||
|
var isVar : Bool = false;
|
||||||
|
|
||||||
#if hscriptPos
|
#if hscriptPos
|
||||||
var origin : String;
|
var origin : String;
|
||||||
var tokenMin : Int;
|
var tokenMin : Int;
|
||||||
@@ -234,6 +242,16 @@ class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractIdent(tk: Token): String {
|
||||||
|
switch (tk) {
|
||||||
|
case TId(id):
|
||||||
|
return id;
|
||||||
|
default:
|
||||||
|
unexpected(tk);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline function expr(e:Expr):#if hscriptPos ExprDef #else Expr #end {
|
inline function expr(e:Expr):#if hscriptPos ExprDef #else Expr #end {
|
||||||
#if hscriptPos
|
#if hscriptPos
|
||||||
return e.e;
|
return e.e;
|
||||||
@@ -272,7 +290,7 @@ class Parser {
|
|||||||
function isBlock(e:Expr):Bool {
|
function isBlock(e:Expr):Bool {
|
||||||
if( e == null ) return false;
|
if( e == null ) return false;
|
||||||
return switch( expr(e) ) {
|
return switch( expr(e) ) {
|
||||||
case EBlock(_), EObject(_), ESwitch(_): true;
|
case EBlock(_), EObject(_), ESwitch(_), EEnum(_, _): true;
|
||||||
case EFunction(_,e,_,_,_,_): isBlock(e);
|
case EFunction(_,e,_,_,_,_): isBlock(e);
|
||||||
case EClass(_,e,_,_): true;
|
case EClass(_,e,_,_): true;
|
||||||
case EVar(_, t, e, _,_): e != null ? isBlock(e) : t != null ? t.match(CTAnon(_)) : false;
|
case EVar(_, t, e, _,_): e != null ? isBlock(e) : t != null ? t.match(CTAnon(_)) : false;
|
||||||
@@ -285,19 +303,22 @@ class Parser {
|
|||||||
case EReturn(e): e != null && isBlock(e);
|
case EReturn(e): e != null && isBlock(e);
|
||||||
case ETry(_, _, _, e): isBlock(e);
|
case ETry(_, _, _, e): isBlock(e);
|
||||||
case EMeta(_, _, e): isBlock(e);
|
case EMeta(_, _, e): isBlock(e);
|
||||||
|
case EIgnore(skip): skip;
|
||||||
default: false;
|
default: false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseFullExpr( exprs : Array<Expr> ):Void {
|
function parseFullExpr( exprs : Array<Expr> ):Void {
|
||||||
var e = parseExpr();
|
var e = parseExpr();
|
||||||
exprs.push(e);
|
if(!expr(e).match(EIgnore(_)))
|
||||||
|
exprs.push(e);
|
||||||
|
|
||||||
var tk = token();
|
var tk = token();
|
||||||
// this is a hack to support var a,b,c; with a single EVar
|
// this is a hack to support var a,b,c; with a single EVar
|
||||||
while( tk == TComma && e != null && expr(e).match(EVar(_)) ) {
|
while( tk == TComma && e != null && expr(e).match(EVar(_)) ) {
|
||||||
e = parseStructure("var"); // next variable
|
e = parseStructure("var"); // next variable
|
||||||
exprs.push(e);
|
if(!expr(e).match(EIgnore(_)))
|
||||||
|
exprs.push(e);
|
||||||
tk = token();
|
tk = token();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,15 +484,24 @@ class Parser {
|
|||||||
case TBkOpen:
|
case TBkOpen:
|
||||||
var a = [];
|
var a = [];
|
||||||
tk = token();
|
tk = token();
|
||||||
|
var first:Bool = true;
|
||||||
while( tk != TBkClose && (!resumeErrors || tk != TEof) ) {
|
while( tk != TBkClose && (!resumeErrors || tk != TEof) ) {
|
||||||
|
if(!first) {
|
||||||
|
if(tk != TComma)
|
||||||
|
unexpected(tk);
|
||||||
|
else {
|
||||||
|
tk = token();
|
||||||
|
if (tk == TBkClose)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
push(tk);
|
push(tk);
|
||||||
var oldoo = disableOrOp;
|
var oldoo = disableOrOp;
|
||||||
disableOrOp = false;
|
disableOrOp = false;
|
||||||
a.push(parseExpr());
|
a.push(parseExpr());
|
||||||
disableOrOp = oldoo;
|
disableOrOp = oldoo;
|
||||||
tk = token();
|
tk = token();
|
||||||
if( tk == TComma )
|
|
||||||
tk = token();
|
|
||||||
}
|
}
|
||||||
if( a.length == 1 && a[0] != null ) // What is this for???
|
if( a.length == 1 && a[0] != null ) // What is this for???
|
||||||
switch( expr(a[0]) ) {
|
switch( expr(a[0]) ) {
|
||||||
@@ -827,7 +857,40 @@ class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var ident = getIdent();
|
var ident = getIdent();
|
||||||
|
var get = ADefault, set = ADefault;
|
||||||
|
var hasGetSet:Bool = false;
|
||||||
|
|
||||||
var tk = token();
|
var tk = token();
|
||||||
|
if( tk == TPOpen) {
|
||||||
|
if( id == "final" )
|
||||||
|
unexpected(tk);
|
||||||
|
|
||||||
|
var getId = getIdent();
|
||||||
|
switch (getId) {
|
||||||
|
case 'default': // Do nothing
|
||||||
|
case 'get': get = AGet;
|
||||||
|
case 'null': get = ANull;
|
||||||
|
case 'dynamic': get = ADynamic;
|
||||||
|
case 'never': get = ANever;
|
||||||
|
default: error(ECustom("Custom property accessor is not supported"), p1, tokenMax);
|
||||||
|
}
|
||||||
|
ensure(TComma);
|
||||||
|
var setId = getIdent();
|
||||||
|
switch (setId) {
|
||||||
|
case 'default': // Do nothing
|
||||||
|
case 'set': set = ASet;
|
||||||
|
case 'null': set = ANull;
|
||||||
|
case 'dynamic': set = ADynamic;
|
||||||
|
case 'never': set = ANever;
|
||||||
|
default: error(ECustom("Custom property accessor is not supported"), p1, tokenMax);
|
||||||
|
}
|
||||||
|
ensure(TPClose);
|
||||||
|
|
||||||
|
hasGetSet = true;
|
||||||
|
|
||||||
|
tk = token();
|
||||||
|
}
|
||||||
|
|
||||||
var t = null;
|
var t = null;
|
||||||
nextType = null;
|
nextType = null;
|
||||||
if( tk == TDoubleDot && allowTypes ) {
|
if( tk == TDoubleDot && allowTypes ) {
|
||||||
@@ -841,8 +904,12 @@ class Parser {
|
|||||||
e = parseExpr();
|
e = parseExpr();
|
||||||
else
|
else
|
||||||
push(tk);
|
push(tk);
|
||||||
|
|
||||||
|
if(hasGetSet)
|
||||||
|
checkAccess(get, set, e, t);
|
||||||
|
|
||||||
nextType = null;
|
nextType = null;
|
||||||
mk(EVar(ident, t, e, nextIsPublic, nextIsStatic, nextIsPrivate, id == "final", nextIsInline), p1, (e == null) ? tokenMax : pmax(e));
|
mk(EVar(ident, t, e, nextIsPublic, nextIsStatic, nextIsPrivate, id == "final", nextIsInline, get, set), p1, (e == null) ? tokenMax : pmax(e));
|
||||||
case "while":
|
case "while":
|
||||||
var econd = parseExpr();
|
var econd = parseExpr();
|
||||||
var e = parseExpr();
|
var e = parseExpr();
|
||||||
@@ -892,6 +959,18 @@ class Parser {
|
|||||||
var tk = token();
|
var tk = token();
|
||||||
push(tk);
|
push(tk);
|
||||||
mk(EFunction(inf.args, inf.body, name, inf.ret, nextIsPublic, nextIsStatic, nextIsOverride, nextIsPrivate, nextIsFinal, nextIsInline),p1,pmax(inf.body));
|
mk(EFunction(inf.args, inf.body, name, inf.ret, nextIsPublic, nextIsStatic, nextIsOverride, nextIsPrivate, nextIsFinal, nextIsInline),p1,pmax(inf.body));
|
||||||
|
case "package":
|
||||||
|
// ignore package
|
||||||
|
var tk = token();
|
||||||
|
push(tk);
|
||||||
|
packageName = "";
|
||||||
|
if (tk == TSemicolon)
|
||||||
|
return mk(EIgnore(false));
|
||||||
|
|
||||||
|
var path = parsePath();
|
||||||
|
// mk(EPackage(path.join(".")));
|
||||||
|
packageName = path.join(".");
|
||||||
|
mk(EIgnore(false));
|
||||||
case "import":
|
case "import":
|
||||||
var oldReadPos = readPos;
|
var oldReadPos = readPos;
|
||||||
var tk = token();
|
var tk = token();
|
||||||
@@ -1033,7 +1112,91 @@ class Parser {
|
|||||||
var tk = token();
|
var tk = token();
|
||||||
push(tk);
|
push(tk);
|
||||||
mk(EClass(name, fields, extend, interfaces, nextIsFinal, nextIsPrivate), p1);
|
mk(EClass(name, fields, extend, interfaces, nextIsFinal, nextIsPrivate), p1);
|
||||||
|
case "typedef":
|
||||||
|
var name = getIdent();
|
||||||
|
|
||||||
|
ensureToken(TOp("="));
|
||||||
|
|
||||||
|
var t = parseType();
|
||||||
|
|
||||||
|
switch(t) {
|
||||||
|
case CTAnon(_) | CTFun(_):
|
||||||
|
/*
|
||||||
|
Ignore parsing if its,
|
||||||
|
typedef Name = {
|
||||||
|
> Person
|
||||||
|
var name:String;
|
||||||
|
var age:Int;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
mk(EIgnore(true));
|
||||||
|
case CTPath(path, params):
|
||||||
|
if (params != null && params.length > 1)
|
||||||
|
error(ECustom("Typedefs can't have parameters"), p1, tokenMax);
|
||||||
|
|
||||||
|
if (path.length == 0)
|
||||||
|
error(ECustom("Typedefs can't be empty"), p1, tokenMax);
|
||||||
|
|
||||||
|
var className = path.join(".");
|
||||||
|
var cl = Tools.getClass(className);
|
||||||
|
if(cl != null)
|
||||||
|
return mk(ERedirect(name, className, cl));
|
||||||
|
|
||||||
|
var expr = mk(EIdent(path.shift()));
|
||||||
|
while (path.length > 0) {
|
||||||
|
expr = mk(EField(expr, path.shift(), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo? add import to the beginning of the file?
|
||||||
|
mk(EVar(name, null, expr));
|
||||||
|
default:
|
||||||
|
error(ECustom("Typedef, unknown type " + t), p1, tokenMax);
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
case "using":
|
||||||
|
var path = parsePath();
|
||||||
|
mk(EUsing(path.join(".")));
|
||||||
|
case "enum":
|
||||||
|
var name = getIdent();
|
||||||
|
|
||||||
|
ensure(TBrOpen);
|
||||||
|
|
||||||
|
var fields = [];
|
||||||
|
|
||||||
|
var currentName = "";
|
||||||
|
var currentArgs: Array<Argument> = null;
|
||||||
|
while (true) {
|
||||||
|
var tk = token();
|
||||||
|
switch (tk) {
|
||||||
|
case TBrClose:
|
||||||
|
break;
|
||||||
|
case TSemicolon | TComma:
|
||||||
|
if (currentName == "")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (currentArgs != null && currentArgs.length > 0) {
|
||||||
|
fields.push(EnumType.EConstructor(currentName, currentArgs));
|
||||||
|
currentArgs = null;
|
||||||
|
} else {
|
||||||
|
fields.push(EnumType.ESimple(currentName));
|
||||||
|
}
|
||||||
|
currentName = "";
|
||||||
|
case TPOpen:
|
||||||
|
if (currentArgs != null) {
|
||||||
|
error(ECustom("Cannot have multiple argument lists in one enum constructor"), tokenMin, tokenMax);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentArgs = parseFunctionArgs();
|
||||||
|
default:
|
||||||
|
if (currentName != "") {
|
||||||
|
error(ECustom("Expected comma or semicolon"), tokenMin, tokenMax);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var name = extractIdent(tk);
|
||||||
|
currentName = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mk(EEnum(name, fields));
|
||||||
case "return":
|
case "return":
|
||||||
var tk = token();
|
var tk = token();
|
||||||
push(tk);
|
push(tk);
|
||||||
@@ -1448,6 +1611,24 @@ class Parser {
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkAccess(get:FieldPropertyAccess, set:FieldPropertyAccess, ?expr:Expr, ?type:CType) {
|
||||||
|
#if hscriptPos
|
||||||
|
var p1 = tokenMin;
|
||||||
|
#end
|
||||||
|
switch([get, set]) {
|
||||||
|
case [AGet, ASet] | [AGet, ANever] | [ANever, ASet]:
|
||||||
|
if(expr != null && !isVar)
|
||||||
|
error(ECustom("Attempt to assign on field that is not a real variable"), p1, pmax(expr));
|
||||||
|
else if(type == null)
|
||||||
|
error(ECustom("Property requires type-hint"), p1, tokenMax);
|
||||||
|
case [ANever, ANever]:
|
||||||
|
error(ECustom("Unsupported property combination"), p1, (expr == null) ? tokenMax : pmax(expr));
|
||||||
|
default:
|
||||||
|
if(expr == null && type == null)
|
||||||
|
error(ECustom("Property requires type-hint or initialization"), p1, tokenMax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------ module -------------------------------
|
// ------------------------ module -------------------------------
|
||||||
|
|
||||||
public function parseModule( content : String, ?origin : String = "hscript" ):Array<ModuleDecl> {
|
public function parseModule( content : String, ?origin : String = "hscript" ):Array<ModuleDecl> {
|
||||||
@@ -1463,6 +1644,13 @@ class Parser {
|
|||||||
push(tk);
|
push(tk);
|
||||||
decls.push(parseModuleDecl());
|
decls.push(parseModuleDecl());
|
||||||
}
|
}
|
||||||
|
haxe.ds.ArraySort.sort(decls, (m1, m2) -> {
|
||||||
|
var i1 = Type.enumIndex(m1);
|
||||||
|
var i2 = Type.enumIndex(m2);
|
||||||
|
|
||||||
|
if(i1 > i2) return 0;
|
||||||
|
return -1;
|
||||||
|
});
|
||||||
return decls;
|
return decls;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1509,10 +1697,22 @@ class Parser {
|
|||||||
return DPackage(path);
|
return DPackage(path);
|
||||||
case "import":
|
case "import":
|
||||||
var path = [getIdent()];
|
var path = [getIdent()];
|
||||||
var star = false;
|
var asname:String = null;
|
||||||
|
var star = false; // For now, unused
|
||||||
while( true ) {
|
while( true ) {
|
||||||
var t = token();
|
var t = token();
|
||||||
if( t != TDot ) {
|
if( t != TDot ) {
|
||||||
|
if (t.match(TId("as"))) {
|
||||||
|
t = token();
|
||||||
|
switch (t) {
|
||||||
|
case TId(id):
|
||||||
|
asname = id;
|
||||||
|
default:
|
||||||
|
unexpected(t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
push(t);
|
push(t);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1520,34 +1720,56 @@ class Parser {
|
|||||||
switch( t ) {
|
switch( t ) {
|
||||||
case TId(id):
|
case TId(id):
|
||||||
path.push(id);
|
path.push(id);
|
||||||
|
/* idk if put this work
|
||||||
case TOp("*"):
|
case TOp("*"):
|
||||||
star = true;
|
star = true;
|
||||||
|
*/
|
||||||
default:
|
default:
|
||||||
unexpected(t);
|
unexpected(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ensure(TSemicolon);
|
ensure(TSemicolon);
|
||||||
return DImport(path, star);
|
return DImport(path, star, asname);
|
||||||
|
case "using":
|
||||||
|
var path = parsePath();
|
||||||
|
ensure(TSemicolon);
|
||||||
|
return DUsing(path);
|
||||||
case "class":
|
case "class":
|
||||||
var name = getIdent();
|
var name = getIdent();
|
||||||
var params = parseParams();
|
var params = parseParams();
|
||||||
var extend = null;
|
var extend:Null<CType> = null;
|
||||||
var implement = [];
|
var implement:Array<CType> = [];
|
||||||
|
|
||||||
while( true ) {
|
while( true ) {
|
||||||
var t = token();
|
var t = token();
|
||||||
switch( t ) {
|
switch( t ) {
|
||||||
case TId("extends"):
|
case TId("extends"):
|
||||||
extend = parseType();
|
var e = parseType();
|
||||||
|
switch(e) {
|
||||||
|
case CTPath(path, params):
|
||||||
|
if(extend != null) {
|
||||||
|
error(ECustom('Cannot extend a class twice.'), 0, 0);
|
||||||
|
}
|
||||||
|
extend = e;
|
||||||
|
default:
|
||||||
|
error(ECustom('${Std.string(e)} is not a valid path.'), 0, 0);
|
||||||
|
}
|
||||||
case TId("implements"):
|
case TId("implements"):
|
||||||
implement.push(parseType());
|
var i = parseType();
|
||||||
|
switch(i) {
|
||||||
|
case CTPath(path, params):
|
||||||
|
implement.push(i);
|
||||||
|
default:
|
||||||
|
error(ECustom('${Std.string(i)} is not a valid interface path.'), 0, 0);
|
||||||
|
}
|
||||||
|
//implement.push(parseType());
|
||||||
default:
|
default:
|
||||||
push(t);
|
push(t);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fields = [];
|
var fields:Array<FieldDecl> = [];
|
||||||
ensure(TBrOpen);
|
ensure(TBrOpen);
|
||||||
while( !maybe(TBrClose) )
|
while( !maybe(TBrClose) )
|
||||||
fields.push(parseField());
|
fields.push(parseField());
|
||||||
@@ -1611,17 +1833,42 @@ class Parser {
|
|||||||
ret : inf.ret,
|
ret : inf.ret,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
case "var":
|
case "var" | "final":
|
||||||
var name = getIdent();
|
var name = getIdent();
|
||||||
var get = null, set = null;
|
var get = ADefault, set = ADefault;
|
||||||
if( maybe(TPOpen) ) {
|
var hasGetSet:Bool = false;
|
||||||
get = getIdent();
|
|
||||||
|
if(maybe(TPOpen)) {
|
||||||
|
if(id == "final")
|
||||||
|
unexpected(TPOpen);
|
||||||
|
|
||||||
|
var getId = getIdent();
|
||||||
|
switch(getId) {
|
||||||
|
case 'default': // Do nothing
|
||||||
|
case 'get': get = AGet;
|
||||||
|
case 'null': get = ANull;
|
||||||
|
case 'dynamic': get = ADynamic;
|
||||||
|
case 'never': get = ANever;
|
||||||
|
default: error(ECustom("Custom property accessor is not supported"), tokenMin, tokenMax);
|
||||||
|
}
|
||||||
ensure(TComma);
|
ensure(TComma);
|
||||||
set = getIdent();
|
var setId = getIdent();
|
||||||
|
switch(setId) {
|
||||||
|
case 'default': // Do nothing
|
||||||
|
case 'set': set = ASet;
|
||||||
|
case 'null': set = ANull;
|
||||||
|
case 'dynamic': set = ADynamic;
|
||||||
|
case 'never': set = ANever;
|
||||||
|
default: error(ECustom("Custom property accessor is not supported"), tokenMin, tokenMax);
|
||||||
|
}
|
||||||
ensure(TPClose);
|
ensure(TPClose);
|
||||||
|
hasGetSet = true;
|
||||||
}
|
}
|
||||||
var type = maybe(TDoubleDot) ? parseType() : null;
|
var type:Null<CType> = maybe(TDoubleDot) ? parseType() : null;
|
||||||
var expr = maybe(TOp("=")) ? parseExpr() : null;
|
var expr:Null<Expr> = maybe(TOp("=")) ? parseExpr() : null;
|
||||||
|
|
||||||
|
if(hasGetSet)
|
||||||
|
checkAccess(get, set, expr, type);
|
||||||
|
|
||||||
if( expr != null ) {
|
if( expr != null ) {
|
||||||
if( isBlock(expr) )
|
if( isBlock(expr) )
|
||||||
@@ -1640,8 +1887,9 @@ class Parser {
|
|||||||
kind : KVar({
|
kind : KVar({
|
||||||
get : get,
|
get : get,
|
||||||
set : set,
|
set : set,
|
||||||
|
isFinal: id == "final",
|
||||||
type : type,
|
type : type,
|
||||||
expr : expr,
|
expr : expr
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
@@ -1797,6 +2045,8 @@ class Parser {
|
|||||||
}
|
}
|
||||||
if( pow == null )
|
if( pow == null )
|
||||||
invalidChar(char);
|
invalidChar(char);
|
||||||
|
if( exp == 0 )
|
||||||
|
exp = 10;
|
||||||
return TConst(CFloat((Math.pow(10, pow) / exp) * n * 10));
|
return TConst(CFloat((Math.pow(10, pow) / exp) * n * 10));
|
||||||
case ".".code:
|
case ".".code:
|
||||||
if( exp > 0 ) {
|
if( exp > 0 ) {
|
||||||
|
|||||||
+38
-1
@@ -113,7 +113,8 @@ class Printer {
|
|||||||
add("??NULL??");
|
add("??NULL??");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch( #if hscriptPos e.e #else e #end ) {
|
switch( Tools.expr(e) ) {
|
||||||
|
case EIgnore(_):
|
||||||
case EImport(c, n):
|
case EImport(c, n):
|
||||||
add("import " + c);
|
add("import " + c);
|
||||||
if(n != null)
|
if(n != null)
|
||||||
@@ -133,6 +134,8 @@ class Printer {
|
|||||||
|
|
||||||
tabs = tabs.substr(1);
|
tabs = tabs.substr(1);
|
||||||
add("}");
|
add("}");
|
||||||
|
case ERedirect(n, cl):
|
||||||
|
add('typedef $n = $cl');
|
||||||
case EConst(c):
|
case EConst(c):
|
||||||
switch( c ) {
|
switch( c ) {
|
||||||
case CInt(i): add(i);
|
case CInt(i): add(i);
|
||||||
@@ -344,9 +347,43 @@ class Printer {
|
|||||||
add(" : ");
|
add(" : ");
|
||||||
addType(t);
|
addType(t);
|
||||||
add(")");
|
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("}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addArgument(a: Argument) {
|
||||||
|
if (a.opt)
|
||||||
|
add("?");
|
||||||
|
add(a.name);
|
||||||
|
addType(a.t);
|
||||||
|
}
|
||||||
|
|
||||||
public static function toString( e : Expr ):String {
|
public static function toString( e : Expr ):String {
|
||||||
return new Printer().exprToString(e);
|
return new Printer().exprToString(e);
|
||||||
}
|
}
|
||||||
|
|||||||
+147
-1
@@ -22,6 +22,8 @@
|
|||||||
package hscript;
|
package hscript;
|
||||||
import hscript.Expr;
|
import hscript.Expr;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
class Tools {
|
class Tools {
|
||||||
|
|
||||||
public static function iter( e : Expr, f : Expr -> Void ):Void {
|
public static function iter( e : Expr, f : Expr -> Void ):Void {
|
||||||
@@ -59,7 +61,7 @@ class Tools {
|
|||||||
if( def != null ) f(def);
|
if( def != null ) f(def);
|
||||||
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
|
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
|
||||||
case ECheckType(e,_): f(e);
|
case ECheckType(e,_): f(e);
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +93,7 @@ class Tools {
|
|||||||
case ECheckType(e,t): ECheckType(f(e), t);
|
case ECheckType(e,t): ECheckType(f(e), t);
|
||||||
case EImport(c): EImport(c);
|
case EImport(c): EImport(c);
|
||||||
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
|
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);
|
return mk(edef, e);
|
||||||
}
|
}
|
||||||
@@ -111,4 +114,147 @@ class Tools {
|
|||||||
#end
|
#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;
|
using StringTools;
|
||||||
|
|
||||||
class UsingHandler {
|
class AbstractHandler {
|
||||||
public static function init() {
|
public static function init() {
|
||||||
#if !display
|
#if !display
|
||||||
if(Context.defined("display")) return;
|
if(Context.defined("display")) return;
|
||||||
for(apply in Config.ALLOWED_ABSTRACT_AND_ENUM) {
|
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
|
#end
|
||||||
}
|
}
|
||||||
@@ -211,6 +211,15 @@ class ClassExtendMacro {
|
|||||||
continue;
|
continue;
|
||||||
if (f.name == "new") {
|
if (f.name == "new") {
|
||||||
hasNew = true;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if (f.name.startsWith(FUNC_PREFIX))
|
if (f.name.startsWith(FUNC_PREFIX))
|
||||||
@@ -245,7 +254,7 @@ class ClassExtendMacro {
|
|||||||
if (returns) {
|
if (returns) {
|
||||||
overrideExpr = macro {
|
overrideExpr = macro {
|
||||||
var name:String = $v{name};
|
var name:String = $v{name};
|
||||||
|
/*
|
||||||
if (__custom__variables != null) {
|
if (__custom__variables != null) {
|
||||||
if(__custom__variables.exists(name)) {
|
if(__custom__variables.exists(name)) {
|
||||||
var v:Dynamic = null;
|
var v:Dynamic = null;
|
||||||
@@ -254,12 +263,17 @@ class ClassExtendMacro {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
|
||||||
|
return __customClass.callFunction(name, [$a{arguments}]);
|
||||||
|
}
|
||||||
|
|
||||||
return super.$name($a{arguments});
|
return super.$name($a{arguments});
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
overrideExpr = macro {
|
overrideExpr = macro {
|
||||||
var name:String = $v{name};
|
var name:String = $v{name};
|
||||||
|
/*
|
||||||
if (__custom__variables != null) {
|
if (__custom__variables != null) {
|
||||||
if(__custom__variables.exists(name)) {
|
if(__custom__variables.exists(name)) {
|
||||||
var v:Dynamic = null;
|
var v:Dynamic = null;
|
||||||
@@ -269,6 +283,12 @@ class ClassExtendMacro {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
|
||||||
|
__customClass.callFunction(name, [$a{arguments}]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
super.$name($a{arguments});
|
super.$name($a{arguments});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -331,7 +351,7 @@ class ClassExtendMacro {
|
|||||||
pack: cl.pack.copy(),
|
pack: cl.pack.copy(),
|
||||||
name: cl.name
|
name: cl.name
|
||||||
}, [
|
}, [
|
||||||
{name: "IHScriptCustomAccessBehaviour", pack: ["hscript"]},
|
{name: "IHScriptCustomBehaviour", pack: ["hscript"]},
|
||||||
{name: "IHScriptCustomClassBehaviour", pack: ["hscript"]}
|
{name: "IHScriptCustomClassBehaviour", pack: ["hscript"]}
|
||||||
], false, true, false);
|
], false, true, false);
|
||||||
shadowClass.name = '${cl.name}$CLASS_SUFFIX';
|
shadowClass.name = '${cl.name}$CLASS_SUFFIX';
|
||||||
@@ -339,15 +359,29 @@ class ClassExtendMacro {
|
|||||||
Utils.setupMetas(shadowClass, imports);
|
Utils.setupMetas(shadowClass, imports);
|
||||||
Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect");
|
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({
|
shadowClass.fields.push({
|
||||||
name: "__interp",
|
name: "__interp",
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
kind: FVar(macro: hscript.Interp),
|
kind: FVar(macro: hscript.Interp),
|
||||||
access: [APublic]
|
access: [APublic]
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
shadowClass.fields.push({
|
||||||
|
name: "__customClass",
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
kind: FVar(macro: hscript.customclass.CustomClass),
|
||||||
|
access: [APublic]
|
||||||
|
});
|
||||||
|
/*
|
||||||
shadowClass.fields.push({
|
shadowClass.fields.push({
|
||||||
name: "__custom__variables",
|
name: "__custom__variables",
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
@@ -361,21 +395,23 @@ class ClassExtendMacro {
|
|||||||
kind: FVar(macro: Bool, macro true),
|
kind: FVar(macro: Bool, macro true),
|
||||||
access: [APublic]
|
access: [APublic]
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
shadowClass.fields.push({
|
shadowClass.fields.push({
|
||||||
name: "__real_fields",
|
name: "__real_fields",
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
kind: FVar(macro: Array<String>),
|
kind: FVar(macro: Array<String>),
|
||||||
access: [APublic]
|
access: [APublic]
|
||||||
});
|
});
|
||||||
|
/*
|
||||||
shadowClass.fields.push({
|
shadowClass.fields.push({
|
||||||
name: "__class__fields",
|
name: "__class__fields",
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
kind: FVar(macro: Array<String>),
|
kind: FVar(macro: Array<String>),
|
||||||
access: [APublic]
|
access: [APublic]
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
// Unneccessary since the get/set call is already handled.
|
||||||
|
/*
|
||||||
shadowClass.fields.push({
|
shadowClass.fields.push({
|
||||||
name: "__callGetter",
|
name: "__callGetter",
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
@@ -429,7 +465,7 @@ class ClassExtendMacro {
|
|||||||
}),
|
}),
|
||||||
access: [APublic]
|
access: [APublic]
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
// Todo: make it possible to override
|
// Todo: make it possible to override
|
||||||
if(cl.name == "FunkinShader" || cl.name == "CustomShader" || cl.name == "MultiThreadedScript") {
|
if(cl.name == "FunkinShader" || cl.name == "CustomShader" || cl.name == "MultiThreadedScript") {
|
||||||
Context.defineModule(cl.module, [shadowClass], imports);
|
Context.defineModule(cl.module, [shadowClass], imports);
|
||||||
@@ -472,31 +508,43 @@ class ClassExtendMacro {
|
|||||||
|
|
||||||
var hgetField = if(hasHgetInSuper) {
|
var hgetField = if(hasHgetInSuper) {
|
||||||
macro {
|
macro {
|
||||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
/*
|
||||||
return __callGetter(name);
|
if(__custom__variables != null) {
|
||||||
if (__custom__variables.exists(name))
|
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||||
return __custom__variables.get(name);
|
return __callGetter(name);
|
||||||
|
if (__custom__variables.exists(name))
|
||||||
|
return __custom__variables.get(name);
|
||||||
|
}
|
||||||
|
*/
|
||||||
return super.hget(name);
|
return super.hget(name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
macro {
|
macro {
|
||||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
/*
|
||||||
return __callGetter(name);
|
if(__custom__variables != null) {
|
||||||
if (__custom__variables.exists(name))
|
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||||
return __custom__variables.get(name);
|
return __callGetter(name);
|
||||||
|
if (__custom__variables.exists(name))
|
||||||
|
return __custom__variables.get(name);
|
||||||
|
}
|
||||||
|
*/
|
||||||
return UnsafeReflect.getProperty(this, name);
|
return UnsafeReflect.getProperty(this, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hsetField = if(hasHsetInSuper) {
|
var hsetField = if(hasHsetInSuper) {
|
||||||
macro {
|
macro {
|
||||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
/*
|
||||||
return __callSetter(name, val);
|
if(__custom__variables != null) {
|
||||||
if (__custom__variables.exists(name)) {
|
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||||
__custom__variables.set(name, val);
|
return __callSetter(name, val);
|
||||||
return val;
|
if (__custom__variables.exists(name)) {
|
||||||
|
__custom__variables.set(name, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(__real_fields.contains(name)) {
|
*/
|
||||||
|
if(__real_fields != null && __real_fields.contains(name)) {
|
||||||
UnsafeReflect.setProperty(this, name, val);
|
UnsafeReflect.setProperty(this, name, val);
|
||||||
return UnsafeReflect.field(this, name);
|
return UnsafeReflect.field(this, name);
|
||||||
}
|
}
|
||||||
@@ -504,17 +552,21 @@ class ClassExtendMacro {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
macro {
|
macro {
|
||||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
/*
|
||||||
return __callSetter(name, val);
|
if(__custom__variables != null) {
|
||||||
if (__custom__variables.exists(name)) {
|
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||||
__custom__variables.set(name, val);
|
return __callSetter(name, val);
|
||||||
return val;
|
if (__custom__variables.exists(name)) {
|
||||||
|
__custom__variables.set(name, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(__real_fields.contains(name)) {
|
*/
|
||||||
|
if(__real_fields != null && __real_fields.contains(name)) {
|
||||||
UnsafeReflect.setProperty(this, name, val);
|
UnsafeReflect.setProperty(this, name, val);
|
||||||
return UnsafeReflect.field(this, name);
|
return UnsafeReflect.field(this, name);
|
||||||
}
|
}
|
||||||
__custom__variables.set(name, val);
|
//if(__custom__variables != null) __custom__variables.set(name, val);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -566,7 +618,7 @@ class ClassExtendMacro {
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
/*var p = new Printer();
|
/*var p = new Printer();
|
||||||
var aa = p.printTypeDefinition(shadowClass);
|
var aa = p.printTypeDefinition(shadowClass);
|
||||||
if(aa.length < 5024)
|
if(aa.length < 5024)
|
||||||
@@ -579,6 +631,32 @@ class ClassExtendMacro {
|
|||||||
|
|
||||||
return fields;
|
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
|
#else
|
||||||
class ClassExtendMacro {
|
class ClassExtendMacro {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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