haxehxcpp

Haxe / hscript - Prevent exposing certain methods to scripts


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

Solution

  • 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.