Пример #1
0
def reduce_anno_mtd(smpls, tmpl, cls, mtd):
  if not mtd.annos: return
  mname = mtd.name
  for _anno in mtd.annos:

    ##
    ## @Error void mname()
    ##
    if _anno.name == C.A.ERR:
      if not hasattr(cls, "state"): raise ReduceError("no state variable")
      setattr(cls.state, "error", mtd)
      state = cls.state.name
      body = T("${state} = ??;").safe_substitute(locals())
      mtd.body = st.to_statements(mtd, body) + mtd.body

    ##
    ## @Assemble typ mname(...)
    ##

    ##
    ## @CFG(other_mname) typ mname(...)
    ##
    elif _anno.name == C.A.CFG:
      # find the designated method
      _, cls_c_name, mtd_c_name = util.explode_mname(_anno.mid)
      if cls_c_name: mtd_c = class_lookup(cls_c_name).mtd_by_name(mtd_c_name)
      else: mtd_c = cls.mtd_by_name(mtd_c_name)
      # and then copy its body
      if mtd_c: mtd.body = mtd_c[0].body
Пример #2
0
def reduce_anno_mtd(smpls, tmpl, cls, mtd):
    if not mtd.annos: return
    mname = mtd.name
    for _anno in mtd.annos:

        ##
        ## @Error void mname()
        ##
        if _anno.name == C.A.ERR:
            if not hasattr(cls, "state"):
                raise ReduceError("no state variable")
            setattr(cls.state, "error", mtd)
            state = cls.state.name
            body = T("${state} = ??;").safe_substitute(locals())
            mtd.body = st.to_statements(mtd, body) + mtd.body

        ##
        ## @Assemble typ mname(...)
        ##

        ##
        ## @CFG(other_mname) typ mname(...)
        ##
        elif _anno.name == C.A.CFG:
            # find the designated method
            _, cls_c_name, mtd_c_name = util.explode_mname(_anno.mid)
            if cls_c_name:
                mtd_c = class_lookup(cls_c_name).mtd_by_name(mtd_c_name)
            else:
                mtd_c = cls.mtd_by_name(mtd_c_name)
            # and then copy its body
            if mtd_c: mtd.body = mtd_c[0].body
Пример #3
0
def add_getter(cls, fld, mname):
    mtd_g = Method(clazz=cls, mods=fld.mods, typ=fld.typ, name=mname)
    fname = fld.name
    body = T("return ${fname};").safe_substitute(locals())
    mtd_g.body = st.to_statements(mtd_g, body)
    cls.mtds.append(mtd_g)
    # to replace annotation @Get(e) in expressions
    # or to encode static fields into sketch
    setattr(fld, "getter", mtd_g)
Пример #4
0
def add_getter(cls, fld, mname):
  mtd_g = Method(clazz=cls, mods=fld.mods, typ=fld.typ, name=mname)
  fname = fld.name
  body = T("return ${fname};").safe_substitute(locals())
  mtd_g.body = st.to_statements(mtd_g, body)
  cls.mtds.append(mtd_g)
  # to replace annotation @Get(e) in expressions
  # or to encode static fields into sketch
  setattr(fld, "getter", mtd_g)
Пример #5
0
def mk_harness_pattern(tmpl, cls, smpl):
    harness = mk_harness(cls, smpl)

    buf = cStringIO.StringIO()

    call_stack = []

    objs = {}  # { @Object(typ, idx): obj_typ_idx, ... }

    def lkup_obj(v):
        if type(kind(v)) is unicode: return objs[v]
        else: return v

    def to_obj(v):
        _anno = to_expression(v).anno
        rep = u"obj_{}_{}".format(_anno.typ, _anno.idx)
        objs[v] = rep
        return rep

    for i, log in enumerate(smpl.logs):
        if isinstance(log, CallEnt):
            call_stack.append(log)
            if len(call_stack) > 1: continue

            if log.is_init:
                rcv = to_obj(smpl.logs[i + 1].vals[0])
                args = ", ".join(map(lkup_obj, log.vals))
                buf.write("{0} {1} = new {0}({2});\n".format(
                    log.cls, rcv, args))

            else:  # normal call
                try:
                    args = map(lkup_obj, log.vals)
                    if log.vals and type(kind(log.vals[0])) is unicode:
                        args_mod_rcv = ", ".join(args[1:])
                        buf.write("{}.{}({});\n".format(
                            args[0], log.mtd, args_mod_rcv))
                    else:
                        buf.write("{}({});\n".format(log.mtd, ", ".join(args)))
                except KeyError:
                    continue  # means, unknown obj (Evt) occurs

        elif isinstance(log, CallExt):
            call_stack.pop()
            if len(call_stack) > 0: continue

        else:  # Evt
            buf.write("@React;\n")

    harness.body = to_statements(harness, unicode(buf.getvalue()))
    cls.add_mtds([harness])
