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)
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
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
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
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