flexxui / pscript

Python to JavaScript compiler
http://pscript.readthedocs.io
BSD 2-Clause "Simplified" License
256 stars 25 forks source link

`flx_args` is overwritten while parsing #23

Open Winand opened 6 years ago

Winand commented 6 years ago

pscript generates broken code for functions like def partial2(func, *args, **keywords): When trying to parse *args it overwrites arguments[0]:

    func = arguments[0].flx_args[0]; /* overwrites here */
    args = arguments[0].flx_args.slice(1); /* arguments[0].flx_args == undefined */
almarklein commented 5 years ago

I am sorry, but I am having trouble understanding the problem. I don't see how fle_args[0] is overwritten; it is only indexed, right?

I tried to work with the example in your PR:

from pscript import py2js, evaljs

def partial2(a1, a2, *args, **kwargs):
    print(a1, a2)
    print(args)
    print(kwargs)
    print('--')

def main():
    partial2(3, 4, 5, 6, foo=3, bar=4)

js = py2js(partial2)
# print(js)
print(evaljs(js + py2js(main) + 'main(); partial2(3, 4, 5)'))

Which produces this, which seems fine:

3 4
[ 5, 6 ]
{ foo: 3, bar: 4 }
--
3 4
[ 5 ]
{}
--
null

Could you please try to adjust this code for it to cause the error on your end? That would hopefully make things more explicit :)

Winand commented 5 years ago

Ok, I created a file test.py:

def partial2(a1, a2, *args, **kwargs):
    print(a1, a2)
    print(args)
    print(kwargs)
    print('--')

def main():
    partial2(3, 4, 5, 6, foo=3, bar=4)

main()
partial2(3, 4, 5)

Then I compiled it to test.js with script2js:

/* Do not edit, autogenerated by pscript */

var _pyfunc_op_error = function (etype, msg) { // nargs: 2
    var e = new Error(etype + ': ' + msg);
    e.name = etype
    return e;
};
var _pyfunc_op_parse_kwargs = function (arg_names, arg_values, kwargs, strict) { // nargs: 3
    for (var i=0; i<arg_values.length; i++) {
        var name = arg_names[i];
        if (kwargs[name] !== undefined) {
            arg_values[i] = kwargs[name];
            delete kwargs[name];
        }
    }
    if (strict && Object.keys(kwargs).length > 0) {
        throw _pyfunc_op_error('TypeError',
            'Function ' + strict + ' does not accept **kwargs.');
    }
    return kwargs;
};
var main, partial2;
partial2 = function flx_partial2 (a1, a2) {
    var args, kwargs, stub1_args;
    kwargs = {};
    if (arguments.length == 1 && typeof arguments[0] == 'object' && Object.keys(arguments[0]).toString() == 'flx_args,flx_kwargs') {
        kwargs = _pyfunc_op_parse_kwargs([], [], arguments[0].flx_kwargs);
        stub1_args = arguments[0].flx_args;
        a1 = stub1_args[0];
        a2 = stub1_args[1];
        args = arguments[0].flx_args.slice(2);
    } else {args = Array.prototype.slice.call(arguments, 2);}
    console.log(a1 + " " + a2);
    console.log(args);
    console.log(kwargs);
    console.log("--");
    return null;
};

main = function flx_main () {
    partial2({flx_args: [3, 4, 5, 6], flx_kwargs: {foo: 3, bar: 4}});
    return null;
};

main();
partial2(3, 4, 5);

When running this script in Chrome 70 I get the following error: image

How is it possible? I couldn't believe it but when we assign a1 = stub1_args[0]; we assign 3 to arguments[0] :disappointed: image

almarklein commented 5 years ago

Wow! This is madness ... but I can confirm it on both Chrome and Firefox (Windows 10). It looks like a1 is somehow bound to the same spot in memory as argumens[0], but that's not how variables are supposed to work, right? Is this some obscure result of the browser's JS optimization?

JS is full of surprises :(

almarklein commented 5 years ago

https://spin.atomicobject.com/2011/04/10/javascript-don-t-reassign-your-function-arguments/