Пример #6
0
def mk_harness_pattern(tmpl, cls, smpl):
  harness = mk_harness(cls, smpl)

  buf = cStringIO.StringIO()

  call_stack = []

  objs = {} # { @Object(typ, idx): obj_typ_idx, ... }
  def lkup_obj(v):
    if type(kind(v)) is unicode: return objs[v]
    else: return v

  def to_obj(v):
    _anno = to_expression(v).anno
    rep = u"obj_{}_{}".format(_anno.typ, _anno.idx)
    objs[v] = rep
    return rep

  for i, log in enumerate(smpl.logs):
    if isinstance(log, CallEnt):
      call_stack.append(log)
      if len(call_stack) > 1: continue

      if log.is_init:
        rcv = to_obj(smpl.logs[i+1].vals[0])
        args = ", ".join(map(lkup_obj, log.vals))
        buf.write("{0} {1} = new {0}({2});\n".format(log.cls, rcv, args))

      else: # normal call
        try:
          args = map(lkup_obj, log.vals)
          if log.vals and type(kind(log.vals[0])) is unicode:
            args_mod_rcv = ", ".join(args[1:])
            buf.write("{}.{}({});\n".format(args[0], log.mtd, args_mod_rcv))
          else: buf.write("{}({});\n".format(log.mtd, ", ".join(args)))
        except KeyError: continue # means, unknown obj (Evt) occurs

    elif isinstance(log, CallExt):
      call_stack.pop()
      if len(call_stack) > 0: continue

    else: # Evt
      buf.write("@React;\n")

  harness.body = to_statements(harness, unicode(buf.getvalue()))
  cls.add_mtds([harness])
Пример #7
0
def mk_harness_gui(tmpl, cls, smpl):
  harness = mk_harness(cls, smpl)

  buf = cStringIO.StringIO()

  # initialize Toolkit/EventQueue
  buf.write("{0} t = {0}.{1}{0}();\n".format(C.GUI.TOOL, "getDefault"))
  buf.write("{0} q = t.{1}{0}();\n".format(C.GUI.QUE, "getSystem"))

  # run main() in the client
  main_cls = tmpl.main
  # TODO: passing proper parameters
  buf.write("{}.{}();\n".format(main_cls.clazz.name, main_cls.name))
  buf.write("@React;\n") # to handle InvokeEvent

  # post events in the sample to Toolkit's EventQueue
  post = u"q.postEvent"
  evts = smpl.evts
  ev_name = C.GUI.EVT
  logging.debug("# event(s) in {}: {}".format(smpl.name, len(evts)))
  for i, evt in enumerate(evts):
    e_i = "e{}".format(i)
    init = str(evt)
    buf.write("""
      {ev_name} {e_i} = new {init};
    """.format(**locals()))

    e_kind = tmpl.get_event_id(evt.kind)
    buf.write("""
      {e_i}.kind = {e_kind};
      {post}({e_i});
      @React;
    """.format(**locals()))

  harness.body = to_statements(harness, unicode(buf.getvalue()))
  cls.add_mtds([harness])
Пример #8
0
def mk_harness_gui(tmpl, cls, smpl):
    harness = mk_harness(cls, smpl)

    buf = cStringIO.StringIO()

    # initialize Toolkit/EventQueue
    buf.write("{0} t = {0}.{1}{0}();\n".format(C.GUI.TOOL, "getDefault"))
    buf.write("{0} q = t.{1}{0}();\n".format(C.GUI.QUE, "getSystem"))

    # run main() in the client
    main_cls = tmpl.main
    # TODO: passing proper parameters
    buf.write("{}.{}();\n".format(main_cls.clazz.name, main_cls.name))
    buf.write("@React;\n")  # to handle InvokeEvent

    # post events in the sample to Toolkit's EventQueue
    post = u"q.postEvent"
    evts = smpl.evts
    ev_name = C.GUI.EVT
    logging.debug("# event(s) in {}: {}".format(smpl.name, len(evts)))
    for i, evt in enumerate(evts):
        e_i = "e{}".format(i)
        init = str(evt)
        buf.write("""
      {ev_name} {e_i} = new {init};
    """.format(**locals()))

        e_kind = tmpl.get_event_id(evt.kind)
        buf.write("""
      {e_i}.kind = {e_kind};
      {post}({e_i});
      @React;
    """.format(**locals()))

    harness.body = to_statements(harness, unicode(buf.getvalue()))
    cls.add_mtds([harness])
