def supported_funcs(gv, funcs): supported = [] for func in funcs: if func.isGenerator or not cpp.hmcpa(func): continue if func.ident in ['__setattr__', '__getattr__', '__iadd__', '__isub__', '__imul__']: # XXX continue if isinstance(func.parent, class_): if func.invisible or func.inherited or not gv.inhcpa(func): continue if isinstance(func.parent, class_) and func.ident in func.parent.staticmethods: print "*WARNING* '%s' method not exported (staticmethod)" % (func.parent.ident+'.'+func.ident) continue builtins = True for formal in func.formals: try: typestr.nodetypestr(func.vars[formal], func, check_extmod=True) except typestr.ExtmodError: builtins = False reason = "cannot convert argument '%s'" % formal try: typestr.nodetypestr(func.retnode.thing, func, check_extmod=True, check_ret=True) except typestr.ExtmodError: builtins = False reason = 'cannot convert return value' if builtins: supported.append(func) else: if isinstance(func.parent, class_): print "*WARNING* '%s' method not exported (%s)" % (func.parent.ident+'.'+func.ident, reason) else: print "*WARNING* '%s' function not exported (%s)" % (func.ident, reason) return supported
def do_reduce_setstate(gv, cl, vars): if defclass('Exception') in cl.ancestors(): # XXX return print >> gv.out, 'PyObject *%s__reduce__(PyObject *self, PyObject *args, PyObject *kwargs) {' % clname( cl) print >> gv.out, ' PyObject *t = PyTuple_New(3);' print >> gv.out, ' PyTuple_SetItem(t, 0, PyObject_GetAttrString(__ss_mod_%s, "__newobj__"));' % '_'.join( gv.module.mod_path) print >> gv.out, ' PyObject *a = PyTuple_New(1);' print >> gv.out, ' PyTuple_SetItem(a, 0, (PyObject *)&%sObjectType);' % clname( cl) print >> gv.out, ' PyTuple_SetItem(t, 1, a);' print >> gv.out, ' PyObject *b = PyTuple_New(2);' for i, var in enumerate(vars): print >> gv.out, ' PyTuple_SetItem(b, %d, __to_py(((%sObject *)self)->__ss_object->%s));' % ( i, clname(cl), var.cpp_name()) print >> gv.out, ' PyTuple_SetItem(t, 2, b);' print >> gv.out, ' return t;' print >> gv.out, '}\n' print >> gv.out, 'PyObject *%s__setstate__(PyObject *self, PyObject *args, PyObject *kwargs) {' % clname( cl) print >> gv.out, ' int l = PyTuple_Size(args);' print >> gv.out, ' PyObject *state = PyTuple_GetItem(args, 0);' for i, var in enumerate(vars): vartype = typestr.nodetypestr(var, var.parent) print >> gv.out, ' ((%sObject *)self)->__ss_object->%s = __to_ss<%s>(PyTuple_GetItem(state, %d));' % ( clname(cl), var.cpp_name(), vartype, i) print >> gv.out, ' return Py_None;' print >> gv.out, '}\n'
def supported_funcs(gv, funcs): supported = [] for func in funcs: if func.isGenerator or not cpp.hmcpa(func): continue if func.ident in [ '__setattr__', '__getattr__', '__iadd__', '__isub__', '__imul__' ]: # XXX continue if isinstance(func.parent, class_): if func.invisible or func.inherited or not gv.inhcpa(func): continue if isinstance(func.parent, class_) and func.ident in func.parent.staticmethods: print "*WARNING* '%s' method not exported (staticmethod)" % ( func.parent.ident + '.' + func.ident) continue builtins = True for formal in func.formals: try: typestr.nodetypestr(func.vars[formal], func, check_extmod=True) except typestr.ExtmodError: builtins = False reason = "cannot convert argument '%s'" % formal try: typestr.nodetypestr(func.retnode.thing, func, check_extmod=True, check_ret=True) except typestr.ExtmodError: builtins = False reason = 'cannot convert return value' if builtins: supported.append(func) else: if isinstance(func.parent, class_): print "*WARNING* '%s' method not exported (%s)" % ( func.parent.ident + '.' + func.ident, reason) else: print "*WARNING* '%s' function not exported (%s)" % ( func.ident, reason) return supported
def do_extmod_method(gv, func): is_method = isinstance(func.parent, class_) if is_method: formals = func.formals[1:] else: formals = func.formals if isinstance(func.parent, class_): id = clname(func.parent)+'_'+func.ident else: id = 'Global_'+'_'.join(gv.module.mod_path)+'_'+func.ident print >>gv.out, 'PyObject *%s(PyObject *self, PyObject *args, PyObject *kwargs) {' % id print >>gv.out, ' try {' for i, formal in enumerate(formals): gv.start('') typ = typestr.nodetypestr(func.vars[formal], func) if func.ident in OVERLOAD: print >>gv.out, ' %(type)sarg_%(num)d = __to_ss<%(type)s>(args);' % {'type' : typ, 'num' : i} continue gv.append(' %(type)sarg_%(num)d = __ss_arg<%(type)s>("%(name)s", %(num)d, ' % {'type' : typ, 'num' : i, 'name': formal}) if i >= len(formals)-len(func.defaults): gv.append('1, ') defau = func.defaults[i-(len(formals)-len(func.defaults))] if defau in func.mv.defaults: if gv.mergeinh[defau] == set([(defclass('none'),0)]): gv.append('0') else: gv.append('%s::default_%d' % ('__'+func.mv.module.ident+'__', func.mv.defaults[defau][0])) else: gv.visit(defau, func) elif typ.strip() == '__ss_bool': gv.append('0, False') else: gv.append('0, 0') gv.append(', args, kwargs)') gv.eol() print >>gv.out # call if is_method: where = '((%sObject *)self)->__ss_object->' % clname(func.parent) else: where = '__'+gv.module.ident+'__::' print >>gv.out, ' return __to_py('+where+gv.cpp_name(func.ident)+'('+', '.join(['arg_%d' % i for i in range(len(formals))])+'));\n' # convert exceptions print >>gv.out, ' } catch (Exception *e) {' print >>gv.out, ' PyErr_SetString(__to_py(e), ((e->message)?(e->message->unit.c_str()):""));' print >>gv.out, ' return 0;' print >>gv.out, ' }' print >>gv.out, '}\n'
def supported_vars(vars): # XXX virtuals? supported = [] for var in vars: if not var in getgx().merged_inh or not getgx().merged_inh[var]: continue if var.name.startswith('__'): # XXX continue if var.invisible or singletype2(getgx().merged_inh[var], module): continue try: typehu = typestr.nodetypestr(var, var.parent, check_extmod=True) except typestr.ExtmodError: if isinstance(var.parent, class_): print "*WARNING* '%s' variable not exported (cannot convert)" % (var.parent.ident+'.'+var.name) else: print "*WARNING* '%s' variable not exported (cannot convert)" % var.name continue supported.append(var) return supported
def supported_vars(vars): # XXX virtuals? supported = [] for var in vars: if not var in getgx().merged_inh or not getgx().merged_inh[var]: continue if var.name.startswith('__'): # XXX continue if var.invisible or singletype2(getgx().merged_inh[var], module): continue try: typehu = typestr.nodetypestr(var, var.parent, check_extmod=True) except typestr.ExtmodError: if isinstance(var.parent, class_): print "*WARNING* '%s' variable not exported (cannot convert)" % ( var.parent.ident + '.' + var.name) else: print "*WARNING* '%s' variable not exported (cannot convert)" % var.name continue supported.append(var) return supported
def do_extmod_class(gv, cl): for n in cl.module.mod_path: print >> gv.out, 'namespace __%s__ { /* XXX */' % n print >> gv.out # determine methods, vars to expose funcs = supported_funcs(gv, cl.funcs.values()) vars = supported_vars(cl.vars.values()) # python object print >> gv.out, '/* class %s */\n' % cl.ident print >> gv.out, 'typedef struct {' print >> gv.out, ' PyObject_HEAD' print >> gv.out, ' %s::%s *__ss_object;' % (cl.module.full_path(), cl.cpp_name()) print >> gv.out, '} %sObject;\n' % clname(cl) print >> gv.out, 'static PyMemberDef %sMembers[] = {' % clname(cl) print >> gv.out, ' {NULL}\n};\n' # methods for func in funcs: do_extmod_method(gv, func) do_extmod_methoddef(gv, cl.ident, funcs, cl) # tp_init if hasmethod(cl, '__init__') and cl.funcs['__init__'] in funcs: print >> gv.out, 'int %s___tpinit__(PyObject *self, PyObject *args, PyObject *kwargs) {' % clname( cl) print >> gv.out, ' if(!%s___init__(self, args, kwargs))' % clname( cl) print >> gv.out, ' return -1;' print >> gv.out, ' return 0;' print >> gv.out, '}\n' # tp_new print >> gv.out, 'PyObject *%sNew(PyTypeObject *type, PyObject *args, PyObject *kwargs) {' % clname( cl) print >> gv.out, ' %sObject *self = (%sObject *)type->tp_alloc(type, 0);' % ( clname(cl), clname(cl)) print >> gv.out, ' self->__ss_object = new %s::%s();' % ( cl.module.full_path(), cl.cpp_name()) print >> gv.out, ' self->__ss_object->__class__ = %s::cl_%s;' % ( cl.module.full_path(), cl.ident) print >> gv.out, ' __ss_proxy->__setitem__(self->__ss_object, self);' print >> gv.out, ' return (PyObject *)self;' print >> gv.out, '}\n' # tp_dealloc print >> gv.out, 'void %sDealloc(%sObject *self) {' % (clname(cl), clname(cl)) print >> gv.out, ' self->ob_type->tp_free((PyObject *)self);' print >> gv.out, ' __ss_proxy->__delitem__(self->__ss_object);' print >> gv.out, '}\n' # getset for var in vars: print >> gv.out, 'PyObject *__ss_get_%s_%s(%sObject *self, void *closure) {' % ( clname(cl), var.name, clname(cl)) print >> gv.out, ' return __to_py(self->__ss_object->%s);' % var.cpp_name( ) print >> gv.out, '}\n' print >> gv.out, 'int __ss_set_%s_%s(%sObject *self, PyObject *value, void *closure) {' % ( clname(cl), var.name, clname(cl)) print >> gv.out, ' try {' typ = typestr.nodetypestr(var, var.parent) if typ == 'void *': # XXX investigate print >> gv.out, ' self->__ss_object->%s = NULL;' % var.cpp_name( ) else: print >> gv.out, ' self->__ss_object->%s = __to_ss<%s>(value);' % ( var.cpp_name(), typ) print >> gv.out, ' } catch (Exception *e) {' print >> gv.out, ' PyErr_SetString(__to_py(e), ((e->message)?(e->message->unit.c_str()):""));' print >> gv.out, ' return -1;' print >> gv.out, ' }' print >> gv.out, ' return 0;' print >> gv.out, '}\n' print >> gv.out, 'PyGetSetDef %sGetSet[] = {' % clname(cl) for var in vars: print >> gv.out, ' {(char *)"%s", (getter)__ss_get_%s_%s, (setter)__ss_set_%s_%s, (char *)"", NULL},' % ( var.name, clname(cl), var.name, clname(cl), var.name) print >> gv.out, ' {NULL}\n};\n' # python type print >> gv.out, 'PyTypeObject %sObjectType = {' % clname(cl) print >> gv.out, ' PyObject_HEAD_INIT(NULL)' print >> gv.out, ' 0, /* ob_size */' print >> gv.out, ' "%s.%s", /* tp_name */' % ( cl.module.ident, cl.ident) print >> gv.out, ' sizeof(%sObject), /* tp_basicsize */' % clname( cl) print >> gv.out, ' 0, /* tp_itemsize */' print >> gv.out, ' (destructor)%sDealloc, /* tp_dealloc */' % clname( cl) print >> gv.out, ' 0, /* tp_print */' print >> gv.out, ' 0, /* tp_getattr */' print >> gv.out, ' 0, /* tp_setattr */' print >> gv.out, ' 0, /* tp_compare */' if hasmethod(cl, '__repr__'): print >> gv.out, ' (PyObject *(*)(PyObject *))%s___repr__, /* tp_repr */' % clname( cl) else: print >> gv.out, ' 0, /* tp_repr */' print >> gv.out, ' &%s_as_number, /* tp_as_number */' % clname(cl) print >> gv.out, ' 0, /* tp_as_sequence */' print >> gv.out, ' 0, /* tp_as_mapping */' print >> gv.out, ' 0, /* tp_hash */' print >> gv.out, ' 0, /* tp_call */' if hasmethod(cl, '__str__'): print >> gv.out, ' (PyObject *(*)(PyObject *))%s___str__, /* tp_str */' % clname( cl) else: print >> gv.out, ' 0, /* tp_str */' print >> gv.out, ' 0, /* tp_getattro */' print >> gv.out, ' 0, /* tp_setattro */' print >> gv.out, ' 0, /* tp_as_buffer */' print >> gv.out, ' Py_TPFLAGS_DEFAULT, /* tp_flags */' print >> gv.out, ' 0, /* tp_doc */' print >> gv.out, ' 0, /* tp_traverse */' print >> gv.out, ' 0, /* tp_clear */' print >> gv.out, ' 0, /* tp_richcompare */' print >> gv.out, ' 0, /* tp_weaklistoffset */' print >> gv.out, ' 0, /* tp_iter */' print >> gv.out, ' 0, /* tp_iternext */' print >> gv.out, ' %sMethods, /* tp_methods */' % clname(cl) print >> gv.out, ' %sMembers, /* tp_members */' % clname(cl) print >> gv.out, ' %sGetSet, /* tp_getset */' % clname(cl) if cl.bases and not cl.bases[0].mv.module.builtin: print >> gv.out, ' &%sObjectType, /* tp_base */' % clname( cl.bases[0]) else: print >> gv.out, ' 0, /* tp_base */' print >> gv.out, ' 0, /* tp_dict */' print >> gv.out, ' 0, /* tp_descr_get */' print >> gv.out, ' 0, /* tp_descr_set */' print >> gv.out, ' 0, /* tp_dictoffset */' if hasmethod(cl, '__init__') and cl.funcs['__init__'] in funcs: print >> gv.out, ' %s___tpinit__, /* tp_init */' % clname( cl) else: print >> gv.out, ' 0, /* tp_init */' print >> gv.out, ' 0, /* tp_alloc */' print >> gv.out, ' %sNew, /* tp_new */' % clname(cl) print >> gv.out, '};\n' do_reduce_setstate(gv, cl, vars) for n in cl.module.mod_path: print >> gv.out, '} // namespace __%s__' % n print >> gv.out
def do_extmod_method(gv, func): is_method = isinstance(func.parent, class_) if is_method: formals = func.formals[1:] else: formals = func.formals if isinstance(func.parent, class_): id = clname(func.parent) + '_' + func.ident else: id = 'Global_' + '_'.join(gv.module.mod_path) + '_' + func.ident print >> gv.out, 'PyObject *%s(PyObject *self, PyObject *args, PyObject *kwargs) {' % id print >> gv.out, ' try {' for i, formal in enumerate(formals): gv.start('') typ = typestr.nodetypestr(func.vars[formal], func) if func.ident in OVERLOAD: print >> gv.out, ' %(type)sarg_%(num)d = __to_ss<%(type)s>(args);' % { 'type': typ, 'num': i } continue gv.append( ' %(type)sarg_%(num)d = __ss_arg<%(type)s>("%(name)s", %(num)d, ' % { 'type': typ, 'num': i, 'name': formal }) if i >= len(formals) - len(func.defaults): gv.append('1, ') defau = func.defaults[i - (len(formals) - len(func.defaults))] if defau in func.mv.defaults: if gv.mergeinh[defau] == set([(defclass('none'), 0)]): gv.append('0') else: gv.append('%s::default_%d' % ('__' + func.mv.module.ident + '__', func.mv.defaults[defau][0])) else: gv.visit(defau, func) elif typ.strip() == '__ss_bool': gv.append('0, False') else: gv.append('0, 0') gv.append(', args, kwargs)') gv.eol() print >> gv.out # call if is_method: where = '((%sObject *)self)->__ss_object->' % clname(func.parent) else: where = '__' + gv.module.ident + '__::' print >> gv.out, ' return __to_py(' + where + gv.cpp_name( func.ident) + '(' + ', '.join( ['arg_%d' % i for i in range(len(formals))]) + '));\n' # convert exceptions print >> gv.out, ' } catch (Exception *e) {' print >> gv.out, ' PyErr_SetString(__to_py(e), ((e->message)?(e->message->unit.c_str()):""));' print >> gv.out, ' return 0;' print >> gv.out, ' }' print >> gv.out, '}\n'
def analyze(source, testing=False): if testing: setgx(newgx()) ast = parse(source + '\n') else: ast = graph.parsefile(source) mv = None setmv(mv) # --- build dataflow graph from source code getgx().main_module = graph.parse_module(getgx().main_mod, ast) getgx().main_module.filename = getgx().main_mod + '.py' getgx().modules[getgx().main_mod] = getgx().main_module mv = getgx().main_module.mv setmv(mv) # --- seed class_.__name__ attributes.. for cl in getgx().allclasses: if cl.ident == 'class_': var = defaultvar('__name__', cl) getgx().types[inode(var)] = set([(defclass('str_'), 0)]) # --- non-ifa: copy classes for each allocation site for cl in getgx().allclasses: if cl.ident in ['int_', 'float_', 'none', 'class_', 'str_', 'bool_']: continue if cl.ident == 'list': cl.dcpa = len(getgx().list_types) + 2 elif cl.ident != '__iter': # XXX huh cl.dcpa = 2 for dcpa in range(1, cl.dcpa): class_copy(cl, dcpa) var = defaultvar('unit', defclass('str_')) getgx().types[inode(var)] = set([(defclass('str_'), 0)]) # --- cartesian product algorithm & iterative flow analysis iterative_dataflow_analysis() if not getgx().silent: print '[generating c++ code..]' for cl in getgx().allclasses: for name in cl.vars: if name in cl.parent.vars and not name.startswith('__'): error( "instance variable '%s' of class '%s' shadows class variable" % (name, cl.ident)) mv = getgx().main_module.mv setmv(mv) getgx().merged_inh = merged(getgx().types, inheritance=True) virtual.analyze_virtuals() copy_.determine_classes() # --- add inheritance relationships for non-original Nodes (and tempvars?); XXX register more, right solution? for func in getgx().allfuncs: if func in getgx().inheritance_relations: for inhfunc in getgx().inheritance_relations[func]: for a, b in zip(func.registered, inhfunc.registered): graph.inherit_rec(a, b, func.mv) for a, b in zip( func.registered_tempvars, inhfunc.registered_tempvars): # XXX more general getgx().inheritance_tempvars.setdefault(a, []).append(b) getgx().merged_inh = merged(getgx().types, inheritance=True) # error for dynamic expression without explicit type declaration for node in getgx().merged_inh: if isinstance(node, Node) and not isinstance( node, AssAttr) and not inode(node).mv.module.builtin: typestr.nodetypestr(node, inode(node).parent) return getgx()
def analyze(source, testing=False): if testing: setgx(newgx()) ast = parse(source+'\n') else: ast = graph.parsefile(source) mv = None setmv(mv) # --- build dataflow graph from source code getgx().main_module = graph.parse_module(getgx().main_mod, ast) getgx().main_module.filename = getgx().main_mod+'.py' getgx().modules[getgx().main_mod] = getgx().main_module mv = getgx().main_module.mv setmv(mv) # --- seed class_.__name__ attributes.. for cl in getgx().allclasses: if cl.ident == 'class_': var = defaultvar('__name__', cl) getgx().types[inode(var)] = set([(defclass('str_'), 0)]) # --- non-ifa: copy classes for each allocation site for cl in getgx().allclasses: if cl.ident in ['int_','float_','none', 'class_','str_', 'bool_']: continue if cl.ident == 'list': cl.dcpa = len(getgx().list_types)+2 elif cl.ident != '__iter': # XXX huh cl.dcpa = 2 for dcpa in range(1, cl.dcpa): class_copy(cl, dcpa) var = defaultvar('unit', defclass('str_')) getgx().types[inode(var)] = set([(defclass('str_'), 0)]) # --- cartesian product algorithm & iterative flow analysis iterative_dataflow_analysis() if not getgx().silent: print '[generating c++ code..]' for cl in getgx().allclasses: for name in cl.vars: if name in cl.parent.vars and not name.startswith('__'): error("instance variable '%s' of class '%s' shadows class variable" % (name, cl.ident)) mv = getgx().main_module.mv setmv(mv) getgx().merged_inh = merged(getgx().types, inheritance=True) virtual.analyze_virtuals() copy_.determine_classes() # --- add inheritance relationships for non-original Nodes (and tempvars?); XXX register more, right solution? for func in getgx().allfuncs: if func in getgx().inheritance_relations: for inhfunc in getgx().inheritance_relations[func]: for a, b in zip(func.registered, inhfunc.registered): graph.inherit_rec(a, b, func.mv) for a, b in zip(func.registered_tempvars, inhfunc.registered_tempvars): # XXX more general getgx().inheritance_tempvars.setdefault(a, []).append(b) getgx().merged_inh = merged(getgx().types, inheritance=True) # error for dynamic expression without explicit type declaration for node in getgx().merged_inh: if isinstance(node, Node) and not isinstance(node, AssAttr) and not inode(node).mv.module.builtin: typestr.nodetypestr(node, inode(node).parent) return getgx()