diff options
-rw-r--r-- | ChangeLog | 15 | ||||
-rw-r--r-- | elaborate.cxx | 171 | ||||
-rw-r--r-- | staptree.cxx | 45 | ||||
-rwxr-xr-x | testsuite/semko/fifteen.stp | 14 | ||||
-rwxr-xr-x | testsuite/semko/fourteen.stp | 20 | ||||
-rwxr-xr-x | testsuite/semko/thirteen.stp | 20 | ||||
-rwxr-xr-x | testsuite/transok/eight.stp | 30 | ||||
-rw-r--r-- | translate.cxx | 142 |
8 files changed, 438 insertions, 19 deletions
@@ -1,3 +1,18 @@ +2005-07-22 Graydon Hoare <graydon@redhat.com> + + * translate.cxx (itervar): New class. + (*::visit_foreach_loop): Implement. + Various bug fixes. + * staptree.cxx (deep_copy_visitor::*): Copy tok fields. + * elaborate.cxx (lvalue_aware_traversing_visitor): + (mutated_map_collector): + (no_map_mutation_during_iteration_check): New classes. + (semantic_pass_maps): New function to check map usage. + (semantic_pass): Call it. + * testsuite/transok/eight.stp: Test 'foreach' loop translation. + * testsuite/semko/{thirteen,fourteen,fifteen}.stp: + Test prohibited forms of foreach loops. + 2005-07-21 Martin Hunt <hunt@redhat.com> * Makefile.am (EXTRA_DIST): Add systemtap.spec. diff --git a/elaborate.cxx b/elaborate.cxx index 8adf9f96..8980895f 100644 --- a/elaborate.cxx +++ b/elaborate.cxx @@ -15,8 +15,11 @@ extern "C" { #include <sys/utsname.h> } -#include <fstream> #include <algorithm> +#include <fstream> +#include <map> +#include <set> +#include <vector> #if 0 #ifdef HAVE_ELFUTILS_LIBDW_H @@ -378,11 +381,176 @@ symresolution_info::derive_probes (match_node * root, } // ------------------------------------------------------------------------ +// +// Map usage checks +// + +class lvalue_aware_traversing_visitor + : public traversing_visitor +{ + unsigned lval_depth; +public: + + lvalue_aware_traversing_visitor() : lval_depth(0) {} + + bool is_in_lvalue() + { + return lval_depth > 0; + } + + virtual void visit_pre_crement (pre_crement* e) + { + ++lval_depth; + e->operand->visit (this); + --lval_depth; + } + + virtual void visit_post_crement (post_crement* e) + { + ++lval_depth; + e->operand->visit (this); + --lval_depth; + } + + virtual void visit_assignment (assignment* e) + { + ++lval_depth; + e->left->visit (this); + --lval_depth; + e->right->visit (this); + } + + virtual void visit_delete_statement (delete_statement* s) + { + ++lval_depth; + s->value->visit (this); + --lval_depth; + } + +}; + + +struct mutated_map_collector + : public lvalue_aware_traversing_visitor +{ + set<vardecl *> * mutated_maps; + + mutated_map_collector(set<vardecl *> * mm) + : mutated_maps (mm) + {} + + void visit_arrayindex (arrayindex *e) + { + if (is_in_lvalue()) + mutated_maps->insert(e->referent); + } +}; + + +struct no_map_mutation_during_iteration_check + : public lvalue_aware_traversing_visitor +{ + systemtap_session & session; + map<functiondecl *,set<vardecl *> *> & function_mutates_maps; + vector<vardecl *> maps_being_iterated; + + no_map_mutation_during_iteration_check + (systemtap_session & sess, + map<functiondecl *,set<vardecl *> *> & fmm) + : session(sess), function_mutates_maps (fmm) + {} + + void visit_arrayindex (arrayindex *e) + { + if (is_in_lvalue()) + { + for (unsigned i = 0; i < maps_being_iterated.size(); ++i) + { + vardecl *m = maps_being_iterated[i]; + if (m == e->referent) + { + string err = ("map '" + m->name + + "' modified during 'foreach' iteration"); + session.print_error (semantic_error (err, e->tok)); + } + } + } + } + + void visit_functioncall (functioncall* e) + { + map<functiondecl *,set<vardecl *> *>::const_iterator i + = function_mutates_maps.find (e->referent); + + if (i != function_mutates_maps.end()) + { + for (unsigned j = 0; j < maps_being_iterated.size(); ++j) + { + vardecl *m = maps_being_iterated[j]; + if (i->second->find (m) != i->second->end()) + { + string err = ("function call modifies map '" + m->name + + "' during 'foreach' iteration"); + session.print_error (semantic_error (err, e->tok)); + } + } + } + + for (unsigned i=0; i<e->args.size(); i++) + e->args[i]->visit (this); + } + + void visit_foreach_loop(foreach_loop* s) + { + maps_being_iterated.push_back (s->base_referent); + for (unsigned i=0; i<s->indexes.size(); i++) + s->indexes[i]->visit (this); + s->block->visit (this); + maps_being_iterated.pop_back(); + } +}; +static int +semantic_pass_maps (systemtap_session & sess) +{ + + map<functiondecl *, set<vardecl *> *> fmm; + no_map_mutation_during_iteration_check chk(sess, fmm); + + for (unsigned i = 0; i < sess.functions.size(); ++i) + { + functiondecl * fn = sess.functions[i]; + if (fn->body) + { + set<vardecl *> * m = new set<vardecl *>(); + mutated_map_collector mc (m); + fn->body->visit (&mc); + fmm[fn] = m; + } + } + + for (unsigned i = 0; i < sess.functions.size(); ++i) + { + if (sess.functions[i]->body) + sess.functions[i]->body->visit (&chk); + } + + for (unsigned i = 0; i < sess.probes.size(); ++i) + { + if (sess.probes[i]->body) + sess.probes[i]->body->visit (&chk); + } + + return sess.num_errors; +} + +// ------------------------------------------------------------------------ + static int semantic_pass_symbols (systemtap_session&); static int semantic_pass_types (systemtap_session&); +static int semantic_pass_maps (systemtap_session&); @@ -485,6 +653,7 @@ semantic_pass (systemtap_session& s) int rc = semantic_pass_symbols (s); if (rc == 0) rc = semantic_pass_types (s); + if (rc == 0) rc = semantic_pass_maps (s); return rc; } diff --git a/staptree.cxx b/staptree.cxx index 8183cffb..9a6d72bd 100644 --- a/staptree.cxx +++ b/staptree.cxx @@ -1015,6 +1015,7 @@ void deep_copy_visitor::visit_block (block *s) { block *n = new block; + n->tok = s->tok; for (unsigned i = 0; i < s->statements.size(); ++i) { statement *ns; @@ -1027,13 +1028,16 @@ deep_copy_visitor::visit_block (block *s) void deep_copy_visitor::visit_null_statement (null_statement *s) { - provide <null_statement*> (this, new null_statement); + null_statement *n = new null_statement; + n->tok = s->tok; + provide <null_statement*> (this, n); } void deep_copy_visitor::visit_expr_statement (expr_statement *s) { expr_statement *n = new expr_statement; + n->tok = s->tok; require <expression*> (this, &(n->value), s->value); provide <expr_statement*> (this, n); } @@ -1042,6 +1046,7 @@ void deep_copy_visitor::visit_if_statement (if_statement* s) { if_statement *n = new if_statement; + n->tok = s->tok; require <expression*> (this, &(n->condition), s->condition); require <statement*> (this, &(n->thenblock), s->thenblock); require <statement*> (this, &(n->elseblock), s->elseblock); @@ -1052,6 +1057,7 @@ void deep_copy_visitor::visit_for_loop (for_loop* s) { for_loop *n = new for_loop; + n->tok = s->tok; require <expr_statement*> (this, &(n->init), s->init); require <expression*> (this, &(n->cond), s->cond); require <expr_statement*> (this, &(n->incr), s->incr); @@ -1063,6 +1069,7 @@ void deep_copy_visitor::visit_foreach_loop (foreach_loop* s) { foreach_loop *n = new foreach_loop; + n->tok = s->tok; for (unsigned i = 0; i < s->indexes.size(); ++i) { symbol *sym; @@ -1079,6 +1086,7 @@ void deep_copy_visitor::visit_return_statement (return_statement* s) { return_statement *n = new return_statement; + n->tok = s->tok; require <expression*> (this, &(n->value), s->value); provide <return_statement*> (this, n); } @@ -1087,6 +1095,7 @@ void deep_copy_visitor::visit_delete_statement (delete_statement* s) { delete_statement *n = new delete_statement; + n->tok = s->tok; require <expression*> (this, &(n->value), s->value); provide <delete_statement*> (this, n); } @@ -1094,31 +1103,41 @@ deep_copy_visitor::visit_delete_statement (delete_statement* s) void deep_copy_visitor::visit_next_statement (next_statement* s) { - provide <next_statement*> (this, new next_statement); + next_statement *n = new next_statement; + n->tok = s->tok; + provide <next_statement*> (this, n); } void deep_copy_visitor::visit_break_statement (break_statement* s) { - provide <break_statement*> (this, new break_statement); + break_statement *n = new break_statement; + n->tok = s->tok; + provide <break_statement*> (this, n); } void deep_copy_visitor::visit_continue_statement (continue_statement* s) { - provide <continue_statement*> (this, new continue_statement); + continue_statement *n = new continue_statement; + n->tok = s->tok; + provide <continue_statement*> (this, n); } void deep_copy_visitor::visit_literal_string (literal_string* e) { - provide <literal_string*> (this, new literal_string(e->value)); + literal_string *n = new literal_string(e->value); + n->tok = e->tok; + provide <literal_string*> (this, n); } void deep_copy_visitor::visit_literal_number (literal_number* e) { - provide <literal_number*> (this, new literal_number(e->value)); + literal_number *n = new literal_number(e->value); + n->tok = e->tok; + provide <literal_number*> (this, n); } void @@ -1126,6 +1145,7 @@ deep_copy_visitor::visit_binary_expression (binary_expression* e) { binary_expression *n = new binary_expression; n->op = e->op; + n->tok = e->tok; require <expression*> (this, &(n->left), e->left); require <expression*> (this, &(n->right), e->right); provide <binary_expression*> (this, n); @@ -1136,6 +1156,7 @@ deep_copy_visitor::visit_unary_expression (unary_expression* e) { unary_expression *n = new unary_expression; n->op = e->op; + n->tok = e->tok; require <expression*> (this, &(n->operand), e->operand); provide <unary_expression*> (this, n); } @@ -1145,6 +1166,7 @@ deep_copy_visitor::visit_pre_crement (pre_crement* e) { pre_crement *n = new pre_crement; n->op = e->op; + n->tok = e->tok; require <expression*> (this, &(n->operand), e->operand); provide <pre_crement*> (this, n); } @@ -1154,6 +1176,7 @@ deep_copy_visitor::visit_post_crement (post_crement* e) { post_crement *n = new post_crement; n->op = e->op; + n->tok = e->tok; require <expression*> (this, &(n->operand), e->operand); provide <post_crement*> (this, n); } @@ -1164,6 +1187,7 @@ deep_copy_visitor::visit_logical_or_expr (logical_or_expr* e) { logical_or_expr *n = new logical_or_expr; n->op = e->op; + n->tok = e->tok; require <expression*> (this, &(n->left), e->left); require <expression*> (this, &(n->right), e->right); provide <logical_or_expr*> (this, n); @@ -1174,6 +1198,7 @@ deep_copy_visitor::visit_logical_and_expr (logical_and_expr* e) { logical_and_expr *n = new logical_and_expr; n->op = e->op; + n->tok = e->tok; require <expression*> (this, &(n->left), e->left); require <expression*> (this, &(n->right), e->right); provide <logical_and_expr*> (this, n); @@ -1183,6 +1208,7 @@ void deep_copy_visitor::visit_array_in (array_in* e) { array_in *n = new array_in; + n->tok = e->tok; require <arrayindex*> (this, &(n->operand), e->operand); provide <array_in*> (this, n); } @@ -1192,6 +1218,7 @@ deep_copy_visitor::visit_comparison (comparison* e) { comparison *n = new comparison; n->op = e->op; + n->tok = e->tok; require <expression*> (this, &(n->left), e->left); require <expression*> (this, &(n->right), e->right); provide <comparison*> (this, n); @@ -1202,6 +1229,7 @@ deep_copy_visitor::visit_concatenation (concatenation* e) { concatenation *n = new concatenation; n->op = e->op; + n->tok = e->tok; require <expression*> (this, &(n->left), e->left); require <expression*> (this, &(n->right), e->right); provide <concatenation*> (this, n); @@ -1211,6 +1239,7 @@ void deep_copy_visitor::visit_ternary_expression (ternary_expression* e) { ternary_expression *n = new ternary_expression; + n->tok = e->tok; require <expression*> (this, &(n->cond), e->cond); require <expression*> (this, &(n->truevalue), e->truevalue); require <expression*> (this, &(n->falsevalue), e->falsevalue); @@ -1222,6 +1251,7 @@ deep_copy_visitor::visit_assignment (assignment* e) { assignment *n = new assignment; n->op = e->op; + n->tok = e->tok; require <expression*> (this, &(n->left), e->left); require <expression*> (this, &(n->right), e->right); provide <assignment*> (this, n); @@ -1231,6 +1261,7 @@ void deep_copy_visitor::visit_symbol (symbol* e) { symbol *n = new symbol; + n->tok = e->tok; n->name = e->name; n->referent = NULL; provide <symbol*> (this, n); @@ -1240,6 +1271,7 @@ void deep_copy_visitor::visit_arrayindex (arrayindex* e) { arrayindex *n = new arrayindex; + n->tok = e->tok; n->base = e->base; n->referent = NULL; for (unsigned i = 0; i < e->indexes.size(); ++i) @@ -1255,6 +1287,7 @@ void deep_copy_visitor::visit_functioncall (functioncall* e) { functioncall *n = new functioncall; + n->tok = e->tok; n->function = e->function; n->referent = NULL; for (unsigned i = 0; i < e->args.size(); ++i) diff --git a/testsuite/semko/fifteen.stp b/testsuite/semko/fifteen.stp new file mode 100755 index 00000000..612271db --- /dev/null +++ b/testsuite/semko/fifteen.stp @@ -0,0 +1,14 @@ +#! stap -p2 + +global tangle + +probe begin +{ + foreach (state in tangle) + { + state = tangle[state]++ + } + printk("hello from systemtap") +} + + diff --git a/testsuite/semko/fourteen.stp b/testsuite/semko/fourteen.stp new file mode 100755 index 00000000..16bd784b --- /dev/null +++ b/testsuite/semko/fourteen.stp @@ -0,0 +1,20 @@ +#! stap -p2 + +global foo + +function bar() +{ + a = foo["hello"]++ + return 10 +} + +probe begin +{ + foreach (a in foo) + { + bar() + } + printk("hello from systemtap") +} + + diff --git a/testsuite/semko/thirteen.stp b/testsuite/semko/thirteen.stp new file mode 100755 index 00000000..88761ded --- /dev/null +++ b/testsuite/semko/thirteen.stp @@ -0,0 +1,20 @@ +#! stap -p2 + +global foo + +function bar() +{ + foo["hello"] = 10 + return 10 +} + +probe begin +{ + foreach (a in foo) + { + bar() + } + printk("hello from systemtap") +} + + diff --git a/testsuite/transok/eight.stp b/testsuite/transok/eight.stp new file mode 100755 index 00000000..e703a5ae --- /dev/null +++ b/testsuite/transok/eight.stp @@ -0,0 +1,30 @@ +#! stap -p3 + +global foo +global baz + +function bar() +{ + return foo["hello"] +} + +probe begin +{ + foo["hello"] = 10 + foreach (a in foo) + { + bar() + } + + baz[1,"hello",a] = "chicken" + baz[1,"goodbye",a] = "supreme" + + foreach ([x,y,z] in baz) + { + printk(baz[x,y,z] . y) + } + + printk("hello from systemtap") +} + + diff --git a/translate.cxx b/translate.cxx index 3c26fade..d794c6be 100644 --- a/translate.cxx +++ b/translate.cxx @@ -33,6 +33,7 @@ stringify(T t) struct var; struct tmpvar; struct mapvar; +struct itervar; struct c_unparser: public unparser, public visitor { @@ -67,6 +68,7 @@ struct c_unparser: public unparser, public visitor string c_typename (exp_type e); string c_varname (const string& e); + void c_assign (var& lvalue, const string& rvalue, const token *tok); void c_assign (const string& lvalue, expression* rvalue, const string& msg); void c_assign (const string& lvalue, const string& rvalue, exp_type type, const string& msg, const token* tok); @@ -84,6 +86,7 @@ struct c_unparser: public unparser, public visitor tmpvar gensym(exp_type ty); var getvar(vardecl *v, token const *tok = NULL); + itervar getiter(foreach_loop *f); mapvar getmap(vardecl *v, token const *tok = NULL); void load_map_indices(arrayindex *e, @@ -131,10 +134,13 @@ struct c_tmpcounter: { c_unparser* parent; c_tmpcounter (c_unparser* p): - parent (p) {} + parent (p) + { + parent->tmpvar_counter = 0; + } // void visit_for_loop (for_loop* s); - // void visit_foreach_loop (foreach_loop* s); + void visit_foreach_loop (foreach_loop* s); // void visit_return_statement (return_statement* s); // void visit_delete_statement (delete_statement* s); void visit_binary_expression (binary_expression* e); @@ -170,7 +176,7 @@ struct c_unparser_assignment: tmpvar const & rval, token const * tok); - void c_assignop(tmpvar const & dst, + void c_assignop(tmpvar & res, var const & lvar, tmpvar const & tmp, token const * tok); @@ -240,13 +246,13 @@ public: if (ty == pe_long) { c.o->newline() << "if (" << qname() << " == 0) {"; - c.o->newline(1) << "errorcount++;"; + c.o->newline(1) << "c->errorcount++;"; c.o->newline() << qname() << " = 1;"; c.o->newline(-1) << "}"; } } - string init() + string init() const { switch (type()) { @@ -377,6 +383,65 @@ struct mapvar }; +class itervar +{ + exp_type referent_ty; + string name; + +public: + + itervar (foreach_loop *e, unsigned & counter) + : referent_ty(e->base_referent->type), + name("__tmp" + stringify(counter++)) + { + if (referent_ty != pe_long && referent_ty != pe_string) + throw semantic_error("iterating over illegal reference type", e->tok); + } + + string declare () const + { + return "struct map_node *" + name + ";"; + } + + string start (mapvar const & mv) const + { + if (mv.type() != referent_ty) + throw semantic_error("inconsistent iterator type in itervar::start()"); + + return "_stp_map_start (" + mv.qname() + ")"; + } + + string next (mapvar const & mv) const + { + if (mv.type() != referent_ty) + throw semantic_error("inconsistent iterator type in itervar::next()"); + + return "_stp_map_iter (" + mv.qname() + ", " + qname() + ")"; + } + + string qname () const + { + return "l->" + name; + } + + string get_key (exp_type ty, unsigned i) const + { + switch (ty) + { + case pe_long: + return "_stp_key_get_int64 ("+ qname() + ", " + stringify(i) + ")"; + case pe_string: + return "_stp_key_get_str ("+ qname() + ", " + stringify(i) + ")"; + default: + throw semantic_error("illegal key type"); + } + } +}; + +ostream & operator<<(ostream & o, itervar const & v) +{ + return o << v.qname(); +} // ------------------------------------------------------------------------ @@ -915,6 +980,21 @@ c_unparser::c_varname (const string& e) return e; } +void +c_unparser::c_assign (var& lvalue, const string& rvalue, const token *tok) +{ + switch (lvalue.type()) + { + case pe_string: + c_strcpy(lvalue.qname(), rvalue); + break; + case pe_long: + o->newline() << lvalue << " = " << rvalue << ";"; + break; + default: + throw semantic_error ("unknown rvalue type in assignment", tok); + } +} void c_unparser::c_assign (const string& lvalue, expression* rvalue, @@ -959,7 +1039,7 @@ c_unparser::c_assign (const string& lvalue, const string& rvalue, void -c_unparser_assignment::c_assignop(tmpvar const & _res, +c_unparser_assignment::c_assignop(tmpvar & res, var const & lval, tmpvar const & rval, token const * tok) @@ -975,7 +1055,7 @@ c_unparser_assignment::c_assignop(tmpvar const & _res, // we'd like to work with a local tmpvar so we can overwrite it in // some optimized cases - tmpvar res = _res; + translator_output* o = parent->o; if (res.type() == pe_string) @@ -1152,6 +1232,13 @@ c_unparser::getmap(vardecl *v, token const *tok) } +itervar +c_unparser::getiter(foreach_loop *f) +{ + return itervar (f, tmpvar_counter); +} + + void c_unparser::visit_block (block *s) { @@ -1248,9 +1335,40 @@ c_unparser::visit_for_loop (for_loop *s) void +c_tmpcounter::visit_foreach_loop (foreach_loop *s) +{ + itervar iv = parent->getiter (s); + parent->o->newline() << iv.declare(); + s->block->visit (this); +} + +void c_unparser::visit_foreach_loop (foreach_loop *s) { - throw semantic_error ("foreach loop not yet implemented", s->tok); + mapvar mv = getmap (s->base_referent, s->tok); + itervar iv = getiter (s); + vector<var> keys; + + varlock guard (*this, mv); + + o->newline() << "for (" + << iv << " = " << iv.start (mv) << "; "; + o->newline() << " " << iv << "; "; + o->newline() << " " << iv << " = " << iv.next (mv) << ")"; + o->newline() << "{"; + o->indent (1); + + for (unsigned i = 0; i < s->indexes.size(); ++i) + { + // copy the iter values into the specified locals + var v = getvar (s->indexes[i]->referent); + c_assign (v, iv.get_key (v.type(), i), s->tok); + } + + s->block->visit (this); + + o->indent (-1); + o->newline() << "}"; } @@ -1760,13 +1878,13 @@ c_unparser::visit_arrayindex (arrayindex* e) // reentrancy issues that pop up with nested expressions: // e.g. a[a[c]=5] could deadlock - o->newline() << "if (errorcount) goto out;"; + o->newline() << "if (c->errorcount) goto out;"; { mapvar mvar = getmap (e->referent, e->tok); varlock guard (*this, mvar); o->newline() << mvar.seek (idx) << ";"; - o->newline() << res << " = " << mvar.get() << ";"; + c_assign (res, mvar.get(), e->tok); } o->newline() << res << ";"; @@ -1836,7 +1954,7 @@ c_unparser_assignment::visit_arrayindex (arrayindex *e) // reentrancy issues that pop up with nested expressions: // e.g. ++a[a[c]=5] could deadlock - o->newline() << "if (errorcount) goto out;"; + o->newline() << "if (c->errorcount) goto out;"; prepare_rvalue (op, rvar, e->tok); @@ -1844,7 +1962,7 @@ c_unparser_assignment::visit_arrayindex (arrayindex *e) mapvar mvar = parent->getmap (e->referent, e->tok); varlock guard (*parent, mvar); o->newline() << mvar.seek (idx) << ";"; - o->newline() << lvar << " = " << mvar.get() << ";"; + parent->c_assign (lvar, mvar.get(), e->tok); c_assignop (res, lvar, rvar, e->tok); o->newline() << mvar.set (lvar) << ";"; } |