Пример #9
0
def mk_harness_android(tmpl, cls, smpl):
  harness = mk_harness(cls, smpl)

  buf = cStringIO.StringIO()

  # TODO: launch resource managers

  # TODO: post an Intent for AUT to ActivityManager
  # XXX: rather, start and post the Intent to the global Context directly
  main_cls = tmpl.main # SystemServer.main
  # TODO: passing proper parameters
  buf.write("{}.{}();\n".format(main_cls.clazz.name, main_cls.name))

  # global variables, sort of singletons
  buf.write("""
    {0} l = {0}.getMain{0}();
    {1} q = l.myQueue();
    {2} t = {2}.current{2}(); // global Context
  """.format(C.ADR.LOOP, C.ADR.QUE, C.ADR.ACTT))

  # generate an Intent to trigger the main Activity
  # TODO: how to figure out the *main* Activity?
  # XXX: assume there is only one Activity
  acts = tmpl.find_cls_kind(C.ADR.ACT)
  if not acts:
    raise Exception("no Activity at all?")
  elif len(acts) > 1:
    raise Exception("no idea what to start among multiple Activity's", acts)
  main_act = acts[0]
  buf.write("""
    {0} c = new {0}(\"{1}\", \"{2}\");
    {3} i = new {3}(c);
  """.format(C.ADR.CMP, "DONT_CARE_PKG_NAME", main_act.name, C.ADR.INTT))

  post = u"q.enqueue{}".format(C.ADR.MSG)

  # TODO: use ActivityManager's Handler
  # XXX: use the main UI thread's Handler
  buf.write("""
    {0} h = t.get{0}();
    {1} m = new {1}(h);
    m.obj = i;
    m.what = -1; // Intent
    {2}(m, 0);
    @React;
  """.format(C.ADR.HDL, C.ADR.MSG, post))

  # additional global variable(s)
  buf.write("""
    {0} a = t.get{0}();
  """.format(C.ADR.ACT))

  cls_ievt = class_lookup(u"InputEvent")

  # post events in the sample to the main Looper's MessageQueue
  evts = smpl.evts
  _hdl = C.ADR.HDL
  _msg = C.ADR.MSG
  logging.debug("# event(s) in {}: {}".format(smpl.name, len(evts)))
  for i, evt in enumerate(evts):
    cls_evt = class_lookup(evt.kind)
    if not cls_evt:
      logging.debug("undeclared event sort: {}".format(evt.kind))
      continue

    e_i = "e{}".format(i)
    init = str(evt)
    buf.write("""
      {evt.kind} {e_i} = new {init};
    """.format(**locals()))

    # generate a message by wrapping the event
    h_i = "h{}".format(i)
    if cls_evt <= cls_ievt: # InputEvent, KeyEvent, MotionEvent
      # TODO: use Window(Manager)'s Handler
      # XXX: use the source View's Handler at the moment
      s_i = "s{}".format(i)
      v_i = "v{}".format(i)
      buf.write("""
        int {s_i} = {e_i}.getSource();
        View {v_i} = a.findViewById({s_i});
        {_hdl} {h_i} = {v_i}.get{_hdl}();
      """.format(**locals()))

    else: # TODO: how to retrieve an appropriate Handler in general?
      buf.write("""
        {_hdl} {h_i} = ...;
      """.format(**locals()))

    m_i = "m{}".format(i)
    e_kind = tmpl.get_event_id(evt.kind)
    buf.write("""
      {_msg} {m_i} = new {_msg}({h_i});
      {m_i}.obj = {e_i};
      {m_i}.what = {e_kind};
    """.format(**locals()))

    # post that message (to the main Looper's MessageQueue)
    buf.write("""
      {post}({m_i}, 0);
      @React;
    """.format(**locals()))

  harness.body = to_statements(harness, unicode(buf.getvalue()))
  cls.add_mtds([harness])
