Exemplo n.º 1
0
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
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
  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)
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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
Exemplo n.º 7
0
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()
Exemplo n.º 8
0
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()
Exemplo n.º 9
0
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()
Exemplo n.º 10
0
  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)