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* method not exported:', func.parent.ident+'.'+func.ident continue builtins = True for formal in func.formals: try: cpp.nodetypestr(func.vars[formal], func, check_extmod=True) cpp.nodetypestr(func.retnode.thing, func, check_extmod=True, check_ret=True) except cpp.ExtmodError: builtins = False if builtins: supported.append(func) else: if isinstance(func.parent, class_): print '*WARNING* method not exported:', func.parent.ident+'.'+func.ident else: print '*WARNING* function not exported:', func.ident 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 = cpp.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))] cast = cpp.assign_needs_cast(defau, None, func.vars[formal], func) if cast: gv.append('(('+cpp.nodetypestr(func.vars[formal], func)+')') 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])) else: gv.visit(defau, func) if cast: gv.append(')') 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->msg)?(e->msg->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 cpp.singletype2(getgx().merged_inh[var], module): continue try: typehu = cpp.nodetypestr(var, var.parent, check_extmod=True) except cpp.ExtmodError: if isinstance(var.parent, class_): print '*WARNING* variable not exported:', var.parent.ident+'.'+var.name else: print '*WARNING* variable not exported:', var.name continue supported.append(var) 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 = cpp.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 annotate(): if not getgx().annotation: return re_comment = re.compile(r'#[^\"\']*$') def paste(expr, text): if not expr.lineno: return if (expr,0,0) in getgx().cnode and inode(expr).mv != mv: return # XXX line = source[expr.lineno-1][:-1] match = re_comment.search(line) if match: line = line[:match.start()] if text: text = '# '+text line = string.rstrip(line) if text and len(line) < 40: line += (40-len(line))*' ' source[expr.lineno-1] = line if text: source[expr.lineno-1] += ' ' + text source[expr.lineno-1] += '\n' for module in getgx().modules.values(): if module.builtin: continue mv = module.mv setmv(mv) # merge type information for nodes in module XXX inheritance across modules? merge = merged([n for n in getgx().types if n.mv == mv], inheritance=True) source = open(module.filename).readlines() # --- constants/names/attributes for expr in merge: if isinstance(expr, (Const, Name)): paste(expr, nodetypestr(expr, inode(expr).parent, False)) for expr in merge: if isinstance(expr, Getattr): paste(expr, nodetypestr(expr, inode(expr).parent, False)) for expr in merge: if isinstance(expr, (Tuple,List,Dict)): paste(expr, nodetypestr(expr, inode(expr).parent, False)) # --- instance variables funcs = getmv().funcs.values() for cl in getmv().classes.values(): labels = [var.name+': '+nodetypestr(var, cl, False) for var in cl.vars.values() if var in merge and merge[var] and not var.name.startswith('__')] if labels: paste(cl.node, ', '.join(labels)) funcs += cl.funcs.values() # --- function variables for func in funcs: if not func.node or func.node in getgx().inherited: continue vars = [func.vars[f] for f in func.formals] labels = [var.name+': '+nodetypestr(var, func, False) for var in vars if not var.name.startswith('__')] paste(func.node, ', '.join(labels)) # --- callfuncs for callfunc, _ in getmv().callfuncs: if isinstance(callfunc.node, Getattr): if not isinstance(callfunc.node, (fakeGetattr, fakeGetattr2, fakeGetattr3)): paste(callfunc.node.expr, nodetypestr(callfunc, inode(callfunc).parent, False)) else: paste(callfunc.node, nodetypestr(callfunc, inode(callfunc).parent, False)) # --- higher-level crap (listcomps, returns, assignments, prints) for expr in merge: if isinstance(expr, ListComp): paste(expr, nodetypestr(expr, inode(expr).parent, False)) elif isinstance(expr, Return): paste(expr, nodetypestr(expr.value, inode(expr).parent, False)) elif isinstance(expr, (AssTuple, AssList)): paste(expr, nodetypestr(expr, inode(expr).parent, False)) elif isinstance(expr, (Print,Printnl)): paste(expr, ', '.join([nodetypestr(child, inode(child).parent, False) for child in expr.nodes])) # --- assignments for expr in merge: if isinstance(expr, Assign): pairs = assign_rec(expr.nodes[0], expr.expr) paste(expr, ', '.join([nodetypestr(r, inode(r).parent, False) for (l,r) in pairs])) elif isinstance(expr, AugAssign): paste(expr, nodetypestr(expr.expr, inode(expr).parent, False)) # --- output annotated file (skip if no write permission) try: out = open(os.path.join(getgx().output_dir, module.filename[:-3]+'.ss.py'),'w') out.write(''.join(source)) out.close() except IOError: pass
def annotate(): if not getgx().annotation: return re_comment = re.compile(r'#[^\"\']*$') def paste(expr, text): if not expr.lineno: return if (expr, 0, 0) in getgx().cnode and inode(expr).mv != mv: return # XXX line = source[expr.lineno - 1] match = re_comment.search(line) if match: line = line[:match.start()] if text: text = '# ' + text line = line.rstrip() if text and len(line) < 40: line += (40 - len(line)) * ' ' source[expr.lineno - 1] = line if text: source[expr.lineno - 1] += ' ' + text source[expr.lineno - 1] += '\n' for module in getgx().modules.values(): if module.builtin: continue mv = module.mv setmv(mv) # merge type information for nodes in module XXX inheritance across modules? merge = merged([n for n in getgx().types if n.mv == mv], inheritance=True) source = open(module.filename).readlines() # --- constants/names/attributes for expr in merge: if isinstance(expr, (Const, Name)): paste(expr, nodetypestr(expr, inode(expr).parent, False)) for expr in merge: if isinstance(expr, Getattr): paste(expr, nodetypestr(expr, inode(expr).parent, False)) for expr in merge: if isinstance(expr, (Tuple, List, Dict)): paste(expr, nodetypestr(expr, inode(expr).parent, False)) # --- instance variables funcs = getmv().funcs.values() for cl in getmv().classes.values(): labels = [ var.name + ': ' + nodetypestr(var, cl, False) for var in cl.vars.values() if var in merge and merge[var] and not var.name.startswith('__') ] if labels: paste(cl.node, ', '.join(labels)) funcs += cl.funcs.values() # --- function variables for func in funcs: if not func.node or func.node in getgx().inherited: continue vars = [func.vars[f] for f in func.formals] labels = [ var.name + ': ' + nodetypestr(var, func, False) for var in vars if not var.name.startswith('__') ] paste(func.node, ', '.join(labels)) # --- callfuncs for callfunc, _ in getmv().callfuncs: if isinstance(callfunc.node, Getattr): if not isinstance(callfunc.node, (fakeGetattr, fakeGetattr2, fakeGetattr3)): paste(callfunc.node.expr, nodetypestr(callfunc, inode(callfunc).parent, False)) else: paste(callfunc.node, nodetypestr(callfunc, inode(callfunc).parent, False)) # --- higher-level crap (listcomps, returns, assignments, prints) for expr in merge: if isinstance(expr, ListComp): paste(expr, nodetypestr(expr, inode(expr).parent, False)) elif isinstance(expr, Return): paste(expr, nodetypestr(expr.value, inode(expr).parent, False)) elif isinstance(expr, (AssTuple, AssList)): paste(expr, nodetypestr(expr, inode(expr).parent, False)) elif isinstance(expr, (Print, Printnl)): paste( expr, ', '.join([ nodetypestr(child, inode(child).parent, False) for child in expr.nodes ])) # --- assignments for expr in merge: if isinstance(expr, Assign): pairs = assign_rec(expr.nodes[0], expr.expr) paste( expr, ', '.join([ nodetypestr(r, inode(r).parent, False) for (l, r) in pairs ])) elif isinstance(expr, AugAssign): paste(expr, nodetypestr(expr.expr, inode(expr).parent, False)) # --- output annotated file (skip if no write permission) try: out = open(module.filename[:-3] + '.ss.py', 'w') out.write(''.join(source)) out.close() except IOError: pass
def analyze(source, testing=False): try: gc.set_threshold(23456, 10, 10) except AttributeError: # not all Python implementations support this pass 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)]) # --- number classes (-> constant-time subclass check) cpp.number_classes() # --- 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() print '[generating c++ code..]' # XXX 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), fatal = False) getgx().merged_all = merged(getgx().types) #, inheritance=True) getgx().merge_dcpa = merged(getgx().types, dcpa=True) mv = getgx().main_module.mv setmv(mv) propagate() # XXX remove getgx().merged_all = merged(getgx().types) #, inheritance=True) getgx().merged_inh = merged(getgx().types, inheritance=True) # --- detect inheritance stuff cpp.upgrade_variables() getgx().merged_all = merged(getgx().types) getgx().merged_inh = merged(getgx().types, inheritance=True) cpp.analyze_virtuals() getgx().merge_dcpa = merged(getgx().types, dcpa=True) getgx().merged_all = merged(getgx().types) #, inheritance=True) # XXX # --- determine which classes need an __init__ method for node, types in getgx().merged_all.items(): if isinstance(node, CallFunc): objexpr, ident, _ , method_call, _, _, _ = analyze_callfunc(node) if method_call and ident == '__init__': for t in getgx().merged_all[objexpr]: t[0].has_init = True # --- determine which classes need copy, deepcopy methods if 'copy' in getgx().modules: func = getgx().modules['copy'].mv.funcs['copy'] var = func.vars[func.formals[0]] for cl in set([t[0] for t in getgx().merged_inh[var]]): cl.has_copy = True # XXX transitive, modeling func = getgx().modules['copy'].mv.funcs['deepcopy'] var = func.vars[func.formals[0]] for cl in set([t[0] for t in getgx().merged_inh[var]]): cl.has_deepcopy = True # XXX transitive, modeling # --- add inheritance relationships for non-original Nodes (and tempvars?); XXX register more, right solution? for func in getgx().allfuncs: #if not func.mv.module.builtin and func.ident == '__init__': 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) # XXX why X times # error for dynamic expression for node in getgx().merged_all: if isinstance(node, Node) and not isinstance(node, AssAttr) and not inode(node).mv.module.builtin: cpp.nodetypestr(node, inode(node).parent) return getgx()
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(), cpp.nokeywords(cl.ident)) 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(), cpp.nokeywords(cl.ident)) 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 = cpp.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->msg)?(e->msg->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