Пример #10
0
def reduce_anno_e(tmpl, cls, mtd, e, pe=None):
    curried = partial(reduce_anno_e, tmpl, cls, mtd)
    if e.kind == C.E.ANNO:
        _anno = e.anno

        ##
        ## Getters
        ##
        if _anno.name in [C.A.STATE, C.A.GET]:
            args = []
            if _anno.name == C.A.STATE:
                # @State(this) -> this.getter()
                if _anno.accessed == C.J.THIS:
                    mtd_g = cls.state.getter
                    f = st.gen_E_id(mtd_g.name)
                # @State(var) -> var.getter()
                else:
                    var = _anno.accessed
                    tname = mtd.vars[var]
                    mtd_g = class_lookup(tname).state.getter
                    f = st.gen_E_dot(st.gen_E_id(var), st.gen_E_id(mtd_g.name))
            # @Get(var.fname, args?) -> var.getFname(args?)
            elif _anno.name == C.A.GET:
                var, fname = _anno.fid.split('.')
                tname = mtd.vars[var]
                mtd_g = class_lookup(tname).fld_by_name(fname).getter
                f = st.gen_E_dot(st.gen_E_id(var), st.gen_E_id(mtd_g.name))
                if hasattr(_anno, "args"): args = _anno.args
            return st.gen_E_call(f, args)

        ##
        ## Setter
        ##
        # @Update(var) -> var.setter(??)
        elif _anno.name == C.A.UPDATE:
            var, fname = _anno.fid.split('.')
            tname = mtd.vars[var]
            mtd_s = class_lookup(tname).fld_by_name(fname).setter
            f = st.gen_E_dot(st.gen_E_id(var), st.gen_E_id(mtd_s.name))
            return st.gen_E_call(f, [st.gen_E_hole()])

        ##
        ## Generator
        ##
        elif _anno.name in [C.A.ALL, C.A.TAG]:
            if _anno.name == C.A.ALL: tag_g = u"gen_all"
            else: tag_g = u"gen_" + _anno.tag  # assume tag name is unique
            # (var.)? @anno
            if not pe: cls_g = cls
            else: cls_g = class_lookup(mtd.vars[unicode(pe.le)])

            if hasattr(cls_g, tag_g): mtd_g = getattr(cls_g, tag_g)
            else:  # introduce generator
                if _anno.name == C.A.ALL: mtds = cls_g.mtds
                else:  # C.A.TAG
                    find_tag = partial(anno.by_attr, {"tag": _anno.tag})
                    mtds = cls_g.mtds_w_anno(find_tag)
                mtd_g = Method(clazz=cls_g, mods=[C.mod.GN], name=tag_g)
                body = "int t = ??;\n"
                for i, mtd in enumerate(mtds):
                    mid = mtd.name
                    case = T("if (t == ${i}) { ${mid}(); }\n").safe_substitute(
                        locals())
                    body = body + case

                body = body + "assert t <= {};".format(len(mtds))
                mtd_g.body = st.to_statements(mtd_g, body)

                cls_g.mtds.append(mtd_g)
                setattr(cls_g, tag_g, mtd_g)

            # @Tag("tag") | @All -> gen_tag | gen_all
            return st.gen_E_id(mtd_g.name)

        ##
        ## Reflection
        ##
        # @New should be handled differently
        # e.g., for "ClassName".@New({ args })
        # Sketch: new ClassName(args);
        # Java: (ClassName)(Class.forName("ClassName").newInstance(args));
        elif _anno.name == C.A.NEW:
            pass

            ##
            ## Comparator
            ##
            # @Compare will be encoded into a regex generator in Sketch
            # and then will be replaced with a proper operator in Java
        elif _anno.name in [C.A.CMP, C.A.CMP_STR]:
            pass

            ##
            ## Observers
            ##
            # event sending
        elif _anno.name == C.A.EVENT:
            pass  # TODO

            # a list that maintains the registered observers
        elif _anno.name == C.A.OBSS:
            if not hasattr(cls, "obs"):
                cls_o_name, _ = find_observer_at_cls(tmpl, cls)
                add_obs(cls, cls_o_name)
            return st.gen_E_id(cls.obs.name)

        # invoke the @Notified method
        elif _anno.name == C.A.NOTI:
            mtd_noti = find_noti(tmpl, cls)
            rcv = _anno.args[0]
            call_noti = st.gen_E_dot(rcv, st.gen_E_id(mtd_noti.name))
            return st.gen_E_call(call_noti, _anno.args[1:])

    elif e.kind in [C.E.BOP, C.E.DOT]:
        e.le = curried(e.le, e)
        e.re = curried(e.re, e)

    elif e.kind == C.E.NEW:
        e.e = curried(e.e)

    elif e.kind == C.E.CALL:
        e.f = curried(e.f)
        map(curried, e.a)

    return e
