TranscryptOrg / Transcrypt

Python 3.9 to JavaScript compiler - Lean, fast, open!
https://www.transcrypt.org
Apache License 2.0
2.86k stars 215 forks source link

Class variables generation problem #630

Open faerot opened 5 years ago

faerot commented 5 years ago

when declaring class variable:

class A(B):
    X = 1
    Y = X + 1

transcrypt compiles this into object of properties:

var A = __class__ ('A', [B], {
    __module__: __name__,
    X: 1,
    Y: X + 1,

Which naturally leads to error. You need to adjust class generation mechanics.

AlexECX commented 5 years ago

I guess replacing the object literal with an anonymous function would do the job.

Maybe have transcrypt compile to this:

var A = __class__ ('A', [B], (function(){
        let o = {};
    o.__module__ = __name__;
    o.X = 1;
        o.Y = o.X + 2;
        return o;
    })());

but that would also make getters more verbose. Example with method that returns self.X: (EDIT: This is not working, the new property is not picked up by for-in loops)

var A = __class__ ('A', [B], (function(){
        let o = {};
    o.__module__ = __name__;
    Object.defineProperty(o, 'someFunctionName', { get: function() { return __get__ (this, function (self) {
        return self.X;
    });}});
        return o;
    })());
AlexECX commented 5 years ago

This seems to be working pretty well. Haven't looked into the python transpile code yet, might give it a try sometime.

Example.py

class B:
    pass

class A(B):
    X = 1
    Y = X + 2
    def test(self):
        return self.X

transcrypt.org js


// ^older code
export var __def_method__ = function (obj, method, recurence_name) {
    var method_name = (recurence_name ? recurence_name : method.name);
    Object.defineProperty (obj, method_name, {'get': (function __lambda__ () {
        return __get__ (this, method);
    }), 'set': (function __lambda__ (func) {
        return __def_method__ (this, func, method_name);
    }), 'configurable': true, 'enumerable': true});
};

Example.js

//...
export var B =  __class__ ('B', [object], function(){
    let cls = {}; 
    cls.__module__ = __name__,
    return cls;
}());
export var A = __class__ ('A', [B], function(){
    let cls = {}; 
    cls.__module__ = __name__;
    cls.X = 1;
    cls.Y = cls.X + 2;
    __def_method__(cls, function test(self){
        return self.X;
    });
    return cls;
}());

Performance for class declaration, object instantiation and method calls are the same, the memory profile seems to be the same. It also allows method redefinition of instances.

some more in-depth testing

AlexECX commented 5 years ago

A simpler, closer to current transcrypt and probably easier to implement version:

transcrypt.org js

export var __def__ = function (obj, method, recurence_name) {
    var method_name = (recurence_name ? recurence_name : method.name);
    Object.defineProperty (obj, method_name, {
        'get': method, 
        'set': (function __lambda__ (func) { 
            return __def__ (this, func, method_name); 
        }), 'configurable': true, 'enumerable': true
    });
};

Examplse.js

//...
export var B =  __class__ ('B', [object], function(){
    let cls = {}; 
    cls.__module__ = __name__,
    return cls;
}());
export var A = __class__ ('A', [B], function(){
    let cls = {}; 
    cls.__module__ = __name__;
    cls.X = 1;
    cls.Y = cls.X + 2;
    __def__(cls, function test() { return __get__ (this, function (self) {
    return self.X;
    });});
    return cls;
}());