def trans_ty(tname): _tname = util.sanitize_ty(tname.strip()) array_regex = r"([^ \[\]]+)((\[\])+)" m = re.match(array_regex, _tname) global _ty r_ty = _tname # to avoid primitive types that Sketch doesn't support if _tname == C.J.z: r_ty = C.SK.z elif _tname in [C.J.b, C.J.s, C.J.j]: r_ty = C.J.i # unboxing primitive Classes, e.g., Character -> char elif _tname in C.autoboxing: r_ty = util.unboxing(_tname) # TODO: parameterize len? elif _tname in [C.J.c+"[]"]: r_ty = u"{}[51]".format(C.J.c) elif _tname in [C.J.B, C.J.S, C.J.J, C.J.I]: r_ty = C.J.i # array bounds elif m: r_ty = trans_ty(m.group(1)) + \ "[{}]".format(len(methods())) * len(re.findall(r"\[\]", m.group(2))) # use memoized type conversion elif _tname in _ty: r_ty = _ty[_tname] # convert Java collections into an appropriate struct name # Map<K,V> / List<T> / ... -> Map_K_V / List_T / ... elif util.is_collection(_tname): r_ty = '_'.join(util.of_collection(_tname)) logging.debug("{} => {}".format(_tname, r_ty)) _ty[_tname] = r_ty return r_ty
def trans_mname(cname, mname, arg_typs=[]): global _mtds r_mtd = mname mid = u'_'.join([cname, mname] + arg_typs) # use memoized method name conversion if mid in _mtds: return _mtds[mid] # methods of Java collections elif util.is_collection(cname): _arg_typs = map(trans_ty, arg_typs) r_mtd = u'_'.join([mname, trans_ty(cname)] + _arg_typs) else: if is_replaced(cname): tr_name = trans_ty(cname) cls = class_lookup(tr_name) if cls and cls.is_aux: cname = tr_name mtds = find_mtds_by_sig(cname, mname, arg_typs) if mtds and 1 == len(mtds): r_mtd = unicode(repr(mtds[0])) else: # ambiguous or not found r_mtd = '_'.join([mname, util.sanitize_ty(cname)]) r_mtd = sanitize_mname(r_mtd) _mtds[mid] = r_mtd return r_mtd
def per_cls(sup_flds, cls): # keep mappings from original subclasses to the representative # so that subclasses can refer to the representative # e.g., for C < B < A, { B : A, C : A } cname = util.sanitize_ty(cls.name) if cname != cls_v.name: # exclude the root of this family logging.debug("{} => {}".format(cname, cls_v.name)) _ty[cname] = cls_v.name if cls.is_inner: # to handle inner class w/ outer class name logging.debug("{} => {}".format(repr(cls), cls_v.name)) _ty[unicode(repr(cls))] = cls_v.name # if this class implements an interface which has constants, # then copy those constants for itf in cls.itfs: cls_i = class_lookup(itf) if not cls_i or not cls_i.flds: continue for fld in cls_i.flds: sup_flds[fld.name] = fld # also, keep mappings from original member fields to newer ones # so that usage of old fields can be replaced accordingly # e.g., for A.f1 and B.f2, { A.f1 : f1_A, B.f1 : f1_A, B.f2 : f2_B } for sup_fld in sup_flds.keys(): fld = sup_flds[sup_fld] fname = unicode(repr(fld)) fid = '.'.join([cname, sup_fld]) logging.debug("{} => {}".format(fid, fname)) if fld.is_static: _s_flds[fid] = fname else: _flds[fid] = fname # { ..., B.f1 : f1_A } cur_flds = cp.deepcopy(sup_flds) # { f1 : f1_A } @takes(Field) @returns(nothing) def cp_fld(fld): cur_flds[fld.name] = fld # { ..., f2 : f2_B } fname = unicode(repr(fld)) fld_v = cp.deepcopy(fld) fld_v.clazz = cls_v fld_v.name = fname cls_v.flds.append(fld_v) def upd_flds(cname): fid = '.'.join([cname, fld.name]) # if A.f1 exists and B redefines f1, then B.f1 : f1_A # except for enum, which can (re)define its own fields # e.g., SwingConstands.LEADING vs. GroupLayout.Alignment.LEADING if not cls.is_enum and (fid in _s_flds or fid in _flds): return logging.debug("{} => {}".format(fid, fname)) if fld.is_static: _s_flds[fid] = fname else: _flds[fid] = fname # { ..., B.f2 : f2_B } upd_flds(cname) map(cp_fld, cls.flds) map(partial(per_cls, cur_flds), cls.subs)
def gen_cls_sk(sk_dir, cls): mtds = collect_decls(cls, "mtds") flds = collect_decls(cls, "flds") s_flds = filter(op.attrgetter("is_static"), flds) if cls.is_class: if not mtds and not s_flds: return None else: # cls.is_itf or cls.is_enum if not s_flds: return None cname = util.sanitize_ty(cls.name) buf = cStringIO.StringIO() buf.write("package {};\n".format(cname)) buf.write(_const) # static fields buf.write('\n'.join(map(trans_fld, s_flds))) if len(s_flds) > 0: buf.write('\n') # migrating static fields' initialization to <clinit> for fld in ifilter(op.attrgetter("init"), s_flds): if not fld.init.has_call and not fld.init.has_str and not fld.is_aliasing: continue # retrieve (or declare) <clinit> clinit = fld.clazz.get_or_declare_clinit() if clinit not in mtds: mtds.append(clinit) # add assignment assign = st.gen_S_assign(exp.gen_E_id(fld.name), fld.init) clinit.body.append(assign) # accessors for static fields for fld in ifilterfalse(op.attrgetter("is_private"), s_flds): fname = fld.name accessor = trans_fname(fld.clazz.name, fname, True) buf.write(""" {0} {1}() {{ return {2}; }} """.format(trans_ty(fld.typ), accessor, fname)) # methods clinits, mtds = util.partition(lambda m: m.is_clinit, mtds) inits, mtds = util.partition(lambda m: m.is_init, mtds) # <init>/<clinit> should be dumped out in any case buf.write('\n'.join(map(to_func, clinits))) buf.write('\n'.join(map(to_func, inits))) for mtd in mtds: # interface won't have method bodies if mtd.clazz.is_itf: continue buf.write(to_func(mtd) + os.linesep) cls_sk = cname + ".sk" with open(os.path.join(sk_dir, cls_sk), 'w') as f: f.write(buf.getvalue()) logging.info("encoding " + f.name) return cls_sk
def gen_cls_sk(sk_dir, smpls, cls): mtds = collect_decls(cls, "mtds") flds = collect_decls(cls, "flds") s_flds = filter(op.attrgetter("is_static"), flds) if cls.is_class: if not mtds and not s_flds: return None else: # cls.is_itf or cls.is_enum if not s_flds: return None cname = util.sanitize_ty(cls.name) buf = cStringIO.StringIO() buf.write("package {};\n".format(cname)) buf.write(_const) # static fields buf.write('\n'.join(map(trans_fld, s_flds))) if len(s_flds) > 0: buf.write('\n') # migrating static fields' initialization to <clinit> for fld in ifilter(op.attrgetter("init"), s_flds): if not fld.init.has_call and not fld.init.has_str and not fld.is_aliasing: continue # retrieve (or declare) <clinit> clinit = fld.clazz.get_or_declare_clinit() if clinit not in mtds: mtds.append(clinit) # add assignment assign = st.gen_S_assign(exp.gen_E_id(fld.name), fld.init) clinit.body.append(assign) # accessors for static fields for fld in ifilterfalse(op.attrgetter("is_private"), s_flds): fname = fld.name accessor = trans_fname(fld.clazz.name, fname, True) buf.write(""" {0} {1}() {{ return {2}; }} """.format(trans_ty(fld.typ), accessor, fname)) # methods clinits, mtds = util.partition(lambda m: m.is_clinit, mtds) inits, mtds = util.partition(lambda m: m.is_init, mtds) # <init>/<clinit> should be dumped out in any case buf.write('\n'.join(map(partial(to_func, smpls), clinits))) buf.write('\n'.join(map(partial(to_func, smpls), inits))) for mtd in mtds: # interface won't have method bodies if mtd.clazz.is_itf: continue buf.write(to_func(smpls, mtd) + os.linesep) cls_sk = cname + ".sk" with open(os.path.join(sk_dir, cls_sk), 'w') as f: f.write(buf.getvalue()) logging.info("encoding " + f.name) return cls_sk
def to_struct(cls): # make mappings from static fields to corresponding accessors def gen_s_flds_accessors(cls): s_flds = filter(op.attrgetter("is_static"), cls.flds) global _s_flds for fld in ifilterfalse(op.attrgetter("is_private"), s_flds): cname = fld.clazz.name fid = '.'.join([cname, fld.name]) fname = unicode(repr(fld)) logging.debug("{} => {}".format(fid, fname)) _s_flds[fid] = fname cname = util.sanitize_ty(cls.name) global _ty # if this is an interface, merge this into another family of classes # as long as classes that implement this interface are in the same family if cls.is_itf: # interface may have static constants gen_s_flds_accessors(cls) subss = util.flatten_classes(cls.subs, "subs") bases = util.rm_dup(map(lambda sub: find_base(sub), subss)) # filter out interfaces that extend other interfaces, e.g., Action base_clss, _ = util.partition(op.attrgetter("is_class"), bases) if not base_clss: logging.debug("no implementer of {}".format(cname)) elif len(base_clss) > 1: logging.debug("ambiguous inheritance of {}: {}".format(cname, base_clss)) else: # len(base_clss) == 1 base = base_clss[0] base_name = base.name logging.debug("{} => {}".format(cname, base_name)) _ty[cname] = base_name if cls.is_inner: # to handle inner interface w/ outer class name logging.debug("{} => {}".format(repr(cls), base_name)) _ty[unicode(repr(cls))] = base_name return '' # if this is the base class having subclasses, # make a virtual struct first if cls.subs: cls = to_v_struct(cls) cname = cls.name # cls can be modified above, thus generate static fields accessors here gen_s_flds_accessors(cls) # for unique class numbering, add an identity mapping if cname not in _ty: _ty[cname] = cname buf = cStringIO.StringIO() buf.write("struct " + cname + " {\n int hash;\n") # to avoid static fields, which will be bound to a class-representing package _, i_flds = util.partition(op.attrgetter("is_static"), cls.flds) buf.write('\n'.join(map(trans_fld, i_flds))) if len(i_flds) > 0: buf.write('\n') buf.write("}\n") return buf.getvalue()
def trans_e(mtd, e): curried = partial(trans_e, mtd) buf = cStringIO.StringIO() if e.kind == C.E.ANNO: anno = e.anno if anno.name == C.A.NEW: pass # TODO elif anno.name == C.A.OBJ: buf.write("retrieve_{}@log({})".format(util.sanitize_ty(anno.typ), anno.idx)) # @Compare(exps) => {| exps[0] (< | <= | == | != | >= | >) exps[1] |} # @CompareString(exps) => exps[0].eqauls(exps[1]) elif anno.name in [C.A.CMP, C.A.CMP_STR]: le = curried(anno.exps[0]) re = curried(anno.exps[1]) if anno.name == C.A.CMP: buf.write("{| " + le + " (< | <= | == | != | >= | >) " + re + " |}") else: buf.write("{}({},{})".format(trans_mname(C.J.STR, u"equals"), le, re)) elif e.kind == C.E.GEN: if e.es: buf.write("{| ") buf.write(" | ".join(map(curried, e.es))) buf.write(" |}") else: buf.write(C.T.HOLE) elif e.kind == C.E.ID: if hasattr(e, "ty"): buf.write(trans_ty(e.ty) + ' ') fld = None if mtd and e.id not in mtd.param_vars: fld = find_fld(mtd.clazz.name, e.id) if fld: # fname -> self.new_fname (unless the field is static) new_fname = trans_fname(fld.clazz.name, e.id, fld.is_static) if fld.is_static: # access to the static field inside the same class if fld.clazz.name == mtd.clazz.name: buf.write(e.id) # o.w., e.g., static constant in an interface, call the accessor else: buf.write(new_fname + "()") else: buf.write('.'.join([C.SK.self, new_fname])) elif e.id in [C.J.THIS, C.J.SUP]: buf.write(C.SK.self) elif util.is_str(e.id): # constant string, such as "Hello, World" str_init = trans_mname(C.J.STR, C.J.STR, [u"char[]", C.J.i, C.J.i]) s_hash = hash(e.id) % 256 # hash string value itself buf.write("{}(new Object(hash={}), {}, 0, {})".format(str_init, s_hash, e.id, len(e.id))) else: buf.write(e.id) elif e.kind == C.E.UOP: buf.write(' '.join([e.op, curried(e.e)])) elif e.kind == C.E.BOP: buf.write(' '.join([curried(e.le), e.op, curried(e.re)])) elif e.kind == C.E.DOT: # with package names, e.g., javax.swing.SwingUtilities if util.is_class_name(e.re.id) and class_lookup(e.re.id): buf.write(curried(e.re)) elif e.re.id == C.J.THIS: # ClassName.this buf.write(C.SK.self) else: rcv_ty = typ_of_e(mtd, e.le) fld = find_fld(rcv_ty, e.re.id) new_fname = trans_fname(rcv_ty, e.re.id, fld.is_static) if fld.is_static: # access to the static field inside the same class if mtd and rcv_ty == mtd.clazz.name: buf.write(e.re.id) # o.w., e.g., static constant in an interface, call the accessor else: buf.write(new_fname + "()") else: buf.write('.'.join([curried(e.le), new_fname])) elif e.kind == C.E.IDX: buf.write(curried(e.e) + '[' + curried(e.idx) + ']') elif e.kind == C.E.NEW: if e.e.kind == C.E.CALL: ty = typ_of_e(mtd, e.e.f) cls = class_lookup(ty) if cls and cls.has_init: arg_typs = map(partial(typ_of_e, mtd), e.e.a) mname = trans_mname(cls.name, cls.name, arg_typs) obj = "alloc@log({})".format(cls.id) args = [obj] + map(unicode, map(curried, e.e.a)) buf.write("{}({})".format(mname, ", ".join(args))) else: # collection or Object buf.write(C.J.NEW + ' ' + trans_ty(ty) + "()") else: # o.w., array initialization, e.g., new int[] { ... } buf.write(str(e.init)) elif e.kind == C.E.CALL: arg_typs = map(partial(typ_of_e, mtd), e.a) def trans_call(callee, rcv_ty, rcv): if callee.is_static: rcv = None logging = None if not util.is_collection(callee.clazz.name): logging = str(check_logging(mtd, callee)).lower() args = util.rm_none([rcv] + map(curried, e.a) + [logging]) mid = trans_mname(rcv_ty, callee.name, arg_typs) return u"{}({})".format(mid, ", ".join(args)) def dynamic_dispatch(rcv_ty, rcv, acc, callee): _dispatched = trans_call(callee, callee.clazz.name, rcv) _guarded = "{}.__cid == {} ? {}".format(rcv, callee.clazz.id, _dispatched) return "({} : {})".format(_guarded, acc) if e.f.kind == C.E.DOT: # rcv.mid rcv_ty = typ_of_e(mtd, e.f.le) rcv = curried(e.f.le) mname = e.f.re.id mtd_callees = find_mtds_by_sig(rcv_ty, mname, arg_typs) if mtd_callees and 1 < len(mtd_callees): # needs dynamic dispatch curried_dispatch = partial(dynamic_dispatch, rcv_ty, rcv) # TODO: use least upper bound? default_v = util.default_value(mtd_callees[0].typ) buf.write(reduce(curried_dispatch, mtd_callees, default_v)) elif mtd_callees and 1 == len(mtd_callees): mtd_callee = mtd_callees[0] buf.write(trans_call(mtd_callee, rcv_ty, rcv)) else: # unresolved, maybe library method mid = trans_mname(rcv_ty, mname, arg_typs) args = util.rm_none([rcv] + map(curried, e.a)) buf.write("{}({})".format(mid, ", ".join(args))) else: # mid mname = e.f.id # pre-defined meta information or Sketch primitive functions if mname in C.typ_arrays + [u"minimize"]: mid = mname rcv = None args = util.rm_none([rcv] + map(curried, e.a)) buf.write("{}({})".format(mid, ", ".join(args))) elif mname == C.J.SUP and mtd.is_init: # super(...) inside <init> sup = class_lookup(mtd.clazz.sup) mid = trans_mname(sup.name, sup.name, arg_typs) rcv = C.SK.self args = util.rm_none([rcv] + map(curried, e.a)) buf.write("{}({})".format(mid, ", ".join(args))) else: # member methods mtd_callees = find_mtds_by_sig(mtd.clazz.name, mname, arg_typs) if mtd_callees and 1 < len(mtd_callees): # needs dynamic dispatch curried_dispatch = partial(dynamic_dispatch, mtd.clazz.name, C.SK.self) # TODO: use least upper bound? default_v = util.default_value(mtd_callees[0].typ) buf.write(reduce(curried_dispatch, mtd_callees, default_v)) elif mtd_callees and 1 == len(mtd_callees): mtd_callee = mtd_callees[0] buf.write(trans_call(mtd_callee, mtd.clazz.name, C.SK.self)) else: # unresolved, maybe library method mid = trans_mname(mtd.clazz.name, mname, arg_typs) args = util.rm_none([rcv] + map(curried, e.a)) buf.write("{}({})".format(mid, ", ".join(args))) elif e.kind == C.E.CAST: # since a family of classes is merged, simply ignore the casting buf.write(curried(e.e)) elif e.kind == C.E.INS_OF: ty = typ_of_e(mtd, e.ty) cls = class_lookup(ty) if cls: buf.write(curried(e.e) + ".__cid == " + str(cls.id)) else: logging.debug("unknown type: {}".format(ty)) buf.write("0") else: buf.write(str(e)) return buf.getvalue()
def to_struct(cls): # make mappings from static fields to corresponding accessors def gen_s_flds_accessors(cls): s_flds = filter(op.attrgetter("is_static"), cls.flds) global _s_flds for fld in ifilterfalse(op.attrgetter("is_private"), s_flds): cname = fld.clazz.name fid = '.'.join([cname, fld.name]) fname = unicode(repr(fld)) logging.debug("{} => {}".format(fid, fname)) _s_flds[fid] = fname cname = util.sanitize_ty(cls.name) global _ty # if this is an interface, merge this into another family of classes # as long as classes that implement this interface are in the same family if cls.is_itf: # interface may have static constants gen_s_flds_accessors(cls) subss = util.flatten_classes(cls.subs, "subs") bases = util.rm_dup(map(lambda sub: find_base(sub), subss)) # filter out interfaces that extend other interfaces, e.g., Action base_clss, _ = util.partition(op.attrgetter("is_class"), bases) if not base_clss: logging.debug("no implementer of {}".format(cname)) elif len(base_clss) > 1: logging.debug("ambiguous inheritance of {}: {}".format(cname, base_clss)) else: # len(base_clss) == 1 base = base_clss[0] base_name = base.name logging.debug("{} => {}".format(cname, base_name)) _ty[cname] = base_name if cls.is_inner: # to handle inner interface w/ outer class name logging.debug("{} => {}".format(repr(cls), base_name)) _ty[unicode(repr(cls))] = base_name return '' # if this is the base class having subclasses, # make a virtual struct first if cls.subs and not cls.is_aux: cls = to_v_struct(cls) cname = cls.name # cls can be modified above, thus generate static fields accessors here gen_s_flds_accessors(cls) # for unique class numbering, add an identity mapping if cname not in _ty: _ty[cname] = cname buf = cStringIO.StringIO() buf.write("struct " + cname + " {\n int hash;\n") # to avoid static fields, which will be bound to a class-representing package _, i_flds = util.partition(op.attrgetter("is_static"), cls.flds) buf.write('\n'.join(map(trans_fld, i_flds))) if len(i_flds) > 0: buf.write('\n') buf.write("}\n") return buf.getvalue()
def per_cls(sup_flds, cls): aux_name = None # if this class is suppose to be replaced (due to pattern rewriting) # apply that replacement first, and then replace that aux type as well if not cls.is_aux and cls.name in _ty: aux_name = _ty[cls.name] logging.debug("{} => {}".format(aux_name, cls_v.name)) # check that aux type is already involved in this family if aux_name not in _ty: _ty[aux_name] = cls_v.name # keep mappings from original subclasses to the representative # so that subclasses can refer to the representative # e.g., for C < B < A, { B : A, C : A } cname = util.sanitize_ty(cls.name) if cname != cls_v.name: # exclude the root of this family logging.debug("{} => {}".format(cname, cls_v.name)) _ty[cname] = cls_v.name if cls.is_inner: # to handle inner class w/ outer class name logging.debug("{} => {}".format(repr(cls), cls_v.name)) _ty[unicode(repr(cls))] = cls_v.name # if this class implements an interface which has constants, # then copy those constants for itf in cls.itfs: cls_i = class_lookup(itf) if not cls_i or not cls_i.flds: continue for fld in cls_i.flds: sup_flds[fld.name] = fld # also, keep mappings from original member fields to newer ones # so that usage of old fields can be replaced accordingly # e.g., for A.f1 and B.f2, { A.f1 : f1_A, B.f1 : f1_A, B.f2 : f2_B } for sup_fld in sup_flds.keys(): fld = sup_flds[sup_fld] fname = unicode(repr(fld)) fid = '.'.join([cname, sup_fld]) logging.debug("{} => {}".format(fid, fname)) if fld.is_static: _s_flds[fid] = fname else: _flds[fid] = fname # { ..., B.f1 : f1_A } cur_flds = cp.deepcopy(sup_flds) # { f1 : f1_A } @takes(Field) @returns(nothing) def cp_fld(fld): cur_flds[fld.name] = fld # { ..., f2 : f2_B } fname = unicode(repr(fld)) fld_v = cp.deepcopy(fld) fld_v.clazz = cls_v fld_v.name = fname cls_v.flds.append(fld_v) def upd_flds(cname): fid = '.'.join([cname, fld.name]) # if A.f1 exists and B redefines f1, then B.f1 : f1_A # except for enum, which can (re)define its own fields # e.g., SwingConstands.LEADING vs. GroupLayout.Alignment.LEADING if not cls.is_enum and (fid in _s_flds or fid in _flds): return logging.debug("{} => {}".format(fid, fname)) if fld.is_static: _s_flds[fid] = fname else: _flds[fid] = fname # { ..., B.f2 : f2_B } upd_flds(cname) if aux_name: upd_flds(aux_name) map(cp_fld, cls.flds) # subclass relations of aux types are virtual, so do not visit further if not cls.is_aux: map(partial(per_cls, cur_flds), cls.subs)