Пример #11
0
def reduce_anno_fld(smpls, tmpl, cls, fld):
    if not fld.annos: return
    cname = cls.name
    fname = fld.name
    Fname = util.cap_1st_only(fname)
    for _anno in fld.annos:

        ##
        ## @State(@Tag(...) | @All) int _state
        ##
        if _anno.name == C.A.STATE:
            setattr(cls, "state", fld)

            # find all the methods involved in this state variable
            where = _anno.accessed
            if where.name == C.A.ALL:
                mtds = cls.mtds[:]  # quick copy, since mtds will be modified below
            elif where.name == C.A.TAG:
                find_tag = partial(anno.by_attr, {"tag": where.tag})
                mtds = cls.mtds_w_anno(find_tag)
            else:
                mtds = cls.mtd_by_name(where)

            # exclude setter to avoid a recursive call
            if hasattr(fld, "setter"): mtds.remove(fld.setter)
            # exclude error, if exists, to avoid redundant state update
            find_err = partial(anno.by_name, C.A.ERR)
            errs = cls.mtds_w_anno(find_err)
            if errs:
                if len(errs) > 1: raise ReduceError("ambiguous error state")
                err = errs[0]
                mtds.remove(err)

            # then add a statement to update this state variable into those methods
            for mtd in mtds:

                # TODO: currently, @Set should be reduced before @State
                if hasattr(fld, "setter"):  # use setter if exists
                    f_setter = fld.setter.name
                    upd = "${f_setter}(??);"
                else:
                    upd = "${fname} = ??;"

                if errs and err:
                    e_name = err.name
                    trans = "if (??) { " + upd + " } else { ${e_name}(); }"
                else:
                    trans = upd

                body_tmpl = "repeat(??) { if (${fname} == ??) { " + trans + " } }"
                body = T(body_tmpl).safe_substitute(locals())
                mtd.body = st.to_statements(mtd, body) + mtd.body

            # introduce getter to replace @State in expressions later
            name = "get" + Fname
            mtd_g = Method(clazz=cls, typ=fld.typ, name=name)
            body = T("return ${fname};").safe_substitute(locals())
            mtd_g.body = st.to_statements(mtd, body)
            cls.mtds.append(mtd_g)
            setattr(fld, "getter", mtd_g)

        ##
        ## @Multiton({typ_v, ...})? Map<key,value> fname
        ##
        elif _anno.name == C.A.MULTI:
            # assume fld.typ is Map<key,value>
            m, key, value = util.extract_generics(fld.typ)
            if hasattr(_anno, "values"): values = _anno.values
            elif C.J.MAP in fld.typ: values = [value]

            # add a getter
            mname = sample.find_getter(smpls, values, Fname)
            params = [(key, u"key")]
            mtd_g = Method(clazz=cls, typ=value, name=mname, params=params)
            cls.mtds.append(mtd_g)

            # usage 1: Map<String,Object> systemService
            if len(values) > 1:
                pass
                # TODO: collect key-value mappings occurred at the samples
                # TODO: then add code to initialize those mappings into the constructor
                # usage 2: Map<int,View> viewById
            else:  # i.e. values = [value]
                body = T("""
boolean chk = ${fname}.containsKey(key);
if (chk != true) {
  ${fname}.put(key, new ${value}(key));
}
return ${fname}.get(key);
""").safe_substitute(locals())
            mtd_g.body = st.to_statements(mtd_g, body)
            setattr(fld, "getter", mtd_g)

            # initialize this field
            cname = cls.name
            init = cls.mtd_by_sig(cname)
            if not init: init = cls.add_default_init()
            fld_typ = fld.typ
            body = T("${fname} = new ${fld_typ}();").safe_substitute(locals())
            init.body.extend(st.to_statements(init, body))

        ##
        ## @Get typ fname
        ## @Is typ fname
        ##
        elif _anno.name in [C.A.GET, C.A.IS]:
            # special case: Map<String, Object> _elements;
            if C.J.MAP in fld.typ: pass
            # TODO: collect get$T$ occurrences, where $T$ is any types
            # TODO: then add all those getters which look like below:
            # TODO: $T$ get$T$(String key) { return _elements.get(k); }

        ##
        ## @Has typ fname
        ##
        elif _anno.name == C.A.HAS:
            # add a checker, along with a boolean field, say "_setFname"
            name = "_set" + Fname
            fld_h = Field(clazz=cls, typ=C.J.z, name=name)
            cls.flds.append(fld_h)

            mname = sample.find_getter(smpls, [C.J.z], Fname, "has")
            mtd_h = Method(clazz=cls, typ=C.J.z, name=mname)
            body = T("return ${name};").safe_substitute(locals())
            mtd_h.body = st.to_statements(mtd_h, body)

            # find the setter and add a statement: _setFname := true
            if hasattr(fld, "setter"):
                mtd_s = fld.setter
                body = T("${name} = true;").safe_substitute(locals())
                mtd_s.body.extend(st.to_statements(mtd_s, body))

        ##
        ## @Put(typ_b) typ fname
        ##
        elif _anno.name == C.A.PUT:
            # special case: Map<String, Object> _elements;
            if C.J.MAP in fld.typ:
                pass
                # TODO: collect put$T$ occurrences, where $T$ is any types
                # TODO: then add all those putters which look like below:
                # TODO: void put$T$(String k, $T$ v) { _elements.put(k, v); }
            else:
                pass
