2

So, I've created "interface class" with all static methods, which I want to expose to hscript scripts. It looks like this:

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);
    }
}

The problem is, that I need to set terrain object somehow, but I don't want it to be exposed to scripts. I expose whole classes with

var interp = new Interp();
var module = Type.resolveClass("com.application.interfaces.Terrain");
interp.variables.set("Terrain", module)

The idea was to override method call in hscript.Interp so it doesn't execute any method named _init, but I don't know how to do that. Original call method looks like this:

function call( o : Dynamic, f : Dynamic, args : Array<Dynamic> ) : Dynamic {
    return Reflect.callMethod(o,f,args);
}
wildfireheart
  • 373
  • 2
  • 4
  • 13

2 Answers2

2

Can you use a class instance of Terrain instead of using static members? Eg:

interp.variables.set("Terrain", new Terrain(new TerrainCore()));

Script users wont know if they are using static or instance methods as it will still be access via:

Terrain.test(123);

in script.

Another option (based on clemos), is to use rtti to work out what is allowed (instead of maintaining a list of it), eg:

Terrain._init(new TerrainCore());

_init is a private function now, so you need to @:allow it from your calling class (see below), also, you need to annotate with @:rtti so you can grab info about the functions at runtime, so Terrain now looks like:

@: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);
    }
}

Finally, the script interp fcall now honours whether a field is public or private, ie:

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);
}

Its worth noting that I used statics rather than fields for obvious reasons. Im also not sure what overhead this would cause with the loop and the rtti.

Ian Harrigan
  • 836
  • 6
  • 14
  • Yes, I shouldn't used static members here in the first place, it didn't occur to me that I can use class instances somehow. Thank you. Btw, @clemos and your other solution would also be a possibility, but using instances seems more elegant solution in my case, I shouldn't use static methods for what I am currently doing... – wildfireheart Oct 25 '15 at 13:02
  • Yeah you're both right. Also, you may probably want to have different access at compile-time and at "interpretation time"; If I were you, I'd use metadata (such as `@:hscriptAccess('public')`) to control access at interpretation time, rather than `public` / `private`, that are rather for compile-time : http://haxe.org/manual/lf-metadata.html – clemos Oct 26 '15 at 08:33
2

I believe it's fcall you should override, as call is used for toplevel calls only :

https://github.com/HaxeFoundation/hscript/blob/master/hscript/Interp.hx#L328-L331

It should be easy to filter f argument like :

if ( FORBIDDEN_FIELDS.indexOf( f ) > -1 ) throw EInvalidAccess( f );

or

if ( f.indexOf('_') == 0 ) throw EInvalidAccess( f );
clemos
  • 426
  • 2
  • 5