所以,我已经创建了带有所有静态方法的"接口类",我想将其公开给hscript
脚本。它看起来像这样:
package com.application.interfaces.Terrain;
import com.application.TerrainCore
class Terrain {
private static var terrain:TerrainCore;
public static function _init(inTerrain:TerrainCore):Void {
terrain = inTerrain;
}
public static function test(s:Int):Void {
terrain.test(s);
}
}
问题是,我需要以某种方式设置terrain
对象,但我不希望它暴露给脚本。我用
var interp = new Interp();
var module = Type.resolveClass("com.application.interfaces.Terrain");
interp.variables.set("Terrain", module)
这个想法是在hscript.Interp
中覆盖call
方法,因此它不执行任何名为_init
的方法,但我不知道如何做到这一点。原来的call
方法是这样的:
function call( o : Dynamic, f : Dynamic, args : Array<Dynamic> ) : Dynamic {
return Reflect.callMethod(o,f,args);
}
是否可以使用Terrain的类实例而不是使用静态成员?如:
interp.variables.set("Terrain", new Terrain(new TerrainCore()));
脚本用户不会知道他们使用的是静态方法还是实例方法,因为它仍然可以通过:
Terrain.test(123);
脚本。
另一种选择(基于clemos)是使用rtti来计算允许的内容(而不是维护一个列表),例如:
Terrain._init(new TerrainCore());
_init现在是一个私有函数,所以你需要从你的调用类中@:allow
它(见下文),此外,你需要用@:rtti
注释,这样你就可以在运行时获取有关函数的信息,所以地形现在看起来像:
@:rtti
class Terrain {
private static var terrain:TerrainCore;
@:allow(test.hscript.demo.Main)
private static function _init(inTerrain:TerrainCore):Void {
terrain = inTerrain;
}
public static function test(s:Int):Void {
terrain.test(s);
}
}
最后,脚本接口fcall
现在确定字段是公共的还是私有的,即:
public override function fcall(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic
var rtti = haxe.rtti.Rtti.getRtti(o);
for (field in rtti.statics) {
if (field.name == f && field.isPublic == false) {
error(EInvalidAccess(f));
}
}
return super.fcall(o, f, args);
}
值得注意的是,我使用statics
而不是fields
的原因很明显。我也不确定这会导致循环和rtti的开销。
我相信这是fcall
你应该重写,因为call
只用于高层调用:
https://github.com/HaxeFoundation/hscript/blob/master/hscript/Interp.hx L328-L331
应该很容易过滤f
参数,如:
if ( FORBIDDEN_FIELDS.indexOf( f ) > -1 ) throw EInvalidAccess( f );
或
if ( f.indexOf('_') == 0 ) throw EInvalidAccess( f );