Пример #12
0
def mk_harness_android(tmpl, cls, smpl):
    harness = mk_harness(cls, smpl)

    buf = cStringIO.StringIO()

    # TODO: launch resource managers

    # TODO: post an Intent for AUT to ActivityManager
    # XXX: rather, start and post the Intent to the global Context directly
    main_cls = tmpl.main  # SystemServer.main
    # TODO: passing proper parameters
    buf.write("{}.{}();\n".format(main_cls.clazz.name, main_cls.name))

    # global variables, sort of singletons
    buf.write("""
    {0} l = {0}.getMain{0}();
    {1} q = l.myQueue();
    {2} t = {2}.current{2}(); // global Context
  """.format(C.ADR.LOOP, C.ADR.QUE, C.ADR.ACTT))

    # generate an Intent to trigger the main Activity
    # TODO: how to figure out the *main* Activity?
    # XXX: assume there is only one Activity
    acts = tmpl.find_cls_kind(C.ADR.ACT)
    if not acts:
        raise Exception("no Activity at all?")
    elif len(acts) > 1:
        raise Exception("no idea what to start among multiple Activity's",
                        acts)
    main_act = acts[0]
    buf.write("""
    {0} c = new {0}(\"{1}\", \"{2}\");
    {3} i = new {3}(c);
  """.format(C.ADR.CMP, "DONT_CARE_PKG_NAME", main_act.name, C.ADR.INTT))

    post = u"q.enqueue{}".format(C.ADR.MSG)

    # TODO: use ActivityManager's Handler
    # XXX: use the main UI thread's Handler
    buf.write("""
    {0} h = t.get{0}();
    {1} m = new {1}(h);
    m.obj = i;
    m.what = -1; // Intent
    {2}(m, 0);
    @React;
  """.format(C.ADR.HDL, C.ADR.MSG, post))

    # additional global variable(s)
    buf.write("""
    {0} a = t.get{0}();
  """.format(C.ADR.ACT))

    cls_ievt = class_lookup(u"InputEvent")

    # post events in the sample to the main Looper's MessageQueue
    evts = smpl.evts
    _hdl = C.ADR.HDL
    _msg = C.ADR.MSG
    logging.debug("# event(s) in {}: {}".format(smpl.name, len(evts)))
    for i, evt in enumerate(evts):
        cls_evt = class_lookup(evt.kind)
        if not cls_evt:
            logging.debug("undeclared event sort: {}".format(evt.kind))
            continue

        e_i = "e{}".format(i)
        init = str(evt)
        buf.write("""
      {evt.kind} {e_i} = new {init};
    """.format(**locals()))

        # generate a message by wrapping the event
        h_i = "h{}".format(i)
        if cls_evt <= cls_ievt:  # InputEvent, KeyEvent, MotionEvent
            # TODO: use Window(Manager)'s Handler
            # XXX: use the source View's Handler at the moment
            s_i = "s{}".format(i)
            v_i = "v{}".format(i)
            buf.write("""
        int {s_i} = {e_i}.getSource();
        View {v_i} = a.findViewById({s_i});
        {_hdl} {h_i} = {v_i}.get{_hdl}();
      """.format(**locals()))

        else:  # TODO: how to retrieve an appropriate Handler in general?
            buf.write("""
        {_hdl} {h_i} = ...;
      """.format(**locals()))

        m_i = "m{}".format(i)
        e_kind = tmpl.get_event_id(evt.kind)
        buf.write("""
      {_msg} {m_i} = new {_msg}({h_i});
      {m_i}.obj = {e_i};
      {m_i}.what = {e_kind};
    """.format(**locals()))

        # post that message (to the main Looper's MessageQueue)
        buf.write("""
      {post}({m_i}, 0);
      @React;
    """.format(**locals()))

    harness.body = to_statements(harness, unicode(buf.getvalue()))
    cls.add_mtds([harness])
