"null" access handling + get/set for variables
- It handles "null" allowing access only inside of the class. - "final" variables can be initialized in the constructor if they doesn't have a default value. - "var a(get, set):Int" is parsed outside of Custom Class, but for now it doesn't call their accessors. almost finished :3
This commit is contained in:
+12
-4
@@ -54,7 +54,7 @@ enum Expr {
|
||||
EIgnore(skip:Bool);
|
||||
EConst( c : Const );
|
||||
EIdent( v : String );
|
||||
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool, ?get : String, ?set : String );
|
||||
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool, ?get : FieldPropertyAccess, ?set : FieldPropertyAccess );
|
||||
EParent( e : Expr );
|
||||
EBlock( e : Array<Expr> );
|
||||
EField( e : Expr, f : String , ?safe : Bool );
|
||||
@@ -216,11 +216,11 @@ final class FunctionDecl {
|
||||
}
|
||||
|
||||
typedef VarDecl = {
|
||||
var get : Null<String>;
|
||||
var set : Null<String>;
|
||||
var get : FieldPropertyAccess;
|
||||
var set : FieldPropertyAccess;
|
||||
var isFinal : Bool;
|
||||
var expr : Null<Expr>;
|
||||
var type : Null<CType>;
|
||||
var isFinal : Null<Bool>;
|
||||
}
|
||||
|
||||
typedef TypeParamDecl = {};
|
||||
@@ -229,3 +229,11 @@ enum EnumType {
|
||||
ESimple(name: String);
|
||||
EConstructor(name: String, args: Array<Argument>);
|
||||
}
|
||||
|
||||
enum abstract FieldPropertyAccess(UInt8) {
|
||||
var ADefault;
|
||||
var ANull;
|
||||
var ANever;
|
||||
var AGet;
|
||||
var ASet;
|
||||
}
|
||||
|
||||
+30
-4
@@ -275,7 +275,9 @@ class Interp {
|
||||
public function setVar(name:String, v:Dynamic):Void {
|
||||
if (_inCustomClass && _proxy.superClass != null) {
|
||||
if (_proxy.superIsCustomClass) {
|
||||
_proxy.__allowInnerAccess = true;
|
||||
cast(_proxy.superClass, CustomClass).hset(name, v);
|
||||
_proxy.__allowInnerAccess = false;
|
||||
}
|
||||
else if (_proxy.superHasField(name)) {
|
||||
Reflect.setProperty(_proxy.superClass, name, v);
|
||||
@@ -299,7 +301,9 @@ class Interp {
|
||||
if (_inCustomClass) {
|
||||
if (_proxy.__class.hasField(id)) {
|
||||
var v = expr(e2);
|
||||
_proxy.__class.__allowInnerAccess = true;
|
||||
_proxy.__class.hset(id, v);
|
||||
_proxy.__class.__allowInnerAccess = false;
|
||||
return v;
|
||||
}
|
||||
/*
|
||||
@@ -324,7 +328,9 @@ class Interp {
|
||||
else {
|
||||
try {
|
||||
var v = expr(e2);
|
||||
_proxy.__allowInnerAccess = true;
|
||||
_proxy.hset(id, v); // superClass check already handled in Custom Class
|
||||
_proxy.__allowInnerAccess = false;
|
||||
return v;
|
||||
}
|
||||
catch(e) {}
|
||||
@@ -405,7 +411,9 @@ class Interp {
|
||||
else {
|
||||
try {
|
||||
var v = expr(e2);
|
||||
_proxy.__allowInnerAccess = true;
|
||||
_proxy.hset(f, v);
|
||||
_proxy.__allowInnerAccess = false;
|
||||
return v;
|
||||
}
|
||||
catch(e){}
|
||||
@@ -448,7 +456,9 @@ class Interp {
|
||||
// Also ensures property functions are accounted for.
|
||||
if(_inCustomClass) {
|
||||
if (_proxy.__class.hasField(id)) {
|
||||
_proxy.__class.__allowInnerAccess = true;
|
||||
_proxy.__class.hset(id, v);
|
||||
_proxy.__class.__allowInnerAccess = false;
|
||||
return v;
|
||||
}
|
||||
/*
|
||||
@@ -467,7 +477,9 @@ class Interp {
|
||||
}
|
||||
else {
|
||||
try {
|
||||
_proxy.__allowInnerAccess = true;
|
||||
_proxy.hset(id, v); // superClass check already handled in Custom Class
|
||||
_proxy.__allowInnerAccess = false;
|
||||
return v;
|
||||
}
|
||||
catch(e) {}
|
||||
@@ -552,7 +564,9 @@ class Interp {
|
||||
else {
|
||||
try {
|
||||
v = fop(get(obj, f), expr(e2));
|
||||
_proxy.__allowInnerAccess = true;
|
||||
_proxy.hset(f, v); // superClass check already handled in Custom Class
|
||||
_proxy.__allowInnerAccess = false;
|
||||
return v;
|
||||
}
|
||||
catch(e) {}
|
||||
@@ -762,11 +776,20 @@ class Interp {
|
||||
// Custom Class
|
||||
if (_inCustomClass) {
|
||||
// Static access
|
||||
if (_proxy.__class.hasField(id))
|
||||
return _proxy.__class.hget(id);
|
||||
if (_proxy.__class.hasField(id)) {
|
||||
_proxy.__class.__allowInnerAccess = true;
|
||||
var r = _proxy.__class.hget(id);
|
||||
_proxy.__class.__allowInnerAccess = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
if (_proxy.hasVar(id))
|
||||
return _proxy.hget(id);
|
||||
|
||||
if (_proxy.hasVar(id)) {
|
||||
_proxy.__allowInnerAccess = true;
|
||||
var r = _proxy.hget(id);
|
||||
_proxy.__allowInnerAccess = false;
|
||||
return r;
|
||||
}
|
||||
// We are calling a LOCAL function from the same module.
|
||||
if (_proxy.hasFunction(id)) {
|
||||
_nextCallObject = _proxy;
|
||||
@@ -777,12 +800,15 @@ class Interp {
|
||||
return Reflect.getProperty(_proxy.superClass, id);
|
||||
} else {
|
||||
try {
|
||||
_proxy.__allowInnerAccess = true;
|
||||
var r = _proxy.hget(id);
|
||||
_nextCallObject = _proxy;
|
||||
_proxy.__allowInnerAccess = false;
|
||||
return r;
|
||||
} catch (e:Dynamic) {
|
||||
if(doException)
|
||||
error(EUnknownVariable(id));
|
||||
_proxy.__allowInnerAccess = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+49
-8
@@ -22,6 +22,7 @@
|
||||
package hscript;
|
||||
import haxe.rtti.Meta;
|
||||
import hscript.Expr;
|
||||
import hscript.Expr.FieldPropertyAccess;
|
||||
|
||||
enum Token {
|
||||
TEof;
|
||||
@@ -854,9 +855,33 @@ class Parser {
|
||||
}
|
||||
}
|
||||
var ident = getIdent();
|
||||
var get = null, set = null;
|
||||
// TODO: getter and setter
|
||||
var get = ADefault, set = ADefault;
|
||||
var tk = token();
|
||||
// TODO: throw "Missing ;" if tried to use (get, set) on final
|
||||
// and "Property requires type-hint or initialization" on missing type.
|
||||
if( tk == TPOpen && id != "final" ) {
|
||||
var getId = getIdent();
|
||||
switch (getId) {
|
||||
case 'default': // Do nothing
|
||||
case 'get': get = AGet;
|
||||
case 'null': get = ANull;
|
||||
case 'never': get = ANever;
|
||||
default: unexpected(TId(getId));
|
||||
}
|
||||
ensure(TComma);
|
||||
var setId = getIdent();
|
||||
switch (setId) {
|
||||
case 'default': // Do nothing
|
||||
case 'set': set = ASet;
|
||||
case 'null': set = ANull;
|
||||
case 'never': set = ANever;
|
||||
default: unexpected(TId(getId));
|
||||
}
|
||||
ensure(TPClose);
|
||||
|
||||
tk = token();
|
||||
}
|
||||
|
||||
var t = null;
|
||||
nextType = null;
|
||||
if( tk == TDoubleDot && allowTypes ) {
|
||||
@@ -1779,11 +1804,27 @@ class Parser {
|
||||
};
|
||||
case "var" | "final":
|
||||
var name = getIdent();
|
||||
var get = null, set = null;
|
||||
if(id != "final" && maybe(TPOpen) ) {
|
||||
get = getIdent();
|
||||
var get = ADefault, set = ADefault;
|
||||
// TODO: throw "Missing ;" if tried to use (get, set) on final
|
||||
// and "Property requires type-hint or initialization" on missing type.
|
||||
if(maybe(TPOpen) && id != "final") {
|
||||
var getId = getIdent();
|
||||
switch(getId) {
|
||||
case 'default': // Do nothing
|
||||
case 'get': get = AGet;
|
||||
case 'null': get = ANull;
|
||||
case 'never': get = ANever;
|
||||
default: unexpected(TId(getId));
|
||||
}
|
||||
ensure(TComma);
|
||||
set = getIdent();
|
||||
var setId = getIdent();
|
||||
switch(setId) {
|
||||
case 'default': // Do nothing
|
||||
case 'set': set = ASet;
|
||||
case 'null': set = ANull;
|
||||
case 'never': set = ANever;
|
||||
default: unexpected(TId(getId));
|
||||
}
|
||||
ensure(TPClose);
|
||||
}
|
||||
var type = maybe(TDoubleDot) ? parseType() : null;
|
||||
@@ -1806,9 +1847,9 @@ class Parser {
|
||||
kind : KVar({
|
||||
get : get,
|
||||
set : set,
|
||||
isFinal: id == "final",
|
||||
type : type,
|
||||
expr : expr,
|
||||
isFinal: id == "final"
|
||||
expr : expr
|
||||
}),
|
||||
};
|
||||
default:
|
||||
|
||||
@@ -3,6 +3,7 @@ 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;
|
||||
@@ -44,9 +45,11 @@ class CustomClass implements IHScriptCustomAccessBehaviour {
|
||||
private var __cachedVarDecls:Map<String, VarDecl> = [];
|
||||
|
||||
public var __allowSetGet:Bool = false;
|
||||
public var __allowInnerAccess: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;
|
||||
@@ -76,7 +79,10 @@ class CustomClass implements IHScriptCustomAccessBehaviour {
|
||||
|
||||
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) {
|
||||
@@ -94,8 +100,6 @@ class CustomClass implements IHScriptCustomAccessBehaviour {
|
||||
__cachedFieldDecls.set(f.name, f);
|
||||
switch (f.kind) {
|
||||
case KFunction(fn):
|
||||
if(f.name.startsWith('set_') || f.name.startsWith('get_'))
|
||||
__allowSetGet = true;
|
||||
__cachedFunctionDecls.set(f.name, fn);
|
||||
#if hscriptPos
|
||||
var fexpr:Expr = {
|
||||
@@ -111,6 +115,8 @@ class CustomClass implements IHScriptCustomAccessBehaviour {
|
||||
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);
|
||||
@@ -316,15 +322,19 @@ class CustomClass implements IHScriptCustomAccessBehaviour {
|
||||
|
||||
if (hasVar(name)) {
|
||||
var v = getVar(name);
|
||||
var getter = v.get != null ? v.get : 'default';
|
||||
var getter = v.get;
|
||||
|
||||
var value:Dynamic = null;
|
||||
|
||||
if(getter == "never")
|
||||
if(getter == ANever || getter == ANull && !__allowInnerAccess)
|
||||
throw 'field $name cannot be accessed for reading';
|
||||
|
||||
if (__allowSetGet && getter == "get" && hasFunction('get_${name}'))
|
||||
value = __callGetter(name);
|
||||
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 {
|
||||
@@ -370,13 +380,17 @@ class CustomClass implements IHScriptCustomAccessBehaviour {
|
||||
default:
|
||||
if (hasVar(name)) {
|
||||
var v = getVar(name);
|
||||
var setter = v.set != null ? v.set : 'default';
|
||||
var setter = v.set;
|
||||
|
||||
if (setter == "never" || (v.isFinal != null && v.isFinal))
|
||||
if (setter == ANever || setter == ANull && !__allowInnerAccess || (v.isFinal && !initializing))
|
||||
throw 'field $name cannot be accessed for writing';
|
||||
|
||||
if (__allowSetGet && setter == "set" && hasFunction('set_${name}'))
|
||||
return __callSetter(name, val);
|
||||
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) {
|
||||
@@ -411,14 +425,14 @@ class CustomClass implements IHScriptCustomAccessBehaviour {
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('get_${name}');
|
||||
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]);
|
||||
var r = callFunction('set_$name', [val]);
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ class CustomClassDecl implements IHScriptCustomAccessBehaviour {
|
||||
|
||||
public var __allowSetGet:Bool = true;
|
||||
|
||||
public var __allowInnerAccess:Bool = false;
|
||||
|
||||
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;
|
||||
@@ -111,6 +113,8 @@ class CustomClassDecl implements IHScriptCustomAccessBehaviour {
|
||||
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);
|
||||
@@ -166,24 +170,8 @@ class CustomClassDecl implements IHScriptCustomAccessBehaviour {
|
||||
return _cachedStaticVariables.exists(name);
|
||||
}
|
||||
|
||||
private function getVar(name:String):Dynamic {
|
||||
var staticVar = _cachedStaticVariables.get(name);
|
||||
|
||||
if(staticVar != null) {
|
||||
var varValue:Dynamic = null;
|
||||
if(!this.staticInterp.variables.exists(name)) {
|
||||
if(staticVar.expr != null) {
|
||||
varValue = this.staticInterp.expr(staticVar.expr);
|
||||
this.staticInterp.variables.set(name, varValue);
|
||||
}
|
||||
}
|
||||
else {
|
||||
varValue = this.staticInterp.variables.get(name);
|
||||
}
|
||||
return varValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
private function getVar(name:String):VarDecl {
|
||||
return _cachedStaticVariables.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,16 +186,33 @@ class CustomClassDecl implements IHScriptCustomAccessBehaviour {
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
var r:Dynamic = null;
|
||||
|
||||
if(hasVar(name)) {
|
||||
if(__allowSetGet && hasFunction('get_${name}'))
|
||||
r = __callGetter(name);
|
||||
else
|
||||
r = getVar(name);
|
||||
var v = getVar(name);
|
||||
var getter = v.get;
|
||||
|
||||
var r:Dynamic = null;
|
||||
|
||||
if (getter == ANever || getter == ANull && !__allowInnerAccess)
|
||||
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);
|
||||
});
|
||||
@@ -219,8 +224,18 @@ class CustomClassDecl implements IHScriptCustomAccessBehaviour {
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if (hasVar(name)) {
|
||||
if (__allowSetGet && hasFunction('set_${name}'))
|
||||
return __callSetter(name, val);
|
||||
var v = getVar(name);
|
||||
var setter = v.set;
|
||||
|
||||
if (setter == ANever || setter == ANull && !__allowInnerAccess || 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;
|
||||
|
||||
Reference in New Issue
Block a user