def sanitize_id(dot_id): pkg, cls, mtd = util.explode_mname(dot_id) if cls and util.is_class_name(cls) and class_lookup(cls): clazz = class_lookup(cls) if clazz.pkg and pkg and clazz.pkg != pkg: # to avoid u'' != None raise Exception("wrong package", pkg, clazz.pkg) return '.'.join([cls, mtd]) return dot_id
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
def trans_init(cls_name, arg_typs, args): buf = cStringIO.StringIO() cls = class_lookup(cls_name) if util.is_collection(cls_name) or not cls: buf.write(trans_ty(cls_name) + "()") elif is_replaced(cls_name): buf.write(trans_ty(cls_name) + "(hash=nonce())") else: add_on = [] if args: # NOTE: assume the order of arguments is same as that of fields # NOTE: for missing fields, just call default constructors # TODO: use template.sig_match kwargs = zip(cls.flds, args) if kwargs: assigned, _ = zip(*kwargs) else: assigned = [] not_assigned = [fld for fld in cls.flds if fld not in assigned] if not_assigned: def default_init(fld): if util.is_class_name(fld.typ): return C.J.NEW + ' ' + trans_init(fld.typ, [], []) else: return '0' add_on = map(default_init, not_assigned) # else: # means, default constructor flds = ["hash"] + map(op.attrgetter("name"), cls.flds) vals = ["nonce()"] + args + add_on kwargs = map(lambda (f, v): "{}={}".format(f, v), zip(flds, vals)) buf.write('_'.join([cls_name] + arg_typs)) buf.write('(' + ", ".join(kwargs) + ')') return buf.getvalue()
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 marker(cname): cls = class_lookup(cname) if not cls: return if is_marked(cls): return mark(cls) if cls.sup: marker(cls.sup) for itf in cls.itfs: marker(itf) for fld in cls.flds: marker(fld.typ) for mtd in cls.mtds: marker(mtd.typ) for ty, _ in mtd.params: marker(ty)
def gen_log_sk(sk_dir, pgr): buf = cStringIO.StringIO() buf.write("package log;\n") buf.write(_const) buf.write(""" // distinct hash values for runtime objects int obj_cnt = 0; int nonce () { return obj_cnt++; } """) # factory of Object buf.write(""" // factory of Object Object alloc(int ty) {{ Object {0} = new Object(hash=nonce(), __cid=ty); return {0}; }} """.format(C.SK.self)) global _ty; _clss = [] for ty in _ty.keys(): if util.is_collection(ty): continue if util.is_array(ty): continue cls = class_lookup(ty) if not cls: continue # to avoid None definition # inner class may appear twice: w/ and w/o outer class name if cls not in _clss: _clss.append(cls) buf.write("\n// distinct class IDs\n") for cls in _clss: buf.write("int {cls!r} () {{ return {cls.id}; }}\n".format(**locals())) buf.write("\n// distinct method IDs\n") for cls in pgr.classes: mtds = collect_decls(cls, "mtds") if not mtds: continue for mtd in mtds: mname = sanitize_mname(unicode(repr(mtd))) buf.write(""" int {mname}_ent () {{ return {mtd.id}; }} int {mname}_ext () {{ return -{mtd.id}; }} """.format(**locals())) with open(os.path.join(sk_dir, "log.sk"), 'w') as f: f.write(buf.getvalue()) logging.info("encoding " + f.name) buf.close()
def rm_subs(clss): # { cname: Clazz(cname, ...), ... } decls = { cls.name: cls for cls in clss } # remove subclasses for cname in decls.keys(): if util.is_collection(cname): continue cls = class_lookup(cname) if not cls.is_class: continue for sub in cls.subs: if sub.name in decls: logging.debug("{} < {}".format(sub.name, cname)) del decls[sub.name] for sup in util.ffilter([cls.sup]): if sup in decls and cname in decls: logging.debug("{} < {}".format(cname, sup)) del decls[cname] return decls.values()
def rm_subs(clss): # { cname: Clazz(cname, ...), ... } decls = { cls.name: cls for cls in clss } # remove subclasses for cname in decls.keys(): if util.is_collection(cname): continue cls = class_lookup(cname) if not cls.is_class: continue if cls.is_aux: continue # virtual relations; don't remove sub classes for sub in cls.subs: if sub.name in decls: logging.debug("{} < {}".format(sub.name, cname)) del decls[sub.name] for sup in util.ffilter([cls.sup]): if sup in decls and cname in decls: logging.debug("{} < {}".format(cname, sup)) del decls[cname] return decls.values()
def main(cmd, smpl_paths, tmpl_paths, patterns, out_dir, log_lv=logging.DEBUG): ## logging configuration logging.config.fileConfig(os.path.join(pwd, "logging.conf")) logging.getLogger().setLevel(log_lv) ## check custom codegen was built codegen_jar = os.path.join("codegen", "lib", "codegen.jar") if not os.path.isfile(codegen_jar): raise Exception("can't find " + codegen_jar) if cmd == "android": tmpl_paths.append(adr_tmpl) elif cmd == "gui": tmpl_paths.append(gui_tmpl) if cmd == "pattern": _patterns = patterns[:] else: ## android or gui _patterns = [C.P.ACCA, C.P.ACCU, C.P.ACCM, C.P.ADP, C.P.BLD, C.P.FAC, C.P.SNG, C.P.PRX, C.P.OBS, C.P.STA] opts = [] ## sketch options if conf["verbose"]: opts.extend(["-V", "10"]) if conf["timeout"]: opts.extend(["--fe-timeout", str(conf["timeout"])]) opts.extend(["--slv-timeout", str(conf["timeout"])]) # place to keep sketch's temporary files opts.extend(["--fe-tempdir", out_dir]) opts.append("--fe-keep-tmp") tmpls = [] output_paths = [] for p in patterns: ## for each pattern or demo logging.info("demo: " + p) _smpl_paths = smpl_paths[:] _tmpl_paths = tmpl_paths[:] _smpl_paths.append(os.path.join(smpl_dir, cmd, p)) if cmd == "pattern": client_path = os.path.join(tmpl_dir, cmd, p) else: ## android or gui client_path = os.path.join(tmpl_dir, app, cmd, p) _tmpl_paths.append(client_path) ## (smpl|tmpl)_path is either a single file or a folder containing files ## read and parse templates tmpl_files = [] for tmpl_path in _tmpl_paths: tmpl_files.extend(util.get_files_from_path(tmpl_path, "java")) ast = util.toAST(tmpl_files) ## convert AST to meta data tmpl = Template(ast) ## mark client-side classes client_files = util.get_files_from_path(client_path, "java") for client in client_files: base = os.path.basename(client) cname = os.path.splitext(base)[0] cls = class_lookup(cname) cls.client = True ## read and parse samples smpl_files = [] for smpl_path in _smpl_paths: smpl_files.extend(util.get_files_from_path(smpl_path, "txt")) sample.reset() smpls = [] for fname in smpl_files: smpl = Sample(fname, tmpl.is_event) smpls.append(smpl) ## make harness harness.mk_harnesses(cmd, tmpl, smpls) ## pattern rewriting rewrite.visit(cmd, smpls, tmpl, _patterns) java_sk_dir = os.path.join(out_dir, "_".join(["java_sk", p])) decode.dump(cmd, java_sk_dir, tmpl) ## clean up templates # reducer.reduce_anno(smpls, tmpl) # reducer.remove_cls(smpls, tmpl) tmpl.freeze() tmpls.append(tmpl) ## encode (rewritten) templates into sketch files sk_dir = os.path.join(out_dir, "_".join(["sk", p])) if conf["encoding"]: encoder.to_sk(cmd, smpls, tmpl, sk_dir) else: # not encoding logging.info("pass the encoding phase; rather use previous files") ## run sketch output_path = os.path.join(out_dir, "output", "{}.txt".format(p)) output_paths.append(output_path) if conf["sketch"]: if os.path.exists(output_path): os.remove(output_path) # custom codegen _opts = opts[:] _opts.extend(["--fe-custom-codegen", codegen_jar]) if conf["randassign"] or conf["parallel"]: _opts.append("--slv-randassign") _opts.extend(["--bnd-dag-size", "16000000"]) # 16M ~> 8G memory sketch.set_default_option(_opts) if conf["parallel"]: ## Python implementation as a CEGIS (sketch-backend) wrapper # _, r = sketch.be_p_run(sk_dir, output_path) # Java implementation inside sketch-frontend _opts.append("--slv-parallel") if conf["p_cpus"]: _opts.extend(["--slv-p-cpus", str(conf["p_cpus"])]) if conf["ntimes"]: _opts.extend(["--slv-ntimes", str(conf["ntimes"])]) if conf["randdegree"]: # assume FIXED strategy _opts.extend(["--slv-randdegree", str(conf["randdegree"])]) else: # adaptive concretization _opts.extend(["--slv-strategy", "WILCOXON"]) _, r = sketch.run(sk_dir, output_path) else: _, r = sketch.run(sk_dir, output_path) # if sketch fails, halt the process here if not r: sys.exit(1) ## run sketch again to obtain control-flows sketch.set_default_option(opts) r = sketch.ctrl_flow_run(sk_dir, output_path, out_dir) if not r: sys.exit(1) else: # not running sketch logging.info("pass sketch; rather read: {}".format(output_path)) ## end of loop (per pattern/demo) ## generate compilable model java_dir = os.path.join(out_dir, "java") decode.to_java(cmd, java_dir, tmpls, output_paths, _patterns) logging.info("synthesis done") return 0
def gen_log_sk(sk_dir, tmpl): buf = cStringIO.StringIO() buf.write("package log;\n") buf.write(_const) global max_objs buf.write("int O = {}; // # of objects\n".format(max_objs + 1)) buf.write(""" int log_cnt = 0; int[P][N] ev; int[O] obj; // to enforce the length of logs int get_log_cnt() { return log_cnt; } // after writing logs, reset the cursor in order to check logs in order void reset_log_cnt() { log_cnt = 0; } // to clean up the logs totally void clear_log() { reset_log_cnt(); ev = {}; obj = {}; } // to write the log from samples void write_log (int[P] params) { ev[log_cnt++] = params; } // to check whether control-flow conforms to the samples @Native("{ std::cout << \\\"log::check_log::\\\" << params[0] << std::endl; }") void check_log (int[P] params) { assert params[0] == ev[log_cnt][0]; // check mid for (int i = 1; i < P; i++) { if (ev[log_cnt][i] != 0) { if (obj[ev[log_cnt][i]] == 0) { // not set yet obj[ev[log_cnt][i]] = params[i]; } else { // o.w. check obj eq. assert obj[ev[log_cnt][i]] == params[i]; } } } log_cnt++; // advance } // distinct hash values for runtime objects int obj_cnt = 0; int nonce () { return obj_cnt++; } """) global _inits reg_codes = [] for ty in _inits: cls = class_lookup(ty) if not cls: continue buf.write(""" int obj_{0}_cnt = 0; {1}[O] obj_{0}; // to register runtime instances of {0} void register_{0} ({1} {2}) {{ if (obj_{0}_cnt < O) {{ obj_{0}[obj_{0}_cnt++] = {2}; }} }} // to access to a certain instance of {0} {1} retrieve_{0} (int idx) {{ if (0 <= idx && idx < obj_{0}_cnt) {{ return obj_{0}[idx]; }} else {{ return null; }} }} """.format(ty, trans_ty(ty), ty.lower())) reg_code = "if (ty == {0}) register_{1}@log({2});".format(cls.id, repr(cls), C.SK.self) reg_codes.append(reg_code) # factory of Object buf.write(""" // factory of Object Object alloc(int ty) {{ Object {0} = new Object(hash=nonce(), __cid=ty); {1} return {0}; }} """.format(C.SK.self, "\nelse ".join(reg_codes))) global _ty; _clss = [] for ty in _ty.keys(): if util.is_collection(ty): continue if util.is_array(ty): continue cls = class_lookup(ty) if not cls: continue # to avoid None definition # inner class may appear twice: w/ and w/o outer class name if cls not in _clss: _clss.append(cls) buf.write("\n// distinct class IDs\n") for cls in _clss: buf.write("int {cls!r} () {{ return {cls.id}; }}\n".format(**locals())) buf.write("\n// distinct method IDs\n") for cls in tmpl.classes: mtds = collect_decls(cls, "mtds") if not mtds: continue for mtd in mtds: mname = sanitize_mname(unicode(repr(mtd))) buf.write(""" int {mname}_ent () {{ return {mtd.id}; }} int {mname}_ext () {{ return -{mtd.id}; }} """.format(**locals())) with open(os.path.join(sk_dir, "log.sk"), 'w') as f: f.write(buf.getvalue()) logging.info("encoding " + f.name) buf.close()
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 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)
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])
def get_arg_typ(param): return str(class_lookup(param[0]).id) return '{' + ", ".join(map(get_arg_typ, mtd.params)) + '}'
def get_ret_typ(mtd): cls = class_lookup(mtd.typ) if cls: return cls.id else: return -1
def trans_e(mtd, e): curried = partial(trans_e, mtd) buf = cStringIO.StringIO() if 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 args = util.rm_none([rcv] + map(curried, e.a)) 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 trans_s(mtd, s): curried_e = partial(trans_e, mtd) curried_s = partial(trans_s, mtd) buf = cStringIO.StringIO() if s.kind == C.S.IF: e = curried_e(s.e) t = '\n'.join(map(curried_s, s.t)) f = '\n'.join(map(curried_s, s.f)) buf.write("if (" + e + ") {\n" + t + "\n}") if f: buf.write("\nelse {\n" + f + "\n}") elif s.kind == C.S.WHILE: e = curried_e(s.e) b = '\n'.join(map(curried_s, s.b)) buf.write("while (" + e + ") {\n" + b + "\n}") elif s.kind == C.S.REPEAT: e = curried_e(s.e) b = '\n'.join(map(curried_s, s.b)) if e == "??": buf.write("minrepeat {\n" + b + "\n}") else: buf.write("repeat (" + e + ") {\n" + b + "\n}") elif s.kind == C.S.MINREPEAT: b = '\n'.join(map(curried_s, s.b)) buf.write("minrepeat {\n" + b + "\n}") elif s.kind == C.S.FOR: # assume "for" is used for List<T> and LinkedList<T> only col = mtd.vars[s.init.id] if not util.is_collection(col) or \ util.of_collection(col)[0] not in [C.J.LST, C.J.LNK]: raise Exception("not iterable type", col) # if this is about observers, let sketch choose iteration direction is_obs = hasattr(class_lookup(util.of_collection(col)[1]), "obs") s_init = curried_e(s.init) if is_obs: init = "{{| 0 | {}.idx - 1 |}}".format(s_init) else: init = '0' buf.write(" int idx = {};".format(init)) s_i_typ = trans_ty(s.i.ty) buf.write(""" while (0 <= idx && idx < S && {s_init}.elts[idx] != null) {{ {s_i_typ} {s.i.id} = {s_init}.elts[idx]; """.format(**locals())) buf.write('\n'.join(map(curried_s, s.b))) if is_obs: upd = "{| idx (+ | -) 1 |}" else: upd = "idx + 1" buf.write(""" idx = {}; }} """.format(upd)) elif s.kind == C.S.TRY: # NOTE: no idea how to handle catch blocks # at this point, just walk through try/finally blocks buf.write('\n'.join(map(curried_s, s.b + s.fs))) else: buf.write(s.__str__(curried_e)) 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 main(cmd, smpl_paths, tmpl_paths, patterns, out_dir, log_lv=logging.DEBUG): ## logging configuration logging.config.fileConfig(os.path.join(pwd, "logging.conf")) logging.getLogger().setLevel(log_lv) ## check custom codegen was built codegen_jar = os.path.join("codegen", "lib", "codegen.jar") if not os.path.isfile(codegen_jar): raise Exception("can't find " + codegen_jar) if cmd == "android": tmpl_paths.append(adr_tmpl) elif cmd == "gui": tmpl_paths.append(gui_tmpl) if cmd == "pattern": _patterns = patterns[:] else: ## android or gui _patterns = [C.P.ACCA, C.P.ACCU, C.P.ACCM, C.P.ADP, \ C.P.BLD, C.P.FAC, C.P.SNG, C.P.PRX, C.P.OBS, C.P.STA] opts = [] ## sketch options if conf["verbose"]: opts.extend(["-V", "10"]) if conf["timeout"]: opts.extend(["--fe-timeout", str(conf["timeout"])]) opts.extend(["--slv-timeout", str(conf["timeout"])]) # place to keep sketch's temporary files opts.extend(["--fe-tempdir", out_dir]) opts.append("--fe-keep-tmp") tmpls = [] output_paths = [] for p in patterns: ## for each pattern or demo logging.info("demo: " + p) _smpl_paths = smpl_paths[:] _tmpl_paths = tmpl_paths[:] _smpl_paths.append(os.path.join(smpl_dir, cmd, p)) if cmd == "pattern": client_path = os.path.join(tmpl_dir, cmd, p) else: ## android or gui client_path = os.path.join(tmpl_dir, app, cmd, p) _tmpl_paths.append(client_path) ## (smpl|tmpl)_path is either a single file or a folder containing files ## read and parse templates tmpl_files = [] for tmpl_path in _tmpl_paths: tmpl_files.extend(util.get_files_from_path(tmpl_path, "java")) ast = util.toAST(tmpl_files) ## convert AST to meta data tmpl = Template(ast) ## mark client-side classes client_files = util.get_files_from_path(client_path, "java") for client in client_files: base = os.path.basename(client) cname = os.path.splitext(base)[0] cls = class_lookup(cname) cls.client = True ## read and parse samples smpl_files = [] for smpl_path in _smpl_paths: smpl_files.extend(util.get_files_from_path(smpl_path, "txt")) sample.reset() smpls = [] for fname in smpl_files: smpl = Sample(fname, tmpl.is_event) smpls.append(smpl) ## make harness harness.mk_harnesses(cmd, tmpl, smpls) ## pattern rewriting rewrite.visit(cmd, smpls, tmpl, _patterns) java_sk_dir = os.path.join(out_dir, '_'.join(["java_sk", p])) decode.dump(cmd, java_sk_dir, tmpl) ## clean up templates #reducer.reduce_anno(smpls, tmpl) #reducer.remove_cls(smpls, tmpl) tmpl.freeze() tmpls.append(tmpl) ## encode (rewritten) templates into sketch files sk_dir = os.path.join(out_dir, '_'.join(["sk", p])) if conf["encoding"]: encoder.to_sk(cmd, smpls, tmpl, sk_dir) else: # not encoding logging.info("pass the encoding phase; rather use previous files") ## run sketch output_path = os.path.join(out_dir, "output", "{}.txt".format(p)) output_paths.append(output_path) if conf["sketch"]: if os.path.exists(output_path): os.remove(output_path) # custom codegen _opts = opts[:] _opts.extend(["--fe-custom-codegen", codegen_jar]) if conf["randassign"] or conf["parallel"]: _opts.append("--slv-randassign") _opts.extend(["--bnd-dag-size", "16000000"]) # 16M ~> 8G memory sketch.set_default_option(_opts) if conf["parallel"]: ## Python implementation as a CEGIS (sketch-backend) wrapper #_, r = sketch.be_p_run(sk_dir, output_path) # Java implementation inside sketch-frontend _opts.append("--slv-parallel") if conf["p_cpus"]: _opts.extend(["--slv-p-cpus", str(conf["p_cpus"])]) if conf["ntimes"]: _opts.extend(["--slv-ntimes", str(conf["ntimes"])]) if conf["randdegree"]: # assume FIXED strategy _opts.extend(["--slv-randdegree", str(conf["randdegree"])]) else: # adaptive concretization _opts.extend(["--slv-strategy", "WILCOXON"]) _, r = sketch.run(sk_dir, output_path) else: _, r = sketch.run(sk_dir, output_path) # if sketch fails, halt the process here if not r: sys.exit(1) ## run sketch again to obtain control-flows sketch.set_default_option(opts) r = sketch.ctrl_flow_run(sk_dir, output_path, out_dir) if not r: sys.exit(1) else: # not running sketch logging.info("pass sketch; rather read: {}".format(output_path)) ## end of loop (per pattern/demo) ## generate compilable model java_dir = os.path.join(out_dir, "java") decode.to_java(cmd, java_dir, tmpls, output_paths, _patterns) logging.info("synthesis done") return 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