Пример #13
0
def reduce_anno_fld(smpls, tmpl, cls, fld):
  if not fld.annos: return
  cname = cls.name
  fname = fld.name
  Fname = util.cap_1st_only(fname)
  for _anno in fld.annos:

    ##
    ## @State(@Tag(...) | @All) int _state
    ##
    if _anno.name == C.A.STATE:
      setattr(cls, "state", fld)

      # find all the methods involved in this state variable
      where = _anno.accessed
      if where.name == C.A.ALL:
        mtds = cls.mtds[:] # quick copy, since mtds will be modified below
      elif where.name == C.A.TAG:
        find_tag = partial(anno.by_attr, {"tag": where.tag})
        mtds = cls.mtds_w_anno(find_tag)
      else: mtds = cls.mtd_by_name(where)

      # exclude setter to avoid a recursive call
      if hasattr(fld, "setter"): mtds.remove(fld.setter)
      # exclude error, if exists, to avoid redundant state update
      find_err = partial(anno.by_name, C.A.ERR)
      errs = cls.mtds_w_anno(find_err)
      if errs:
        if len(errs) > 1: raise ReduceError("ambiguous error state")
        err = errs[0]
        mtds.remove(err)

      # then add a statement to update this state variable into those methods
      for mtd in mtds:

        # TODO: currently, @Set should be reduced before @State
        if hasattr(fld, "setter"): # use setter if exists
          f_setter = fld.setter.name
          upd = "${f_setter}(??);"
        else: upd = "${fname} = ??;"

        if errs and err:
          e_name = err.name
          trans = "if (??) { " + upd + " } else { ${e_name}(); }"
        else: trans = upd

        body_tmpl = "repeat(??) { if (${fname} == ??) { " + trans + " } }"
        body = T(body_tmpl).safe_substitute(locals())
        mtd.body = st.to_statements(mtd, body) + mtd.body

      # introduce getter to replace @State in expressions later
      name = "get" + Fname
      mtd_g = Method(clazz=cls, typ=fld.typ, name=name)
      body = T("return ${fname};").safe_substitute(locals())
      mtd_g.body = st.to_statements(mtd, body)
      cls.mtds.append(mtd_g)
      setattr(fld, "getter", mtd_g)

    ##
    ## @Multiton({typ_v, ...})? Map<key,value> fname
    ##
    elif _anno.name == C.A.MULTI:
      # assume fld.typ is Map<key,value>
      m, key, value = util.extract_generics(fld.typ)
      if hasattr(_anno, "values"): values = _anno.values
      elif C.J.MAP in fld.typ: values = [value]

      # add a getter
      mname = sample.find_getter(smpls, values, Fname)
      params = [ (key, u"key") ]
      mtd_g = Method(clazz=cls, typ=value, name=mname, params=params)
      cls.mtds.append(mtd_g)

      # usage 1: Map<String,Object> systemService
      if len(values) > 1: pass
        # TODO: collect key-value mappings occurred at the samples
        # TODO: then add code to initialize those mappings into the constructor
      # usage 2: Map<int,View> viewById
      else: # i.e. values = [value]
        body = T("""
boolean chk = ${fname}.containsKey(key);
if (chk != true) {
  ${fname}.put(key, new ${value}(key));
}
return ${fname}.get(key);
""").safe_substitute(locals())
      mtd_g.body = st.to_statements(mtd_g, body)
      setattr(fld, "getter", mtd_g)

      # initialize this field
      cname = cls.name
      init = cls.mtd_by_sig(cname)
      if not init: init = cls.add_default_init()
      fld_typ = fld.typ
      body = T("${fname} = new ${fld_typ}();").safe_substitute(locals())
      init.body.extend(st.to_statements(init, body))

    ##
    ## @Get typ fname
    ## @Is typ fname
    ##
    elif _anno.name in [C.A.GET, C.A.IS]:
      # special case: Map<String, Object> _elements;
      if C.J.MAP in fld.typ: pass
        # TODO: collect get$T$ occurrences, where $T$ is any types
        # TODO: then add all those getters which look like below:
        # TODO: $T$ get$T$(String key) { return _elements.get(k); }

    ##
    ## @Has typ fname
    ##
    elif _anno.name == C.A.HAS:
      # add a checker, along with a boolean field, say "_setFname"
      name = "_set" + Fname
      fld_h = Field(clazz=cls, typ=C.J.z, name=name)
      cls.flds.append(fld_h)

      mname = sample.find_getter(smpls, [C.J.z], Fname, "has")
      mtd_h = Method(clazz=cls, typ=C.J.z, name=mname)
      body = T("return ${name};").safe_substitute(locals())
      mtd_h.body = st.to_statements(mtd_h, body)

      # find the setter and add a statement: _setFname := true
      if hasattr(fld, "setter"):
        mtd_s = fld.setter
        body = T("${name} = true;").safe_substitute(locals())
        mtd_s.body.extend(st.to_statements(mtd_s, body))

    ##
    ## @Put(typ_b) typ fname
    ##
    elif _anno.name == C.A.PUT:
      # special case: Map<String, Object> _elements;
      if C.J.MAP in fld.typ: pass
        # TODO: collect put$T$ occurrences, where $T$ is any types
        # TODO: then add all those putters which look like below:
        # TODO: void put$T$(String k, $T$ v) { _elements.put(k, v); }
      else: pass
