ellie-idb / blocklandjs

BlocklandJS - a implementation of V8 in Blockland. Bad code included.
2 stars 0 forks source link

Add getter/setter to object function #12

Closed McTwist closed 6 years ago

McTwist commented 6 years ago

According to following sites ECMAScript added getter/setter support for 5.1 and duktape supports ECMAScript 5.1. Therefore, make sure to implement this into ts_linkClass to make setting and getting properties a lot easier. Of course, issues may raise if the function and property have the same name, but I suggest that we ignore such thing as it is the issue of the user, not us.

ellie-idb commented 6 years ago

yeah they also have proxy support like this

// Underlying plain object.
var target = { foo: 'bar' };

// Handler table, provides traps for interaction (can be modified on-the-fly).
var handler = {
    has: function (targ, key) {
        print('has called for key=' + key);
        return key in targ;  // return unmodified existence status
    },

    get: function (targ, key, recv) {
        print('get called for key=' + key);
        return targ[key];  // return unmodified value
    },

    set: function (targ, key, val, recv) {
        print('set called for key=' + key + ', val=' + val);
        targ[key] = val;  // must perform write to target manually if 'set' defined
        return true;      // true: indicate that property write was allowed
    },

    deleteProperty: function (targ, key) {
        print('deleteProperty called for key=' + key);
        delete targ[key];  // must perform delete to target manually if 'deleteProperty' defined
        return true;       // true: indicate that property delete was allowed
    }
};

// Create proxy object.
var proxy = new Proxy(target, handler);

// Proxy object is then accessed normally.
print('foo' in proxy);
proxy.foo = 321;
print(proxy.foo);
McTwist commented 6 years ago

I found it problematic to create this as there is no general way to allow creation of an object through a function and still put a proxy on it to handle all getter and setters. If it would be possible to handle them like PHP's __get and __set, then it would solve everything.

So if we really want such a feature, we need to either add a Proxy on it within a function that is called after the object is created, or we remove the previous functor and instead make it just a function that returns an object.

ellie-idb commented 6 years ago

https://tc39.github.io/ecma262/#sec-additional-properties-of-the-object.prototype-object We forgot about this!!

McTwist commented 6 years ago

It should work. If I ever come around fixing this, it is really easy to do.

ellie-idb commented 6 years ago

That, or, I wrote some prototype code that works for now using proxies.

var blocklandJS_objhandler = {
    get: function(tar, name) {
        if(name == "_id") {
            return ts_call(tar.type, "getID", tar.obj);
        }
        return ts_getObjectField(tar.obj, name);
    },
    set: function(tar, name, val) {
        ts_setObjectField(tar.obj, name, val);
    }
};
// Link a namespace to a function that will create an object
function ts_linkClass(type) {
    // Create a new method
    function _createMethod(that, obj, name, func) {
        that[name] = function() {
            var args = [obj];
            args.push.apply(args, arguments);
            return func.apply(null, args);
        };
    }
    var kk = function(args) {
        var _type = type;
        var _obj = ts_newObj(_type);
        // Apply all methods
        var _functions = ts_getMethods(_type);
        for (var i = 0; i < _functions.length; ++i) {
            var _func_name = _functions[i];
            var _func = ts_func(_type, _func_name);
            // Create method
            _createMethod(this, _obj, _func_name, _func);
        }
        // Setter
        this.obj = _obj;
        this.type = _type;
        this.set = function(name, value) {
            ts_setObjectField(_obj, name, value);
        };
        //this.prototype.__defineGetter__(function(name) {
        //      return ts_getObjectField(_obj, name);
        //  });
        // Getter
        this.get = function(name) {
            return ts_getObjectField(_obj, name);
        };

        // Add arguments
        for (var i in args) {
            // Workaround
            if (i === 'datablock' && this.setDatablock)
                this.setDatablock(args[i]);
            this.set(i, args[i]);
        }
        // Make it visible from TS
        ts_registerObject(_obj);
    };
    return kk;
}

function ts_nbj(type){
    var q = ts_linkClass(type);
    var kk = new q();
    var proxy = new Proxy(kk, blocklandJS_objhandler);
    return proxy;
}

function ts_globalVariables() {
    var h = {
        get: function(tar, name) {
            return ts_getVariable(name);
        },
        set: function(tar, name, val) {
            ts_setVariable(name, val);
        }
    }
    return new Proxy({}, h);
}

Thoughts?

ellie-idb commented 6 years ago

Actually, I finished a rework of the entire thing.

var blocklandJS_objhandler = {
    construct: function(target, argumentsList, newT) {
        newT.apply(target, argumentsList);
        return new Proxy(target, blocklandJS_funcHandler);
    }
};

var blocklandJS_funcHandler = {
    get: function(tar, name) {
        if(ts_getMethods(tar.type).some(function(e, i) {
            if(name.toLowerCase() === e.toLowerCase()){
                return true;
            }
        }))
        {
            return function() {
                var args = [tar.type, name, tar.obj];
                args.push.apply(args, arguments);
                return ts_call.apply(null, args);
            }
        } //Handle function calls.
        return ts_getObjectField(tar.obj, name);
    },
    set: function(tar, name, val) {
        ts_setObjectField(tar.obj, name, val);
    }
};

// Link a namespace to a function that will create an object
function ts_linkClass(type) {
    var kk = function(args) {
        var _type = type;
        var _obj = ts_newObj(_type);
        this.obj = _obj;
        this.type = _type;

        // Add arguments
        for (var i in args) {
            // Workaround
            if (i === 'datablock' && ts_func(_type, "setDatablock"))
                ts_func(_type, "setDatablock")(args[i]);
            ts_setObjectField(_obj, i, args[i]);
        }
        print("Registered " + _type);
        // Make it visible from TS
        ts_registerObject(_obj);
    };
    return new Proxy(kk, blocklandJS_objhandler);
}
McTwist commented 6 years ago

Not entirely working correctly as the duktape is still v1.8.0.

ellie-idb commented 6 years ago

With the SpiderMonkey version, this has been fixed. The Duktape version should have a compatible init.js script.