Пример #14
0
def reduce_anno_e(tmpl, cls, mtd, e, pe=None):
  curried = partial(reduce_anno_e, tmpl, cls, mtd)
  if e.kind == C.E.ANNO:
    _anno = e.anno

    ##
    ## Getters
    ##
    if _anno.name in [C.A.STATE, C.A.GET]:
      args = []
      if _anno.name == C.A.STATE:
        # @State(this) -> this.getter()
        if _anno.accessed == C.J.THIS:
          mtd_g = cls.state.getter
          f = st.gen_E_id(mtd_g.name)
        # @State(var) -> var.getter()
        else:
          var = _anno.accessed
          tname = mtd.vars[var]
          mtd_g = class_lookup(tname).state.getter
          f = st.gen_E_dot(st.gen_E_id(var), st.gen_E_id(mtd_g.name))
      # @Get(var.fname, args?) -> var.getFname(args?)
      elif _anno.name == C.A.GET:
        var, fname = _anno.fid.split('.')
        tname = mtd.vars[var]
        mtd_g = class_lookup(tname).fld_by_name(fname).getter
        f = st.gen_E_dot(st.gen_E_id(var), st.gen_E_id(mtd_g.name))
        if hasattr(_anno, "args"): args = _anno.args
      return st.gen_E_call(f, args)

    ##
    ## Setter
    ##
    # @Update(var) -> var.setter(??)
    elif _anno.name == C.A.UPDATE:
      var, fname = _anno.fid.split('.')
      tname = mtd.vars[var]
      mtd_s = class_lookup(tname).fld_by_name(fname).setter
      f = st.gen_E_dot(st.gen_E_id(var), st.gen_E_id(mtd_s.name))
      return st.gen_E_call(f, [st.gen_E_hole()])

    ##
    ## Generator
    ##
    elif _anno.name in [C.A.ALL, C.A.TAG]:
      if _anno.name == C.A.ALL: tag_g = u"gen_all"
      else: tag_g = u"gen_" + _anno.tag # assume tag name is unique
      # (var.)? @anno
      if not pe: cls_g = cls
      else: cls_g = class_lookup(mtd.vars[unicode(pe.le)])

      if hasattr(cls_g, tag_g): mtd_g = getattr(cls_g, tag_g)
      else: # introduce generator
        if _anno.name == C.A.ALL: mtds = cls_g.mtds
        else: # C.A.TAG
          find_tag = partial(anno.by_attr, {"tag": _anno.tag})
          mtds = cls_g.mtds_w_anno(find_tag)
        mtd_g = Method(clazz=cls_g, mods=[C.mod.GN], name=tag_g)
        body = "int t = ??;\n"
        for i, mtd in enumerate(mtds):
          mid = mtd.name
          case = T("if (t == ${i}) { ${mid}(); }\n").safe_substitute(locals())
          body = body + case

        body = body + "assert t <= {};".format(len(mtds))
        mtd_g.body = st.to_statements(mtd_g, body)

        cls_g.mtds.append(mtd_g)
        setattr(cls_g, tag_g, mtd_g)

      # @Tag("tag") | @All -> gen_tag | gen_all
      return st.gen_E_id(mtd_g.name)

    ##
    ## Reflection
    ##
    # @New should be handled differently
    # e.g., for "ClassName".@New({ args })
    # Sketch: new ClassName(args);
    # Java: (ClassName)(Class.forName("ClassName").newInstance(args));
    elif _anno.name == C.A.NEW: pass

    ##
    ## Comparator
    ##
    # @Compare will be encoded into a regex generator in Sketch
    # and then will be replaced with a proper operator in Java
    elif _anno.name in [C.A.CMP, C.A.CMP_STR]: pass

    ##
    ## Observers
    ##
    # event sending
    elif _anno.name == C.A.EVENT: pass # TODO

    # a list that maintains the registered observers
    elif _anno.name == C.A.OBSS:
      if not hasattr(cls, "obs"):
        cls_o_name, _ = find_observer_at_cls(tmpl, cls)
        add_obs(cls, cls_o_name)
      return st.gen_E_id(cls.obs.name)

    # invoke the @Notified method
    elif _anno.name == C.A.NOTI:
      mtd_noti = find_noti(tmpl, cls)
      rcv = _anno.args[0]
      call_noti = st.gen_E_dot(rcv, st.gen_E_id(mtd_noti.name))
      return st.gen_E_call(call_noti, _anno.args[1:])

  elif e.kind in [C.E.BOP, C.E.DOT]:
    e.le = curried(e.le, e)
    e.re = curried(e.re, e)

  elif e.kind == C.E.NEW:
    e.e = curried(e.e)

  elif e.kind == C.E.CALL:
    e.f = curried(e.f)
    map(curried, e.a)

  return e