pax_global_header00006660000000000000000000000064141476724660014533gustar00rootroot0000000000000052 comment=38a201f16cda1b54b4054b3601dbdeee4be9c391 byteman-4.0.18/000077500000000000000000000000001414767246600132645ustar00rootroot00000000000000byteman-4.0.18/.gitignore000066400000000000000000000001531414767246600152530ustar00rootroot00000000000000release.properties *.releaseBackup .idea .classpath .project .settings .metadata target *.iml *.ipr *.iws byteman-4.0.18/README000066400000000000000000000065721414767246600141560ustar00rootroot00000000000000Byteman README -------------- Byteman supports injection of side effects into Java programs for the purpose of tracing and testing application behaviour. Installation ------------ When you unzip the downloaded release it will explode to a single target install directory containing everything you need to use byteman. You should set environment variable BYTEMAN_HOME so that it points to this target install directory. The install directory for the binary release contains the following subdirectories bin -- contains Unix scripts to simplify: using Byteman (bmjava.sh); typechecking Byteman rule scripts (bytemancheck.sh); communicating with the Byteman agent listener (bmsubmit.sh); and installing the agent into a running JVM (bminstall.sh). You will need to change the permissions of these files in order to execute them directly from the command line. docs -- contains the Byteman Programmer's guide and license information for Byteman plus the third-party software it uses lib -- contains the Byteman agent and rule engine (byteman.jar); the subset of this code needed to compile/run the submit client (byteman-submit.jar); the subset of this code needed to compile/run the install client (byteman-install.jar). sample/scripts -- contains example rule scripts showing how to use Byteman sample/lib -- contains helper code for the sample scripts contrib -- contains software contributed by other Byteman users see the README in each contrib subdirectory for details of how to use the package The install directory for the full source release contains all the above directories plus the following extra directories sources -- contains source code jars for each of the component jars in lib, sample/lib, contrib/dtest and contrib/bmunit javadoc -- contains javadoc format documentation jars for each of the component jars in lib, sample/lib, contrib/dtest and contrib/bmunit Using Byteman ------------- Access to the Byteman project downloads, documentation, user and developer forums and source code is available from the project home page at http://www.jboss.org/byteman Please consult the Programmer's Guide for details of how Byteman operates, how to write Byteman rule scripts and how to run a Java program with the Byteman agent in place. Take a look at the sample scripts directory to see some examples of how you can use Byteman to trace application and JVM execution or to gather and present statistics detailing the operation of JVM or application code. (n.b. these examples still need extending to show how to use Byteman for fault injection based testing). Compiling Byteman ----------------- To compile source code use Maven command `mvn clean install`. Byteman will be compiled, the command generates documentation and distribution artifacts can be found at `download/target`. To speed-up compilation you can skip tests and generating the documentation with `mvn clean install -DskipITs -DskipTests -DskipDocs`. Copyright --------- See the copyright file in the docs directory for details of the open source license under which this code is released. Note that this code employs the ObjectWeb ASM package to do bytecode manipulation and the JFlex and JavaCUP tokeniser and parser generators to generate and provide runtime support for the rule parser which are also released under open source licenses. byteman-4.0.18/agent/000077500000000000000000000000001414767246600143625ustar00rootroot00000000000000byteman-4.0.18/agent/grammar/000077500000000000000000000000001414767246600160105ustar00rootroot00000000000000byteman-4.0.18/agent/grammar/cup/000077500000000000000000000000001414767246600165775ustar00rootroot00000000000000byteman-4.0.18/agent/grammar/cup/ECAGrammar.cup000066400000000000000000000603501414767246600212130ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2008-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ import java_cup.runtime.*; import org.jboss.byteman.rule.grammar.ParseNode; // cannot do this! // import static org.jboss.byteman.rule.grammar.ParseNode.*; /* parser methods to make it easier to access the ParseNode package */ action code {: /* private ParseNode node(int tag) { return ParseNode.node(tag, parser.getFile()); } */ private ParseNode node(int tag, int line, int column) { return ParseNode.node(tag, parser.getFile(), line, column); } private ParseNode node(int tag, int line, int column, Object child0) { return ParseNode.node(tag, parser.getFile(), line, column, child0); } private ParseNode node(int tag, int line, int column, Object child0, Object child1) { return ParseNode.node(tag, parser.getFile(), line, column, child0, child1); } private ParseNode node(int tag, int line, int column, Object child0, Object child1, Object child2) { return ParseNode.node(tag, parser.getFile(), line, column, child0, child1, child2); } /* private ParseNode node(int tag, int line, int column, Object child0, Object child1, Object child2, Object child3) { return ParseNode.node(tag, parser.getFile(), line, column, child0, child1, child2, child3); } */ public void error(String message, int line, int col) { parser.error(message, line, col); } public void error(String message) { parser.error(message); } :} parser code {: private String file = ""; private int errorCount = 0; private StringBuffer errorBuffer = new StringBuffer(); public void setFile(String file) { this.file = file; } /** * Called by the parser when it detects a syntax error. This is overridden so * that it does nothing. Instead the parser relies upon explicit calls to routine * error(String) or error(String, int line, int column) which store details of * synatx errors into an error buffer for retrieval after the parse call completes. * * @param message an error message. * @param info an extra object reserved for use by specialized subclasses. */ public void report_error(String message, Object info) { } /** * Called by the parser when it is unable to recover from one or more syntax errors * * @param cur_token the token current at the point in the token stream where the recovery * process fails */ public void unrecovered_syntax_error(Symbol cur_token) { int line = cur_token.left; errorCount++; errorBuffer.append('\n'); errorBuffer.append(file); if (line >= 0) { errorBuffer.append(" line "); errorBuffer.append(cur_token.left); } errorBuffer.append(" : unable to recover from previous errors"); } public void error(String message) { errorCount++; errorBuffer.append('\n'); errorBuffer.append(file); errorBuffer.append(" : "); errorBuffer.append(message); } public void error(String message, int line, int col) { errorCount++; errorBuffer.append('\n'); errorBuffer.append(file); errorBuffer.append(" line "); errorBuffer.append(line); errorBuffer.append(" : "); errorBuffer.append(message); } public String getFile() { return file; } public int getErrorCount() { return errorCount; } public String getErrors() { return errorBuffer.toString(); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Do debug output for a reduce. * * @param prod_num the production we are reducing with. * @param nt_num the index of the LHS non terminal. * @param rhs_size the size of the RHS. */ public void debug_reduce(int prod_num, int nt_num, int rhs_size) { debug_message("# Reduce with prod #" + prod_num + " [NT=" + nt_num + ", " + "SZ=" + rhs_size + "]"); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Do debug output for shift. * * @param shift_tkn the Symbol being shifted onto the stack. */ public void debug_shift(Symbol shift_tkn) { debug_message("# Shift under term " + shift_tkn + " to state #" + shift_tkn.parse_state); } :} /* Preliminaries to set up and use the scanner. */ /* not needed init with {: scanner.init(); :} */ scan with {: Symbol token = getScanner().next_token(); return token; :}; /* */ /* Terminals (tokens returned by the scanner). */ /* rule definition keywords */ /* first the redundant ones */ /* terminal RULE, METHOD, LINE, ENDRULE; */ /* now the required ones */ terminal CLASS; terminal BIND, IF, DO; /* keywords ocurring in bindings, conditions and actions */ terminal NOTHING, RETURN, THROW; /* brackets */ terminal LPAREN, RPAREN, LSQUARE, RSQUARE; /* braces are used for array initializers */ terminal LBRACE, RBRACE; /* separators */ terminal SEMI, COMMA, DOT; /* assignment */ terminal ASSIGN; /* instance creation */ terminal NEW; /* operators of various flavours */ /* ternary operators */ /* condition operator -- also sepr for var and type in decl */ terminal TERN_IF, COLON; /* binary operators */ /* logical operators */ terminal OR, AND; /* comparison operators */ terminal LT, LE, EQ, NE, GE, GT; /* bitwise operators */ terminal URSH, RSH, LSH, BOR, BAND, BXOR; /* arithmetic operators */ terminal MUL, DIV, PLUS, MINUS, MOD; /* type test operators */ terminal INSTANCEOF; /* unary operators */ /* logical operator */ terminal NOT; /* bitwise operators */ terminal TWIDDLE; /* pseudo operator for minus as prefix */ terminal UMINUS; /* dollar prefixed integer or identifier for referencing paams */ terminal DOLLAR; /* literals. strings and identifiers */ terminal NULL_LITERAL; terminal Float FLOAT_LITERAL; terminal Double DOUBLE_LITERAL; terminal Integer INTEGER_LITERAL; terminal Long LONG_LITERAL; terminal Boolean BOOLEAN_LITERAL; terminal String STRING_LITERAL; terminal String IDENTIFIER; /* non terminals */ non terminal ParseNode eca_rule; non terminal ParseNode eca; non terminal ParseNode ca; non terminal ParseNode event; non terminal ParseNode bindings; non terminal ParseNode binding; non terminal ParseNode binding_expr; non terminal ParseNode bind_sym; non terminal ParseNode condition; non terminal ParseNode actions; non terminal ParseNode action_expr_list; non terminal ParseNode action_expr; non terminal ParseNode throw_return_expr; non terminal ParseNode expr_list; non terminal ParseNode expr; non terminal ParseNode ternary_oper_expr; non terminal ParseNode binary_oper_expr; non terminal ParseNode unary_oper_expr; non terminal ParseNode array_expr; non terminal ParseNode field_expr; non terminal ParseNode expr_field_expr; non terminal ParseNode meth_expr; non terminal ParseNode expr_meth_expr; non terminal ParseNode new_expr; non terminal ParseNode new_array_spec; non terminal ParseNode array_dims; non terminal ParseNode array_dim; non terminal ParseNode class_expr; non terminal ParseNode simple_expr; non terminal ParseNode null_expr; non terminal ParseNode array_idx_list; non terminal ParseNode array_idx; non terminal ParseNode array_init_list; non terminal ParseNode array_init; /* names and paths */ non terminal ParseNode typename; non terminal Integer empty_array_dims; non terminal ParseNode name; non terminal ParseNode simple_name; non terminal ParseNode path; /* error patterns */ non terminal ParseNode eca_error; non terminal ParseNode eca_error_in_event; non terminal ParseNode eca_error_in_condition; non terminal ParseNode eca_error_in_action; non terminal ParseNode ca_error; non terminal ParseNode ca_error_in_condition; non terminal ParseNode ca_error_in_action; /* precedences from weakest to tightest binding */ precedence left SEMI; precedence left COMMA; precedence left ASSIGN; precedence left COLON; precedence left TERN_IF; precedence left OR, AND, NOT; precedence nonassoc EQ, NE; precedence left LT, LE, GE, GT; precedence left PLUS, MINUS; precedence left MUL, DIV, MOD; precedence left URSH, RSH, LSH, BOR, BAND, BXOR; precedence left INSTANCEOF; precedence left TWIDDLE; precedence left UMINUS; precedence left DOT; eca_rule ::= eca:rule {: RESULT = rule; :} | ca:rule {: RESULT = rule; :} ; eca ::= BIND event:e IF condition:c DO actions:a {: RESULT = node(ParseNode.BIND, eleft, eright, e, c, a); :} | eca_error ; eca_error ::= eca_error_in_event | eca_error_in_condition | eca_error_in_action ; eca_error_in_event ::= BIND:b error {: error("invalid event", bleft, bright); :} IF condition:c DO actions:a {: RESULT = node(ParseNode.BIND, bleft, bright, null, c, a); :} ; eca_error_in_condition ::= BIND event:e IF:i error {: error("invalid condition", ileft, iright); :} DO actions:a {: RESULT = node(ParseNode.BIND, eleft, eright, e, null, a); :} ; eca_error_in_action ::= BIND event:e IF condition:c DO:d error {: error("invalid action", dleft, dright); RESULT = node(ParseNode.BIND, eleft, eright, e, c, null); :} ; ca ::= IF condition:c DO actions:a {: RESULT = node(ParseNode.BIND, cleft, cright, null, c, a); :} | ca_error ; ca_error ::= ca_error_in_condition | ca_error_in_action ; ca_error_in_condition ::= IF:i error {: error("invalid condition", ileft, iright); :} DO actions:a {: RESULT = node(ParseNode.BIND, ileft, iright, null, null, a); :} | DO actions:a {: error("missing IF clause", aleft, aright); RESULT = node(ParseNode.BIND, aleft, aright, null, null, a); :} ; ca_error_in_action ::= IF condition:c DO:d error {: error("invalid action", dleft, dright); RESULT = node(ParseNode.BIND, cleft, cright, null, c, null); :} | DO:d error {: error("invalid action", dleft, dright); error("missing IF clause", dleft, dright); RESULT = node(ParseNode.BIND, dleft, dright, null, null, null); :} ; // event specifications -- for now events are just a list of bindings event ::= NOTHING:n {: RESULT = node(ParseNode.NOTHING, nleft, nright); :} | bindings:b {: RESULT = b; :} ; // zero event bindings is specified by an empty event string so we always expect at least one binding bindings ::= binding:b COMMA bindings:bs {: RESULT = node(ParseNode.COMMA, bleft, bright, b, bs); :} | binding:b SEMI bindings:bs {: RESULT = node(ParseNode.COMMA, bleft, bright, b, bs); :} | error:err SEMI bindings:bs {: error("invalid binding", errleft, errright); RESULT = bs; :} | binding:b SEMI error:err {: error("invalid binding", errleft, errright); RESULT = b; :} | binding:b SEMI {: RESULT = b; :} | binding:b {: RESULT = b; :} ; binding ::= bind_sym:s ASSIGN binding_expr:e {: RESULT = node(ParseNode.ASSIGN, sleft, sright, s, e); :} ; binding_expr ::= expr:e {: RESULT = e; :} | array_init:ai {: RESULT = ai; :} ; array_init ::= LBRACE:l RBRACE {: RESULT = node(ParseNode.ARRAY_INIT, lleft, lright); :} | LBRACE expr_list:el RBRACE {: RESULT = node(ParseNode.ARRAY_INIT, elleft, elright, el); :} | LBRACE array_init_list:ail RBRACE {: RESULT = node(ParseNode.ARRAY_INIT, ailleft, ailright, ail); :} ; array_init_list ::= array_init:ai {: RESULT = ai; :} | array_init:ai COMMA array_init_list:ail {: RESULT = node(ParseNode.COMMA, aileft, airight, ai, ail); :} ; // a bound symbol can optionally be specified to be of a particular type bind_sym ::= simple_name:var COLON typename:type {: RESULT = node(ParseNode.COLON, varleft, varright, var, type); :} | simple_name:var {: RESULT = var; :} ; // a typename is a simple type or an array type typename ::= name:t empty_array_dims:d {: ParseNode tmp = t; for (int i = 0; i < d; i++) { tmp = node(ParseNode.ARRAY, tleft, tright, tmp); } RESULT = tmp; :} | name:t {: RESULT = t; :} ; empty_array_dims ::= LSQUARE RSQUARE empty_array_dims:a {: RESULT = a + 1; :} | LSQUARE RSQUARE {: RESULT = 1; :} ; // a condition is simply an expression. it not type-constrained by the grammar. // it's easier to do the type checking after parsing n.b. we always have at // least one condition as an empty (i.e. vacuously true) condition is defined // by an empty input string. condition ::= expr:e {: RESULT = e; :} ; // actions area defined as a sequence of expressions.it not type-constrained // by the grammar -- it's easier to do the type checking after parsing n.b. // we always have at least one action as an empty (i.e. do nothing) action // is defined by an empty action string actions ::= NOTHING:n {: RESULT = node(ParseNode.NOTHING, nleft, nright); :} | action_expr_list:ael {: RESULT = ael; :} ; action_expr_list ::= expr:e SEMI action_expr_list:ael {: RESULT = node(ParseNode.SEMI, eleft, eright, e, ael); :} | expr:e COMMA action_expr_list:ael {: RESULT = node(ParseNode.SEMI, eleft, eright, e, ael); :} | error:err SEMI action_expr_list:ael {: error("invalid action", errleft, errright); RESULT = ael; :} | expr:e SEMI error:err {: error("invalid action", errleft, errright); RESULT = e; :} | action_expr:ae {: RESULT = ae; :} ; action_expr ::= expr:e {: RESULT = e; :} | expr:e SEMI {: RESULT = e; :} | throw_return_expr:tre {: RESULT = tre; :} | throw_return_expr:tre SEMI {: RESULT = tre; :} ; throw_return_expr ::= RETURN:r {: RESULT = node(ParseNode.RETURN, rleft, rright, null); :} | RETURN:r expr:e {: RESULT = node(ParseNode.RETURN, rleft, rright, e); :} | THROW name:i LPAREN RPAREN {: RESULT = node(ParseNode.THROW, ileft, iright, i, null); :} | THROW name:i LPAREN expr_list:args RPAREN {: RESULT = node(ParseNode.THROW, ileft, iright, i, args); :} | THROW NEW name:i LPAREN RPAREN {: RESULT = node(ParseNode.THROW, ileft, iright, i, null); :} | THROW NEW name:i LPAREN expr_list:args RPAREN {: RESULT = node(ParseNode.THROW, ileft, iright, i, args); :} ; expr_list ::= expr:e {: RESULT = e; :} | expr:e COMMA expr_list:el {: RESULT = node(ParseNode.COMMA, eleft, eright, e, el); :} | expr:e SEMI expr_list:el {: RESULT = node(ParseNode.COMMA, eleft, eright, e, el); :} | error:err SEMI expr_list:el {: error("invalid expression", errleft, errright); RESULT = el; :} ; expr ::= ternary_oper_expr:e {: RESULT = e; :} | binary_oper_expr:e {: RESULT = e; :} | unary_oper_expr:e {: RESULT = e; :} | array_expr:e {: RESULT = e; :} | field_expr:e {: RESULT = e; :} | class_expr:e {: RESULT = e; :} | meth_expr:e {: RESULT = e; :} | new_expr:ne {: RESULT = ne; :} | simple_expr:e {: RESULT = e; :} | null_expr:e {: RESULT = e; :} | simple_name:n {: RESULT = n; :} | simple_name:s ASSIGN expr:e {: RESULT = node(ParseNode.ASSIGN, sleft, sright, s, e); :} | DOLLAR:d ASSIGN expr:e {: RESULT = node(ParseNode.ASSIGN, dleft, dright, node(ParseNode.DOLLAR, dleft, dright, d), e); :} | field_expr:f ASSIGN expr:e {: RESULT = node(ParseNode.ASSIGN, fleft, fright, f, e); :} | array_expr:a ASSIGN expr:e {: RESULT = node(ParseNode.ASSIGN, aleft, aright, a, e); :} | error:err expr:e {: error("invalid expression", errleft, errright); RESULT = e; :} ; ternary_oper_expr ::= expr:cond TERN_IF expr:iftrue COLON expr:iffalse {: RESULT = node(ParseNode.TERNOP, condleft, condright, cond, iftrue, iffalse); :} ; binary_oper_expr // logical operators ::= expr:e1 OR:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.OR, oleft, oright), e1, e2); :} | expr:e1 AND:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.AND, oleft, oright), e1, e2); :} // comparison operators | expr:e1 LT:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.LT, oleft, oright), e1, e2); :} | expr:e1 LE:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.LE, oleft, oright), e1, e2); :} | expr:e1 EQ:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.EQ, oleft, oright), e1, e2); :} | expr:e1 NE:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.NE, oleft, oright), e1, e2); :} | expr:e1 GE:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.GE, oleft, oright), e1, e2); :} | expr:e1 GT:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.GT, oleft, oright), e1, e2); :} // bitwise operators | expr:e1 URSH:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.URSH, oleft, oright), e1, e2); :} | expr:e1 RSH:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.RSH, oleft, oright), e1, e2); :} | expr:e1 LSH:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.LSH, oleft, oright), e1, e2); :} | expr:e1 BOR:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.BOR, oleft, oright), e1, e2); :} | expr:e1 BAND:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.BAND, oleft, oright), e1, e2); :} | expr:e1 BXOR:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.BXOR, oleft, oright), e1, e2); :} // arithmetic operators | expr:e1 PLUS:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.PLUS, oleft, oright), e1, e2); :} | expr:e1 MINUS:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.MINUS, oleft, oright), e1, e2); :} | expr:e1 MUL:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.MUL, oleft, oright), e1, e2); :} | expr:e1 DIV:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.DIV, oleft, oright), e1, e2); :} | expr:e1 MOD:o expr:e2 {: RESULT = node(ParseNode.BINOP, e1left, e1right, node(ParseNode.MOD, oleft, oright), e1, e2); :} // type tets expression | expr:e INSTANCEOF:o path:p {: RESULT = node(ParseNode.BINOP, eleft, eright, node(ParseNode.INSTANCEOF, oleft, oright), e, p); :} ; unary_oper_expr /* logical operators */ ::= NOT:o expr:e {: RESULT = node(ParseNode.UNOP, eleft, eright, node(ParseNode.NOT, oleft, oright), e); :} /* bitwise operators */ | TWIDDLE:o expr:e {: RESULT = node(ParseNode.UNOP, eleft, eright, node(ParseNode.TWIDDLE, oleft, oright), e); :} /* arithmetic operators */ | MINUS:o expr:e {: RESULT = node(ParseNode.UNOP, eleft, eright, node(ParseNode.UMINUS, oleft, oright), e); :} %prec UMINUS ; array_expr ::= simple_expr:se array_idx_list:ail {: RESULT = node(ParseNode.ARRAY, seleft, seright, se, ail); :} | simple_name:name array_idx_list:ail {: RESULT = node(ParseNode.ARRAY, nameleft, nameright, name, ail); :} | field_expr:fe array_idx_list:ail {: RESULT = node(ParseNode.ARRAY, feleft, feright, fe, ail); :} | meth_expr:me array_idx_list:ail {: RESULT = node(ParseNode.ARRAY, meleft, meright, me, ail); :} ; array_idx_list ::= array_idx:ai array_idx_list:ail {: RESULT = node(ParseNode.SEMI, aileft, airight, ai, ail); :} | array_idx:ai {: RESULT = ai; :} ; array_idx ::= LSQUARE expr:e RSQUARE {: RESULT = e; :} ; class_expr ::= path:p DOT CLASS {: RESULT = node(ParseNode.CLASS, pleft, pright, p); :} ; field_expr ::= path:p DOT simple_name:f {: RESULT = node(ParseNode.FIELD, fleft, fright, f, p); :} | expr_field_expr:efe {: RESULT = efe; :} ; expr_field_expr ::= simple_expr:se DOT simple_name:f {: RESULT = node(ParseNode.FIELD, fleft, fright, f, se); :} | class_expr:ce DOT simple_name:f {: error("illegal access to field of Class", fleft, fright); RESULT = node(ParseNode.FIELD, fleft, fright, f, ce); :} | meth_expr:me DOT simple_name:f {: RESULT = node(ParseNode.FIELD, fleft, fright, f, me); :} | array_expr:ae DOT simple_name:f {: RESULT = node(ParseNode.FIELD, fleft, fright, f, ae); :} | expr_field_expr:efe DOT simple_name:f {: RESULT = node(ParseNode.FIELD, fleft, fright, f, efe); :} ; meth_expr ::= simple_name:m LPAREN RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, null, null); :} | simple_name:m LPAREN expr_list:args RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, null, args); :} | path:p DOT simple_name:m LPAREN RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, p, null); :} | path:p DOT simple_name:m LPAREN expr_list:args RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, p, args); :} | expr_meth_expr:eme {: RESULT = eme; :} ; expr_meth_expr ::= simple_expr:se DOT simple_name:m LPAREN RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, se, null); :} | simple_expr:se DOT simple_name:m LPAREN expr_list:args RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, se, args); :} | class_expr:ce DOT simple_name:m LPAREN RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, ce, null); :} | class_expr:ce DOT simple_name:m LPAREN expr_list:args RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, ce, args); :} | meth_expr:eme DOT simple_name:m LPAREN RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, eme, null); :} | meth_expr:eme DOT simple_name:m LPAREN expr_list:args RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, eme, args); :} | expr_field_expr:efe DOT simple_name:m LPAREN RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, efe, null); :} | expr_field_expr:efe DOT simple_name:m LPAREN expr_list:args RPAREN {: RESULT = node(ParseNode.METH, mleft, mright, m, efe, args); :} ; new_expr ::= NEW name:n LPAREN RPAREN {: RESULT = node(ParseNode.NEW, nleft, nright, n, null, null); :} | NEW name:n LPAREN expr_list:args RPAREN {: RESULT = node(ParseNode.NEW, nleft, nright, n, args, null); :} | NEW name:n new_array_spec :nas {: RESULT = node(ParseNode.NEW, nleft, nright, n, null, nas); :} ; // we can either have // array_dims empty_array_dims // or // empty_array_dims array_init new_array_spec ::= array_dims:ad {: RESULT = ad; :} | array_dims:ad array_init:ai {: RESULT = node(ParseNode.ARRAY_INIT, adleft, adright, ad, ai); :} ; array_dims ::= array_dim:ad array_dims:ads {: RESULT = node(ParseNode.COMMA, adleft, adright, ad, ads); :} | array_dim:ad {: RESULT = ad; :} ; array_dim ::= LSQUARE expr:e RSQUARE {: RESULT = e; :} | LSQUARE:l RSQUARE {: RESULT = node(ParseNode.NOTHING, lleft, lright, null); :} ; simple_expr ::= INTEGER_LITERAL:i {: RESULT = node(ParseNode.INTEGER_LITERAL, ileft, iright, i); :} | LONG_LITERAL:l {: RESULT = node(ParseNode.INTEGER_LITERAL, lleft, lright, l); :} | FLOAT_LITERAL:f {: RESULT = node(ParseNode.FLOAT_LITERAL, fleft, fright, f); :} | DOUBLE_LITERAL:d {: RESULT = node(ParseNode.FLOAT_LITERAL, dleft, dright, d); :} | BOOLEAN_LITERAL:b {: RESULT = node(ParseNode.BOOLEAN_LITERAL, bleft, bright, b); :} | STRING_LITERAL:s {: RESULT = node(ParseNode.STRING_LITERAL, sleft, sright, s); :} | DOLLAR:s {: RESULT = node(ParseNode.DOLLAR, sleft, sright, s); :} | LPAREN expr:e RPAREN {: RESULT = e; :} ; null_expr ::= NULL_LITERAL:n {: RESULT = node(ParseNode.NULL_LITERAL, nleft, nright); :} ; name ::= simple_name:n {: RESULT = n; :} | path:p DOT IDENTIFIER:i {: RESULT = node(ParseNode.IDENTIFIER, ileft, iright, i, p); :} ; simple_name ::= IDENTIFIER:i {: RESULT = node(ParseNode.IDENTIFIER, ileft, iright, i, null); :} ; path ::= IDENTIFIER:i {: RESULT = node(ParseNode.PATH, ileft, iright, i, null); :} | path:p DOT IDENTIFIER:i {: RESULT = node(ParseNode.PATH, ileft, iright, i, p); :} ; byteman-4.0.18/agent/grammar/flex/000077500000000000000000000000001414767246600167465ustar00rootroot00000000000000byteman-4.0.18/agent/grammar/flex/ECAToken.flex000066400000000000000000000163511414767246600212250ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2008-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.rule.grammar; import java_cup.runtime.*; import org.jboss.byteman.rule.grammar.PrintableSymbol; %% %class ECATokenLexer %unicode %cup %line %column %public // %debug %{ StringBuffer string = new StringBuffer(); private int startLine = 0; private String file = ""; public void setStartLine(int startLine) { this.startLine = startLine; } public void setFile(String file) { this.file = file; } private Symbol symbol(int type) { return new PrintableSymbol(type, file, yyline + startLine, yycolumn); } private Symbol symbol(int type, Object value) { return new PrintableSymbol(type, file, yyline + startLine, yycolumn, value); } %} LineTerminator = \r|\n|\r\n WhiteSpace = {LineTerminator} | [ \t\f] Identifier = ([A-Za-z_]) ([A-Za-z0-9$_])* PosInteger = 0 | [1-9][0-9]* Sign = [+-] Exp = [Ee] Dot = "." DotTrailing = {Dot} {PosInteger}? ExpTrailing = {Exp} {Sign}? {PosInteger} FloatTrailing = {ExpTrailing} | {DotTrailing} {ExpTrailing}? PosFloat = {PosInteger} {FloatTrailing} Long = {Sign}? {PosInteger} L Double = {Sign}? {PosFloat} D Integer = {Sign}? {PosInteger} Float = {Sign}? {PosFloat} F? %state STRING %state QUOTEDIDENT %state COMMENT %% /* keywords */ { "BIND"|"bind" { return symbol(sym.BIND); } "IF"|"if" { return symbol(sym.IF); } "DO"|"do" { return symbol(sym.DO); } /* rule and rule header keywords are not required "RULE" { return symbol(sym.RULE); } "CLASS" { return symbol(sym.CLASS); } "class" { return symbol(sym.CLASS); } "METHOD" { return symbol(sym.METHOD); } "LINE" { return symbol(sym.LINE); } "ENDRULE" { return symbol(sym.ENDRULE); } */ "NOTHING"|"nothing" { return symbol(sym.NOTHING); } "TRUE"|"true" { return symbol(sym.BOOLEAN_LITERAL, Boolean.TRUE); } "FALSE"|"false" { return symbol(sym.BOOLEAN_LITERAL, Boolean.FALSE); } "RETURN"|"return" { return symbol(sym.RETURN); } "THROW"|"throw" { return symbol(sym.THROW); } "NEW"|"new" { return symbol(sym.NEW); } "class" { return symbol(sym.CLASS); } "instanceof" { return symbol(sym.INSTANCEOF); } /* various bracket pairs */ "(" { return symbol(sym.LPAREN); } ")" { return symbol(sym.RPAREN); } "[" { return symbol(sym.LSQUARE); } "]" { return symbol(sym.RSQUARE); } /* braces are required for array initializations */ "{" { return symbol(sym.LBRACE); } "}" { return symbol(sym.RBRACE); } /* expression separator */ ";" { return symbol(sym.SEMI); } /* bindings separator */ "," { return symbol(sym.COMMA); } /* identifier punctuator */ "." { return symbol(sym.DOT); } /* binding for events */ "=" | "<--" { return symbol(sym.ASSIGN); } /* logical operators */ "||" | "OR" | "or" { return symbol(sym.OR); } "&&" | "AND" | "and" { return symbol(sym.AND); } "!" | "NOT" | "not" { return symbol(sym.NOT); } /* bitwise operators */ ">>>" { return symbol(sym.URSH); } ">>" { return symbol(sym.RSH); } "<<" { return symbol(sym.LSH); } "|" { return symbol(sym.BOR); } "&" { return symbol(sym.BAND); } "^" { return symbol(sym.BXOR); } "~" { return symbol(sym.TWIDDLE); } /* comparison operators */ "<" | "LT" | "lt" { return symbol(sym.LT); } "<=" | "LE" | "le" { return symbol(sym.LE); } "==" | "EQ" | "eq" { return symbol(sym.EQ); } "!=" | "NE" | "ne" { return symbol(sym.NE); } ">=" | "GE" | "ge" { return symbol(sym.GE); } ">" | "GT" | "gt" { return symbol(sym.GT); } /* arithmetic operators */ "*" | "TIMES" | "times" { return symbol(sym.MUL); } "/" | "DIVIDE" | "divide" { return symbol(sym.DIV); } "+" | "PLUS" | "plus" { return symbol(sym.PLUS); } "-" | "MINUS" | "minus" { return symbol(sym.MINUS); } "%" | "MOD" | "mod" { return symbol(sym.MOD); } /* ternary condition operator -- also sepr for var and type in decl */ "?" { return symbol(sym.TERN_IF); } ":" { return symbol(sym.COLON); } /* dollar prefixed symbols */ /* trigger method recipient and params */ "$" {Integer} { return symbol(sym.DOLLAR, yytext()); } /* trigger method local variable */ "$" {Identifier} { return symbol(sym.DOLLAR, yytext()); } /* return value on stack in AT EXIT rule */ "$" "!" { return symbol(sym.DOLLAR, yytext()); } /* throwable on stack in AT THROW rule */ "$" "^" { return symbol(sym.DOLLAR, yytext()); } /* trigger method parameter count */ "$" "#" { return symbol(sym.DOLLAR, yytext()); } /* trigger method parameter array */ "$" "*" { return symbol(sym.DOLLAR, yytext()); } /* invoked method parameter array -- for use in AT INVOKE rules */ "$" "@" { return symbol(sym.DOLLAR, yytext()); } /* identifiers */ "NULL" | "null" { return symbol(sym.NULL_LITERAL); } {Identifier} { return symbol(sym.IDENTIFIER, yytext()); } /* numbers */ {Long} { return symbol(sym.LONG_LITERAL, Long.valueOf(yytext().substring(0, yytext().length() - 1))); } {Integer} { return symbol(sym.INTEGER_LITERAL, Integer.valueOf(yytext())); } {Double} { return symbol(sym.DOUBLE_LITERAL, Double.valueOf(yytext().substring(0, yytext().length() - 1))); } {Float} { return symbol(sym.FLOAT_LITERAL, Float.valueOf(yytext())); } /* strings */ \" { string.setLength(0); yybegin(STRING); } \' { string.setLength(0); yybegin(QUOTEDIDENT); } # { yybegin(COMMENT); } /* whitespace */ {WhiteSpace} { /* ignore */ } /* anything else is an error! */ . { throw new Error("Illegal character <"+ yytext()+">"); } } { \" { yybegin(YYINITIAL); return symbol(sym.STRING_LITERAL, string.toString()); } [^\n\r\"\\]+ { string.append( yytext() ); } \\t { string.append('\t'); } \\n { string.append('\n'); } \\r { string.append('\r'); } \\\" { string.append('\"'); } \\\\ { string.append('\\'); } /* anything else is an error! */ \n { throw new Error("File " + file + " line " + (yyline + startLine) + " : newline in string"); } . { throw new Error("File " + file + " line " + (yyline + startLine) + " : illegal character in string <"+ yytext()+">"); } } { [^\n\r']+ { string.append( yytext() ); } ' { yybegin(YYINITIAL); return symbol(sym.IDENTIFIER, string.toString()); } /* anything else is an error! */ {LineTerminator} { throw new Error("File " + file + " line " + (yyline + startLine) + " : newline in quoted identifier"); } } { [^\n\r] { /*ignore */ } {LineTerminator} { yybegin(YYINITIAL); } } byteman-4.0.18/agent/noverification.xml000066400000000000000000000005521414767246600201250ustar00rootroot00000000000000 byteman-4.0.18/agent/pom.xml000066400000000000000000004413031414767246600157040ustar00rootroot00000000000000 4.0.0 byteman-agent jar The Byteman agent jar contains the implementation of the Byteman java agent, including the bytecode transformer, rule parser, type checker and execution engine and the agent listener. byteman-agent org.jboss.byteman byteman-root 4.0.18 true true -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -Dorg.jboss.byteman.verbose org.apache.maven.plugins maven-jar-plugin org.jboss.byteman.agent.Main org.jboss.byteman.agent.Main true true ${project.version} org.apache.maven.plugins maven-shade-plugin package shade org.ow2.asm:asm org.ow2.asm:asm-tree org.ow2.asm:asm-analysis org.ow2.asm:asm-commons net.sf.squirrel-sql.thirdparty-non-maven:java-cup net.sf.squirrel-sql.thirdparty-non-maven:java-cup java_cup/runtime/** org.ow2.asm:asm org/objectweb/asm/** org.ow2.asm:asm-tree org/objectweb/asm/** org.ow2.asm:asm-analysis org/objectweb/asm/** org.ow2.asm:asm-commons org/objectweb/asm/** org.objectweb.asm org.jboss.byteman.objectweb.asm java_cup.runtime org.jboss.byteman.java_cup.runtime org.apache.maven.plugins maven-surefire-plugin true org.apache.maven.plugins maven-failsafe-plugin false javaops.TestArithmetic integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestArithmetic.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestArithmetic.btm javaops.TestArray integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestArray.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestArray.btm javaops.TestAssign integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestAssign.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestAssign.btm javaops.TestComparison integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestComparison.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestComparison.btm javaops.TestField integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestField.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestField.btm javaops.TestLogical integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestLogical.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestLogical.btm javaops.TestMethod integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestMethod.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestMethod.btm javaops.TestNew integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestNew.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestNew.btm javaops.TestClassLiteral integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestClassLiteral.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestClassLiteral.btm location.TestAll integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestAll.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestAll.btm location.TestAtNew integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestAtNew.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestAtNew.btm location.TestCall integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestCall.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestCall.btm location.TestEntry integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestEntry.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestEntry.btm location.TestExit integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestExit.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestExit.btm location.TestExceptionExit integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestExceptionExit.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestExceptionExit.btm location.TestLine integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestLine.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestLine.btm location.TestReadWrite integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestReadWrite.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestReadWrite.btm location.TestReadWriteParams integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestReadWriteParams.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestReadWriteParams.btm location.TestSynch integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestSynch.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestSynch.btm location.TestThrow integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestThrow.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestThrow.btm helpertests.TestCallerMatches integration-test integration-test verify 1 true org/jboss/byteman/tests/helpertests/TestCallerMatches.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/helpertests/TestCallerMatches.btm helpertests.TestStackTrace integration-test integration-test verify 1 true org/jboss/byteman/tests/helpertests/TestStackTrace.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/helpertests/TestStackTrace.btm helpertests.TestLinkMap integration-test integration-test verify 1 true org/jboss/byteman/tests/helpertests/TestLinkMap.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/helpertests/TestLinkMap.btm misc.TestInterfaceInjection integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestInterfaceInjection.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestInterfaceInjection.btm misc.TestInvokeParamBinding integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestInvokeParamBinding.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestInvokeParamBinding.btm misc.TestOverridingInjection integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestOverridingInjection.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestOverridingInjection.btm misc.TestOverridingInterfaceInjection integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestOverridingInterfaceInjection.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestOverridingInterfaceInjection.btm misc.TestParamBinding integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestParamBinding.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestParamBinding.btm misc.TestRecursiveTriggers integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestRecursiveTriggers.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestRecursiveTriggers.btm misc.TestReturnBindingAssignment integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestReturnBindingAssignment.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestReturnBindingAssignment.btm misc.TestReturnBinding integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestReturnBinding.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestReturnBinding.btm misc.TestThrowBinding integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestThrowBinding.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestThrowBinding.btm misc.TestTriggerClassMethodBinding integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestTriggerClassMethodBinding.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestTriggerClassMethodBinding.btm misc.TestDowncast integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestDowncast.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestDowncast.btm bugfixes.TestAtNewWithCountAll integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestAtNewWithCountAll.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestAtNewWithCountAll.btm bugfixes.TestEmptySignature integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestEmptySignature.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestEmptySignature.btm bugfixes.TestEnclosedSynchronizationPropagation integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestEnclosedSynchronizationPropagation.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestEnclosedSynchronizationPropagation.btm bugfixes.TestInterfaceHierarchy integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestInterfaceHierarchy.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestInterfaceHierarchy.btm bugfixes.TestMethodActualAgainstFormal integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMethodActualAgainstFormal.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMethodActualAgainstFormal.btm bugfixes.TestMethodClauseReturnType integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMethodClauseReturnType.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMethodClauseReturnType.btm bugfixes.TestMethodClauseReturnTypeDynamic integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMethodClauseReturnTypeDynamic.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=listener:true bugfixes.TestMethodParamAssign integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMethodParamAssign.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMethodParamAssign.btm bugfixes.TestMethodParamName integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMethodParamName.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMethodParamName.btm bugfixes.TestMultiMethodMatch integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMultiMethodMatch.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMultiMethodMatch.btm bugfixes.TestPromotePrimitiveToObject integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestPromotePrimitiveToObject.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestPromotePrimitiveToObject.btm bugfixes.TestStringAsCharSequence integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestStringAsCharSequence.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestStringAsCharSequence.btm bugfixes.TestThrowAction integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestThrowAction.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestThrowAction.btm bugfixes.TestThrowError integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestThrowError.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestThrowError.btm bugfixes.TestThrowRuleInjection integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestThrowRuleInjection.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestThrowRuleInjection.btm bugfixes.TestWaitAfterSignalWakeMustMeet integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestWaitAfterSignalWakeMustMeet.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestWaitAfterSignalWakeMustMeet.btm bugfixes.TestThrowRuntimeException integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestThrowRuntimeException.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestThrowRuntimeException.btm bugfixes.TestAfterCallAssign integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestAfterCallAssign.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestAfterCallAssign.btm bugfixes.TestAbstractInterfaceCall integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestAbstractInterfaceCall.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestAbstractInterfaceCall.btm bugfixes.TestMaxStackComputation integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMaxStackComputation.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMaxStackComputation.btm bugfixes.TestBooleanComparisons integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestBooleanComparisons.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestBooleanComparisons.btm bugfixes.TestStackHeight integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestStackHeight.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestStackHeight.btm bugfixes.TestLocalUpdateType integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestLocalUpdateType.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestLocalUpdateType.btm bugfixes.TestRendezvousTimeout integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestRendezvousTimeout.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestRendezvousTimeout.btm bugfixes.TestJoinWaitTimeout integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestJoinWaitTimeout.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestJoinWaitTimeout.btm bugfixes.TestPlusExpressionCoerce integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestPlusExpressionCoerce.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestPlusExpressionCoerce.btm bugfixes.TestArrayArgTypeCheck integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestArrayArgTypeCheck.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestArrayArgTypeCheck.btm bugfixes.TestBooleanLocal integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestBooleanLocal.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestBooleanLocal.btm bugfixes.TestDoubleEntryBookKeeping integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestDoubleEntryBookKeeping.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestDoubleEntryBookKeeping.btm bugfixes.TestConstructorArgUpcast integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestConstructorArgUpcast.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestConstructorArgUpcast.btm bugfixes.TestBindNull integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestBindNull.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestBindNull.btm bugfixes.TestTraceOpenAndWrite integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestTraceOpenAndWrite.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestTraceOpenAndWrite.btm javaops.TestInnerClasses integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestInnerClasses.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestInnerClasses.btm bugfixes.TestAsTarget integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestAsTarget.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestAsTarget.btm submit.TestSubmit integration-test integration-test verify 1 true org/jboss/byteman/tests/submit/TestSubmit.class -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=listener:true javaops.TestArithmetic.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestArithmetic.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestArithmetic.btm javaops.TestArray.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestArray.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestArray.btm javaops.TestAssign.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestAssign.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestAssign.btm javaops.TestComparison.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestComparison.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestComparison.btm javaops.TestField.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestField.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestField.btm javaops.TestLogical.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestLogical.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestLogical.btm javaops.TestMethod.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestMethod.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestMethod.btm javaops.TestNew.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestNew.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestNew.btm javaops.TestClassLiteral.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/javaops/TestClassLiteral.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestClassLiteral.btm location.TestAll.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestAll.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestAll.btm location.TestAtNew.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestAtNew.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestAtNew.btm location.TestCall.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestCall.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestCall.btm location.TestEntry.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestEntry.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestEntry.btm location.TestExit.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestExit.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestExit.btm location.TestExceptionExit.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestExceptionExit.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestExceptionExit.btm location.TestLine.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestLine.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestLine.btm location.TestReadWrite.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestReadWrite.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestReadWrite.btm location.TestReadWriteParams.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestReadWriteParams.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestReadWriteParams.btm location.TestSynch.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestSynch.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestSynch.btm location.TestThrow.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/location/TestThrow.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/location/TestThrow.btm helpertests.TestCallerMatches.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/helpertests/TestCallerMatches.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/helpertests/TestCallerMatches.btm helpertests.TestStackTrace.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/helpertests/TestStackTrace.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/helpertests/TestStackTrace.btm helpertests.TestLinkMap.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/helpertests/TestLinkMap.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/helpertests/TestLinkMap.btm misc.TestInterfaceInjection.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestInterfaceInjection.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestInterfaceInjection.btm misc.TestInvokeParamBinding.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestInvokeParamBinding.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestInvokeParamBinding.btm misc.TestOverridingInjection.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestOverridingInjection.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestOverridingInjection.btm misc.TestOverridingInterfaceInjection.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestOverridingInterfaceInjection.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestOverridingInterfaceInjection.btm misc.TestParamBinding.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestParamBinding.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestParamBinding.btm misc.TestRecursiveTriggers.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestRecursiveTriggers.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestRecursiveTriggers.btm misc.TestReturnBindingAssignment.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestReturnBindingAssignment.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestReturnBindingAssignment.btm misc.TestReturnBinding.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestReturnBinding.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestReturnBinding.btm misc.TestThrowBinding.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestThrowBinding.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestThrowBinding.btm misc.TestTriggerClassMethodBinding.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestTriggerClassMethodBinding.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestTriggerClassMethodBinding.btm misc.TestDowncast.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/misc/TestDowncast.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestDowncast.btm bugfixes.TestAtNewWithCountAll.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestAtNewWithCountAll.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestAtNewWithCountAll.btm bugfixes.TestEmptySignature.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestEmptySignature.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestEmptySignature.btm bugfixes.TestEnclosedSynchronizationPropagation.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestEnclosedSynchronizationPropagation.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestEnclosedSynchronizationPropagation.btm bugfixes.TestInterfaceHierarchy.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestInterfaceHierarchy.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestInterfaceHierarchy.btm bugfixes.TestMethodActualAgainstFormal.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMethodActualAgainstFormal.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMethodActualAgainstFormal.btm bugfixes.TestMethodClauseReturnType.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMethodClauseReturnType.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMethodClauseReturnType.btm bugfixes.TestMethodParamAssign.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMethodParamAssign.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMethodParamAssign.btm bugfixes.TestMethodParamName.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMethodParamName.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMethodParamName.btm bugfixes.TestMultiMethodMatch.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMultiMethodMatch.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMultiMethodMatch.btm bugfixes.TestPromotePrimitiveToObject.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestPromotePrimitiveToObject.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestPromotePrimitiveToObject.btm bugfixes.TestStringAsCharSequence.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestStringAsCharSequence.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestStringAsCharSequence.btm bugfixes.TestThrowAction.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestThrowAction.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestThrowAction.btm bugfixes.TestThrowError.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestThrowError.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestThrowError.btm bugfixes.TestThrowRuleInjection.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestThrowRuleInjection.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestThrowRuleInjection.btm bugfixes.TestWaitAfterSignalWakeMustMeet.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestWaitAfterSignalWakeMustMeet.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestWaitAfterSignalWakeMustMeet.btm bugfixes.TestThrowRuntimeException.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestThrowRuntimeException.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestThrowRuntimeException.btm bugfixes.TestAfterCallAssign.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestAfterCallAssign.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestAfterCallAssign.btm bugfixes.TestAbstractInterfaceCall.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestAbstractInterfaceCall.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestAbstractInterfaceCall.btm bugfixes.TestMaxStackComputation.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestMaxStackComputation.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestMaxStackComputation.btm bugfixes.TestBooleanComparisons.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestBooleanComparisons.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestBooleanComparisons.btm bugfixes.TestStackHeight.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestStackHeight.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestStackHeight.btm bugfixes.TestLocalUpdateType.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestLocalUpdateType.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestLocalUpdateType.btm bugfixes.TestRendezvousTimeout.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestRendezvousTimeout.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestRendezvousTimeout.btm bugfixes.TestJoinWaitTimeout.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestJoinWaitTimeout.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestJoinWaitTimeout.btm bugfixes.TestPlusExpressionCoerce.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestPlusExpressionCoerce.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestPlusExpressionCoerce.btm bugfixes.TestArrayArgTypeCheck.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestArrayArgTypeCheck.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestArrayArgTypeCheck.btm bugfixes.TestBooleanLocal.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestBooleanLocal.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestBooleanLocal.btm bugfixes.TestDoubleEntryBookKeeping.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestDoubleEntryBookKeeping.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestDoubleEntryBookKeeping.btm bugfixes.TestConstructorArgUpcast.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestConstructorArgUpcast.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestConstructorArgUpcast.btm bugfixes.TestTraceOpenAndWrite.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestTraceOpenAndWrite.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestTraceOpenAndWrite.btm javaops.TestInnerClasses.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestInnerClasses.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestInnerClasses.btm bugfixes.TestBindNull.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestBindNull.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestBindNull.btm bugfixes.TestAsTarget.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/bugfixes/TestAsTarget.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestAsTarget.btm submit.TestSubmit.compiled integration-test integration-test verify 1 true org/jboss/byteman/tests/submit/TestSubmit.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=listener:true check.TestRuleCheck integration-test integration-test verify 1 true TestRuleCheck org/jboss/byteman/tests/check/TestRuleCheck.class -Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=listener:true org.apache.maven.plugins maven-verifier-plugin ${agent.verification.file} true main verify verify asmdebug org.apache.maven.plugins maven-shade-plugin package shade org.ow2.asm:asm org.ow2.asm:asm-tree org.ow2.asm:asm-analysis org.ow2.asm:asm-commons net.sf.squirrel-sql.thirdparty-non-maven:java-cup net.sf.squirrel-sql.thirdparty-non-maven:java-cup java_cup/runtime/** org.ow2.asm:asm org/objectweb/asm/** org.ow2.asm:asm-tree org/objectweb/asm/** org.ow2.asm:asm-analysis org/objectweb/asm/** org.ow2.asm:asm-commons org/objectweb/asm/** org.objectweb.asm org.objectweb.asm java_cup.runtime org.jboss.byteman.java_cup.runtime parser maven-antrun-plugin 1.7 generate-sources run de.jflex jflex fix javadoc lint errors org.apache.maven.plugins maven-javadoc-plugin -Xdoclint:-missing org.ow2.asm asm compile org.ow2.asm asm-tree compile org.ow2.asm asm-analysis compile org.ow2.asm asm-commons compile net.sf.squirrel-sql.thirdparty-non-maven java-cup compile junit junit test org.jboss.byteman byteman-submit test byteman-4.0.18/agent/src/000077500000000000000000000000001414767246600151515ustar00rootroot00000000000000byteman-4.0.18/agent/src/main/000077500000000000000000000000001414767246600160755ustar00rootroot00000000000000byteman-4.0.18/agent/src/main/java/000077500000000000000000000000001414767246600170165ustar00rootroot00000000000000byteman-4.0.18/agent/src/main/java/org/000077500000000000000000000000001414767246600176055ustar00rootroot00000000000000byteman-4.0.18/agent/src/main/java/org/jboss/000077500000000000000000000000001414767246600207255ustar00rootroot00000000000000byteman-4.0.18/agent/src/main/java/org/jboss/byteman/000077500000000000000000000000001414767246600223645ustar00rootroot00000000000000byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/000077500000000000000000000000001414767246600234625ustar00rootroot00000000000000byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/AccessEnabler.java000066400000000000000000000054151414767246600270240ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2016, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * interface encapsulating behaviour required both to check * for the need to access a member reflectively and to ensure * that the member can be so used. */ public interface AccessEnabler { /** * test whether reference to the class from a classpath * class requires the use of reflection or a method handle * and possibly also module jiggery-pokery. * * @param klazz the clas to be checked * @return true if reference to the class from a classpath * class requires the use of reflection or a method handle * and possibly module jiggery-pokery otherwise false. */ public boolean requiresAccess(Class klazz); /** * test whether access to the accessible from a classpath * class requires the use of reflection or a method handle * and possibly also module jiggery-pokery. * * @param accessible this must be a Member * @return true if access requires reflection or a method handle and * possibly also module jiggery-pokery otherwise false. */ public boolean requiresAccess(AccessibleObject accessible); /** * ensure that accessible can be accessed using reflection * or a method handle * * @param accessible this must be a Member */ public void ensureAccess(AccessibleObject accessible); public AccessibleMethodInvoker createMethodInvoker(Method method); public AccessibleConstructorInvoker createConstructorInvoker(Constructor constructor); public AccessibleFieldGetter createFieldGetter(Field field); public AccessibleFieldSetter createFieldSetter(Field field); }byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/AccessManager.java000066400000000000000000000106401414767246600270220ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2016, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import org.jboss.byteman.rule.helper.Helper; import java.lang.instrument.Instrumentation; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Class used to construct an AccessEnabler appropriate to the * JDK Byteman is running in i.e. whether or not it includes * modules. */ public class AccessManager { /** * Create and return an AccessEnabler to manage enabling * reflective access. * * For JDK8 and lower releases return a DefaultAccessEnabler * which does not now about module encapsulation. * * For JDK9 and higher releases return a JigsawAccessEnabler * which is capable of enabling access to members of classes which * are normally inaccessible because of module restrictions. * * caveat: during testing class JigsawAccessEnabler may fail to * load even thought the JDK is modular. That happens when testing * that the JDK8- core classes work without the JDK9 code present. * In this specific situation a DefaultAccessEnabler is returned. * A helper trace message is logged just in case. * * @param inst an Instrumentation instance which may be needed * enable access to members of unexported module classes * @return an AccessEnabler to manage enabling reflective access */ public static AccessEnabler init(Instrumentation inst) { // if 1) we have a module system and 2) we can load an access manager // for the module system then we delegate to it otherwise we use a // default access enabler ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); try { systemLoader.loadClass("java.lang.Module"); } catch (ClassNotFoundException ce) { // we are not in a Jigsaw runtime so use // a default access enabler return initDefault(); } // ok let's look for a Jigsaw access enabler try { Class jigsawAccessManagerClazz = systemLoader.loadClass("org.jboss.byteman.agent.JigsawAccessManager"); try { Method initMethod = jigsawAccessManagerClazz.getDeclaredMethod("init", Instrumentation.class); AccessEnabler accessEnabler = (AccessEnabler) initMethod.invoke(null, inst); return accessEnabler; } catch (Exception e) { // this should not happen Helper.err("AccessManager:init unexpected error initialising JigsawAccessManager"); Helper.errTraceException(e); // continue with a default accessor // TODO - maybe revise this decision return initDefault(); } } catch (ClassNotFoundException e) { // this can happen legitimately when we // testthe base agent jar or even if someone // decides to use it without JDK9 support // generate verbose trace for now but ... // TODO replace wirth noisy trace when that gets implemented Helper.verbose("AccessManager:init hmm, JigsawAccessManager not present in Jigsaw runtime!"); return initDefault(); } } private static AccessEnabler initDefault() { Helper.verbose("AccessManager:init Initialising default AccessManager"); return new DefaultAccessEnabler(); } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/AccessibleConstructorInvoker.java000066400000000000000000000022461414767246600321720ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2016, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; /** * Created by adinn on 01/11/16. */ public interface AccessibleConstructorInvoker { public Object invoke(Object[] args); } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/AccessibleFieldGetter.java000066400000000000000000000022331414767246600305010ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2016, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; /** * Created by adinn on 01/11/16. */ public interface AccessibleFieldGetter { public Object get(Object owner); } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/AccessibleFieldSetter.java000066400000000000000000000022471414767246600305220ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2016, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; /** * Created by adinn on 01/11/16. */ public interface AccessibleFieldSetter { public void set(Object owner, Object value); } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/AccessibleMethodInvoker.java000066400000000000000000000023461414767246600310660ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2016, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import java.lang.reflect.InvocationTargetException; /** * Created by adinn on 01/11/16. */ public interface AccessibleMethodInvoker { public Object invoke(Object receiver, Object[] args); } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/BytemanPolicy.java000066400000000000000000000056341414767246600271140ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; /** * Security Policy class used to give the Byteman agent code all permissions * see JIRA issue BYTEMAN-233 for details */ import java.security.AllPermission; import java.security.CodeSource; import java.security.Permission; import java.security.PermissionCollection; import java.security.Permissions; import java.security.Policy; import java.security.ProtectionDomain; import java.security.Provider; final class BytemanPolicy extends Policy { private static final AllPermission ALL_PERMISSION = new AllPermission(); static final Permissions DEFAULT_PERMISSION_COLLECTION = getAllPermission(); private static final CodeSource ourCodeSource = BytemanPolicy.class.getProtectionDomain().getCodeSource(); private final Policy policy; private static Permissions getAllPermission() { final Permissions permissions = new Permissions(); permissions.add(ALL_PERMISSION); return permissions; } public BytemanPolicy(final Policy policy) { this.policy = policy; } public Provider getProvider() { return policy.getProvider(); } public String getType() { return policy.getType(); } public Parameters getParameters() { return policy.getParameters(); } public PermissionCollection getPermissions(final CodeSource codesource) { return codesource.equals(ourCodeSource) ? getAllPermission() : policy.getPermissions(codesource); } public PermissionCollection getPermissions(final ProtectionDomain domain) { return domain.getCodeSource().equals(ourCodeSource) ? getAllPermission() : policy.getPermissions(domain); } public boolean implies(final ProtectionDomain domain, final Permission permission) { return domain.getCodeSource().equals(ourCodeSource) || policy.implies(domain, permission); } public void refresh() { policy.refresh(); } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/DefaultAccessEnabler.java000066400000000000000000000225561414767246600303360ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2016, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import org.jboss.byteman.rule.exception.ExecuteException; import org.jboss.byteman.rule.helper.Helper; /** * Implementation of AccessEnabler for use in a * non-Jigsaw enabled JDK runtime */ public class DefaultAccessEnabler implements AccessEnabler { public DefaultAccessEnabler() { } /** * test whether reference to the class from a classpath * class requires the use of reflection or a method handle * and possibly also module jiggery-pokery. * * @param klazz the clas to be checked * @return true if reference to the class from a classpath * class requires the use of reflection or a method handle * and possibly module jiggery-pokery otherwise false. */ public boolean requiresAccess(Class klazz) { // we need to handle private or protected classes with kid gloves // public classes are fine so long as they are not embedded in // private classes while (Modifier.isPublic(klazz.getModifiers())) { if (!klazz.isMemberClass()) { return false; } try { klazz = klazz.getDeclaringClass(); } catch (SecurityException se) { return true; } } return true; } /** * test whether access to the accessible from a classpath * class requires the use of reflection or a method handle * and possibly also module jiggery-pokery. * * @param accessible this must be a Member * @return true if access requires reflection or a method handle and * possibly also module jiggery-pokery otherwise false. */ public boolean requiresAccess(AccessibleObject accessible) { // member specific checks // the accessible has to be a field, method or constructor Member member = (Member)accessible; // we need to use reflection to access non-public members if (!Modifier.isPublic(member.getModifiers())) { return true; } // class level checks Class clazz = member.getDeclaringClass(); // we need to use reflection to access non-public classes // (n.b. it won't be in the Byteman package space) if (!Modifier.isPublic(clazz.getModifiers())) { return true; } // we need to repeat the same check for outer classes while (clazz.isMemberClass()) { clazz = clazz.getEnclosingClass(); if (!Modifier.isPublic(clazz.getModifiers())) { return true; } } return false; } /** * ensure that accessible can be accessed using reflection * or a method handle * * @param accessible this must be a Member */ public void ensureAccess(AccessibleObject accessible) { // make the accessor usable try { accessible.setAccessible(true); } catch (Exception e) { Helper.verbose("DefaultAccessEnabler.ensureAccess: error enabling access for member " + e); Helper.verboseTraceException(e); } } private static class DefaultAccessibleMethodInvoker implements AccessibleMethodInvoker { private Method method; public DefaultAccessibleMethodInvoker(Method method) { this.method = method; } @Override public Object invoke(Object receiver, Object[] args) { try { return method.invoke(receiver, args); } catch (Exception e) { throw new ExecuteException("DefaultAccessibleMethodInvoker.invoke : exception invoking method " + method, e); } } } private static class DefaultAccessibleConstructorInvoker implements AccessibleConstructorInvoker { private Constructor constructor; public DefaultAccessibleConstructorInvoker(Constructor constructor) { this.constructor = constructor; } @Override public Object invoke(Object[] args) { try { return constructor.newInstance(args); } catch (Exception e) { throw new ExecuteException("DefaultAccessibleConstructorInvoker.invoke : exception invoking constructor " + constructor, e); } } } private static class DefaultAccessibleFieldGetter implements AccessibleFieldGetter { private Field field; public DefaultAccessibleFieldGetter(Field field) { this.field = field; } @Override public Object get(Object owner) { try { return field.get(owner); } catch (Exception e) { throw new ExecuteException("DefaultAccessibleFieldGetter.get : exception reading field " + field, e); } } } private static class DefaultAccessibleFieldSetter implements AccessibleFieldSetter { private Field field; public DefaultAccessibleFieldSetter(Field field) { this.field = field; } @Override public void set(Object owner, Object value) { try { field.set(owner, value); } catch (Exception e) { throw new ExecuteException("DefaultAccessibleFieldGetter.get : exception writing field " + field, e); } } } @Override public AccessibleMethodInvoker createMethodInvoker(Method method) { return createMethodInvoker(method, false); } public AccessibleMethodInvoker createMethodInvoker(Method method, boolean alreadyAccessible) { if (!alreadyAccessible) { // make the method usable try { method.setAccessible(true); } catch (Exception e) { Helper.verbose("DefaultAccessEnabler.createMethodInvoker: error enabling access for method " + e); Helper.verboseTraceException(e); } } return new DefaultAccessibleMethodInvoker(method); } @Override public AccessibleConstructorInvoker createConstructorInvoker(Constructor constructor) { return createConstructorInvoker(constructor, false); } public AccessibleConstructorInvoker createConstructorInvoker(Constructor constructor, boolean alreadyAccessible) { if (!alreadyAccessible) { // make the constructor usable try { constructor.setAccessible(true); } catch (Exception e) { Helper.verbose("DefaultAccessEnabler.createConstructorInvoker: error enabling access for constructor " + e); Helper.verboseTraceException(e); } } return new DefaultAccessibleConstructorInvoker(constructor); } @Override public AccessibleFieldGetter createFieldGetter(Field field) { return createFieldGetter(field, false); } public AccessibleFieldGetter createFieldGetter(Field field, boolean alreadyAccessible) { if (!alreadyAccessible) { // make the constructor usable try { field.setAccessible(true); } catch (Exception e) { Helper.verbose("DefaultAccessEnabler.createFieldGetter: error enabling access for field " + e); Helper.verboseTraceException(e); } } return new DefaultAccessibleFieldGetter(field); } @Override public AccessibleFieldSetter createFieldSetter(Field field) { return createFieldSetter(field, false); } public AccessibleFieldSetter createFieldSetter(Field field, boolean alreadyAccessible) { if (!alreadyAccessible) { // make the constructor usable try { field.setAccessible(true); } catch (Exception e) { Helper.verbose("DefaultAccessEnabler.createFieldGetter: error enabling access for field " + e); Helper.verboseTraceException(e); } } return new DefaultAccessibleFieldSetter(field); } }byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/HelperManager.java000066400000000000000000000433341414767246600270460ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2010 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * (C) 2010, * @authors Andrew Dinn */ package org.jboss.byteman.agent; import org.jboss.byteman.rule.Rule; import org.jboss.byteman.modules.ModuleSystem; import org.jboss.byteman.rule.helper.Helper; import java.lang.instrument.Instrumentation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.concurrent.ConcurrentHashMap; /** * class used to manage lifecycle events for rule helpers */ public class HelperManager { // public API /** * construct a manager * * @param inst will be non-null if we are running in an agent * @param moduleSystem must be non-null, use NonModuleSystem if nothing specal is needed */ public HelperManager(Instrumentation inst, ModuleSystem moduleSystem) { this.inst = inst; this.moduleSystem = moduleSystem; this.helperDetailsMap = new ConcurrentHashMap, LifecycleDetails>(); } /** * perform install processing for a rule * @param rule an instantiation of a specific rule script as a Rule which has been * successfully loaded into the agent, injected, type-checked and, optionally, * compiled. * * Note that install processing happens when an injected rule is first triggered, * not when it is injected. This ensures that a rule is not installed until it * has been successfully type checked. It also ensures that helper life-cycle * calls are not made underneath a ClassFileTransformer transform callback. This * is so that execution of life-cycle code does not initiate class-loading without * the desired associated transforms being applied -- transformation is not entered * recursively. The same reasoning accounts for ehy type-checking is delayed until * trigger-time. * * Note also that some given rule script may be injected into more than one method * of more than one class. A METHOD spec may match more than one method when the * descriptor is omitted. A CLASS or INTERFACE specification may match more than one * class for a variety of reasons: multiple deployments of the same named class; * omission of the package name in the specification; injection through interfaces; * use of overriding injection. Every successful install into a specific method (of * some given class) leads to one installed callback even when the rule is injected * at multiple matching points in the bytecode of that method. */ public void installed(Rule rule) { // ignore unless an agent is actually running if (inst == null) { return; } Class helperClass = rule.getHelperClass(); Helper.verbose("HelperManager.install for helper class " + helperClass.getName()); installed(rule, helperClass); } private LifecycleDetails installed(Rule rule, Class helperClass) { // synchronize on the lifecycle class to ensure it is not uninstalled // while we are deciding whether or not to install it synchronized (helperClass) { LifecycleDetails parentDetails = null; // give the super chain a chance to install Class superClass = helperClass.getSuperclass(); if(superClass != Object.class) { parentDetails = installed(rule, superClass); } // now run the install for this class LifecycleDetails details = getDetails(helperClass, true, parentDetails); if (details.installCount == 0 && details.activated != null) { Helper.verbose("calling activated() for helper class " + helperClass.getName()); try { details.activated.invoke(null); } catch (Exception e) { Helper.err("HelperManager.installed : unexpected exception from " + helperClass.getName() + ".activate() : " + e); Helper.errTraceException(e); } } if (details.installed != null) { Helper.verbose("calling installed(" + rule.getName() + ") for helper class" + helperClass.getName()); try { if (details.installedTakesRule) { details.installed.invoke(null, rule); } else { details.installed.invoke(null, rule.getName()); } } catch (Exception e) { Helper.err("HelperManager.installed : unexpected exception from " + helperClass.getName() + ".installed(String) : " + e); Helper.errTraceException(e); } } details.installCount++; return details; } } /** * perform install processing for a rule * @param rule an instantiation of a specific rule script as a Rule which has been * successfully loaded into the agent, injected, type-checked and, optionally, * compiled. * * Note that uninstall processing is performed by the Retransformer during unloading * (or redefinition) of scripts after any retransform of the classesd affected by the * scripts has been performed. When a script is uninstalled an uinstall event should * occur for each case where there a prior install event i.e. for each method into * which the rule was successfully injected, type checked and, possibly, compiled. * * In cases where a rule is redefined the process is slightly different. If the * redefined rule fails to parse, inject or type check then any previously injected rule * gets uninstalled. If a redefined rule is successfully, parsed, injected and type-checked * then uninstall and reinstall of the rule is elided. The obvious benefit of elision is * that the associated helper manager does not get spuriously deactivated and reactivated * by a simple redefinition. However, two potentially (but only mildly) surprising * consequences follow: * * 1) If a newly injected rule is triggered before a subsequent uninstall then it will * be the target of the uninstalled call i.e. callbacks which take a Rule argument cannot * rely on seeing the same rule instance at install and uninstall. Of course, both rules * will still have the same name. * * 2) If a newly injected rule is not triggered before the uninstall happens, or is triggered * and fails to type check or compile, then the previously installed rule will be used as the * target of the uninstalled call i.e. callbacks which take a Rule argument cannot rely upon * the uninstalled rule object being derived from the latest installed script text. */ public void uninstalled(Rule rule) { // ignore unless an agent is actually running if (inst == null) { return; } Class helperClass = rule.getHelperClass(); Helper.verbose("HelperManager.uninstall for helper class " + helperClass.getName()); uninstalled(rule, helperClass); } public void uninstalled(Rule rule, Class helperClass) { // synchronize on the lifecycle class to ensure it is not uninstalled // while we are deciding whether or not to install it synchronized (helperClass) { // uninstall this class first then deal with parents LifecycleDetails details; details = getDetails(helperClass, false, null); if (details == null) { Helper.err("HelperManager.uninstalled : shouldn't happen! uninstall failed to locate helper details for " + helperClass.getName()); return; } details.installCount--; if (details.uninstalled != null) { Helper.verbose("calling uninstalled(" + rule.getName() + ") for helper class " + helperClass.getName()); try { if (details.uninstalledTakesRule) { details.uninstalled.invoke(null, rule); } else { details.uninstalled.invoke(null, rule.getName()); } } catch (Exception e) { Helper.err("HelperManager.installed : unexpected exception from " + helperClass.getName() + ".uninstalled(String) : " + e); Helper.errTraceException(e); } } if (details.installCount == 0 && details.deactivated != null) { Helper.verbose("calling deactivated() for helper class" + helperClass.getName()); try { details.deactivated.invoke(null); } catch (Exception e) { Helper.err("HelperManager.installed : unexpected exception from " + helperClass.getName() + ".deactivate() : " + e); Helper.errTraceException(e); } } if (details.installCount == 0) { purgeDetails(details); } // give the super a chance to uninstall if needed details = details.parent; if (details != null) { uninstalled(rule, details.lifecycleClass); } } } /** * This method exposes a capability of the Byteman agent's * Instrumentation instance while avoiding exposing the instance * itself. It returns an estimate of the object size or -1 in case * an agent has not been installed. * @param o the object to be sized * @return an estimate of the size or -1 */ public long getObjectSize(Object o) { if (inst == null) { Helper.err("Cannot calculate object size since a Byteman agent has not been installed"); return -1; } return this.inst.getObjectSize(o); } public ModuleSystem getModuleSystem() { return moduleSystem; } // private parts /** * the instrumentation object used to install the transformer. If this is null then we are not running in * a real agent so we do no work. */ private Instrumentation inst; /** * the module system implementation. */ private ModuleSystem moduleSystem; /** * a hashmap from helper classes to their corresponding helper details. we don't use weak references here * because there is only ever an entry here if there is a rule installed which references the class. entries * get cleared when the rules are uninstalled. */ private ConcurrentHashMap, LifecycleDetails> helperDetailsMap; /** * a record of a specific helper class tracking the number of installed rules which reference it * and referencing a table detailing the lifecycle methods it implements * * LifeCycleDetails are daisy-chained to ensure that lifecycle processing * associated with a superclass are performed automatically as part of a * given Helper class's lifecycle processing. */ private static class LifecycleDetails { /** * the helper class whose lifecycle this record details */ public Class lifecycleClass; /** * daisy-chain link to the the first parent class which also requires lifecycle processing * or null if there is no such parent */ public LifecycleDetails parent; /** * reference count for installed rules which employ this helper class */ public int installCount; /** * method called when helper is activated */ public Method activated; /** * method called when helper is deactivated */ public Method deactivated; /** * method called when rule is installed */ public Method installed; /** * flag true if installed takes a Rule argument false if it takes a String argument */ public boolean installedTakesRule; /** * method called when rule is uninstalled */ public Method uninstalled; /** * flag true if uninstalled takes a Rule argument false if it takes a String argument */ public boolean uninstalledTakesRule; public LifecycleDetails(Class lifecycleClass, LifecycleDetails parent) { this.lifecycleClass = lifecycleClass; this.parent = parent; this.installCount = 0; } } /** * name of method invoked when helper installed count transitions from 0 to positive */ private final static String ACTIVATED_NAME = "activated"; /** * name of method invoked when helper installed count transitions from positive to 0 */ private final static String DEACTIVATED_NAME = "deactivated"; /** * name of method invoked when rule is installed for a given helper */ private final static String INSTALLED_NAME = "installed"; /** * name of method invoked when rule is uninstalled for a given helper */ private final static String UNINSTALLED_NAME = "uninstalled"; /** * param types of method invoked when helper installed count transitions from 0 to positive */ private final static Class[] ACTIVATED_SIGNATURE = null; /** * param types of method invoked when helper installed count transitions from positive to 0 */ private final static Class[] DEACTIVATED_SIGNATURE = null; /** * param types of method invoked when rule is installed for a given helper */ private final static Class[] INSTALLED_RULE_SIGNATURE = new Class[] { Rule.class }; /** * param types of method invoked when rule is uninstalled for a given helper */ private final static Class[] UNINSTALLED_RULE_SIGNATURE = INSTALLED_RULE_SIGNATURE; /** * param types of method invoked when rule is installed for a given helper */ private final static Class[] INSTALLED_STRING_SIGNATURE = new Class[] { String.class }; /** * param types of method invoked when rule is uninstalled for a given helper */ private final static Class[] UNINSTALLED_STRING_SIGNATURE = INSTALLED_STRING_SIGNATURE; /** * lookup or create a record describing the lifecycle methods of a helper class. this must only be * called when synchronized on the helper class. * @param helperClass * @param createIfAbsent if the details are not present and this is true then create and install new details * @param parent details for the super of helperClass required only when createIfAbsent is true and * the parent class is not Object * @return the relevant details */ private LifecycleDetails getDetails(Class helperClass, boolean createIfAbsent, LifecycleDetails parent) { LifecycleDetails details = helperDetailsMap.get(helperClass); if (details == null && createIfAbsent) { details = new LifecycleDetails(helperClass, parent); details.activated = lookupLifecycleMethod(helperClass, ACTIVATED_NAME, ACTIVATED_SIGNATURE); details.deactivated = lookupLifecycleMethod(helperClass, DEACTIVATED_NAME, DEACTIVATED_SIGNATURE); // check for methods with Rule arguments first details.installed = lookupLifecycleMethod(helperClass, INSTALLED_NAME, INSTALLED_RULE_SIGNATURE); details.uninstalled = lookupLifecycleMethod(helperClass, UNINSTALLED_NAME, UNINSTALLED_RULE_SIGNATURE); if (details.installed != null) { details.installedTakesRule = true; } else { details.installed = lookupLifecycleMethod(helperClass, INSTALLED_NAME, INSTALLED_STRING_SIGNATURE); } if (details.uninstalled != null) { details.uninstalledTakesRule = true; } else { details.uninstalled = lookupLifecycleMethod(helperClass, UNINSTALLED_NAME, UNINSTALLED_STRING_SIGNATURE); } helperDetailsMap.put(helperClass, details); } return details; } /** * return a static public method with the given parameter types it exists otherwise null * @param name * @param paramTypes * @return the method if found otherwise null */ private Method lookupLifecycleMethod(Class clazz, String name, Class[] paramTypes) { try { Method m = clazz.getMethod(name, paramTypes); int mod = m.getModifiers(); if (Modifier.isStatic(mod)) { return m; } } catch (NoSuchMethodException e) { } return null; } /** * purge the details describing the lifecycle methods of a helper class. this must only be * called when synchronized on the helper class. * @param details */ private void purgeDetails(LifecycleDetails details) { helperDetailsMap.remove(details.lifecycleClass); } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/Location.java000066400000000000000000001241421414767246600261010ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2009-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import org.objectweb.asm.ClassVisitor; import org.jboss.byteman.rule.type.TypeHelper; import org.jboss.byteman.agent.adapter.*; /** * Specifies a location in a method at which a rule trigger should be inserted */ public abstract class Location { /** * create a location object of a given type * @param type the type of location being specified * @param parameters the text of the parameters appended to the location specifier * @return a location of the appropriate type or null if the parameters are incorrectly specified */ public static Location create(LocationType type, String parameters) { switch (type) { case ENTRY: return EntryLocation.create(parameters); case LINE: return LineLocation.create(parameters); case READ: return AccessLocation.create(parameters, ACCESS_READ, false); case READ_COMPLETED: return AccessLocation.create(parameters, ACCESS_READ, true); case WRITE: return AccessLocation.create(parameters, ACCESS_WRITE, false); case WRITE_COMPLETED: return AccessLocation.create(parameters, ACCESS_WRITE, true); case INVOKE: return InvokeLocation.create(parameters, false); case INVOKE_COMPLETED: return InvokeLocation.create(parameters, true); case SYNCHRONIZE: return SynchronizeLocation.create(parameters, false); case SYNCHRONIZE_COMPLETED: return SynchronizeLocation.create(parameters, true); case THROW: return ThrowLocation.create(parameters); case EXIT: return ExitLocation.create(parameters); case EXCEPTION_EXIT: return ExceptionExitLocation.create(parameters); case NEW: return NewLocation.create(parameters, false); case NEW_COMPLETED: return NewLocation.create(parameters, true); } return null; } /** * return an adapter which can be used to check whether a method contains a trigger point whose position * matches this location * @param cv the current class visitor * @param transformContext the current transform context * @return the required adapter */ public abstract RuleCheckAdapter getRuleCheckAdapter(ClassVisitor cv, TransformContext transformContext); /** * return an adapter which can be used to insert a trigger call in a method containing a trigger point whose * position matches this location * @param cv the current class visitor * @param transformContext the current transform context * @return the required adapter */ public abstract RuleTriggerAdapter getRuleAdapter(ClassVisitor cv, TransformContext transformContext); /** * identify the type of this location * @return the type of this location */ public abstract LocationType getLocationType(); /** * flag indicating that a field access location refers to field READ operations */ public static final int ACCESS_READ = 1; /** * flag indicating that a field access location refers to field WRITE operations */ public static final int ACCESS_WRITE = 2; /** * location identifying a method entry trigger point */ private static class EntryLocation extends Location { /** * create a location identifying a method entry trigger point * @param parameters the text of the parameters appended to the location specifier * @return a method entry location or null if the parameters is not a blank String */ protected static Location create(String parameters) { if (!parameters.trim().equals("")) { // hmm, not expecting any parameters here return null; } return new EntryLocation(); } /** * return an adapter which can be used to check whether a method contains a trigger point whose position * matches this location * @param cv the current class visitor * @param transformContext the current transform context * @return the required adapter */ public RuleCheckAdapter getRuleCheckAdapter(ClassVisitor cv, TransformContext transformContext) { return new EntryCheckAdapter(cv, transformContext); } /** * return an adapter which can be used to insert a trigger call in a method containing a trigger point whose * position matches this location * @param cv the current class visitor * @param transformContext the current transform context * @return the required adapter */ public RuleTriggerAdapter getRuleAdapter(ClassVisitor cv, TransformContext transformContext) { return new EntryTriggerAdapter(cv, transformContext); } public LocationType getLocationType() { return LocationType.ENTRY; } public String toString() { return "AT ENTRY"; } } /** * location identifying a method line trigger point */ private static class LineLocation extends Location { /** * the line at which the trigger point should be inserted */ private int targetLine; /** * construct a location identifying a method line trigger point * @param targetLine the line at which the trigger point should be inserted */ private LineLocation(int targetLine) { this.targetLine = targetLine; } /** * create a location identifying a method entry trigger point * @param parameters the text of the parameters appended to the location specifier * @return a method entry location or null if the parameters is not a blank String */ protected static Location create(String parameters) { try { int targetLine = Integer.decode(parameters.trim()); return new LineLocation(targetLine); } catch (NumberFormatException nfe) { return null; } } /** * return an adapter which can be used to check whether a method contains a trigger point whose position * matches this location * @param cv the current class visitor * @param transformContext the current transform context * @return the required adapter */ public RuleCheckAdapter getRuleCheckAdapter(ClassVisitor cv, TransformContext transformContext) { return new LineCheckAdapter(cv, transformContext, targetLine); } /** * return an adapter which can be used to insert a trigger call in a method containing a trigger point whose * position matches this location * @param cv the current class visitor * @param transformContext the current transform context * @return the required adapter */ public RuleTriggerAdapter getRuleAdapter(ClassVisitor cv, TransformContext transformContext) { return new LineTriggerAdapter(cv, transformContext, targetLine); } public LocationType getLocationType() { return LocationType.LINE; } public String toString() { return "AT LINE " + targetLine; } } /** * location identifying a generic access trigger point */ private static abstract class AccessLocation extends Location { /** * count identifying which access should be taken as the trigger point. if not specified * as a parameter this defaults to the first access. */ protected int count; /** * flags identifying which type of access should be used to identify the trigger. this is either * ACCESS_READ, ACCESS_WRITE or an OR of these two values */ protected int flags; /** * flag which is false if the trigger should be inserted before the field access is performed * and true if it should be inserted after */ protected boolean whenComplete; protected AccessLocation(int count, int flags, boolean whenComplete) { this.count = count; this.flags = flags; this.whenComplete = whenComplete; } /** * create a location identifying a method entry trigger point * @param parameters the text of the parameters appended to the location specifier * @param flags bit field comprising one or other of flags ACCESS_READ and ACCESS_WRITE identifying * whether this specifies field READ or WRITE operations * @param whenComplete false if the trigger should be inserted before the access is performed * and true if it should be inserted after * @return a method entry location or null if the parameters is not a blank String */ protected static Location create(String parameters, int flags, boolean whenComplete) { String text = parameters.trim(); int count; // check for trailing count if (text.contains(" ")) { int spaceIdx = text.lastIndexOf(" "); String countText = text.substring(spaceIdx).trim(); if (countText.equals("ALL")) { // a zero count means match all count = 0; } else { try { count = Integer.valueOf(countText); } catch (NumberFormatException nfe) { return null; } } text = text.substring(0, spaceIdx).trim(); } else { count = 1; } if (text.equals("")) { return null; } // check for a local or parameter var name identified by a leading $ if (text.startsWith("$")) { String varname = text.substring(1).trim(); return new VariableAccessLocation(varname, count, flags, whenComplete); } else { String typeName; String fieldName; // check for leading type name if (text.contains(".")) { int dotIdx = text.lastIndexOf("."); typeName = text.substring(0, dotIdx).trim(); fieldName=text.substring(dotIdx + 1).trim(); } else { typeName = null; fieldName = text; } // TODO sanity check type and field name return new FieldAccessLocation(typeName, fieldName, count, flags, whenComplete); } } public LocationType getLocationType() { if ((flags & ACCESS_WRITE) != 0) { if (whenComplete) { return LocationType.WRITE_COMPLETED; } else { return LocationType.WRITE; } } else { if (whenComplete) { return LocationType.READ_COMPLETED; } else { return LocationType.READ; } } } } /** * location identifying a field access trigger point */ private static class FieldAccessLocation extends AccessLocation { /** * the name of the field being accessed at the point where the trigger point should be inserted */ private String fieldName; /** * the name of the type to which the field belongs or null if any type will do */ private String typeName; /** * construct a location identifying a field read trigger point * @param typeName the name of the class owning the field * @param fieldName the name of the field being read * @param count count identifying which access should be taken as the trigger point * @param flags bit field comprising one or other of flags ACCESS_READ and ACCESS_WRITE identifying * whether this specifies field READ or WRITE operations * @param whenComplete false if the trigger should be inserted before the access is performed * and true if it should be inserted after */ private FieldAccessLocation(String typeName, String fieldName, int count, int flags, boolean whenComplete) { super(count, flags, whenComplete); this.typeName = typeName; this.fieldName = fieldName; } /** * return an adapter which can be used to check whether a method contains a trigger point whose position * matches this location * @return the required adapter */ public RuleCheckAdapter getRuleCheckAdapter(ClassVisitor cv, TransformContext transformContext) { return new FieldAccessCheckAdapter(cv, transformContext, typeName, fieldName, flags, count); } /** * return an adapter which can be used to insert a trigger call in a method containing a trigger point whose * position matches this location * @return the required adapter */ public RuleTriggerAdapter getRuleAdapter(ClassVisitor cv, TransformContext transformContext) { return new FieldAccessTriggerAdapter(cv, transformContext, typeName, fieldName, flags, count, whenComplete); } public String toString() { String text; if (whenComplete) { text = "AFTER "; } else { text = "AT "; } if (flags == ACCESS_READ) { text += "READ "; } else if (flags == ACCESS_WRITE) { text += "WRITE "; } else { text += "ACCESS "; } if (typeName != null) { text += typeName + "."; } text += fieldName; if (count != 1) { if (count == 0) { text += " ALL"; } else { text += " " + count; } } return text; } } /** * location identifying a variable access trigger point */ private static class VariableAccessLocation extends AccessLocation { /** * the name of the variable being accessed at the point where the trigger point should be inserted */ private String variableName; /** * flag which is true if the name is a method parameter index such as $0, $1 etc otherwise false */ private boolean isIndex; /** * construct a location identifying a variable read trigger point * @param variablename the name of the variable being read * @param count count identifying which access should be taken as the trigger point * @param flags bit field comprising one or other of flags ACCESS_READ and ACCESS_WRITE identifying * whether this specifies field READ or WRITE operations * @param whenComplete false if the trigger should be inserted before the access is performed * and true if it should be inserted after */ protected VariableAccessLocation(String variablename, int count, int flags, boolean whenComplete) { super(count, flags, whenComplete); this.variableName = variablename; isIndex = variablename.matches("[0-9]+"); } /** * return an adapter which can be used to check whether a method contains a trigger point whose position * matches this location * @return the required adapter */ public RuleCheckAdapter getRuleCheckAdapter(ClassVisitor cv, TransformContext transformContext) { if (isIndex) { int paramIdx = Integer.valueOf(variableName); return new IndexParamAccessCheckAdapter(cv, transformContext, paramIdx, flags, count); } else { // we need to insert a BMJSRInliner into the pipeline so that the check adapter gets // notified when local vars go in and out of scope return new VariableAccessCheckAdapter(cv, transformContext, variableName, flags, count); } } /** * return an adapter which can be used to insert a trigger call in a method containing a trigger point whose * position matches this location * @return the required adapter */ public RuleTriggerAdapter getRuleAdapter(ClassVisitor cv, TransformContext transformContext) { if (isIndex) { int paramIdx = Integer.valueOf(variableName); return new IndexParamAccessTriggerAdapter(cv, transformContext, paramIdx, flags, count, whenComplete); } else { return new VariableAccessTriggerAdapter(cv, transformContext, variableName, flags, count, whenComplete); } } public LocationType getLocationType() { if ((flags & ACCESS_WRITE) != 0) { if (whenComplete) { return LocationType.WRITE_COMPLETED; } else { return LocationType.WRITE; } } else { if (whenComplete) { return LocationType.READ_COMPLETED; } else { return LocationType.READ; } } } public String toString() { String text; if (whenComplete) { text = "AFTER "; } else { text = "AT "; } if (flags == ACCESS_READ) { text += "READ "; } else if (flags == ACCESS_WRITE) { text += "WRITE "; } else { text += "ACCESS "; } text += "$"; text += variableName; if (count != 1) { if (count == 0) { text += " ALL"; } else { text += " " + count; } } return text; } } /** * location identifying a method invocation trigger point */ private static class InvokeLocation extends Location { /** * the name of the method being invoked at the point where the trigger point should be inserted */ private String methodName; /** * the name of the type to which the method belongs or null if any type will do */ private String typeName; /** * the method signature in externalised form */ private String signature; /** * count identifying which invocation should be taken as the trigger point. if not specified * as a parameter this defaults to the first invocation. */ private int count; /** * flag which is false if the trigger should be inserted before the method invocation is performed * and true if it should be inserted after */ private boolean whenComplete; /** * construct a location identifying a method invocation trigger point * @param typeName the name of the class owning the method * @param methodName the name of the method being called * @param signature the method signature in externalised form * @param count count identifying which invocation should be taken as the trigger point * @param whenComplete false if the trigger should be inserted before the method invocation is * performed and true if it should be inserted after */ private InvokeLocation(String typeName, String methodName, String signature, int count, boolean whenComplete) { this.typeName = typeName; this.methodName = methodName; this.signature = signature; this.count = count; this.whenComplete = whenComplete; } /** * create a location identifying a method entry trigger point * @param parameters the text of the parameters appended to the location specifier * @param whenComplete false if the trigger should be inserted before the access is performed * and true if ti shoudl be inserted after * @return a method entry location or null if the parameters is not a blank String */ protected static Location create(String parameters, boolean whenComplete) { String text = parameters.trim(); String typeName; String fieldName; String signature; int count; // check for trailing count if (text.contains(")")) { int tailIdx = text.lastIndexOf(")"); String countText = text.substring(tailIdx + 1).trim(); if (!countText.equals("")) { if (countText.equals("ALL")) { // a zero count means all count = 0; } else { try { count = Integer.valueOf(countText); } catch (NumberFormatException nfe) { return null; } } } else { count = 1; } text = text.substring(0, tailIdx + 1).trim(); } else if (text.contains(" ")) { int tailIdx = text.lastIndexOf(" "); String countText = text.substring(tailIdx + 1).trim(); if (!countText.equals("")) { if (countText.equals("ALL")) { // a zero count means all count = 0; } else { try { count = Integer.valueOf(countText); } catch (NumberFormatException nfe) { return null; } } } else { count = 1; } text = text.substring(0, tailIdx).trim(); } else { count = 1; } // check for argument list if (text.contains("(")) { signature = TypeHelper.parseMethodDescriptor(text); text=TypeHelper.parseMethodName(text); } else { signature = ""; } // check for leading type name if (text.contains(".")) { int dotIdx = text.lastIndexOf("."); typeName = text.substring(0, dotIdx).trim(); fieldName=text.substring(dotIdx + 1).trim(); } else { typeName = null; fieldName = text; } // TODO sanity check type and field name return new InvokeLocation(typeName, fieldName, signature, count, whenComplete); } /** * return an adapter which can be used to check whether a method contains a trigger point whose position * matches this location * @return the required adapter */ public RuleCheckAdapter getRuleCheckAdapter(ClassVisitor cv, TransformContext transformContext) { return new InvokeCheckAdapter(cv, transformContext, typeName, methodName, signature, count); } /** * return an adapter which can be used to insert a trigger call in a method containing a trigger point whose * position matches this location * @return the required adapter */ public RuleTriggerAdapter getRuleAdapter(ClassVisitor cv, TransformContext transformContext) { return new InvokeTriggerAdapter(cv, transformContext, typeName, methodName, signature, count, whenComplete); } public LocationType getLocationType() { if (whenComplete) { return LocationType.INVOKE_COMPLETED; } else { return LocationType.INVOKE; } } public String toString() { String text; if (whenComplete) { text = "AFTER INVOKE "; } else { text = "AT INVOKE "; } if (typeName != null) { text += typeName + "."; } text += methodName; if (signature.length() > 0) { text = text + TypeHelper.internalizeDescriptor(signature); } if (count != 1) { if (count == 0) { text += " ALL"; } else { text += " " + count; } } return text; } } /** * location identifying a synchronization trigger point */ private static class SynchronizeLocation extends Location { /** * count identifying which synchronization should be taken as the trigger point. if not specified * as a parameter this defaults to the first synchronization. */ private int count; /** * flag which is false if the trigger should be inserted before the synchronization is performed * and true if it should be inserted after */ private boolean whenComplete; /** * construct a location identifying a synchronization trigger point * @param count count identifying which synchronization should be taken as the trigger point * @param whenComplete false if the trigger should be inserted before the synchronization is * performed and true if it should be inserted after */ private SynchronizeLocation(int count, boolean whenComplete) { this.count = count; this.whenComplete = whenComplete; } /** * create a location identifying a synchronization trigger point * @param parameters the text of the parameters appended to the location specifier * @param whenComplete false if the trigger should be inserted before the synchronization is * performed and true if it should be inserted after * @return a method entry location or null if the parameters is not a blank String */ protected static Location create(String parameters, boolean whenComplete) { String text = parameters.trim(); int count; // check for count if (text.length() != 0) { if (text.equals("ALL")) { // a zero count means all count = 0; } else { try { count = Integer.valueOf(text); } catch (NumberFormatException nfe) { return null; } } } else { count = 1; } return new SynchronizeLocation(count, whenComplete); } /** * return an adapter which can be used to check whether a method contains a trigger point whose position * matches this location * @return the required adapter */ public RuleCheckAdapter getRuleCheckAdapter(ClassVisitor cv, TransformContext transformContext) { return new SynchronizeCheckAdapter(cv, transformContext, count); } /** * return an adapter which can be used to insert a trigger call in a method containing a trigger point whose * position matches this location * @return the required adapter */ public RuleTriggerAdapter getRuleAdapter(ClassVisitor cv, TransformContext transformContext) { return new SynchronizeTriggerAdapter(cv, transformContext, count, whenComplete); } public LocationType getLocationType() { if (whenComplete) { return LocationType.SYNCHRONIZE_COMPLETED; } else { return LocationType.SYNCHRONIZE; } } public String toString() { String text; if (whenComplete) { text= "AFTER SYNCHRONIZE"; } else { text= "AT SYNCHRONIZE"; } if (count != 1) { if (count == 0) { text += " ALL"; } else { text += " " + count; } } return text; } } /** * location identifying a throw trigger point */ private static class ThrowLocation extends Location { /** * count identifying which throw operation should be taken as the trigger point. if not specified * as a parameter this defaults to the first throw. */ private int count; /** * the name of the exception type to which the method belongs or null if any type will do */ private String typeName; /** * construct a location identifying a throw trigger point * @param count count identifying which throw should be taken as the trigger point * @param typeName the name of the exception type associated with the throw operation */ private ThrowLocation(int count, String typeName) { this.count = count; this.typeName = typeName; } /** * create a location identifying a throw trigger point * @param parameters the text of the parameters appended to the location specifier * @return a throw location or null if the parameters does not contain a valid type name */ protected static Location create(String parameters) { String text = parameters.trim(); String typeName = ""; int count; // text may be either blank, a count or ALL if (text.equals("")) { // count defaults to 1 count = 1; } else if (text.equals("ALL")) { // a zero count means all count = 0; } else { try { count = Integer.valueOf(text); } catch (NumberFormatException nfe) { return null; } } // TODO sanity check type name return new ThrowLocation(count, typeName); } /** * return an adapter which can be used to check whether a method contains a trigger point whose position * matches this location * @return the required adapter */ public RuleCheckAdapter getRuleCheckAdapter(ClassVisitor cv, TransformContext transformContext) { return new ThrowCheckAdapter(cv, transformContext, typeName, count); } /** * return an adapter which can be used to insert a trigger call in a method containing a trigger point whose * position matches this location * @return the required adapter */ public RuleTriggerAdapter getRuleAdapter(ClassVisitor cv, TransformContext transformContext) { return new ThrowTriggerAdapter(cv, transformContext, typeName, count); } public LocationType getLocationType() { return LocationType.THROW; } public String toString() { String text = "AT THROW"; if (count != 1) { if (count == 0) { text += " ALL"; } else { text += " " + count; } } return text; } } /** * location identifying a method exit trigger point */ private static class ExitLocation extends Location { /** * create a location identifying a method entry trigger point * @param parameters the text of the parameters appended to the location specifier * @return a method entry location or null if the parameters is not a blank String */ protected static Location create(String parameters) { if (!parameters.trim().equals("")) { // hmm, not expecting any parameters here return null; } return new ExitLocation(); } /** * return an adapter which can be used to check whether a method contains a trigger point whose position * matches this location * @return the required adapter */ public RuleCheckAdapter getRuleCheckAdapter(ClassVisitor cv, TransformContext transformContext) { // a line check adapter with line -1 will do the job return new ExitCheckAdapter(cv, transformContext); } /** * return an adapter which can be used to insert a trigger call in a method containing a trigger point whose * position matches this location * @return the required adapter */ public RuleTriggerAdapter getRuleAdapter(ClassVisitor cv, TransformContext transformContext) { // a line adapter with line -1 will do the job return new ExitTriggerAdapter(cv, transformContext); } public LocationType getLocationType() { return LocationType.EXIT; } public String toString() { return "AT EXIT"; } } /** * location identifying a method exceptional exit trigger point */ private static class ExceptionExitLocation extends Location { /** * create a location identifying a method exceptional exit trigger point * @param parameters the text of the parameters appended to the location specifier * @return a method entry location or null if the parameters is not a blank String */ protected static Location create(String parameters) { if (!parameters.trim().equals("")) { // hmm, not expecting any parameters here return null; } return new ExceptionExitLocation(); } /** * return an adapter which can be used to check whether a method contains a trigger point whose position * matches this location * @return the required adapter */ public RuleCheckAdapter getRuleCheckAdapter(ClassVisitor cv, TransformContext transformContext) { // a line check adapter with line -1 will do the job return new ExceptionExitCheckAdapter(cv, transformContext); } /** * return an adapter which can be used to insert a trigger call in a method containing a trigger point whose * position matches this location * @return the required adapter */ public RuleTriggerAdapter getRuleAdapter(ClassVisitor cv, TransformContext transformContext) { // a line adapter with line -1 will do the job return new ExceptionExitTriggerAdapter(cv, transformContext); } public LocationType getLocationType() { return LocationType.EXCEPTION_EXIT; } public String toString() { return "AT EXCEPTION EXIT"; } } private static class NewLocation extends Location { /** * the name of the new type being created or the empty String if * no typename was specified */ private String typeName; /** * count identifying which new operation should be taken as the trigger point. * if not specified as a parameter this defaults to the first invocation. if * 'ALL' was specified this takes value 0. */ private int count; /** * number of array dimensions that should be matched at an array allocation site * or 0 if plain, non-array object allocations should be matched */ int dims; /** * flag which is false if the trigger should be inserted before the method invocation is performed * and true if it should be inserted after */ private boolean whenComplete; private NewLocation(String typeName, int count, int dims, boolean whenComplete) { this.typeName = typeName; this.count = count; this.dims = dims; this.whenComplete = whenComplete; } /** * create a location identifying a method exceptional exit trigger point * @param parameters the text of the parameters appended to the location specifier * @return a method entry location or null if the parameters is not a blank String */ protected static Location create(String parameters, boolean whenComplete) { // syntax is AT NEW [{typename}] [{count} | 'ALL'] String text = parameters.trim(); String typeNamePattern = "[A-Za-z][A-Za-z0-9_]+"; String arrayDimsPattern = "(\\[\\])+"; String countPattern = "[0-9]+"; String allPattern = "ALL"; String countText = null; String typeNameText = null; String typeName; int count; int dims; if (text.contains(" ")) { int tailIdx = text.lastIndexOf(" "); countText = text.substring(tailIdx + 1).trim(); typeNameText = text.substring(0, tailIdx).trim(); } else if (text.matches(allPattern) || text.matches(countPattern)) { typeNameText = null; countText = text; } else { typeNameText = text; countText = null; } if (countText == null) { count = 1; } else if (countText.matches(allPattern)) { count = 0; } else if (countText.matches(countPattern)){ try { count = Integer.valueOf(countText); } catch (NumberFormatException nfe) { return null; } } else if (countText.matches(arrayDimsPattern)) { // space was separating array dims from type // glue it back together and continue. typeNameText = typeNameText + countText; count = 1; } else { return null; } if (typeNameText == null || typeNameText.length() == 0) { typeName = ""; dims = 0; } else if (typeNameText.matches(typeNamePattern)) { typeName = typeNameText; dims = 0; } else if (typeNameText.matches(arrayDimsPattern)) { typeName = ""; dims = typeNameText.length() / 2; } else { if (!typeNameText.contains("[")) { return null; } int tailIdx = typeNameText.indexOf("["); String dimsText = typeNameText.substring(tailIdx); typeNameText = typeNameText.substring(0, tailIdx); if (typeNameText.matches(typeNamePattern) && dimsText.matches(arrayDimsPattern)) { typeName = typeNameText; dims = dimsText.length() / 2; } else { return null; } } return new NewLocation(typeName, count, dims, whenComplete); } public RuleCheckAdapter getRuleCheckAdapter(ClassVisitor cv, TransformContext transformContext) { if (dims == 0) { return new NewCheckAdapter(cv, transformContext, typeName, count, whenComplete); } else { return new NewArrayCheckAdapter(cv, transformContext, typeName, count, dims, whenComplete); } } public RuleTriggerAdapter getRuleAdapter(ClassVisitor cv, TransformContext transformContext) { if (dims == 0) { return new NewTriggerAdapter(cv, transformContext, typeName, count, whenComplete); } else { return new NewArrayTriggerAdapter(cv, transformContext, typeName, count, dims, whenComplete); } } public LocationType getLocationType() { return (whenComplete ? LocationType.NEW_COMPLETED : LocationType.NEW); } public String toString() { StringBuilder builder = new StringBuilder(); builder.append((whenComplete ? "AFTER NEW" : "AT NEW")); if (typeName != null) { builder.append(" "); builder.append(typeName); } for (int i = 0; i < dims; i++) { builder.append("[]"); } if (count == 0) { builder.append(" ALL"); } else if (count != 1) { builder.append(" "); builder.append(count); } return builder.toString(); } } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/LocationType.java000066400000000000000000000175571414767246600267560ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2009-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * enum categorizing types of locations at which rule triggers can be inserted */ public enum LocationType { /** * specifies the default location for trigger insertion which is either the first line of a method or * the first line of a constructor following any indirection via an alternative constructor or via * the super constructor. * * script syntax : 'AT' 'ENTRY' */ ENTRY, /** * specifies a location for trigger insertion via a line number. * * script syntax : 'AT' 'LINE' {linenumber} */ LINE, /** * specifies a location for trigger insertion by identifying a field read operation or the nth such field * read if a count is supplied or all field reads if ALL is specified. * * script syntax : 'AT' 'READ' [{typename} '.' ] {fieldname} [ {count} | 'ALL' ] */ READ, /** * specifies a location for trigger insertion by identifying a field read operation or the nth such field * read if a count is supplied or all field reads if ALL is specified. * * script syntax : 'AFTER' 'READ' [{typename} '.' ] {fieldname} [ {count} | 'ALL' ] */ READ_COMPLETED, /** * specifies a location for trigger insertion by identifying a field write operation or the nth such field * write if a count is supplied or all field writes if ALL is specified. * * script syntax : 'AT' 'WRITE' [{typename} '.' ] {fieldname} [ {count} | 'ALL' ] */ WRITE, /** * specifies a location for trigger insertion by identifying a field write operation or the nth such field * write if a count is supplied or all field writes if ALL is specified. * * script syntax : 'AFTER' 'WRITE' [{typename} '.' ] {fieldname} [ {count} | 'ALL' ] */ WRITE_COMPLETED, /** * specifies a location for trigger insertion by identifying a method invoke operation or the nth such * method invoke if a count is supplied or all method invocations if ALL is specified. * * script syntax : 'AT' 'INVOKE' [{typename} '.' ] {methodname} ['(' {argtypes} ')' [ {count} | 'ALL' ] */ INVOKE, /** * specifies a location for trigger insertion by identifying return from a method invoke operation or the * nth such return if a count is supplied or all method invocations if ALL is specified. * * script syntax : 'AFTER' 'INVOKE' [{typename} '.' ] {methodname} ['(' {argtypes} ')' [ {count} | 'ALL' ] */ INVOKE_COMPLETED, /** * specifies a location for trigger insertion by identifying a synchronize operation or the nth such * operation if a count is supplied or all synchronize operations if ALL is specified. * * script syntax : 'AT' 'SYNCHRONIZE' [ {count} | 'ALL' ] */ SYNCHRONIZE, /** * specifies a location for trigger insertion by identifying completion of a synchronize operation or the * nth such operation if a count is supplied or all synchronize operations if ALL is specified. * * script syntax : 'AFTER' 'SYNCHRONIZE' [ {count} | 'ALL' ] */ SYNCHRONIZE_COMPLETED, /** * specifies a location for trigger insertion by identifying throw of an exception of the nth such throw * if a count is supplied or all throws if ALL is specified * script syntax : 'AT' 'THROW' [{typename}] [ {count} | 'ALL' ] * n.b. exception typename parsed but not yet implemented */ THROW, /** * specifies a location for trigger insertion at return from the trigger method n.b. a trigger will be * injected at ALL return points * script syntax : 'AT' 'EXIT' */ EXIT, /** * specifies a location for trigger insertion on exception exit from the trigger method * script syntax : 'AT' 'EXCEPTION' 'EXIT' */ EXCEPTION_EXIT, /** * specifies a location for trigger insertion at object allocation * script syntax : 'AT' 'NEW' [{typename}] [ '[]'+ ] [ {count} | 'ALL' ] */ NEW, /** * specifies a location for trigger insertion after object allocation and initialization * script syntax : 'AFTER' 'NEW' [{typename}] [ '[]'+ ] [ {count} | 'ALL' ] */ NEW_COMPLETED; public String specifierText() { for (int i = 0; i < specifiers.length; i++) { if (types[i] == this) { return specifiers[i]; } } // hmm, well default to entry return specifiers[0]; } public static LocationType type(String locationSpec) { locationSpec = locationSpec.trim(); for (int i = 0; i < specifiers.length; i++) { Pattern p = specifierPatterns[i]; Matcher m = p.matcher(locationSpec); if (m.lookingAt()) { return types[i]; } } // hmm, well default to entry return null; } public static String parameterText(String locationSpec) { locationSpec = locationSpec.trim(); for (int i = 0; i < specifiers.length; i++) { Pattern p = specifierPatterns[i]; Matcher m = p.matcher(locationSpec); if (m.lookingAt()) { return locationSpec.substring(m.end()); } } // hmm, doesn't really matter but ENTRY has no parameters return ""; } private static Pattern[] createPatterns() { int length = specifiers.length; Pattern[] patterns = new Pattern[length]; for (int i = 0; i < length; i++) { patterns[i] = Pattern.compile(specifiers[i]); } return patterns; } private static String[] specifiers = { "AT[ \t]*ENTRY", "AT[ \t]*LINE", "AT[ \t]*READ", "AFTER[ \t]*READ", "AT[ \t]*WRITE", "AFTER[ \t]*WRITE", "AT[ \t]*INVOKE", "AFTER[ \t]*INVOKE", "AT[ \t]*SYNCHRONIZE", "AFTER[ \t]*SYNCHRONIZE", "AT[ \t]*THROW", "AT[ \t]*EXIT", "AT[ \t]*EXCEPTION[ \t]*EXIT", "AT[ \t]*NEW", "AFTER[ \t]*NEW", "LINE", // for compatibility "AT[ \t]*CALL", // for ambiguity :-) "AFTER[ \t]*CALL", // for ambiguity :-) "AT[ \t]*RETURN" // for ambiguity :-) }; private static Pattern[] specifierPatterns = createPatterns(); private static LocationType[] types = { ENTRY, LINE, READ, READ_COMPLETED, WRITE, WRITE_COMPLETED, INVOKE, INVOKE_COMPLETED, SYNCHRONIZE, SYNCHRONIZE_COMPLETED, THROW, EXIT, EXCEPTION_EXIT, NEW, NEW_COMPLETED, LINE, INVOKE, INVOKE_COMPLETED, EXIT }; } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/Main.java000066400000000000000000000460171414767246600252210ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2008-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.Socket; import java.util.ArrayList; import java.util.List; import java.util.jar.JarFile; /** * agent class supplied at JVM startup to install byteman package bytecode transformer */ public class Main { public static boolean firstTime = true; public final static String BYTEMAN_PREFIX = "org.jboss.byteman."; public final static String BYTEMAN_AGENT_LOADED = "org.jboss.byteman.agent.loaded"; public static void premain(String args, Instrumentation inst) throws Exception { // guard against the agent being loaded twice synchronized (Main.class) { if (firstTime) { firstTime = false; System.setProperty(BYTEMAN_AGENT_LOADED, Boolean.TRUE.toString()); } else { throw new Exception("Main : attempting to load Byteman agent more than once"); } } boolean installPolicy = false; if (args != null) { // args are supplied separated by ',' characters String[] argsArray = args.split(","); // we accept extra jar files to be added to the boot/sys classpaths // script files to be scanned for rules // listener flag which implies use of a retransformer for (String arg : argsArray) { if (arg.startsWith(BOOT_PREFIX)) { bootJarPaths.add(arg.substring(BOOT_PREFIX.length(), arg.length())); } else if (arg.startsWith(SYS_PREFIX)) { sysJarPaths.add(arg.substring(SYS_PREFIX.length(), arg.length())); } else if (arg.startsWith(ADDRESS_PREFIX)) { hostname = arg.substring(ADDRESS_PREFIX.length(), arg.length()); if (managerClassName == null) { managerClassName=MANAGER_NAME; } } else if (arg.startsWith(PORT_PREFIX)) { try { port = Integer.valueOf(arg.substring(PORT_PREFIX.length(), arg.length())); if (port <= 0) { System.err.println("Invalid port specified [" + port + "]"); port = null; } else if (managerClassName == null) { managerClassName=MANAGER_NAME; } } catch (Exception e) { System.err.println("Invalid port specified [" + arg + "]. Cause: " + e); } } else if (arg.startsWith(SCRIPT_PREFIX)) { scriptPaths.add(arg.substring(SCRIPT_PREFIX.length(), arg.length())); } else if (arg.startsWith(RESOURCE_SCRIPT_PREFIX)) { resourcescriptPaths.add(arg.substring(RESOURCE_SCRIPT_PREFIX.length(), arg.length())); } else if (arg.startsWith(LISTENER_PREFIX)) { // listener:true is an alias for manager:o.j.b.a.TransformListener // listener:false means no manager (yes, not even TransformListener) String value = arg.substring(LISTENER_PREFIX.length(), arg.length()); if (Boolean.parseBoolean(value)) { managerClassName = MANAGER_NAME; } else { managerClassName = null; } } else if (arg.startsWith(REDEFINE_PREFIX)) { // this is only for backwards compatibility -- it is the same as listener String value = arg.substring(REDEFINE_PREFIX.length(), arg.length()); if (Boolean.parseBoolean(value)) { managerClassName = MANAGER_NAME; } else { managerClassName = null; } } else if (arg.startsWith(PROP_PREFIX)) { // this can be used to set byteman properties String prop = arg.substring(PROP_PREFIX.length(), arg.length()); String value=""; if (prop.startsWith(BYTEMAN_PREFIX)) { int index = prop.indexOf('='); if (index > 0) { // need to split off the value if (index == prop.length() - 1) { // value is empty so just drop the = prop = prop.substring(0, index); } else { value = prop.substring(index + 1); prop = prop.substring(0, index); } } System.out.println("Setting " + prop + "=" + value); System.setProperty(prop, value); } else { System.err.println("Invalid property : " + prop); } } else if (arg.startsWith(POLICY_PREFIX)) { String value = arg.substring(POLICY_PREFIX.length(), arg.length()); installPolicy = Boolean.parseBoolean(value); } else if (arg.startsWith(MANAGER_PREFIX)) { managerClassName = arg.substring(MANAGER_PREFIX.length(), arg.length()); if (managerClassName.length() == 0) { managerClassName = null; } } else if (arg.startsWith(MODULE_PREFIX)) { // this can be used to set byteman properties String mod = arg.substring(MODULE_PREFIX.length(), arg.length()); String moduleArgs=""; int index = mod.indexOf('='); if (index > 0) { // need to split off the value if (index == mod.length() - 1) { // value is empty so just drop the = mod = mod.substring(0, index); } else { moduleArgs = mod.substring(index + 1); mod = mod.substring(0, index); } } moduleSystemName = mod; moduleSystemArgs = moduleArgs; } else { System.err.println("org.jboss.byteman.agent.Main:\n" + " illegal agent argument : " + arg + "\n" + " valid arguments are boot:, sys:, script:, resourcescript:," + "prop:, address:, port:, modules:, " + "policy:, manager: or listener:"); } } } // add any boot jars to the boot class path for (String bootJarPath : bootJarPaths) { try { JarFile jarfile = new JarFile(new File(bootJarPath)); inst.appendToBootstrapClassLoaderSearch(jarfile); } catch (IOException ioe) { System.err.println("org.jboss.byteman.agent.Main: unable to open boot jar file : " + bootJarPath); throw ioe; } } // add any sys jars to the system class path for (String sysJarPath : sysJarPaths) { try { JarFile jarfile = new JarFile(new File(sysJarPath)); inst.appendToSystemClassLoaderSearch(jarfile); } catch (IOException ioe) { System.err.println("org.jboss.byteman.agent.Main: unable to open system jar file : " + sysJarPath); throw ioe; } } // create a socket so we can be sure it is loaded before the transformer gets created. otherwise // we seem to hit a deadlock when trying to instrument socket Socket dummy = new Socket(); // look up rules in any script files for (String scriptPath : scriptPaths) { FileInputStream fis = null; try { fis = new FileInputStream(scriptPath); byte[] bytes = new byte[fis.available()]; fis.read(bytes); String ruleScript = new String(bytes); scripts.add(ruleScript); } catch (IOException ioe) { System.err.println("org.jboss.byteman.agent.Main: unable to read rule script file : " + scriptPath); throw ioe; } finally { if (fis != null) fis.close(); } } // look up rules in any resource script files for (String scriptPath : resourcescriptPaths) { try { InputStream is = ClassLoader.getSystemResourceAsStream(scriptPath); if (is == null) { throw new Exception("org.jboss.byteman.agent.Main: could not read rule script resource file : " + scriptPath); } byte[] bytes = new byte[is.available()]; is.read(bytes); String ruleScript = new String(bytes); scripts.add(ruleScript); // merge the resource and file script paths into one list scriptPaths.add(scriptPath); } catch (IOException ioe) { System.err.println("org.jboss.byteman.agent.Main: error reading rule script resource file : " + scriptPath); throw ioe; } } // install an instance of Transformer to instrument the bytecode // n.b. this is done with boxing gloves on using explicit class loading and method invocation // via reflection for a GOOD reason. This class (Main) gets loaded by the System class loader. // If we refer to Transformer by name then it also gets loaded via the System class loader. // But if we want to transform a bootstrap class we need Transformer (et al) to be visible // from the bootstrap class loader. That will not happen until after this method has called // inst.appendToBootstrapClassLoaderSearch (see above) to add the byteman jar to the path. // Directly referring to Transformer will give us two versions of Transformer et al. Not only // does that cause us class mismatch problem it also means that a new done here will not install // the new instance in the static field of the one loaded in the bootstrap loader. If instead we // use boxing gloves then the byteman code will get loaded in the bootstrap loader and its constructor // will be called. // // Of course, if the user does not supply boot:byteman.jar as a -javaagent option then class references // resolve against the system loader and injection into bootstrap classes fails. But that's still ok // because the byteman classes are still only found in one place. ClassLoader loader = ClassLoader.getSystemClassLoader(); if (moduleSystemName == null) { moduleSystemName = "org.jboss.byteman.modules.NonModuleSystem"; } Class moduleSystemInteraceClazz = loader.loadClass(MODULE_SYSTEM_NAME); Class moduleSystemImplClazz = loader.loadClass(moduleSystemName); final Object/*ModuleSystem*/ moduleSystem = moduleSystemImplClazz.newInstance(); final Method/*String->void*/ moduleSystemInit = moduleSystemInteraceClazz.getMethod("initialize", String.class); moduleSystemInit.invoke(moduleSystem, moduleSystemArgs); boolean isRedefine = inst.isRedefineClassesSupported(); Class/**/ transformerClazz; ClassFileTransformer transformer; if (managerClassName != null && isRedefine) { transformerClazz = loader.loadClass(RETRANSFORMER_NAME); //transformer = new Retransformer(inst, moduleSystem, scriptPaths, scripts, true); Constructor/**/ constructor = transformerClazz.getConstructor(Instrumentation.class,moduleSystemInteraceClazz, List.class, List.class, boolean.class); transformer = (ClassFileTransformer)constructor.newInstance(new Object[] { inst, moduleSystem, scriptPaths, scripts, isRedefine}); } else { transformerClazz = loader.loadClass(TRANSFORMER_NAME); //transformer = new Transformer(inst, moduleSystem, scriptPaths, scripts, isRedefine); Constructor/**/ constructor = transformerClazz.getConstructor(Instrumentation.class, moduleSystemInteraceClazz, List.class, List.class, boolean.class); transformer = (ClassFileTransformer)constructor.newInstance(new Object[] { inst, moduleSystem, scriptPaths, scripts, isRedefine}); } inst.addTransformer(transformer, true); if (managerClassName != null && isRedefine) { Class managerClazz = loader.loadClass(managerClassName); try { Method method = managerClazz.getMethod("initialize", transformerClazz, String.class, Integer.class); method.invoke(null, transformer, hostname, port); } catch (NoSuchMethodException e) { Method method = managerClazz.getMethod("initialize", transformerClazz); method.invoke(null, transformer); } } if (installPolicy) { Method method = transformerClazz.getMethod("installPolicy"); method.invoke(transformer); } if (isRedefine) { Method method; method = transformerClazz.getMethod("installBootScripts"); method.invoke(transformer); //transformer.installBootScripts(); } } public static void agentmain(String args, Instrumentation inst) throws Exception { premain(args, inst); } /** * prefix used to specify port argument for agent */ private static final String PORT_PREFIX = "port:"; /** * prefix used to specify bind address argument for agent */ private static final String ADDRESS_PREFIX = "address:"; /** * prefix used to specify boot jar argument for agent */ private static final String BOOT_PREFIX = "boot:"; /** * prefix used to specify system jar argument for agent */ private static final String SYS_PREFIX = "sys:"; /** * prefix used to request installation of an access-all-areas security * policy at install time for agent code */ private static final String POLICY_PREFIX = "policy:"; /** * prefix used to specify file script argument for agent */ private static final String SCRIPT_PREFIX = "script:"; /** * prefix used to specify resource script argument for agent */ private static final String RESOURCE_SCRIPT_PREFIX = "resourcescript:"; /** * prefix used to specify transformer type argument for agent */ private static final String LISTENER_PREFIX = "listener:"; /** * for backwards compatibiltiy */ private static final String REDEFINE_PREFIX = "redefine:"; /** * prefix used to specify system properties to be set before starting the agent */ private static final String PROP_PREFIX = "prop:"; /** * prefix used to specify the manager class */ private static final String MANAGER_PREFIX = "manager:"; /** * prefix used to specify the module system class */ private static final String MODULE_PREFIX = "modules:"; /** * name of basic transformer class. */ private static final String TRANSFORMER_NAME = "org.jboss.byteman.agent.Transformer"; /** * name of retransformer class. */ private static final String RETRANSFORMER_NAME = "org.jboss.byteman.agent.Retransformer"; /** * name of default manager class. */ private static final String MANAGER_NAME = "org.jboss.byteman.agent.TransformListener"; /** * name of module system interface. */ private static final String MODULE_SYSTEM_NAME = "org.jboss.byteman.modules.ModuleSystem"; /** * list of paths to extra bootstrap jars supplied on command line */ private static List bootJarPaths = new ArrayList(); /** * list of paths to extra system jars supplied on command line */ private static List sysJarPaths = new ArrayList(); /** * list of paths to script files supplied on command line */ private static List scriptPaths = new ArrayList(); /** * list of paths to resource script files supplied on command line */ private static List resourcescriptPaths = new ArrayList(); /** * list of scripts read from script files */ private static List scripts = new ArrayList(); /** * The hostname to bind the listener to, supplied on the command line (optional argument) */ private static String hostname = null; /** * The port that the listener will listen to, supplied on the command line (optional argument) */ private static Integer port = null; /** * The name of the manager class responsible for loading/unloading scripts, supplied on the * command line (optional argument) */ private static String managerClassName = null; /** * The name of the module system implementation class, supplied on the * command line (optional argument) */ private static String moduleSystemName = null; /** * The arguments to the module system implementation class, supplied on the * command line (optional argument) */ private static String moduleSystemArgs = ""; } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/Retransformer.java000066400000000000000000000444711414767246600271700ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2008-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import java.io.PrintWriter; import java.lang.instrument.Instrumentation; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.jar.JarFile; import org.jboss.byteman.modules.ModuleSystem; import org.jboss.byteman.rule.helper.Helper; /** * byte code transformer used to introduce byteman events into JBoss code */ public class Retransformer extends Transformer { private Set sysJars = new HashSet(); // jar files that were loaded in the sys CL private Set bootJars = new HashSet(); // jar files that were loaded in the boot CL /** * constructor allowing this transformer to be provided with access to the JVM's instrumentation * implementation * * @param inst the instrumentation object used to interface to the JVM * @param moduleSystem the module system to use for helper and class loading * @param scriptPaths list of file paths for each input script * @param scriptTexts the text of each input script * @param isRedefine true if class redefinition is allowed false if not * @throws Exception if a script is in error */ public Retransformer(Instrumentation inst, ModuleSystem moduleSystem, List scriptPaths, List scriptTexts, boolean isRedefine) throws Exception { super(inst, moduleSystem, scriptPaths, scriptTexts, isRedefine); //addTransformListener(hostname, port); } public void installScript(List scriptTexts, List scriptNames, PrintWriter out) throws Exception { int length = scriptTexts.size(); List toBeAdded = new LinkedList(); List toBeRemoved = new LinkedList(); for (int i = 0; i < length ; i++) { String scriptText = scriptTexts.get(i); String scriptName = scriptNames.get(i); List ruleScripts = scriptRepository.processScripts(scriptText, scriptName); toBeAdded.addAll(ruleScripts); } for (RuleScript ruleScript : toBeAdded) { String name = ruleScript.getName(); String className = ruleScript.getTargetClass(); String baseName = null; int lastDotIdx = className.lastIndexOf('.'); if (lastDotIdx >= 0) { baseName = className.substring(lastDotIdx + 1); } RuleScript previous; previous = scriptRepository.addScript(ruleScript); if (previous != null) { out.println("redefine rule " + name); toBeRemoved.add(previous); } else { out.println("install rule " + name); } } // ok, now that we have updated the indexes we need to find all classes which match the scripts and // retransform them // list all class names for the to be added and to be removed scripts List deletedClassNames = new LinkedList(); for (RuleScript ruleScript : toBeRemoved) { List transforms = ruleScript.allTransforms(); for (Transform transform : transforms) { // only need to retransform classes which were updated // so ignore transforms which include a throwable Throwable throwable = transform.getThrowable(); if(throwable == null) { String className = transform.getInternalClassName(); if(!deletedClassNames.contains(className)) { deletedClassNames.add(className); } } } } // for added scripts we have to transform anything which might be a match ScriptRepository tmpRepository = new ScriptRepository(skipOverrideRules()); for (RuleScript ruleScript : toBeAdded) { tmpRepository.addScript(ruleScript); } // now look for loaded classes whose names are in the deleted list or which match added rules List> transformed = new LinkedList>(); for (Class clazz : inst.getAllLoadedClasses()) { if (isSkipClass(clazz)) { continue; } if (deletedClassNames.contains(clazz.getName())) { transformed.add(clazz); } else if (tmpRepository.matchClass(clazz)) { transformed.add(clazz); } } // retransform all classes whose rules have changed if (!transformed.isEmpty()) { Class[] transformedArray = new Class[transformed.size()]; transformed.toArray(transformedArray); for (int i = 0; i < transformed.size(); i++) { Helper.verbose("retransforming " + transformedArray[i].getName()); } synchronized(this) { try { inst.retransformClasses(transformedArray); } catch(VerifyError ve) { Helper.err("Retransformer : VerifyError during retransformation : some rules may not have been correctly injected or uninjected!"); Helper.errTraceException(ve); out.println("VerifyError during retransformation : some rules may not have been correctly injected or uninjected!"); ve.printStackTrace(out); } } } // now we need to ensure that previously installed // rules are uninstalled. however, if the rule // has been re-injected in an equivalent transform // set then we simply mark it as installed, eliding // an extra uninstall/install cycle. this also // avoids an unhelpful deactivate/activate step // that is not really appropriate when redefining // an existing rule. // // n.b. we mark the set using the last installed Rule // instance so as to to retain a target for any subsequent // uninstall. if/when the newly injected rule gets triggered // it will update to use the new rule as the marker. for (RuleScript oldRuleScript : toBeRemoved) { RuleScript newRuleScript = scriptRepository.scriptForRuleName(oldRuleScript.getName()); // new script must exist! synchronized (newRuleScript) { for (TransformSet oldTransformSet : oldRuleScript.getTransformSets()) { // see if we have an equivalent new rule set TransformSet newTransformSet = newRuleScript.lookupTransformSet(oldTransformSet.getLoader(), oldTransformSet.getTriggerClass()); if(newTransformSet == null || newTransformSet.isInstalled()) { if(oldTransformSet.isInstalled()) { // we need to run an uninstall for the old transform set oldTransformSet.getRule().uninstalled(); } } else { // copy across the rule used for the prior // install so we can use it for a later uninstall // it will be replaced with a new instance // if any of the newly injected rules pass // ensureTypeCheckCompiled if(newTransformSet != null) { newTransformSet.setInstalled(oldTransformSet.getRule()); } else { newRuleScript.ensureTransformSet(oldTransformSet.getLoader(), oldTransformSet.getTriggerClass(), oldTransformSet.getRule()); } } } } } } protected void collectAffectedNames(List ruleScripts, List classList, List interfaceList, List superClassList, List superInterfaceList) { for (RuleScript ruleScript : ruleScripts) { String targetClassName = ruleScript.getTargetClass(); boolean isOverride = ruleScript.isOverride(); if (ruleScript.isInterface()) { if (!interfaceList.contains(targetClassName)) { interfaceList.add(targetClassName); if (isOverride) { superInterfaceList.add(targetClassName); } } } else { if (!classList.contains(targetClassName)) { classList.add(targetClassName); if (isOverride) { superClassList.add(targetClassName); } } } } } public void listScripts(PrintWriter out) throws Exception { Iterator iterator = scriptRepository.currentRules().iterator(); if (!iterator.hasNext()) { out.println("no rules installed"); } else { while (iterator.hasNext()) { RuleScript ruleScript = iterator.next(); ruleScript.writeTo(out); synchronized (ruleScript) { List transforms = ruleScript.allTransforms(); for (Transform transform : transforms) { transform.writeTo(out); } } } } } public void removeScripts(List scriptTexts, PrintWriter out) throws Exception { List toBeRemoved; if (scriptTexts != null) { toBeRemoved = new LinkedList(); int length = scriptTexts.size(); for (int i = 0; i < length ; i++) { String scriptText = scriptTexts.get(i); String[] lines = scriptText.split("\n"); for (int j = 0; j < lines.length; j++) { String line = lines[j].trim(); if (line.startsWith("RULE ")) { String name = line.substring(5).trim(); RuleScript ruleScript = scriptRepository.scriptForRuleName(name); if (ruleScript == null) { out.print("ERROR failed to find loaded rule with name "); out.println(name); } else if (toBeRemoved.contains(ruleScript)) { out.print("WARNING duplicate occurence for rule name "); out.println(name); } else { toBeRemoved.add(ruleScript); } } } } } else { toBeRemoved = scriptRepository.currentRules(); } if (toBeRemoved.isEmpty()) { out.println("ERROR No rule scripts to remove"); return; } for (RuleScript ruleScript : toBeRemoved) { if (scriptRepository.removeScript(ruleScript) != ruleScript) { out.println("ERROR remove failed to find script " + ruleScript.getName()); } } // ok, now that we have updated the maps and deleted the scripts // we need to find all classes which were transformed by // the scripts and retransform them // now look for loaded classes whose names are in the list List> transformed = new LinkedList>(); List deletedClassNames = new LinkedList(); for (RuleScript ruleScript : toBeRemoved) { for (Transform transform : ruleScript.allTransforms()) { // only need to retransform classes which were updated // so ignore transforms which include a throwable Throwable throwable = transform.getThrowable(); if(throwable == null) { String className = transform.getInternalClassName(); if(!deletedClassNames.contains(className)) { deletedClassNames.add(className); } } } } for (Class clazz : inst.getAllLoadedClasses()) { if (isSkipClass(clazz)) { continue; } if (deletedClassNames.contains(clazz.getName())) { transformed.add(clazz); } } // retransform all classes affected by the change if (!transformed.isEmpty()) { Class[] transformedArray = new Class[transformed.size()]; transformed.toArray(transformedArray); for (int i = 0; i < transformed.size(); i++) { Helper.verbose("retransforming " + transformedArray[i].getName()); } try { inst.retransformClasses(transformedArray); } catch(VerifyError ve) { Helper.err("Retransformer : VerifyError during retransformation : some rules may not have been correctly uninjected!"); Helper.errTraceException(ve); out.println("VerifyError during retransformation : some rules may not have been correctly uninjected!"); ve.printStackTrace(out); } } // now we can safely purge keys for all the deleted scripts -- we need to do this // after the retransform because the latter removes the trigger code which uses // the rule key for (RuleScript oldRuleScript : toBeRemoved) { // mark the script as deleted so it doesn't get run any more oldRuleScript.setDeleted(); // now deal with uninstall, allowing for possible reinstall RuleScript newRuleScript = scriptRepository.scriptForRuleName(oldRuleScript.getName()); // new script may not exist! if (newRuleScript != null) { synchronized (newRuleScript) { for (TransformSet oldTransformSet : oldRuleScript.getTransformSets()) { // see if we have an equivalent new rule set TransformSet newTransformSet = newRuleScript.lookupTransformSet(oldTransformSet.getLoader(), oldTransformSet.getTriggerClass()); if(newTransformSet == null || newTransformSet.isInstalled()) { if(oldTransformSet.isInstalled()) { // new rule has already been installed so we need // to run an uninstall for the old transform set oldTransformSet.getRule().uninstalled(); } } else { // new rule is not yet installed so we can elide // the uninstalled + installed lifecycle events // we have to copy across the rule used for the prior // install so we can use it as the default argument // for a later uninstall. it will be replaced with a // new instance if any of the newly injected rules // pass ensureTypeCheckCompiled newTransformSet.setInstalled(oldTransformSet.getRule()); } } } } else { for (TransformSet oldTransformSet : oldRuleScript.getTransformSets()) { if(oldTransformSet.isInstalled()) { // we need to run an uninstall for the old transform set oldTransformSet.getRule().uninstalled(); } } out.println("uninstall RULE " + oldRuleScript.getName()); } } // now purge the rules for the old script } public void appendJarFile(PrintWriter out, JarFile jarfile, boolean isBoot) throws Exception { if (isBoot) { inst.appendToBootstrapClassLoaderSearch(jarfile); bootJars.add(jarfile.getName()); out.println("append boot jar " + jarfile.getName()); } else { inst.appendToSystemClassLoaderSearch(jarfile); sysJars.add(jarfile.getName()); out.println("append sys jar " + jarfile.getName()); } } /** * Returns jars that this retransformer was asked to * {@link #appendJarFile(PrintWriter, JarFile, boolean) add} to the boot classloader. * * Note that the returned set will not include those jars that were added to the * instrumentor object at startup via the -javaagent command line argument. * * @return set of jar pathnames for all jars loaded in the boot classloader */ public Set getLoadedBootJars() { return new HashSet(bootJars); // returns a copy } /** * Returns jars that this retransformer was asked to * {@link #appendJarFile(PrintWriter, JarFile, boolean) add} to the system classloader. * * Note that the returned set will not include those jars that were added to the * instrumentor object at startup via the -javaagent command line argument. * * @return set of jar pathnames for all jars loaded in the system classloader */ public Set getLoadedSystemJars() { return new HashSet(sysJars); // returns a copy } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/RuleScript.java000066400000000000000000000464561414767246600264400ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2009-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import org.jboss.byteman.rule.Rule; import org.jboss.byteman.rule.type.TypeHelper; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.io.StringWriter; import java.io.PrintWriter; /** * details of a single rule obtained from a rule file. RuleScript instances are stored in the script repository * attached to the transformer. They are used to generate Rule instances at transform time. The RuleScript contains * a list of Transforms which detail failed or successful transforms performed using the script. */ public class RuleScript { /** * a counter used to ensure rule identifiers are unique */ private static int nextId = 0; /** * a method to return the next available counter for use in constructing a key for a rule * @return the next id */ private synchronized static int nextId() { return nextId++; } /** * the name of the rule from which this script is derived */ private String name; /** * the name supplied in the CLASS or INTERFACE clause of the rule identifying which class(es) * triggers should be injected into */ private String targetClass; /** * true if the target is an interface or false if the target is a class, in the former case the * rule should be injected into methods of classes which implement the interface. */ private boolean isInterface; /** * the name of the method of the target class or interface into which the rule should be injected */ private String targetMethod; /** * true if the rule should be injected into overriding implementations of the target method false * if it should only be injected into the implementation defined by the target class or, in the * case of an interface rule, by the class directly implementing the target interface */ private boolean isOverride; /** * the name of a class whose public instance methods define the built-in methods available for use * in the rule body */ private String targetHelper; /** * the details of the IMPORT lines */ private String[] imports; /** * identifies the location in the method if the trigger point at which the rule code should be injected. * note that for an AT EXIT rule there may be multiple trigger points. */ private Location targetLocation; /** * the text of the rule's BIND IF and DO clauses which are parsed using a grammar based parser */ private String ruleText; /** * this is set to true if the rule is dynamically deleted or updated so as to inhibit execution of * trigger code between the delete/update and recompilation/reinstatement of the affected bytecode. */ private boolean deleted; /** * the line number at which the rule text starts */ private int line; /** * the name of the file from which the rule has been loaded, if defined, or some suitable dummy string if it * was noti obtained from a file */ private String file; /** * true if this rule should be compiled to bytecode otherwise false */ private final boolean compileToBytecode; /** * true if this rule should be type checked using the target type for this rather * than the trigger type */ private final boolean asTarget; /** * hash map used to lookup a key used at injection time to identify a * rule cloned from this script for injection into a specific trigger * method. the map translates a string constructed from the trigger class * name, method name, method descriptor and class loader hash to a unique * key based on the rule name. This ensures that concurrent attempts to inject * the rule into the same trigger method will employ the same key and hence * perform exactly the same transformation. That way it does not matter which * of the transformations are accepted or dropped by the JVM when defining a * newly loaded class. Any transform result for a given key is as valid as * any other. */ private final HashMap keySet; /** * base string from which to construct rule injection keys */ private final String key_base; /** * a list of records identifying transforms associated with a specific class. * each set is identified by the name of a trigger class and the class's * associated loader i.e. it corresponds with an attempt to transform a unique * class using this rule script. * * A transform set may contain more than one transform because the rule's * METHOD clause may omit a descriptor, leading to injection into multiple * methods. Each transform references the specific method it applies to with * a name and and descriptor string. Also, not all transforms record successful * injections. Entries are added to record parse errors or warnings, including * failure to inject a rule at all. There is at most one successful Transform * for a given class+method, at most one failure or, possibly, one or more * warnings. */ private List transformSets; /** * standard constructor for a rule * @param name the name of the rule * @param targetClass the name of the class or interface to which the rule applies * @param isInterface true if the ruel applies to an interface false if it appies ot a class * @param isOverride true if the rule should inject down class hierarchies false if it should inly inject into direct implementations * @param targetMethod the name of the method to which the rule applies * @param targetHelper the name of the helper class to be used * @param imports the list of imports for the module system * @param targetLocation description of where the rule should be injected * @param ruleText the body of the rule as text including the BIND, IF and DO clasue * @param line the line at which the rule starts in it's rule script * @param file the path to the file containing the rule * @param compileToBytecode true if the rule should be compiled otherwise false * @param asTarget true if the rule should be typed using the target type for this rather than the trigger type */ public RuleScript(String name, String targetClass, boolean isInterface, boolean isOverride, String targetMethod, String targetHelper, String[] imports, Location targetLocation, String ruleText, int line, String file, boolean compileToBytecode, boolean asTarget) { this.name = name; this.targetClass = targetClass; this.isInterface = isInterface; this.isOverride = isOverride; this.targetMethod = targetMethod; this.targetHelper = targetHelper; this.imports = imports; this.targetLocation = (targetLocation != null ? targetLocation : Location.create(LocationType.ENTRY, "")); this.ruleText = ruleText; this.line = line; this.file = file; this.compileToBytecode = compileToBytecode; this.asTarget = asTarget; this.transformSets = new ArrayList(); this.keySet = new HashMap(); this.key_base = name + "_" + nextId(); } public String getName() { return name; } public String getTargetClass() { return targetClass; } public boolean isInterface() { return isInterface; } public String getTargetHelper() { return targetHelper; } public String[] getImports() { return imports; } public String getTargetMethod() { return targetMethod; } public boolean isOverride() { return isOverride; } public Location getTargetLocation() { return targetLocation; } public String getRuleText() { return ruleText; } public int getLine() { return line; } public String getFile() { return file; } public boolean isCompileToBytecode() { return compileToBytecode; } public boolean isAsTarget() { return asTarget; } public synchronized String getRuleKey(String triggerClassName, String triggerMethodName, String triggerMethodDescriptor, ClassLoader loader) { if (triggerMethodName == null) { // this can happen when we get errors ??? return key_base; } String lookup = triggerClassName + "." + triggerMethodName + TypeHelper.internalizeDescriptor(triggerMethodDescriptor) + "_" + loader.hashCode(); String result = keySet.get(lookup); if (result == null) { result = key_base + ":" + keySet.size(); keySet.put(lookup, result); } return result; } /** * getter for list of transforms applied for this script. must be called synchronized on the script. * @return the list of transforms */ public List getTransformSets() { return transformSets; } /** * return a count of the number of transforms applied for this script. must be called synchronized on the script. * @return the size of the list of transforms */ public int getTransformSetsCount() { return (transformSets != null ? transformSets.size() : 0); } public List allTransforms() { List allTransforms = new ArrayList(); for (TransformSet transformSet : transformSets) { allTransforms.addAll(transformSet.getTransforms()); } return allTransforms; } /** * invoked by the scriptmanager when a rule is redefined to inhibit further transformations via this script * @return the previous setting of deleted */ public synchronized boolean setDeleted() { if (!deleted) { deleted = true; return false; } return true; } /** * called when indexing a script to ensure that it has not already been deleted. it must only be called * when synchronized on the script. This avoids a race where a script can be added by thread A, deleted by * thread B, unindexed -- unsuccessfully -- by thread B then indexed by thread A * @return the previous setting of deleted */ public boolean isDeleted() { return deleted; } /** * record the fact that an error was thrown when attempting to transform a given class using this rule script * @param loader the loader of the class for which injection was attempted * @param internalClassName the internal Java name of the class * @param th the Throwable reocrding details of the failure * @return true if the failure was recorded false if not */ public synchronized boolean recordFailedTransform(ClassLoader loader, String internalClassName, Throwable th) { return recordTransform(loader, internalClassName, null, null, null, th); } /** * record the fact that a trigger call has succeeded or else failed to install into bytecode * associated with a specific class and loader * @param loader the loader of the class for which injection was attempted * @param internalClassName the internal Java name of the class * @param triggerMethodName the name of the method injected into * @param desc the descriptor of the method injected into * @param rule the rule which was injected * @param th throwable generated during the attempt to parse the rule text or inject code at the trigger point * @return true if the successful injection was recorded false if not */ public synchronized boolean recordTransform(ClassLoader loader, String internalClassName, String triggerMethodName, String desc, Rule rule, Throwable th) { if (deleted) { return false; } String fullMethodName = null; if (triggerMethodName != null) { fullMethodName = triggerMethodName + TypeHelper.internalizeDescriptor(desc); } // make sure we know about this specific loader and classname combination TransformSet transformSet = ensureTransformSet(loader, internalClassName, null); // and install the transform in the set transformSet.add(new Transform(loader, internalClassName, fullMethodName, rule, th)); return true; } /** * check whether a rule has been used to transform a specific class. this can be used when * rules are redefined to decide whether or not a class needs to be retransformed. Note that * it must only be called after the script has been deleted by calling setDeleted. * @param clazz the class for which a transform is being sought. * @return true if the class has been transformed using this script otherwise false. */ public synchronized boolean hasTransform(Class clazz) { ClassLoader loader = clazz.getClassLoader(); if (loader == null) { loader = ClassLoader.getSystemClassLoader(); } for (TransformSet transformSet : transformSets) { if (transformSet.isFor(loader, clazz.getName())) { return !transformSet.isEmpty(); } } return false; } /** * record the fact that a rule has been compiled with or without success * @param triggerClass the name of the trigger class to which the rule is attached * @param loader the classloader of the trigger class * @param successful true if the rule compiled successfully and false if it suffered from parse, * type or compile errors * @param detail text describing more details of the compilation outcome * @return true if the rule needs to be installed otherwise false */ public synchronized boolean recordCompile(Rule rule, String triggerClass, ClassLoader loader, boolean successful, String detail) { if(deleted) { return false; } // find an existing transform set or create a new one TransformSet transformSet = ensureTransformSet(loader, triggerClass, null); for (Transform transform : transformSet.getTransforms()) { // transform may not employ the same rule // but it may have the same key. if(transform.getRule().getKey() == rule.getKey()) { transform.setCompiled(successful, detail); boolean isInstalled = transformSet.isInstalled(); // record this as the latest rule to be installed transformSet.setInstalled(rule); // if this is the first installed rule then // we need to perform lifecycle processing return !isInstalled; } } // no such rule so no lifecycle processing return false; } /** * delete any transforms associated with a specific trigger class and loader for * deletion. this is called just before any attempt to retransform the class * to inject the script's associated rule. it ensures that records of previous * transforms associated with a prior retransformation of the class are removed * before any new ones are added */ public synchronized void purge(ClassLoader loader, String triggerClassName) { TransformSet transformSet = lookupTransformSet(loader, triggerClassName); if (transformSet != null) { for (Transform transform : transformSet.getTransforms()) { Rule rule = transform.getRule(); if(rule != null) { rule.purge(); } } transformSet.clearTransforms(); } } /** * uninstall all transforms associated with this script. this is called after marking the script as * deleted and regenerating the methods for any associated transformed class to ensure that it does * not cause a rule trigger call to fail. */ public synchronized void purge() { for (TransformSet transformSet : transformSets) { for (Transform transform : transformSet.getTransforms()) { Rule rule = transform.getRule(); if(rule != null) { rule.purge(); } } transformSet.clearTransforms(); } transformSets.clear(); } public TransformSet ensureTransformSet(ClassLoader loader, String triggerClass, Rule installedRule) { TransformSet transformSet = lookupTransformSet(loader, triggerClass); if (transformSet == null) { transformSet = new TransformSet(loader, triggerClass); transformSet.setInstalled(installedRule); transformSets.add(transformSet); } return transformSet; } public TransformSet lookupTransformSet(ClassLoader loader, String triggerClass) { for (TransformSet transformSet : transformSets) { if (transformSet.isFor(loader, triggerClass)) { return transformSet; } } return null; } public String toString() { StringWriter stringWriter = new StringWriter(); PrintWriter writer = new PrintWriter(stringWriter); writeTo(writer); writer.flush(); return stringWriter.toString(); } public void writeTo(PrintWriter writer) { writer.print("# File "); writer.print(file); writer.print(" line "); writer.println(line); writer.print("RULE "); writer.println(name); if (isInterface) { writer.print("INTERFACE "); } else { writer.print("CLASS "); } if (isOverride) { writer.print("^"); } writer.println(targetClass); writer.print("METHOD "); writer.println(targetMethod); if (imports != null) { for (int i = 0; i < imports.length ; i++) { writer.print("IMPORT "); writer.println(imports[i]); } } if (targetHelper != null) { writer.print("HELPER "); writer.println(targetHelper); } if (compileToBytecode) { writer.write("COMPILE\n"); } else { writer.write("NOCOMPILE\n"); } writer.println(targetLocation.toString()); writer.println(ruleText); writer.println("ENDRULE"); } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/ScriptRepository.java000066400000000000000000001035101414767246600276710ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2009-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * (C) 2009-10, * @authors Andrew Dinn */ package org.jboss.byteman.agent; import org.jboss.byteman.rule.helper.Helper; import java.util.*; import java.lang.reflect.Method; /** * Class to manage indexing and lookup of rule scripts by rule name and by either class or interface name */ public class ScriptRepository { public ScriptRepository(boolean skipOverrideRules) { targetClassIndex = new HashMap>(); targetInterfaceIndex = new HashMap>(); ruleNameIndex = new HashMap(); this.skipOverrideRules = skipOverrideRules; } /** * Split the text of a script file into a list of individual rule scripts * @param scriptText the text obtained from a script file * @param scriptFile teh name of the file containing teh text * @return a list of rule scripts * @throws Exception if there is an error in the format of the script file tesxt */ public List processScripts(String scriptText, String scriptFile) throws Exception { List ruleScripts = new LinkedList(); if (scriptText != null) { // split rules into separate lines String[] lines = scriptText.split("\n"); List rules = new ArrayList(); String nextRule = ""; String sepr = ""; String name = null; String targetClass = null; String targetMethod = null; String targetHelper = null; String defaultHelper = null; String[] targetImports = null; String[] defaultImports = new String[0]; // script level compilation defaults to Transformer setting but may be overridden boolean scriptCompileToBytecode = Transformer.isCompileToBytecode(); // rule level compilation defaults to script level but may be overridden boolean ruleCompileToBytecode = scriptCompileToBytecode; // script level use of target type defaults to false but may be overridden boolean scriptAsTarget = false; // rule level use of target type defaults to false but mnay be overridden boolean ruleAsTarget = scriptAsTarget; LocationType locationType = null; Location targetLocation = null; boolean isInterface = false; boolean isOverride = false; int lineNumber = 0; int startNumber = -1; int maxLines = lines.length; boolean inRule = false; for (String line : lines) { line = line.trim(); lineNumber++; if (line.startsWith("#")) { if (inRule) { // add a blank line in place of the comment so the line numbers // are reported consistently during parsing nextRule += sepr; sepr = "\n"; } // else { // just drop comment line } } else if (line.startsWith("RULE ")) { inRule = true; name = line.substring(5).trim(); if (name.equals("")) { throw new Exception("org.jboss.byteman.agent.Transformer : RULE with no name at line " + lineNumber + " in script " + scriptFile); } } else if (line.startsWith("HELPER ")) { if (inRule) { targetHelper = line.substring(7).trim(); } else { defaultHelper = line.substring(7).trim(); // empty classname resets to the default if (defaultHelper.length() == 0) { defaultHelper = null; } } } else if (line.equals("COMPILE")) { if (inRule) { ruleCompileToBytecode = true; } else { scriptCompileToBytecode = true; ruleCompileToBytecode = true; } } else if (line.equals("NOCOMPILE")) { if (inRule) { ruleCompileToBytecode = false; } else { scriptCompileToBytecode = false; ruleCompileToBytecode = false; } } else if (line.matches("AS[ \t]+TARGET")) { if (inRule) { ruleAsTarget = true; } else { scriptAsTarget = true; ruleAsTarget = true; } } else if (line.matches("AS[ \t]+TRIGGER")) { if (inRule) { ruleAsTarget = false; } else { scriptAsTarget = false; ruleAsTarget = false; } } else if (line.startsWith("IMPORT ") || line.equals("IMPORT")) { String imp = line.substring(6).trim(); if (inRule) { if (imp.isEmpty()) { // remove any globally defined imports targetImports = new String[0]; } else { // add to the existing rule imports if any, otherwise the global ones if (targetImports == null) { if (defaultImports == null) targetImports = new String[1]; else targetImports = Arrays.copyOf(defaultImports, defaultImports.length + 1); } else { targetImports = Arrays.copyOf(targetImports, targetImports.length + 1); } targetImports[targetImports.length - 1] = imp; } } else { if (imp.isEmpty()) defaultImports = null; else { if (defaultImports == null) defaultImports = new String[1]; else defaultImports = Arrays.copyOf(defaultImports, defaultImports.length + 1); defaultImports[defaultImports.length - 1] = imp; } } } else if (!inRule) { if (!line.equals("")) { throw new Exception("org.jboss.byteman.agent.Transformer : invalid text outside of RULE/ENDRULE " + "at line " + lineNumber + " in script " + scriptFile); } } else if (line.startsWith("CLASS ")) { targetClass = line.substring(6).trim(); if (targetClass.startsWith("^")) { isOverride = true; targetClass = targetClass.substring(1).trim(); } if (Transformer.isBytemanClass(targetClass)) { throw new Exception("org.jboss.byteman.agent.Transformer : invalid target class " + targetClass + " at line " + lineNumber + " in script " + scriptFile); } } else if (line.startsWith("INTERFACE ")) { targetClass = line.substring(10).trim(); isInterface = true; if (targetClass.startsWith("^")) { isOverride = true; targetClass = targetClass.substring(1).trim(); } if (Transformer.isBytemanClass(targetClass)) { throw new Exception("org.jboss.byteman.agent.Transformer : invalid target class " + targetClass + " at line " + lineNumber + " in script " + scriptFile); } } else if (line.startsWith("METHOD ")) { targetMethod = line.substring(7).trim(); } else if ((locationType = LocationType.type(line)) != null) { String parameters = LocationType.parameterText(line); targetLocation = Location.create(locationType, parameters); if (targetLocation == null) { throw new Exception("org.jboss.byteman.agent.Transformer : invalid target location at line " + lineNumber + " in script " + scriptFile); } } else if (line.startsWith("ENDRULE")) { if (name == null || "".equals(name)) { throw new Exception("org.jboss.byteman.agent.Transformer : no matching RULE for ENDRULE at line " + lineNumber + " in script " + scriptFile); } else if (targetClass == null || "".equals(targetClass)) { throw new Exception("org.jboss.byteman.agent.Transformer : no CLASS for RULE " + name + " in script " + scriptFile); } else if (targetMethod == null || "".equals(targetMethod)) { throw new Exception("org.jboss.byteman.agent.Transformer : no METHOD for RULE " + name + " in script " + scriptFile); } else { if (targetLocation == null) { targetLocation = Location.create(LocationType.ENTRY, ""); } if (targetHelper == null) { targetHelper = defaultHelper; } if (targetImports == null) { targetImports = (defaultImports != null) ? defaultImports : new String[0]; } RuleScript ruleScript = new RuleScript(name, targetClass, isInterface, isOverride, targetMethod, targetHelper, targetImports, targetLocation, nextRule, startNumber, scriptFile, ruleCompileToBytecode, ruleAsTarget); ruleScripts.add(ruleScript); } name = null; targetClass = null; targetMethod = null; targetLocation = null; targetHelper = null; targetImports = null; // reset rule level compilation to script level setting ruleCompileToBytecode = scriptCompileToBytecode; // reset rule level target/trigger to script level setting ruleAsTarget = scriptAsTarget; nextRule = ""; sepr = ""; inRule = false; isInterface = false; // reset start number so we pick up the next rule text line startNumber = -1; } else if (lineNumber == maxLines && !nextRule.trim().equals("")) { throw new Exception("org.jboss.byteman.agent.Transformer : no matching ENDRULE for RULE " + name + " in script " + scriptFile); } else { // this is a line of rule text - see if it is the first one if (startNumber < 0) { startNumber = lineNumber; } nextRule += sepr + line; sepr = "\n"; } } } return ruleScripts; } /** * add a rule script to the repository returning any existing script with the same name or null * if no such script can be found. if a script is returned it will have been deactivated. * @param script the script to be added to the repository * @return any previous script with the same name or null */ public RuleScript addScript(RuleScript script) { String name = script.getName(); RuleScript previous = null; // sanity check override rule setting and print warning if necessary if (skipOverrideRules && script.isOverride()) { Helper.err("ScriptRepository.addScript : injection into overriding methods disabled but found override rules " + script.getName()); } // insert the script by name, invalidating any old script synchronized (ruleNameIndex) { previous = ruleNameIndex.put(name, script); if (previous != null) { boolean isDeleted = previous.setDeleted(); if (isDeleted) { // it is some other thread's responsibility to remove the script previous = null; } } } boolean isOverride = script.isOverride(); if (previous == null) { // increment override count if necessary before indexing if (isOverride) { overrideRuleCount++; } // now index the new script if (script.isInterface()) { indexTarget(script, targetInterfaceIndex); } else { indexTarget(script, targetClassIndex); } } else { boolean wasOverride = previous.isOverride(); // increment override count if necessary before indexing if (isOverride) { overrideRuleCount++; } boolean isInterface = script.isInterface(); boolean wasInterface = previous.isInterface(); if (isInterface == wasInterface) { // both in the same index so try a reindex Map> index = (isInterface ? targetInterfaceIndex : targetClassIndex); reindexTarget(script, previous, index); } else if (isInterface) { // different indexes so unindex then index unindexTarget(previous, targetClassIndex); indexTarget(script, targetInterfaceIndex); } else { unindexTarget(previous, targetInterfaceIndex); indexTarget(script, targetClassIndex); } // decrement count if necessary after unindexing if (wasOverride) { overrideRuleCount--; } } return previous; } /** * remove a rule script from the repository returning the script if it is found or null * if is not found. if a script is returned it will have been deactivated. * @param script the script to be removed from the repository. * @return the script if it was found in the repository and removed successfully or null * if it had already been removed. */ public RuleScript removeScript(RuleScript script) { String name = script.getName(); RuleScript current; // check for the script by name synchronized (ruleNameIndex) { current = ruleNameIndex.get(name); if (current == script) { ruleNameIndex.remove(current.getName()); boolean isDeleted = current.setDeleted(); if (isDeleted) { // it is some other thread's responsibility to remove the script current = null; } } else { // it is some other thread's responsibility to remove the script current = null; } } // if we found a script then we have to unindex it if (current != null) { Map> index = (current.isInterface() ? targetInterfaceIndex : targetClassIndex); unindexTarget(current, index); boolean wasOverride = current.isOverride(); // decrement count if necessary after unindexing if (wasOverride) { overrideRuleCount--; } } return current; } /** * remove a rule script from the repository by name returning the script if it is found or null * if is not found. if a script is returned it will have been deactivated. * @param name the name of the script to be removed from the repository * @return the script if it was found in the repository or null if none was found */ public RuleScript removeScript(String name) { RuleScript current = scriptForRuleName(name); if (current != null) { current = removeScript(current); } return current; } /** * locate a rule script with a given name * @param name the name of the rule script * @return the script with that name or null if no such script can be found */ public RuleScript scriptForRuleName(String name) { synchronized (ruleNameIndex) { return ruleNameIndex.get(name); } } /** * return a list of all class scripts indexed using the supplied name. note that if name is * package qualified then only scripts with the full package qualificaton will be returned * whereas if name is not package qualified then only scripts with the unqualified name * will be returned. Note that the returned list can be iterated safely but will not reflect * later additions to or deletions from the list. * @param name the name of the class for which scripts should be listed * @return the list of scripts for that name */ public List scriptsForClassName(String name) { synchronized (targetClassIndex) { return targetClassIndex.get(name); } } /** * return a list of all interface scripts indexed using the supplied name. note that if name is * package qualified then only scripts with the full package qualificaton will be returned * whereas if name is not package qualified then only scripts with the unqualified name * will be returned. Note that the returned list can be iterated safely but will not reflect * later additions to or deletions from the list. * @param name the name of the interface for which scripts should be listed * @return the list of scripts for that name */ public List scriptsForInterfaceName(String name) { synchronized (targetInterfaceIndex) { return targetInterfaceIndex.get(name); } } /** * return true if there is a rule which applies to the supplied class otherwise false * @param clazz the name of the class for which rules are being sought * @return true if there is a rule which applies * @throws Exception if an error occurs during class lookup */ public boolean matchClass(Class clazz) throws Exception { // see if we have any scripts for the class or its supers Class nextClazz = clazz; boolean isOverride = false; // we create these lazily to avoid unnecessary work LinkedList visited = null; LinkedList toVisit = null; while (nextClazz != null) { String name = nextClazz.getName(); if (matchTarget(name, clazz, false, isOverride)) { return true; } int lastDot = name.lastIndexOf('.'); if (lastDot >= 0) { if (matchTarget(name.substring(lastDot + 1), clazz, false, isOverride)) { return true; } } // ok, now see if we need to inject via any interfaces that the class implements if (checkInterfaces()) { Class[] interfaces = nextClazz.getInterfaces(); int l = interfaces.length; if (l > 0) { // ok, so we have to create the lists here if (visited == null) { visited = new LinkedList(); toVisit = new LinkedList(); } // add the implements list of this class as interfaces to consider for (int i = 0; i < interfaces.length; i++) { Class interfaze = interfaces[i]; if (!visited.contains(interfaze)) { toVisit.add(interfaze); } } while (!toVisit.isEmpty()) { // check the next interface Class interfaze = toVisit.pop(); name = interfaze.getName(); if (matchTarget(name, clazz, true, isOverride)) { return true; } else { lastDot = name.lastIndexOf('.'); if (lastDot >= 0) { if (matchTarget(name.substring(lastDot + 1), clazz, true, isOverride)) { return true; } } } visited.add(interfaze); // check the extends list of this interface for new interfaces to consider interfaces = interfaze.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { interfaze = interfaces[i]; if (!visited.contains(interfaze)) { toVisit.add(interfaze); } } } } } if (skipOverrideRules) { return false; } nextClazz = nextClazz.getSuperclass(); isOverride = true; } return false; } /** * return a list containing all the currently installed rule scripts. * @return the list of all installed scripts */ public List currentRules() { return new ArrayList(ruleNameIndex.values()); } /** * return true if there are any scripts indexed under name which meet the required matching conditions * @param name the name under which the scripts are indexed * @param clazz a class which should be checked for a method whose name matches the script method name * @param isInterface true if we are interested in matching interface rules false if we are interested in * matching class rules * @param isOverride true if we are only interested in rules which apply to overriding methods false * if we are happy with any rule * @return true if any scripts are found otherwise false */ private boolean matchTarget(String name, Class clazz, boolean isInterface, boolean isOverride) { Map> index = (isInterface ? targetInterfaceIndex : targetClassIndex); synchronized (index) { List ruleScripts = index.get(name); if (ruleScripts != null) { for (RuleScript ruleScript: ruleScripts) { if (isOverride && !ruleScript.isOverride()) { continue; } String methodName = ruleScript.getTargetMethod(); int signaturePos = methodName.indexOf("("); if (signaturePos > 0) { methodName = methodName.substring(0, signaturePos).trim(); } int wsPos = methodName.indexOf(' '); if (wsPos < 0) { wsPos = methodName.indexOf('\t'); } if (wsPos > 0) { // ok, so METHOD spec must be in format "type methodname" methodName = methodName.substring(wsPos).trim(); } if ("".equals(methodName) || "".equals(methodName)) { // every class has some sort of constructor so accept it return true; } // this filters out cases where the class does not have a method with the correct name try { Method[] declaredMethods = clazz.getDeclaredMethods(); for (int i = 0; i < declaredMethods.length; i++) { Method method = declaredMethods[i]; if (method.getName().equals(methodName)) { return true; } } } catch (NoClassDefFoundError e) { // we cam sometimes get an Error thrown if the class we are lookingb up has unresolved // refernces ot a non-existent class. don't really know why such classes turn up // in the inst allLoaddedClasses list but they do. // ignore } } } } return false; } /** * insert a script into the index using the script target class name as the index key. * @param script * @param index */ private void indexTarget(RuleScript script, Map> index) { String key = script.getTargetClass(); // synchronize on the new script to avoid ant race with a deleting thread synchronized(script) { if (script.isDeleted()) { return; } synchronized (index) { List entry = index.get(key); // always create a new list so that we don't affect any in progress iteration of the previous value if (entry == null) { entry = new ArrayList(); add(entry, script); } else { entry = new ArrayList(entry); add(entry, script); } index.put(key, entry); } } } /** * remove a script from the index using the script target class name as the index key. * @param script * @param index */ private void unindexTarget(RuleScript script, Map> index) { synchronized (index) { String key = script.getTargetClass(); List entry = index.get(key); // check it has not been deleted by another thread if (entry != null && entry.contains(script)) { if (entry.size() == 1) { // removing the last one so reset entry to null entry = null; } else { // always create a new list so that we don't affect any in progress iteration of the previous value entry = new ArrayList(entry); entry.remove(script); } index.put(key, entry); } } } /** * replace a script in the index using the script target class name as the index key. * @param script * @param index */ private void reindexTarget(RuleScript script, RuleScript previous, Map> index) { // synchronize on the new script to avoid ant race with a deleting thread synchronized (script) { if (script.isDeleted()) { // we just need to delete the old script unindexTarget(previous, index); return; } else { String key = script.getTargetClass(); String oldKey = previous.getTargetClass(); synchronized (index) { if (key == oldKey) { // both in the same list so do one update List entry = index.get(key); // check old one has not been deleted by another thread if (entry == null || !entry.contains(previous)) { // some other thread must have deleted the old one so just insert the new one // always create a new list so that we don't affect any in progress iteration of the previous value entry = new ArrayList(entry); add(entry, script); } else { // always create a new list so that we don't affect any in progress iteration of the previous value entry = new ArrayList(entry); entry.remove(previous); add(entry, script); } index.put(key, entry); } else { List entry = index.get(oldKey); // check old one has not been deleted by another thread if (entry != null && entry.contains(previous)) { // unindex the previous script if (entry.size() == 1) { // removing the last one so reset entry to null entry = null; } else { // always create a new list so that we don't affect any in progress iteration of the previous value entry = new ArrayList(entry); entry.remove(previous); } index.put(oldKey, entry); } // now index the new one entry = index.get(key); // always create a new list so that we don't affect any in progress iteration of the previous value if (entry == null) { entry = new ArrayList(); } else { entry = new ArrayList(entry); } add(entry, script); index.put(key, entry); } } } } } /** * add a rule script to start or end of the index list according to its location type. AT ENTRY rules * are pushed so they are sorted in reverse load order. other rules are appended so they are sorted * in load order. * @param entries * @param script */ private void add(List entries, RuleScript script) { // ENTRY rules are pushed so they are sorted in reverse load order // other rules are appended so they are sorted in load order if (script.getTargetLocation().getLocationType() == LocationType.ENTRY) { entries.add(0, script); } else { entries.add(script); } } /** * a 1-1 mapping from target class names which appear in rules to a script object holding the * rule details */ private final Map> targetClassIndex; /** * a 1-m mapping from target interface names which appear in rules to a script object holding the * rule details */ private final Map> targetInterfaceIndex; /** * a 1-m mapping from rule names which appear in rules to a script object holding the * rule details */ private final Map ruleNameIndex; /** * a flag derived from the transformer which enables us to avoid testing superclass rules for * matches if it is set */ private final boolean skipOverrideRules; /** * a count of how many rules there are in the script repository which employ injection into hierarchies */ private int overrideRuleCount = 0; /** * see if we need to do any transformation of interfaces * @return true if there are any interface rules false if there are none */ public boolean checkInterfaces() { // n.b. this probably ought to be called synchronized rather than ddo its own synchronization // so the caller can avoid the timing window between checking and responding to the check. // but this is a grey area given that there is no way of knowing exactly when a transform // request will be sent to the Transformer and the repsonse is not to ransform anything. if // an uupdate affects a loaded class then it will get retransformed anyway so the risk here // si that the rule gets applied alittle late. we still synchornize here anyway to ensure // the isEmpty check does not get a partial view of the index. synchronized (targetInterfaceIndex) { return !targetInterfaceIndex.isEmpty(); } } /** * check whether any overriding rules are currently loaded * @return true if there are no overriding rules false if there are any */ public boolean skipOverrideRules() { if (skipOverrideRules) { return true; } else { return overrideRuleCount == 0; } } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/Transform.java000066400000000000000000000075001414767246600263020ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2009-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import org.jboss.byteman.rule.Rule; import java.io.PrintWriter; /** * record of a specific bytecode transformation applied by the byteman agent for a given trigger class */ public class Transform { private ClassLoader loader; /** * fully qualified internal name of class */ private String internalClassName; /** * full method name including descriptor */ private String triggerMethodName; private Rule rule; private Throwable throwable; private boolean compiled; private boolean installed; private boolean successful; private String detail; public Transform(ClassLoader loader, String internalClassName, String triggerMethodName, Rule rule, Throwable th) { this.loader = loader; this.internalClassName = internalClassName; this.triggerMethodName = triggerMethodName; this.rule = rule; this.compiled = false; this.installed = false; this.throwable = th; this.successful = false; this.detail = ""; } public ClassLoader getLoader() { return loader; } public String getInternalClassName() { return internalClassName; } public String getTriggerMethodName() { return triggerMethodName; } public Rule getRule() { return rule; } public Throwable getThrowable() { return throwable; } public String getDetail() { return detail; } public void setCompiled(boolean successful, String detail) { this.compiled = true; this.successful = successful; this.detail = detail; } public boolean isTransformed() { return (throwable == null); } public boolean isCompiledOk() { return compiled && successful; } public boolean isInstalled() { return installed; } public void setInstalled() { this.installed = true; } public void writeTo(PrintWriter writer) { writer.print("Transformed in:\n"); writer.print("loader: "); writer.println(loader); if (triggerMethodName == null) { writer.print("trigger class: "); writer.println(internalClassName); } else { writer.print("trigger method: "); writer.print(internalClassName); writer.print('.'); writer.println(triggerMethodName); } if (throwable != null) { writer.print("threw "); writer.println(throwable); throwable.printStackTrace(writer); } else if (compiled) { if (successful) { writer.println("compiled successfully"); } else { writer.println("failed to compile"); writer.println(detail); } } } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/TransformContext.java000066400000000000000000000667361414767246600276670ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2009-10, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * (C) 2009-10, * @authors Andrew Dinn */ package org.jboss.byteman.agent; import org.jboss.byteman.agent.adapter.BMJSRInliner; import org.jboss.byteman.agent.adapter.BMLocalScopeAdapter; import org.jboss.byteman.agent.adapter.RuleCheckAdapter; import org.jboss.byteman.agent.adapter.RuleTriggerAdapter; import org.jboss.byteman.agent.check.ClassChecker; import org.jboss.byteman.rule.exception.ParseException; import org.jboss.byteman.rule.exception.TypeException; import org.jboss.byteman.rule.exception.TypeWarningException; import org.jboss.byteman.rule.helper.Helper; import org.jboss.byteman.rule.type.TypeHelper; import org.jboss.byteman.rule.Rule; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import java.util.HashMap; import java.util.LinkedList; /** * Class used to localise the context information employed when creating a rule from a rule script and * using it to transform a method */ public class TransformContext { public TransformContext(Transformer transformer, RuleScript ruleScript, String triggerClassName, String targetClassName, ClassLoader loader, HelperManager helperManager, AccessEnabler accessEnabler) { // the target method spec may just be a bare method name or it may optionally include a // parameter type list and a return type. With Java syntax the return type appears before // the method name. if so we modify the target method spec so that the return type appears // after the argument list which means we also accept a spec supplied in this format. The // parseMethodDescriptor call below will eat specs in this latter format. final String targetMethodSpec = ruleScript.getTargetMethod(); String mungedMethodSpec = mungeMethodSpecReturnType(targetMethodSpec); this.transformer = transformer; this.ruleScript = ruleScript; this.triggerClassName = triggerClassName; this.targetClassName = targetClassName; this.targetMethodName = TypeHelper.parseMethodName(mungedMethodSpec); this.targetDescriptor = TypeHelper.parseMethodDescriptor(mungedMethodSpec); this.loader = loader; this.helperManager = helperManager; this.ruleMap = new HashMap(); this.firstRule = null; this.accessEnabler = accessEnabler; this.failed = false; } public byte[] transform(byte[] targetClassBytes) { final Location handlerLocation = ruleScript.getTargetLocation(); String ruleName = ruleScript.getName(); // we are about to try to retransform the rule // in the context of a given class and loader // so we can clear any existing transforms // associated with this rule's script // which specify the same class and loader // ruleScript.purge(loader, triggerClassName); try { parseRule(); } catch (ParseException pe) { Helper.verbose("org.jboss.byteman.agent.Transformer : error parsing rule " + ruleName + "\n" + pe); Helper.verboseTraceException(pe); recordFailedTransform(pe); return targetClassBytes; } catch (Throwable th) { Helper.verbose("org.jboss.byteman.agent.Transformer : unexpected error parsing rule " + ruleName + "\n" + th); Helper.verboseTraceException(th); recordFailedTransform(th); return targetClassBytes; } // ok, we have a rule with a matching trigger class and a target method and location // we need to see if the class has a matching trigger method/location and, if so, add a // call to execute the rule when we hit the relevant line // there may be more than one matching method. if so we need to associate a separate Rule // instance with each transformed method because we bind the argument/local vars using types // specific to that method // we may have to ignore certain matches because they don't fit the type spec for // the rule. if we can queue a type warning exception and carry on injecting other // trigger locations then we do so. if not we throw a type exception and invalidate // all injection for the trigger class. it would be better if we could just byapss // a single injection point or just the injection points in the offending method // but sometimes we can only back out by throwing an exception from within a bytecode // visitor and th eonly safe ting to do is back out the whole transform. ClassReader cr = new ClassReader(targetClassBytes); // need to provide a real writer here so that labels get resolved ClassWriter dummy = getNonLoadingClassWriter(0); RuleCheckAdapter checkAdapter = handlerLocation.getRuleCheckAdapter(dummy, this); try { // insert a local scope adapter between the reader and the adapter so // we see info about vars going in and out of scope BMLocalScopeAdapter localScopeAdapter = new BMLocalScopeAdapter(checkAdapter); cr.accept(localScopeAdapter, ClassReader.EXPAND_FRAMES); } catch (TransformFailure te) { // will already be notified return targetClassBytes; } catch (Throwable th) { // hmm, unexpected error Helper.verbose("org.jboss.byteman.agent.Transformer : unexpected error applying rule " + ruleScript.getName() + " to class " + triggerClassName + "\n" + th); Helper.verboseTraceException(th); recordFailedTransform(th); return targetClassBytes; } // only insert the rule trigger call if there is a suitable location in the target method if (!checkAdapter.isVisited()) { // there was no matching method so ignore return targetClassBytes; } Helper.verbose("org.jboss.byteman.agent.Transformer : possible trigger for rule " + ruleScript.getName() + " in class " + triggerClassName); cr = new ClassReader(targetClassBytes); ClassWriter cw = getNonLoadingClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES); RuleTriggerAdapter adapter = handlerLocation.getRuleAdapter(cw, this); // insert a JSR inliner between the reader and the adapter so we don't see JSR/RET sequences // we use a specialised version which provides us with info about vars going in and out of scope BMJSRInliner jsrInliner = new BMJSRInliner(adapter); try { cr.accept(jsrInliner, ClassReader.EXPAND_FRAMES); } catch (TransformFailure te) { // will already be notified return targetClassBytes; } catch (Throwable th) { Helper.verbose("org.jboss.byteman.agent.Transformer : unexpected error injecting trigger for rule " + ruleScript.getName() + " into class " + triggerClassName + "\n" + th); Helper.verboseTraceException(th); recordFailedTransform(th); return targetClassBytes; } // hand back the transformed byte code Helper.verbose("org.jboss.byteman.agent.Transformer : inserted trigger for " + ruleScript.getName() + " in class " + triggerClassName); // record all successfully transformed rules if (!notifyRules()) { // rule must have been deleted so forget the transform return targetClassBytes; } else { return cw.toByteArray(); } } public void parseRule() throws Exception { Rule rule = Rule.create(ruleScript, loader, helperManager, accessEnabler); // stash this rule away under the class name so we can reuse it for the first matching method ruleMap.put(triggerClassName, rule); // keep a handle on the first rule firstRule = rule; } /** * called by a trigger adapter to find a rule specific to a given trigger method, * expects to find a rule created by the corresponding check adapter. if no rule is * found then injection must be bypassed for this method * @param triggerMethodName the name of a candidate method for injection * @param triggerMethodDescriptor the descriptor of a candidate method for injection * @return the rule if it exists or NULL if not */ public Rule lookupRule(String triggerMethodName, String triggerMethodDescriptor) { String key = getRuleKey(triggerMethodName, triggerMethodDescriptor); return ruleMap.get(key); } /** * called by a check adapter to create a rule specific to a given trigger method. * the first such call reuses the rule created by the intiial parse. subsequent calls * create a new rule. * @param triggerMethodName the name of a candidate method for injection * @param triggerMethodDescriptor the descriptor of a candidate method for injection * @return the new rule */ public Rule createRule(String triggerMethodName, String triggerMethodDescriptor) { String key = getRuleKey(triggerMethodName, triggerMethodDescriptor); // use the initially parsed rule if we can otherwise create one Rule rule = ruleMap.remove(triggerClassName); if (rule == null) { try { rule = Rule.create(ruleScript, loader, helperManager, accessEnabler); } catch(Throwable th) { // will not happen } } ruleMap.put(key, rule); return rule; } /** * called by a check adapter to warn that a transform was not possible for a potential match * target. this inhibits injection into the method being warned about allowing other injection * operations to continue. * @param triggerMethodName the name of a candidate method for injection * @param triggerMethodDescriptor the descriptor of a candidate method for injection * @param warningMessage details of the warning */ public void warn(String triggerMethodName, String triggerMethodDescriptor, String warningMessage) { // remove the rule so that we don't try to inject into this method String key = getRuleKey(triggerMethodName, triggerMethodDescriptor); Rule rule = ruleMap.remove(key); // now attach an exception to the rule script String message = warningMessage + " for method " + triggerMethodName + TypeHelper.internalizeDescriptor(triggerMethodDescriptor); TypeWarningException tw = new TypeWarningException(message); ruleScript.recordTransform(loader, triggerClassName, triggerMethodName, triggerMethodDescriptor, rule, tw); } /** * called by a check or trigger adapter to fail a transform because of a type issue. this aborts all * injection into the current class not just injection into the current method. * @param failMessage details of the failure * @param triggerMethodName the name of a candidate method for injection * @param triggerMethodDescriptor the descriptor of a candidate method for injection */ public void fail(String failMessage, String triggerMethodName, String triggerMethodDescriptor) { String key = getRuleKey(triggerMethodName, triggerMethodDescriptor); Rule rule = ruleMap.get(key); String message = failMessage + " for method " + triggerMethodName + TypeHelper.internalizeDescriptor(triggerMethodDescriptor); TypeException te = new TypeException(message); ruleScript.recordTransform(loader, triggerClassName, triggerMethodName, triggerMethodDescriptor, rule, te); failed = true; throw new TransformFailure(); } public void recordFailedTransform(Throwable th) { ruleScript.recordFailedTransform(loader, triggerClassName, th); failed = true; } public boolean matchTargetMethod(int access, String name, String desc) { // check the method is one we are really targeting // cannot inject into a native or abstract method if ((access & (Opcodes.ACC_NATIVE|Opcodes.ACC_ABSTRACT)) != 0) { return false; } // cannot inject into a synthetic method unless it is a local lambda method if ((access & Opcodes.ACC_SYNTHETIC) != 0 && !name.startsWith(LAMBDA_PREFIX)) { return false; } // cannot inject unless the method name matches the target name if (!targetMethodName.equals(name)) { return false; } // cannot inject if there is a target descriptor which fails to match the actual descriptor if (!targetDescriptor.equals("") && !TypeHelper.equalDescriptors(targetDescriptor, desc)) { return false; } // if the method is blacklisted then reject it with a warning if (transformer.isBlacklisted(triggerClassName, targetMethodName, desc)) { warn(targetMethodName, targetDescriptor, "Blacklisted method : cannot safely inject into target class " + triggerClassName); return false; } // yes we do want to inject return true; } public boolean injectIntoMethod(String name, String desc) { return lookupRule(name, desc) != null; } public String getTriggerClassName() { return triggerClassName; } public String getTargetClassName() { return targetClassName; } /** * private exception class used to throw our way out of the ASM adapter code back into the transform * method at the top level. we have to use a RuntimeException for this as we cannot change the ASm * API to allow opther exception types to be plumbed through the code */ private class TransformFailure extends RuntimeException { public TransformFailure() { } } /** * this gets called when a transform attempt completes without any exceptions. if there are rules left * in the rule map then they will belong successful injections. */ private boolean notifyRules() { // if the map is empty then we need to generate a warning // that the rule was not injectable if (ruleMap.isEmpty() && firstRule != null) { // we parsed the rule but failed ever to inject it TypeWarningException twe = new TypeWarningException("failed to find any matching trigger method in class " + TypeHelper.internalizeClass(triggerClassName)); ruleScript.recordTransform(loader, triggerClassName, null, null, firstRule, twe); return false; } if (failed) { // we had an injection failure so purge all successfully // injected rules // purgeRules(); return false; } else { // try to install all successfully injected rules for (String key : ruleMap.keySet()) { String triggerMethodName = getKeyTriggerMethodName(key); String triggerMethodDescriptor = getKeyTriggerMethodDescriptor(key); Rule rule = ruleMap.get(key); if(!ruleScript.recordTransform(loader, triggerClassName, triggerMethodName, triggerMethodDescriptor, rule, null)) { // rule script must have been deleted so purge rules and avoid installing the transformed code // purgeRules(); return false; } } } return true; } /** * this gets called when a transform attempt fails. if there are rules left in the rule map * then they will belong either to earlier successful injections or to the failed transform. * in any case they need to be purged. */ private void purgeRules() { for (Rule rule : ruleMap.values()) { rule.purge(); } } /** * return a unique string key identifying a specific rule compiled against some class and method/signature in the * context of a specific class loader * @return a unique string key */ private String getRuleKey(String triggerMethodName, String triggerMethodDescriptor) { return triggerClassName + "#" + triggerMethodName + "#" + triggerMethodDescriptor; } /** * return the trigger method name used to construct the supplied rule key * @param key * @return the trigger method name */ private String getKeyTriggerMethodName(String key) { int firstHash = key.indexOf('#'); int secondHash = key.lastIndexOf('#'); return key.substring(firstHash + 1, secondHash); } /** * return the trigger method descriptor used to construct the supplied rule key * @param key * @return the trigger method descriptor */ private String getKeyTriggerMethodDescriptor(String key) { int secondHash = key.lastIndexOf('#'); return key.substring(secondHash + 1); } /** * pattern used to identify target method specs which include a return type preceding the * method name and parameter type list. note that we can only handle a return type in * cases where the parameter type list is also specified. */ private static final String JAVA_METHOD_SPEC_PATTERN = "[A-Za-z0-9$.]+ +[A-Za-z0-9$]+\\(.*\\)"; /** * The prefix that identifies a method generated to implement a local lambda expression." */ private static final String LAMBDA_PREFIX = "lambda$"; /** * detect a method specification which includes a return type preceding the method name and transform * it so that the return type is at the end. * @param targetMethodSpec * @return the method spec in the desired format */ private String mungeMethodSpecReturnType(String targetMethodSpec) { // remove any leading or trailing spaces targetMethodSpec = targetMethodSpec.trim(); if (targetMethodSpec.matches(JAVA_METHOD_SPEC_PATTERN)) { // put the return type at the end int spaceIdx = targetMethodSpec.indexOf(' '); String returnType = targetMethodSpec.substring(0, spaceIdx); targetMethodSpec = targetMethodSpec.substring(spaceIdx).trim() + returnType; } return targetMethodSpec; } /** * get a class writer which will not attempt to load classes. The default classwriter tries this when a * reference type local var frame slot aligns with a slot of reference type in a successor block's * frame. This is merely so it can optimize a slot out of the frame change set in the special case where * f1[slot].type < f2[slot].type or vice versa by using the least common supereclass. We have to use * the Transformer to reuse existing loaded classes and, where a class has not been loaded, to * attempt to load the bytecode as a resource and identify supers via the bytecode. * * @param flags * @return a non-loading class writer */ private ClassWriter getNonLoadingClassWriter(int flags) { final TransformContext finalContext = this; return new ClassWriter(flags) { TransformContext context = finalContext; protected String getCommonSuperClass(final String type1, final String type2) { // if we always return Object we cannot go wrong return context.findLeastCommonSuper(type1, type2); } }; } public final static String TOFU = "java/lang/Object"; // TOFU = top of universe public String findLeastCommonSuper(final String t1, final String t2) { // check the simple cases first if (TOFU.equals(t1) || TOFU.equals(t2)) { return TOFU; } if (t1.equals(t2)) { return t2; } // switch to canonical names containing "." instead of "/" when // checking against names found in bytecode but ensure the returned // name contains "/" String type1 = t1.replaceAll("/", "."); String type2 = t2.replaceAll("/", "."); ClassChecker checker1 = transformer.getClassChecker(type1, loader); if (checker1 == null) { return TOFU; } ClassChecker checker2 = transformer.getClassChecker(type2, loader); if (checker2 == null) { return TOFU; } if (checker1.isInterface()) { if (checker2.isInterface()) { // both are interfaces so find the first common parent interface // (including the original interfaces) or return Object LinkedList interfaces2 = listInterfaces(checker2); if (interfaces2.contains(type1)) { return t1; } else { LinkedList interfaces1 = listInterfaces(checker1); while (!interfaces1.isEmpty()) { String next = interfaces1.pop(); if (next.equals(type2)) { return t2; } if (interfaces2.contains(next)) { return next.replaceAll("\\.", "/"); } } return TOFU; } } else { // type1 is an interface but type2 is a class so return the // first parent interface of type2 which implements either type1 or // one of type1's parent interfaces or return Object LinkedList interfaces2 = listInterfaces(checker2); if (interfaces2.contains(type1)) { // type1 is an interface of type2 return t1; } else { LinkedList interfaces1 = listInterfaces(checker1); while (!interfaces1.isEmpty()) { String next = interfaces1.pop(); if (interfaces2.contains(next)) { return next.replaceAll("\\.", "/"); } } return TOFU; } } } else { if (checker2.isInterface()) { // type2 is an interface but type1 is a class so return the // first parent interface of type1 which implements either type1 or // one of type1's parent interfaces or return Object LinkedList interfaces1 = listInterfaces(checker1); if (interfaces1.contains(type2)) { // type2 is an interface of type1 return t2; } else { LinkedList interfaces2 = listInterfaces(checker2); while (!interfaces2.isEmpty()) { String next = interfaces2.pop(); if (interfaces1.contains(next)) { return next.replaceAll("\\.", "/"); } } return TOFU; } } else { // see if the classes have a common super class before Object LinkedList supers2 = listSupers(checker2); if (supers2.contains(type1)) { // type2 is a subclass of type1 return t1; } else { LinkedList supers1 = listSupers(checker1); while (!supers1.isEmpty()) { String next = supers1.pop(); if (next.equals(type2)) { return t2; } if (supers2.contains(next)) { return next.replaceAll("\\.", "/"); } } return TOFU; } } } } private LinkedList listInterfaces(ClassChecker checker) { LinkedList interfaces = new LinkedList(); ClassChecker current = checker; while (current != null) { LinkedList toCheck = new LinkedList(); int count = current.getInterfaceCount(); for (int i = 0; i < count; i++) { toCheck.add(current.getInterface(i)); } // now recursively work through unchecked interfaces adding them // if not already processed and including the interfaces they extend // in the toCheck list. while (!toCheck.isEmpty()) { String next = toCheck.pop(); if (!interfaces.contains(next)) { interfaces.add(next); ClassChecker newChecker = transformer.getClassChecker(next, loader); if (newChecker != null) { count = newChecker.getInterfaceCount(); for (int i = 0; i < count; i++) { toCheck.add(newChecker.getInterface(i)); } } } } // repeat for the next super in line up to java/lang/Object String superType = current.getSuper(); if (superType == null || TOFU.equals(superType)) { current = null; } else { current = transformer.getClassChecker(superType, loader); } } return interfaces; } private LinkedList listSupers(ClassChecker checker) { LinkedList supers = new LinkedList(); ClassChecker current = checker; while (current != null) { String superType = current.getSuper(); if (superType != null) { supers.add(superType); current = transformer.getClassChecker(superType, loader); } else { current = null; } } return supers; } private Transformer transformer; private RuleScript ruleScript; private String triggerClassName; private String targetClassName; private String targetMethodName; private String targetDescriptor; private ClassLoader loader; private HelperManager helperManager; private AccessEnabler accessEnabler; private boolean failed; /** * a hashmap indexing Rule instances using key classname.methodnameandsig@loaderhashcode. rules are * added to this map when they are created and removed when the transform is recorded as having * succeeded or failed. a method check adapter will create a rule when it begins a method scan and * a method trigger adpater will look it up in order to reuse it */ private HashMap ruleMap; private Rule firstRule; } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/TransformListener.java000066400000000000000000000402611414767246600300110ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2009-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * (C) 2009-10, * @authors Andrew Dinn */ package org.jboss.byteman.agent; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.jar.JarFile; import org.jboss.byteman.rule.Rule; import org.jboss.byteman.rule.helper.Helper; /** * a socket based listener class which reads scripts from stdin and installs them in the current runtime */ public class TransformListener extends Thread { public static int DEFAULT_PORT = 9091; public static String DEFAULT_HOST = "localhost"; private static TransformListener theTransformListener = null; private static ServerSocket theServerSocket; private Retransformer retransformer; private TransformListener(Retransformer retransformer) { this.retransformer = retransformer; setDaemon(true); } public static synchronized boolean initialize(Retransformer retransformer) { return (initialize(retransformer, null, null)); } public static synchronized boolean initialize(Retransformer retransformer, String hostname, Integer port) { if (theTransformListener == null) { try { if (hostname == null) { hostname = DEFAULT_HOST; } if (port == null) { port = Integer.valueOf(DEFAULT_PORT); } theServerSocket = new ServerSocket(); theServerSocket.bind(new InetSocketAddress(hostname, port.intValue())); Helper.verbose("TransformListener() : accepting requests on " + hostname + ":" + port); } catch (IOException e) { Helper.err("TransformListener() : unexpected exception opening server socket " + e); Helper.errTraceException(e); return false; } theTransformListener = new TransformListener(retransformer); theTransformListener.start(); } return true; } public static synchronized boolean terminate() { // we don't want the listener shutdown to be aborted because of triggered rules boolean enabled = true; try { enabled = Rule.disableTriggersInternal(); if (theTransformListener != null) { try { theServerSocket.close(); Helper.verbose("TransformListener() : closing port " + DEFAULT_PORT); } catch (IOException e) { // ignore -- the thread should exit anyway } try { theTransformListener.join(); } catch (InterruptedException e) { // ignore } theTransformListener = null; theServerSocket = null; } return true; } finally { if (enabled) { Rule.enableTriggersInternal(); } } } @Override public void run() { // we don't want to see any triggers in the listener thread Rule.disableTriggersInternal(); while (true) { if (theServerSocket.isClosed()) { return; } Socket socket = null; try { socket = theServerSocket.accept(); } catch (IOException e) { if (!theServerSocket.isClosed()) { Helper.err("TransformListener.run : exception from server socket accept " + e); Helper.errTraceException(e); } return; } Helper.verbose("TransformListener() : handling connection on port " + socket.getLocalPort()); try { handleConnection(socket); } catch (Exception e) { Helper.err("TransformListener() : error handling connection on port " + socket.getLocalPort()); try { socket.close(); } catch (IOException e1) { // do nothing } } } } private void handleConnection(Socket socket) { InputStream is = null; try { is = socket.getInputStream(); } catch (IOException e) { // oops. cannot handle this Helper.err("TransformListener.run : error opening socket input stream " + e); Helper.errTraceException(e); try { socket.close(); } catch (IOException e1) { Helper.err("TransformListener.run : exception closing socket after failed input stream open" + e1); Helper.errTraceException(e1); } return; } OutputStream os = null; try { os = socket.getOutputStream(); } catch (IOException e) { // oops. cannot handle this Helper.err("TransformListener.run : error opening socket output stream " + e); Helper.errTraceException(e); try { socket.close(); } catch (IOException e1) { Helper.err("TransformListener.run : exception closing socket after failed output stream open" + e1); Helper.errTraceException(e1); } return; } BufferedReader in = new BufferedReader(new InputStreamReader(is)); PrintWriter out = new PrintWriter(new OutputStreamWriter(os)); String line = null; try { line = in.readLine(); } catch (IOException e) { Helper.err("TransformListener.run : exception " + e + " while reading command"); Helper.errTraceException(e); } try { if (line == null) { out.println("ERROR"); out.println("Expecting input command"); out.println("OK"); out.flush(); } else if (line.equals("BOOT")) { loadJars(in, out, true); } else if (line.equals("SYS")) { loadJars(in, out, false); } else if (line.equals("LOAD")) { loadScripts(in, out); } else if (line.equals("DELETE")) { deleteScripts(in, out); } else if (line.equals("LIST")) { listScripts(in, out); } else if (line.equals("DELETEALL")) { purgeScripts(in, out); } else if (line.equals("VERSION")) { getVersion(in, out); } else if (line.equals("LISTBOOT")) { listBootJars(in, out); } else if (line.equals("LISTSYS")) { listSystemJars(in, out); } else if (line.equals("LISTSYSPROPS")) { listSystemProperties(in, out); } else if (line.equals("SETSYSPROPS")) { setSystemProperties(in, out); } else { out.println("ERROR"); out.println("Unexpected command " + line); out.println("OK"); out.flush(); } } catch (Exception e) { Helper.err("TransformListener.run : exception " + e + " processing command " + line); Helper.errTraceException(e); } finally { try { socket.close(); } catch (IOException e1) { Helper.err("TransformListener.run : exception closing socket " + e1); Helper.errTraceException(e1); } } } private void getVersion(BufferedReader in, PrintWriter out) { String version = this.getClass().getPackage().getImplementationVersion(); if (version == null) { version = "0"; } out.println(version); out.println("OK"); out.flush(); } private void loadScripts(BufferedReader in, PrintWriter out) throws IOException { handleScripts(in, out, false); } private void loadJars(BufferedReader in, PrintWriter out, boolean isBoot) throws IOException { final String endMarker = (isBoot) ? "ENDBOOT" : "ENDSYS"; String line = in.readLine().trim(); while (line != null && !line.equals(endMarker)) { try { JarFile jarfile = new JarFile(new File(line)); retransformer.appendJarFile(out, jarfile, isBoot); } catch (Exception e) { out.append("EXCEPTION "); out.append("Unable to add jar file " + line + "\n"); out.append(e.toString()); out.append("\n"); e.printStackTrace(out); } line = in.readLine().trim(); } if (line == null || !line.equals(endMarker)) { out.append("ERROR\n"); out.append("Unexpected end of line reading " + ((isBoot) ? "boot" : "system") + " jars\n"); } out.println("OK"); out.flush(); } private void deleteScripts(BufferedReader in, PrintWriter out) throws IOException { handleScripts(in, out, true); } private void handleScripts(BufferedReader in, PrintWriter out, boolean doDelete) throws IOException { List scripts = new LinkedList(); List scriptNames = new LinkedList(); String line = in.readLine().trim(); String scriptName = ""; while (line.startsWith("SCRIPT ")) { StringBuffer stringBuffer = new StringBuffer(); scriptName = line.substring("SCRIPT ".length()); line = in.readLine(); while (line != null && !line.equals("ENDSCRIPT")) { stringBuffer.append(line); stringBuffer.append('\n'); line = in.readLine(); } if (line == null || !line.equals("ENDSCRIPT")) { out.append("ERROR\n"); out.append("Unexpected end of line reading script " + scriptName + "\n"); out.append("OK"); out.flush(); return; } String script = stringBuffer.toString(); scripts.add(script); scriptNames.add(scriptName); line = in.readLine(); } if ((doDelete && !line.equals("ENDDELETE")) || (!doDelete && !line.equals("ENDLOAD"))) { out.append("ERROR "); out.append("Unexpected end of line reading script " + scriptName + "\n"); out.println("OK"); out.flush(); return; } try { if (doDelete) { retransformer.removeScripts(scripts, out); } else { retransformer.installScript(scripts, scriptNames, out); } } catch (Exception e) { out.append("EXCEPTION "); out.append(e.toString()); out.append('\n'); e.printStackTrace(out); } out.println("OK"); out.flush(); } private void purgeScripts(BufferedReader in, PrintWriter out) throws Exception { retransformer.removeScripts(null, out); out.println("OK"); out.flush(); } private void listScripts(BufferedReader in, PrintWriter out) throws Exception { retransformer.listScripts(out); out.println("OK"); out.flush(); } private void listBootJars(BufferedReader in, PrintWriter out) throws Exception { Set jars = retransformer.getLoadedBootJars(); for (String jar : jars) { out.println(new File(jar).getAbsolutePath()); } out.println("OK"); out.flush(); } private void listSystemJars(BufferedReader in, PrintWriter out) throws Exception { Set jars = retransformer.getLoadedSystemJars(); for (String jar : jars) { out.println(new File(jar).getAbsolutePath()); } out.println("OK"); out.flush(); } private void listSystemProperties(BufferedReader in, PrintWriter out) throws Exception { Properties sysProps = System.getProperties(); boolean strictMode = false; if (Boolean.parseBoolean(sysProps.getProperty(Transformer.SYSPROPS_STRICT_MODE, "true"))) { strictMode = true; } for (Map.Entry entry : sysProps.entrySet()) { String name = entry.getKey().toString(); if (!strictMode || name.startsWith("org.jboss.byteman.")) { String value = entry.getValue().toString(); out.println(name + "=" + value.replace("\n", "\\n").replace("\r", "\\r")); } } out.println("OK"); out.flush(); } private void setSystemProperties(BufferedReader in, PrintWriter out) throws Exception { boolean strictMode = false; if (Boolean.parseBoolean(System.getProperty(Transformer.SYSPROPS_STRICT_MODE, "true"))) { strictMode = true; } final String endMarker = "ENDSETSYSPROPS"; String line = in.readLine().trim(); while (line != null && !line.equals(endMarker)) { try { String[] nameValuePair = line.split("=", 2); if (nameValuePair.length != 2 ) { throw new Exception("missing '='"); } String name = nameValuePair[0]; String value = nameValuePair[1]; if (strictMode && !name.startsWith("org.jboss.byteman.")) { throw new Exception("strict mode is enabled, cannot set non-byteman system property"); } if (name.equals(Transformer.SYSPROPS_STRICT_MODE) && !value.equals("true")) { // nice try throw new Exception("cannot turn off strict mode"); } // everything looks good and we are allowed to set the system property now if (value.length() > 0) { // "some.sys.prop=" means the client wants to delete the system property System.setProperty(name, value); out.append("Set system property [" + name + "] to value [" + value + "]\n"); } else { System.clearProperty(name); out.append("Deleted system property [" + name + "]\n"); } // ok, now tell the transformer a property has changed retransformer.updateConfiguration(name); } catch (Exception e) { out.append("EXCEPTION "); out.append("Unable to set system property [" + line + "]\n"); out.append(e.toString()); out.append("\n"); e.printStackTrace(out); } line = in.readLine().trim(); } if (line == null || !line.equals(endMarker)) { out.append("ERROR\n"); out.append("Unexpected end of line reading system properties\n"); } out.println("OK"); out.flush(); } }byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/TransformSet.java000066400000000000000000000103151414767246600267540ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2017, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import org.jboss.byteman.rule.Rule; import org.jboss.byteman.rule.helper.Helper; import java.util.ArrayList; import java.util.List; /** * A TransformSet groups together a set of Transform records which * share a common classloader, trigger class name (and RuleScript). * The set includes details of successful or failed transforms. * * This grouping ensures that all transforms arising from a specific * retransform operation for a new, modified or deleted script can * be managed as a unit. In particular this is needed in order to * allow installation and uninstallation of a rule to be performed * consistently. * * Note that although the loader and trigger class name uniquely * identify a single trigger class a transform set may still * contain more than one successful transform. That is possible * because the RuleScript may omit a descriptor and hence may match * multiple overloaded variants of the method named in the rule's * METHOD clause. */ public class TransformSet { private ClassLoader loader; private String triggerClass; private List transforms; private Rule rule; public TransformSet(ClassLoader loader, String triggerClass) { this.loader = loader; this.triggerClass = triggerClass; this.transforms = new ArrayList(); this.rule = null; } public boolean isFor(ClassLoader loader, String triggerClass) { return this.loader == loader && this.triggerClass.equals(triggerClass); } public void add(Transform transform) { if (transform.getThrowable() == null) { // transform was successful. see if we have another // transform for the same method and if so just stick // with the existing one as both are equivalent String key = transform.getRule().getKey(); for (Transform current : transforms) { if (current.getRule() != null && key.equals(current.getRule().getKey())) { // the other transform should not have resulted in an error if (transform.getThrowable() != null && Transformer.isVerbose()) { Helper.verbose("TransformSet.add : mismatch between successful and failed transforms with key " + key); Helper.verboseTraceException(transform.getThrowable()); } return; } } } // add the new transform to the list transforms.add(transform); } public ClassLoader getLoader() { return loader; } public String getTriggerClass() { return triggerClass; } /* * check whether a rule has been installed for this transform */ public boolean isInstalled() { return getRule() != null; } public void setInstalled(Rule key) { this.rule = key; } public Rule getRule() { return rule; } public List getTransforms() { return transforms; } public boolean isEmpty() { return transforms.isEmpty(); } public void clearTransforms() { transforms.clear(); } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/Transformer.java000066400000000000000000001622101414767246600266310ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2008-10, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent; import org.jboss.byteman.agent.check.BytecodeChecker; import org.jboss.byteman.agent.check.CheckerCache; import org.jboss.byteman.agent.check.ClassChecker; import org.jboss.byteman.modules.ModuleSystem; import org.jboss.byteman.rule.Rule; import org.jboss.byteman.rule.helper.Helper; import org.jboss.byteman.rule.type.TypeHelper; import java.io.InputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; import java.net.URL; import java.security.Policy; import java.security.ProtectionDomain; import java.util.*; import java.io.FileOutputStream; import java.io.IOException; import java.io.File; import java.util.jar.Attributes; import java.util.jar.Manifest; /** * byte code transformer used to introduce byteman events into JBoss code */ public class Transformer implements ClassFileTransformer { /** * constructor allowing this transformer to be provided with access to the JVM's instrumentation * implementation * * @param inst the instrumentation object used to interface to the JVM * @param scriptPaths list of file paths for each input script * @param scriptTexts the text of each input script * @param isRedefine true if class redefinition is allowed false if not * @param moduleSystem the module system to use in transformation * @throws Exception if a script is in error */ public Transformer(Instrumentation inst, ModuleSystem moduleSystem, List scriptPaths, List scriptTexts, boolean isRedefine) throws Exception { this.inst = inst; this.isRedefine = isRedefine; scriptRepository = new ScriptRepository(skipOverrideRules); checkerCache = new CheckerCache(); helperManager = new HelperManager(inst, moduleSystem); Iterator scriptsIter = scriptTexts.iterator(); Iterator filesIter = scriptPaths.iterator(); while (scriptsIter.hasNext()) { String scriptText = scriptsIter.next(); String file = filesIter.next(); List ruleScripts = scriptRepository.processScripts(scriptText, file); for (RuleScript ruleScript : ruleScripts) { String name = ruleScript.getName(); RuleScript previous = scriptRepository.scriptForRuleName(name); if (previous == null) { scriptRepository.addScript(ruleScript); } else { StringBuffer buffer = new StringBuffer(); buffer.append("Transformer : duplicate script name "); buffer.append(name); buffer.append("in file "); buffer.append(ruleScript.getFile()); buffer.append(" line "); buffer.append(ruleScript.getLine()); buffer.append("\n previously defined in file "); buffer.append(previous.getFile()); buffer.append(" line "); buffer.append(previous.getLine()); Exception ex = new Exception(buffer.toString()); throw ex; } } } accessEnabler = AccessManager.init(inst); // initialise the agent version property setAgentVersion(); } /** * ensure that scripts which apply to classes loaded before * registering the transformer are installed by retransforming the * relevant classes * @throws Exception if the retransform fails */ public void installBootScripts() throws Exception { // check for scripts which apply to classes already loaded // during bootstrap and retransform those classes so that rule // triggers are injected List> transformed = new LinkedList>(); Class[] loaded = inst.getAllLoadedClasses(); for (Class clazz : loaded) { if (isSkipClass(clazz)) { continue; } if (scriptRepository.matchClass(clazz)) { transformed.add(clazz); } } // retransform all classes for which we found untransformed rules if (!transformed.isEmpty()) { Class[] transformedArray = new Class[transformed.size()]; transformed.toArray(transformedArray); for (int i = 0; i < transformed.size(); i++) { Helper.verbose("retransforming " + transformedArray[i].getName()); } inst.retransformClasses(transformedArray); } } public void installPolicy() { BytemanPolicy policy = new BytemanPolicy(Policy.getPolicy()); Policy.setPolicy(policy); } /** * The implementation of this method may transform the supplied class file and * return a new replacement class file. * * * Once a transformer has been registered with * {@link java.lang.instrument.Instrumentation#addTransformer Instrumentation.addTransformer}, * the transformer will be called for every new class definition and every class redefinition. * The request for a new class definition is made with * {@link ClassLoader#defineClass ClassLoader.defineClass}. * The request for a class redefinition is made with * {@link java.lang.instrument.Instrumentation#redefineClasses Instrumentation.redefineClasses} * or its native equivalents. * The transformer is called during the processing of the request, before the class file bytes * have been verified or applied. * * * If the implementing method determines that no transformations are needed, * it should return null. * Otherwise, it should create a new byte[] array, * copy the input classfileBuffer into it, * along with all desired transformations, and return the new array. * The input classfileBuffer must not be modified. * * * In the redefine case, the transformer must support the redefinition semantics. * If a class that the transformer changed during initial definition is later redefined, the * transformer must insure that the second class output class file is a legal * redefinition of the first output class file. * * * If the transformer believes the classFileBuffer does not * represent a validly formatted class file, it should throw * an IllegalClassFormatException. Subsequent transformers * will still be called and the load or redefine will still * be attempted. Throwing an IllegalClassFormatException thus * has the same effect as returning null but facilitates the * logging or debugging of format corruptions. * * @param originalLoader the defining loader of the class to be transformed, * may be null if the bootstrap loader * @param className the name of the class in the internal form of fully * qualified class and interface names as defined in * The Java Virtual Machine Specification. * For example, "java/util/List". * @param classBeingRedefined if this is a redefine, the class being redefined, * otherwise null * @param protectionDomain the protection domain of the class being defined or redefined * @param classfileBuffer the input byte buffer in class file format - must not be modified * @return a well-formed class file buffer (the result of the transform), * or null if no transform is performed. * @throws java.lang.instrument.IllegalClassFormatException * if the input does not represent a well-formed class file * @see java.lang.instrument.Instrumentation#redefineClasses */ public byte[] transform(ClassLoader originalLoader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { boolean enabled = true; ClassLoader loader = originalLoader; try { enabled = Rule.disableTriggersInternal(); byte[] newBuffer = classfileBuffer; // we only transform certain classes -- we do allow bootstrap classes whose loader is null // but we exclude byteman classes and java.lang classes String internalName = TypeHelper.internalizeClass(className); if (isBytemanClass(internalName) || !isTransformable(internalName)) { return null; } // we will need the super class name any outer class name and the name of the interfaces the class implements ClassChecker checker = getClassChecker(newBuffer);// new ClassChecker(newBuffer); if (checker == null || checker.isInterface()) { return null; } /* if (checker.hasOuterClass()) { // we don't transform inner classes for now // TODO -- see if we can match and transform inner classes via the outer class return null; } */ // TODO-- reconsider this as it is a bit dodgy as far as security is concerned if (loader == null) { loader = ClassLoader.getSystemClassLoader(); } // if we need to traverse the interfaces then we have a DAG to deal with so // we had better find a way to avoid doing things twice LinkedList toVisit = null; HashSet visited = null; // ok, we need to check whether there are any class scripts associated with this class and if so // we will consider transforming the byte code // TODO -- there are almost certainly concurrency issues to deal with here if rules are being loaded/unloaded newBuffer = tryTransform(newBuffer, internalName, loader, internalName, internalName, false, false); int dotIdx = internalName.lastIndexOf('.'); if (dotIdx > 0) { newBuffer = tryTransform(newBuffer, internalName, loader, internalName.substring(dotIdx + 1), internalName, false, false); } if (scriptRepository.checkInterfaces()) { // now we need to do the same for any interface scripts // n.b. resist the temptation to call classBeingRedefined.getInterfaces() as this will // cause the class to be resolved, losing any changes we install // we need to check the transitive closure of the binary links // Class implements Interface and Interface extends Interface for this class // which in general is a DAG. toVisit = new LinkedList(); visited = new HashSet(); // we start with the original list of implemented interfaces int interfaceCount = checker.getInterfaceCount(); for (int i = 0; i < interfaceCount; i++) { String interfaceName = checker.getInterface(i); toVisit.add(interfaceName); } // ok now check each interface in turn while pushing its super interfaces // until we no longer have any new interfaces to check while (!toVisit.isEmpty()) { String interfaceName = toVisit.pop(); String internalInterfaceName = TypeHelper.internalizeClass(interfaceName); if (!visited.contains(interfaceName)) { // avoid visiting this interface again visited.add(interfaceName); // now see if we have any rules for this interface newBuffer = tryTransform(newBuffer, internalName, loader, internalInterfaceName, internalInterfaceName, true, false); dotIdx = internalInterfaceName.lastIndexOf('.'); if (dotIdx >= 0) { newBuffer = tryTransform(newBuffer, internalName, loader, internalInterfaceName.substring(dotIdx + 1), internalInterfaceName, true, false); } // check the extends list of this interface for new interfaces to consider ClassChecker newChecker = getClassChecker(interfaceName, originalLoader); if (newChecker != null) { interfaceCount = newChecker.getInterfaceCount(); for (int i = 0; i < interfaceCount; i++) { interfaceName = newChecker.getInterface(i); toVisit.add(interfaceName); } } } } } // checking supers is expensive so we obey the switch which disables it if (!skipOverrideRules()) { // ok, now check the superclass for this class and so on String superName = checker.getSuper(); while (superName != null) { // we need to check the super class structure // n.b. we use the original loader here because we don't want to search the system loader // when we have a class in the bootstrap loader checker = getClassChecker(superName, originalLoader); if (checker == null) { // we must have a super to continue break; } newBuffer = tryTransform(newBuffer, internalName, loader, superName, superName, false, true); dotIdx = superName.lastIndexOf('.'); if (dotIdx > 0) { newBuffer = tryTransform(newBuffer, internalName, loader, superName.substring(dotIdx + 1), superName, false, true); } if (scriptRepository.checkInterfaces()) { // we need to do another DAG visit but only for interfaces not already considered int interfaceCount = checker.getInterfaceCount(); for (int i = 0; i < interfaceCount; i++) { String interfaceName = checker.getInterface(i); toVisit.add(interfaceName); } // ok now check each interface in turn while pushing its super interfaces // until we no longer have any new interfaces to check while(!toVisit.isEmpty()) { String interfaceName = toVisit.pop(); String internalInterfaceName = TypeHelper.internalizeClass(interfaceName); if (!visited.contains(interfaceName)) { // avoid visiting this interface again visited.add(interfaceName); // now see if we have any rules for this interface newBuffer = tryTransform(newBuffer, internalName, loader, internalInterfaceName, internalInterfaceName, true, true); dotIdx = interfaceName.lastIndexOf('.'); if (dotIdx >= 0) { newBuffer = tryTransform(newBuffer, internalName, loader, internalInterfaceName.substring(dotIdx + 1), internalInterfaceName, true, true); } // check the extends list of this interface for new interfaces to consider ClassChecker newChecker = getClassChecker(interfaceName, originalLoader); if (newChecker != null) { interfaceCount = newChecker.getInterfaceCount(); for (int i = 0; i < interfaceCount; i++) { interfaceName = newChecker.getInterface(i); toVisit.add(interfaceName); } } } } } // move on to the next super superName = checker.getSuper(); } } if (newBuffer != classfileBuffer) { // see if we need to dump the transformed bytecode for checking maybeDumpClass(internalName, newBuffer); newBuffer = maybeVerifyTransformedBytes(originalLoader, internalName, protectionDomain, newBuffer); return newBuffer; } else { return null; } } finally { if (enabled) { Rule.enableTriggersInternal(); } } } /* switches controlling behaviour of transformer */ /** * prefix for byteman package */ public static final String BYTEMAN_PACKAGE_PREFIX = "org.jboss.byteman."; /** * prefix for byteman test package */ public static final String BYTEMAN_TEST_PACKAGE_PREFIX = "org.jboss.byteman.tests."; /** * prefix for byteman sample package */ public static final String BYTEMAN_SAMPLE_PACKAGE_PREFIX = "org.jboss.byteman.sample."; /** * prefix for org.jboss package */ public static final String JAVA_LANG_PACKAGE_PREFIX = "java.lang."; /** * system property set (to any value) in order to switch on dumping of generated bytecode to .class files */ public static final String VERBOSE = BYTEMAN_PACKAGE_PREFIX + "verbose"; /** * system property set (to any value) in order to switch on dumping of control flow graph for * trigger method at each stage of construction */ public static final String DUMP_CFG_PARTIAL = BYTEMAN_PACKAGE_PREFIX + "dump.cfg.partial"; /** * system property set (to any value) in order to switch on dumping of control flow graph for * triger method after construction */ public static final String DUMP_CFG = BYTEMAN_PACKAGE_PREFIX + "dump.cfg"; /** * system property set (to any value) in order to switch on debug statements in the default Helper */ public static final String DEBUG = BYTEMAN_PACKAGE_PREFIX + "debug"; /** * system property set to record the currently installed Byteman agent's version */ public static final String AGENT_VERSION = BYTEMAN_PACKAGE_PREFIX + "agent.version"; /** * retained for compatibility */ public static final String COMPILE_TO_BYTECODE_COMPATIBILITY = BYTEMAN_PACKAGE_PREFIX + "compileToBytecode"; /** * system property set (to any value) in order to switch on compilation of rules and left unset * if rules are to be interpreted. */ public static final String COMPILE_TO_BYTECODE = BYTEMAN_PACKAGE_PREFIX + "compile.to.bytecode"; /** * system property set (to any value) in order to switch on dumping of generated bytecode to .class files */ public static final String DUMP_GENERATED_CLASSES = BYTEMAN_PACKAGE_PREFIX + "dump.generated.classes"; /** * system property set (to any value) in order to switch on dumping of intermediate generated bytecode to .class files */ public static final String DUMP_GENERATED_CLASSES_INTERMEDIATE = BYTEMAN_PACKAGE_PREFIX + "dump.generated.classes.intermediate"; /** * system property identifying directory in which to dump generated bytecode .class files */ public static final String DUMP_GENERATED_CLASSES_DIR = BYTEMAN_PACKAGE_PREFIX + "dump.generated.classes.directory"; /** * system property set to true in order to enable transform of java.lang classes */ public static final String TRANSFORM_ALL = BYTEMAN_PACKAGE_PREFIX + "transform.all"; /** * retained for compatibility */ public static final String TRANSFORM_ALL_COMPATIBILITY = BYTEMAN_PACKAGE_PREFIX + "quodlibet"; /** * system property which turns off injection into overriding methods */ public static final String SKIP_OVERRIDE_RULES = BYTEMAN_PACKAGE_PREFIX + "skip.override.rules"; /** * system property which enables the restriction that only byteman specific system properties * will be gettable/settable via a client using the LISTSYSPROPS and SETSYSPROPS commands. */ public static final String SYSPROPS_STRICT_MODE = BYTEMAN_PACKAGE_PREFIX + "sysprops.strict"; /** * system property which enables the restriction that only byteman specific system properties * will be gettable/settable via a client using the LISTSYSPROPS and SETSYSPROPS commands. */ public static final String VERIFY_TRANSFORMED_BYTES = BYTEMAN_PACKAGE_PREFIX + "verify.transformed.bytes"; /** * system property which determines whether or not byteman configuration can be updated at runtime * via the byteman agent listener */ public static final String ALLOW_CONFIG_UPDATE = BYTEMAN_PACKAGE_PREFIX + "allow.config.update"; /** * system property which disables downcasts in bindings */ public static final String DISALLOW_DOWNCAST = BYTEMAN_PACKAGE_PREFIX + "disallow.downcast"; /** * disable triggering of rules inside the current thread * @param isUser true if this was called by rule code false if called internally by Byteman * @return true if triggering was previously enabled and false if it was already disabled */ public static boolean disableTriggers(boolean isUser) { Integer enabled = isEnabled.get(); if (enabled == ENABLED) { isEnabled.set((isUser ? DISABLED_USER : DISABLED)); return true; } if (enabled == DISABLED && isUser) { isEnabled.set(DISABLED_USER); } return false; } /** * enable triggering of rules inside the current thread * @param isReset true if this was called by rule code and hence should reset a setting * enabled by rule code false if called internally by Byteman and hence * should nto reset a setting enabled by rule code * @return true if triggering was previously enabled and false if it was already disabled */ public static boolean enableTriggers(boolean isReset) { Integer enabled = isEnabled.get(); if (enabled == ENABLED) { return true; } if (isReset || enabled == DISABLED) { isEnabled.set(ENABLED); } return false; } /** * check if triggering of rules is enabled inside the current thread * @return true if triggering is enabled and false if it is disabled */ public static boolean isTriggeringEnabled() { return isEnabled.get() == ENABLED; } /** * check whether verbose mode for rule processing is enabled or disabled * @return true if verbose mode is enabled etherwise false */ public static boolean isVerbose() { if (allowConfigUpdate()) { synchronized (configLock) { return verbose; } } return verbose; } /** * check whether dumping of the control flow graph for the trigger class is enabled * @return true if dumping is enabled etherwise false */ public static boolean isDumpCFG() { if (allowConfigUpdate()) { synchronized (configLock) { return dumpCFG; } } return dumpCFG; } /** * check whether dumping of the control flow graph for the trigger class during construction is enabled * @return true if dumping is enabled etherwise false */ public static boolean isDumpCFGPartial() { if (allowConfigUpdate()) { synchronized (configLock) { return dumpCFGPartial; } } return dumpCFGPartial; } protected static boolean isDumpGeneratedClasses() { if (allowConfigUpdate()) { synchronized (configLock) { return dumpGeneratedClasses; } } return dumpGeneratedClasses; } protected static String getDumpGeneratedClassesDir() { if (allowConfigUpdate()) { synchronized (configLock) { return dumpGeneratedClassesDir; } } return dumpGeneratedClassesDir; } protected static boolean isDumpGeneratedClassesIntermediate() { if (allowConfigUpdate()) { synchronized (configLock) { return dumpGeneratedClassesIntermediate; } } return dumpGeneratedClassesIntermediate; } /** * check whether debug mode for rule processing is enabled or disabled * @return true if debug mode is enabled or verbose mode is enabled otherwise false */ public static boolean isDebug() { if (allowConfigUpdate()) { synchronized (configLock) { return debug || verbose; } } return debug || verbose; } /** * check whether compilation of rules is enabled or disabled * @return true if compilation of rules is enabled etherwise false */ public static boolean isCompileToBytecode() { if (allowConfigUpdate()) { synchronized (configLock) { return compileToBytecode; } } return compileToBytecode; } /** * check whether downcasts in bindings are disallowed. * @return true if downcasts in bindings are disallowed otherwise false */ public static boolean disallowDowncast() { if (allowConfigUpdate()) { synchronized (configLock) { return disallowDowncast; } } return disallowDowncast; } /** * check whether compilation of rules is enabled or disabled * @return true if compilation of rules is enabled otherwise false */ public boolean skipOverrideRules() { return scriptRepository.skipOverrideRules(); } /** * check whether changes to org.jboss.byteman.* system properties will affect the agent configuration. * @return true if changes will affect the agent configuration otherwise false */ public static boolean allowConfigUpdate() { return allowConfigUpdate; } /** * notify a change to an org.jboss.byteman.* system property so that the agent can choose to update its * configuration. n.b. this method is not synchronized because there is an implicit assumption that it is * called from the the listener thread immediately after it has updated the property and that no other * thread will modify org.jboss.byteman.* properties * @param property an org.jboss.byteman.* system property which has been updated. */ public void updateConfiguration(String property) { if (allowConfigUpdate() && property.startsWith(BYTEMAN_PACKAGE_PREFIX)) { checkConfiguration(property); } } /** * test whether a class with a given name is a potential candidate for insertion of event notifications * @param className name of the class to test * @return true if a class is a potential candidate for insertion of event notifications otherwise return false */ protected boolean isTransformable(String className) { /* * java.lang is normally excluded but we can make an exception if asked */ if (className.startsWith(JAVA_LANG_PACKAGE_PREFIX)) { return transformAll; } return true; } public static void maybeDumpClassIntermediate(String fullName, byte[] bytes) { if (isDumpGeneratedClassesIntermediate()) { dumpClass(fullName, bytes, true); } } public static void maybeDumpClass(String fullName, byte[] bytes) { if (isDumpGeneratedClasses()) { dumpClass(fullName, bytes); } } /* implementation */ /** * The routine which actually does the real bytecode transformation. this is public because it needs to be * callable from the type checker script. In normal running the javaagent is the only class which has a handle * on the registered transformer so it is the only one which can reach this point. * @param ruleScript the script * @param loader the loader of the class being injected into * @param className the name of the class being injected into * @param targetClassName the name of the class which matched the rule * @param targetClassBytes the current class bytecode * @return the transformed bytecode or NULL if no transform was applied */ public byte[] transform(RuleScript ruleScript, ClassLoader loader, String className, String targetClassName, byte[] targetClassBytes) { TransformContext transformContext = new TransformContext(this, ruleScript, className, targetClassName, loader, helperManager, accessEnabler); return transformContext.transform(targetClassBytes); } /** * check whether a class should not be considered for transformation * @param clazz the class to check * @return true if clazz should not be considered for transformation otherwise false */ protected boolean isSkipClass(Class clazz) { if (!inst.isModifiableClass(clazz)) { return true; } // we can safely skip array classes, interfaces and primitive classes if (clazz.isArray()) { return true; } if (clazz.isInterface()) { return true; } if (clazz.isPrimitive()) { return true; } String name = clazz.getName(); if (isBytemanClass(name) || !isTransformable(name)) { return true; } return false; } private byte[] tryTransform(byte[] buffer, String name, ClassLoader loader, String key, String fullKey, boolean isInterface, boolean isOverride) { List ruleScripts; if (isInterface) { ruleScripts = scriptRepository.scriptsForInterfaceName(key); } else { ruleScripts = scriptRepository.scriptsForClassName(key); } byte[] newBuffer = buffer; if (ruleScripts != null) { // Helper.verbose("tryTransform : " + name + " for " + key); int counter = 0; for (RuleScript ruleScript : ruleScripts) { try { // we only transform via isOverride rules if isOverride is true // we transform via any matching rules if isOverride is false if (!isOverride || ruleScript.isOverride()) { // only do the transform if the script has not been deleted synchronized (ruleScript) { if (!ruleScript.isDeleted()) { maybeDumpClassIntermediate(name, newBuffer); newBuffer = transform(ruleScript, loader, name, fullKey, newBuffer); } } } } catch (Throwable th) { // yeeeurgh I know this looks ugly with no rethrow but it is appropriate // we do not want to pass on any errors or runtime exceptions // if a transform fails then we should still allow the load to continue // with whatever other transforms succeed. we tarce the throwable to // System.err just to ensure it can be seen. Helper.err("Transformer.transform : caught throwable " + th); Helper.errTraceException(th); } } } return newBuffer; } protected void dumpScript(RuleScript ruleScript) { String file = ruleScript.getFile(); int line = ruleScript.getLine(); if (file != null) { Helper.out("# " + file + " line " + line); } Helper.out("RULE " + ruleScript.getName()); if (ruleScript.isInterface()) { Helper.out("INTERFACE " + ruleScript.getTargetClass()); } else { Helper.out("CLASS " + ruleScript.getTargetClass()); } Helper.out("METHOD " + ruleScript.getTargetMethod()); if (ruleScript.getTargetHelper() != null) { Helper.out("HELPER " + ruleScript.getTargetHelper()); } Helper.out(ruleScript.getTargetLocation() + ""); Helper.out(ruleScript.getRuleText()); Helper.out("ENDRULE"); } private boolean isTransformed(Class clazz, String name, boolean isInterface) { if (isBytemanClass(name) || !isTransformable(name)) { return false; } boolean found = false; List scripts; if (isInterface) { scripts = scriptRepository.scriptsForInterfaceName(name); } else { scripts = scriptRepository.scriptsForClassName(name); } if (scripts != null) { for (RuleScript script : scripts) { if (script.hasTransform(clazz)) { found = true; Helper.verbose("Retransforming loaded bootstrap class " + clazz.getName()); break; } } } return found; } /** * return a checker object which can be used to retrieve the super and interfaces of a class from its defining bytecode * @param bytecode * @return a checker */ private org.jboss.byteman.agent.check.ClassChecker getClassChecker(byte[] bytecode) { return new org.jboss.byteman.agent.check.BytecodeChecker(bytecode); } /** * return a checker object which can be used to retrieve the super and interfaces of a class from its name and * classloader without forcing a load of the class. * * @param name the name of the superclass being checked * @param baseLoader the class loader of the subclass's bytecode * @return the requisite checker or null if the class does not need to be checked or cannot be loaded */ public org.jboss.byteman.agent.check.ClassChecker getClassChecker(String name, ClassLoader baseLoader) { // when looking up the super of a class that is currently being loaded and considered for // transformation we would like to just do this // // Class superClazz = classBeingLoaded.getSuper(); // // or perhaps we might obtain the superName from the bytecode and then try this // // Class superClazz = baseLoader.loadClass(superName) // // We could then then access details of the super using reflection and so on (ditto for interfaces). // // however, both the above options are a FAIL! We are in the middle of transforming the subclass and // it may not be fully resolved. In particular, that means that the JVM may not even have started // loading the super or implemented interfaces (yes, rilly!). if we force a load, whether implicitly // via the reflection API or explicitly by calling loadClass, then transforms will not be performed // on that recursively loaded class. this may cause us to miss the chance to apply rule injection into // classes in the super chain or into other implementors of the interface. // so, instead we must load the bytecode as a resource - user-defined loaders may not support this but // at least the JVM system and boot loaders should. As a performance optimization we use a cache to // retain checker objects derived from the bytecode. if we have already tried to resolve the named class // via this loader we can reuse the checker, avoiding a reload of the class bytes as a resource. // unfortunately, we normally have to load and then cache the super/interface resource using the loader // of the subclass/implementor. That may mean that we end up with multiple checkers for the same class. // To see why, assume classes A and B derive, respectively, from loaders CL_A and CL_B and both inherit // from class C defined by a third loader CL_C. If we load the class bytes for C from CL_A we will store // a checker for them keyed under loader CL_A and name "C". We can detect that CL_A and CL_B both have // CL_C as a parent. However, there is normally no way of knowing that the bytes we obtained were provided // by CL_A or CL_C. So, when we try to resolve C as the super of B via CL_B we cannot safely re-use the // bytes loaded via CL_A. Instead we have to resort to reloading the bytes for C via CL_B and storing a // new checker keyed under loader CL_B and name "C". // // There is one further optimization available. If the class name starts with "java." then it can only be // loaded via the bootstrap loader (even when it is being resolved by some other baseLoader). In that case // the class bytes can only come from the bootstrap loader. So, a checker created for any lookup will suffice // for all lookups. In this case, we can always do the cache lookup, resource load and cache update relative // to the bootstrap loader by ignoring the supplied base loader and passing null instead. if(name.startsWith("java.")) { baseLoader = null; } // if a checker is in cache then just reuse it BytecodeChecker checker = checkerCache.lookup(baseLoader, name); if (checker != null) { return checker; } // ok, we have to look up the class details String resourceName = name.replace('.', '/') + ".class"; try { InputStream is; // use the system loader if the supplied loader is null // the system loader will delegate to the bootstrap loader if (baseLoader == null || resourceName.startsWith("java.")) { is = ClassLoader.getSystemClassLoader().getResourceAsStream(resourceName); } else { is = baseLoader.getResourceAsStream(resourceName); } if (is != null) { int length = is.available(); int count = 0; byte[] bytecode = new byte[length]; while (count < length) { int read = is.read(bytecode, count, length - count); if (read < 0) { throw new IOException("unexpected end of file"); } count += read; } checker = new org.jboss.byteman.agent.check.BytecodeChecker(bytecode); checkerCache.put(baseLoader, name, checker); return checker; } else { // throw new IOException("unable to load bytecode for for class " + name); Helper.verbose("Transformer.getClassChecker : unable to load bytecode for for class " + name); return null; } } catch (IOException e) { // log the exception and return null Helper.errTraceException(e); return null; } } /** * hash set naming blacklisted methods we refuse to inject into */ private HashSet blacklisted = initBlackList(); /** * init method to create hash set naming blacklisted methods we refuse to inject into * * @return the hash set */ private HashSet initBlackList() { HashSet set = new HashSet(); set.add("java.lang.Object."); set.add("java.lang.ThreadLocal.get"); set.add("java.lang.ThreadLocal.put"); return set; } /** * check whether we are unwilling to inject into a given target method * * @param triggerClassName the name of the target class * @param targetMethodName the name of the target method * @param targetDescriptor the descriptor of the target method ignored at present * @return true if we are unwilling to inject into the target method */ public boolean isBlacklisted(String triggerClassName, String targetMethodName, String targetDescriptor) { // return blacklisted.contains(triggerClassName + "." + targetMethodName); } /** * classloader used by transformer when verification is switched on to detect errors in transformed bytecode */ private class VerifyLoader extends ClassLoader { public VerifyLoader(ClassLoader parent) { super(parent); } /** * use the supplied bytes to define a class and try creating an instance via the empty constructor * printing details of any errors which occur * @param classname * @param protectionDomain * @param bytes * @return the bytes if all goes well otherwise null */ public byte[] verify(String classname, ProtectionDomain protectionDomain, byte[] bytes) { try { Class clazz = super.defineClass(classname, bytes, 0, bytes.length, protectionDomain); clazz.newInstance(); } catch (Throwable th) { Helper.err("Transformer:verifyTransformedBytes " + th); Helper.errTraceException(th); return null; } return bytes; } } /** * return the result from calling verifyTransformedBytes if verification is enabled otherwise just return * the supplied bytecode * @param loader * @param classname * @param protectionDomain * @param bytes * @return the verified bytecode or the original */ private byte[] maybeVerifyTransformedBytes(ClassLoader loader, String classname, ProtectionDomain protectionDomain, byte[] bytes) { if (verifyTransformedBytes) { return verifyTransformedBytes(loader, classname, protectionDomain, bytes); } else { return bytes; } } /** * verify the supplied bytecode by converting it to a class and calling newInstance with no args to instantiate. * since not all transformed classes have an empty constructor this should only be enabled for testing of Byteman * itself in cases where a transformed class is known to have an empty constructor. * @param loader * @param classname * @param protectionDomain * @param bytes * @return the supplied bytecode if verification succeeds or null if it fails */ private byte[] verifyTransformedBytes(ClassLoader loader, String classname, ProtectionDomain protectionDomain, byte[] bytes) { VerifyLoader verifyLoader = new VerifyLoader(loader); return verifyLoader.verify(classname, protectionDomain, bytes); } /** * test whether a class with a given name is located in the byteman package * @param className the name to be checked * @return true if a class is located in the byteman package otherwise return false */ public static boolean isBytemanClass(String className) { return className.startsWith(BYTEMAN_PACKAGE_PREFIX) && !className.startsWith(BYTEMAN_TEST_PACKAGE_PREFIX) && !className.startsWith(BYTEMAN_SAMPLE_PACKAGE_PREFIX); } /** * the instrumentation interface to the JVM */ protected final Instrumentation inst; /** * an object we use to enable access to reflective fields where needed */ AccessEnabler accessEnabler; /** * true if the instrumentor allows redefinition */ protected boolean isRedefine; /** * a mapping from target class names which appear in rules to a script object holding the * rule details */ protected final ScriptRepository scriptRepository; protected final CheckerCache checkerCache; /** * a manager for helper lifecycle events which can be safely handed on to rules */ protected final HelperManager helperManager; /* configuration values defined via system property settings */ /** * switch to control verbose output during rule processing */ private static boolean verbose = computeVerbose(); /** * switch to control control flow graph output during rule processing */ private static boolean dumpCFGPartial = computeDumpCFGPartial(); /** * switch to control control flow graph output during rule processing */ private static boolean dumpCFG = computeDumpCFG(); /** * switch to control debug output during rule processing */ private static boolean debug = computeDebug(); /** * switch to control whether rules are compiled to bytecode or not */ private static boolean compileToBytecode = computeCompileToBytecode(); /** * switch to control whether rules are injected into overriding methods */ private static boolean skipOverrideRules = computeSkipOverrideRules(); /** * switch to control dumping of generated bytecode to .class files */ private static boolean dumpGeneratedClasses = computeDumpGeneratedClasses(); /** * switch to control dumping of generated bytecode to .class files */ private static boolean dumpGeneratedClassesIntermediate = computeDumpGeneratedClassesIntermediate(); /** * directory in which to dump generated bytecode .class files (defaults to "." */ private static String dumpGeneratedClassesDir = computeDumpGeneratedClassesDir(); /** * switch to control whether transformations will be applied to java.lang.* classes */ private static boolean transformAll = computeTransformAll(); /** * switch to control whether we attempt to verify transformed bytecode before returning it by * consructing a temporary class from it. */ private static boolean verifyTransformedBytes = computeVerifyTransformedBytes(); /** * switch which determines whether downcasts in binding initialisations are disallowed */ private static boolean disallowDowncast = computeDisallowDowncast(); /** * master switch which determines whether or not config values can be updated */ private static boolean allowConfigUpdate = (System.getProperty(ALLOW_CONFIG_UPDATE) != null); /** * lock object used to control getters and setters when allowConfigUpdate is true */ private static Object configLock = new Object(); /* methods which compute values to be used for the verbose configuration setting */ private static boolean computeVerbose() { return System.getProperty(VERBOSE) != null; } private static boolean computeDumpCFGPartial() { return System.getProperty(DUMP_CFG_PARTIAL) != null; } private static boolean computeDumpCFG() { return System.getProperty(DUMP_CFG) != null || System.getProperty(DUMP_CFG_PARTIAL) != null; } private static boolean computeDebug() { return System.getProperty(DEBUG) != null; } private static boolean computeCompileToBytecode() { return System.getProperty(COMPILE_TO_BYTECODE) != null || System.getProperty(COMPILE_TO_BYTECODE_COMPATIBILITY) != null; } private static boolean computeSkipOverrideRules() { return System.getProperty(SKIP_OVERRIDE_RULES) != null; } public static boolean computeDumpGeneratedClasses() { return System.getProperty(DUMP_GENERATED_CLASSES) != null; } public static boolean computeDumpGeneratedClassesIntermediate() { return System.getProperty(DUMP_GENERATED_CLASSES_INTERMEDIATE) != null; } public static String computeDumpGeneratedClassesDir() { String userDir = System.getProperty(DUMP_GENERATED_CLASSES_DIR); if (userDir != null) { File userFile = new File(userDir); if (userFile.exists() && userFile.isDirectory() && userFile.canWrite()) { return userDir; } else { return "."; } } else { return "."; } } private static boolean computeTransformAll() { return System.getProperty(TRANSFORM_ALL) != null || System.getProperty(TRANSFORM_ALL_COMPATIBILITY) != null; } private static boolean computeVerifyTransformedBytes() { return System.getProperty(VERIFY_TRANSFORMED_BYTES) != null; } private static boolean computeDisallowDowncast() { return (System.getProperty(DISALLOW_DOWNCAST) != null); } private void checkConfiguration(String property) { // n.b. this needs to be kept up to date with each new config setting that is added if (VERBOSE.equals(property)) { boolean value = computeVerbose(); synchronized (configLock) { verbose = value; } return; } /* * hmm. don't think we want to allow this to be overridden if (DUMP_CFG_PARTIAL.equals(property)) { boolean value = computeDumpCFGPartial(); boolean value2 = computeDumpCFG(); synchronized (configLock) { dumpCFGPartial = value; dumpCFG = value2; } return; } */ /* * hmm. don't think we want to allow this to be overridden if (DUMP_CFG.equals(property)) { boolean value = computeDumpCFG(); synchronized (configLock) { dumpCFG = value; } return; } */ if (DEBUG.equals(property)) { boolean value = computeDebug(); synchronized (configLock) { debug = value; } return; } // n.b. this deliberately cannot be mixed with the old compatibility property -- user beware! if (COMPILE_TO_BYTECODE.equals(property)) { boolean value = computeCompileToBytecode(); synchronized (configLock) { compileToBytecode = value; } } /* * hmm. don't think we want to allow this to be overridden if (SKIP_OVERRIDE_RULES.equals(property)) { boolean value = computeSkipOverrideRules(); synchronized (configLock) { skipOverrideRules = value; } return; } */ if (DUMP_GENERATED_CLASSES.equals(property)) { boolean value = computeDumpGeneratedClasses(); synchronized (configLock) { dumpGeneratedClasses = value; } } if (DUMP_GENERATED_CLASSES_DIR.equals(property)) { String value = computeDumpGeneratedClassesDir(); synchronized (configLock) { dumpGeneratedClassesDir = value; } } if (DUMP_GENERATED_CLASSES_INTERMEDIATE.equals(property)) { boolean value = computeDumpGeneratedClassesIntermediate(); synchronized (configLock) { dumpGeneratedClassesIntermediate = value; } } if (TRANSFORM_ALL.equals(property)) { boolean value = computeTransformAll(); synchronized (configLock) { transformAll = value; } } if (DISALLOW_DOWNCAST.equals(property)) { boolean value = computeDisallowDowncast(); synchronized (configLock) { disallowDowncast = value; } } } /* helper methods to dump class files */ private static void dumpClass(String fullName, byte[] bytes) { dumpClass(fullName, bytes, false); } private static void dumpClass(String fullName, byte[] bytes, boolean intermediate) { // wrap this in a try catch in case the file i/o code generates a runtime exception // this may happen e.g. because of a security restriction try { int dotIdx = fullName.lastIndexOf('.'); String name = (dotIdx < 0 ? fullName : fullName.substring(dotIdx + 1)); String prefix = (dotIdx > 0 ? File.separator + fullName.substring(0, dotIdx) : ""); String dir = getDumpGeneratedClassesDir() + prefix.replace('.', File.separatorChar); if (!ensureDumpDirectory(dir)) { Helper.err("org.jboss.byteman.agent.Transformer : Cannot dump transformed bytes to directory " + dir + File.separator + prefix); return; } String newname; if (intermediate) { int counter = 0; // add _ prefix until we come up with a new name newname = dir + File.separator + name + "_" + counter + ".class"; File file = new File(newname); while (file.exists()) { counter++; newname = dir + File.separator + name + "_" + counter + ".class"; file = new File(newname); } } else { newname = dir + File.separator + name + ".class"; } Helper.out("org.jboss.byteman.agent.Transformer : Saving transformed bytes to " + newname); try { FileOutputStream fio = new FileOutputStream(newname); fio.write(bytes); fio.close(); } catch (IOException ioe) { Helper.err("Error saving transformed bytes to" + newname); Helper.errTraceException(ioe); } } catch (Throwable th) { Helper.err("org.jboss.byteman.agent.Transformer : Error saving transformed bytes for class " + fullName); Helper.errTraceException(th); } } private static boolean ensureDumpDirectory(String fileName) { File file = new File(fileName); if (file.exists()) { return (file.isDirectory() && file.canWrite()); } else { return file.mkdirs(); } } private void setAgentVersion() throws Exception { if (inst == null) { // we are just doing rule checks so no // need to bother with this return; } String version = System.getProperty(AGENT_VERSION); if (version != null && !version.equals("")) { throw new Exception("Transformer.setAgentVersion: Byteman agent version already set!"); } String className = this.getClass().getSimpleName() + ".class"; String classPath = this.getClass().getResource(className).toString(); if (classPath.startsWith("file")) { // this happens during building -- just ignore return; } else if (!classPath.startsWith("jar")) { throw new Exception("Transformer.setAgentVersion: could not find manifest for agent jar"); } try { String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF"; Manifest manifest = new Manifest(new URL(manifestPath).openStream()); version = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); } catch (IOException E) { // drop through } if (version == null || version.equals("")) { throw new Exception("Transformer.setAgentVersion: could not find manifest implementation version for byteman agent jar"); } System.setProperty(AGENT_VERSION, version); } /** * Thread local holding a per thread Boolean which is true if triggering is disabled and false if triggering is * enabled */ private static ThreadLocal isEnabled = new ThreadLocal(); private final static Integer DISABLED_USER = Integer.valueOf(0); private final static Integer DISABLED = Integer.valueOf(1); private final static Integer ENABLED = null; } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/adapter/000077500000000000000000000000001414767246600251025ustar00rootroot00000000000000byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/adapter/BMInsnList.java000066400000000000000000000077451414767246600277440ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2010 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent.adapter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.LocalVariableNode; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** */ public class BMInsnList extends InsnList { // the local variables list managed by the method node which owns this instruction list List localvariables; public BMInsnList(List localVariables) { this.localvariables = localVariables; } public void accept(final MethodVisitor mv) { // this method visitor must implement LocalScopeMethodVisitor or else we would not be here LocalScopeMethodVisitor lsmv = (LocalScopeMethodVisitor) mv; // first index all local vars by start and end label HashMap> localStarts = new HashMap>(); HashMap> localEnds = new HashMap>(); Iterator iterator = localvariables.iterator(); while (iterator.hasNext()) { LocalVariableNode local = (LocalVariableNode)iterator.next(); Label label = local.start.getLabel(); LinkedList locals = localStarts.get(label); if (locals == null) { locals = new LinkedList(); localStarts.put(label, locals); } locals.addLast(local); label = local.end.getLabel(); locals = localEnds.get(label); if (locals == null) { locals = new LinkedList(); localEnds.put(label, locals); } locals.addLast(local); } // now visit the instructions intercepting labels AbstractInsnNode insn = getFirst(); while (insn != null) { insn.accept(mv); if (insn.getType() == AbstractInsnNode.LABEL) { LabelNode labelNode = (LabelNode) insn; Label label = labelNode.getLabel(); List localStart = localStarts.get(label); List localEnd = localEnds.get(label); if (localStart != null) { for (LocalVariableNode local : localStart) { lsmv.visitLocalScopeStart(local.name, local.desc, local.signature, local.index, label.getOffset()); } } if (localEnd != null) { for (LocalVariableNode local : localEnd) { lsmv.visitLocalScopeEnd(local.name, local.desc, local.signature, local.index, label.getOffset()); } } } insn = insn.getNext(); } } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/adapter/BMJSRInliner.java000066400000000000000000000033401414767246600301430ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2010 Red Hat individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent.adapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; /** * a modified version of JSRInliner which uses a slightly modified version of JSRInlinerAdapter * to ensure that local variable scopes are notified during code visits */ public class BMJSRInliner extends ClassVisitor { public BMJSRInliner(ClassVisitor cv) { super(RuleAdapter.ASM_VERSION, cv); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); return new BMJSRInlinerAdapter(mv, access, name, desc, signature, exceptions); } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/adapter/BMJSRInlinerAdapter.java000066400000000000000000000034141414767246600314460ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2010 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent.adapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.commons.JSRInlinerAdapter; /** * a subclass of JSRInlinerAdapter which pushes local variable info through to the next * adapter inline during code generation if it wants it */ public class BMJSRInlinerAdapter extends JSRInlinerAdapter { public BMJSRInlinerAdapter(MethodVisitor mv, int access, String name, String desc, String signature, String[] exceptions) { super(RuleAdapter.ASM_VERSION, mv, access, name, desc, signature, exceptions); if (mv instanceof LocalScopeMethodVisitor) { // replace the instruction list so that it generates the required start and end local scope calls instructions = new BMInsnList(localVariables); } } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/adapter/BMLocalScopeAdapter.java000066400000000000000000000036071414767246600315170ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2010 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent.adapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; /** * a class adapter which uses * to ensure that local variable scopes are notified during code visits */ public class BMLocalScopeAdapter extends ClassVisitor { public BMLocalScopeAdapter(ClassVisitor cv) { super(RuleAdapter.ASM_VERSION, cv); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); // only use the local scope adapter if we need to -- avoids creating unnecessary method nodes if (mv instanceof LocalScopeMethodVisitor) { return new BMLocalScopeMethodAdapter(mv, access, name, desc, signature, exceptions); } else { return mv; } } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/adapter/BMLocalScopeMethodAdapter.java000066400000000000000000000047771414767246600326710ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2010 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent.adapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.tree.MethodNode; /** * a subclass of JSRInlinerAdapter which pushes local variable info through to the next * adapter inline during code generation if it wants it */ public class BMLocalScopeMethodAdapter extends MethodNode { private MethodVisitor mv; /** * creates a method node with an instruction list which notifies local var scope start and end * events. should only be called with a method visitor which is an instance of LocalScopeMethodVisitor * @param mv the current method visitor * @param access bitmask of method access permissions * @param name the method name * @param desc the method descriptor * @param signature the unerased method type signature * @param exceptions names of exceptions thrown by the method */ public BMLocalScopeMethodAdapter(MethodVisitor mv, int access, String name, String desc, String signature, String[] exceptions) { super(RuleAdapter.ASM_VERSION, access, name, desc, signature, exceptions); this.mv = mv; if (mv instanceof LocalScopeMethodVisitor) { // replace the instruction list so that it generates the required start and end local scope calls instructions = new BMInsnList(localVariables); } } /** * once we have seen all the opcodes we can push the stored method tree through the next visitor in line */ public void visitEnd() { accept(mv); } } byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/adapter/EntryCheckAdapter.java000066400000000000000000000054271414767246600313150ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2008-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent.adapter; import org.objectweb.asm.*; import org.jboss.byteman.agent.TransformContext; /** * asm Adapter class used to check that the target method for a rule exists in a class */ public class EntryCheckAdapter extends RuleCheckAdapter { public EntryCheckAdapter(ClassVisitor cv, TransformContext transformContext) { super(cv, transformContext); } public MethodVisitor visitMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if (matchTargetMethod(access, name, desc)) { setVisited(); return new EntryCheckMethodAdapter(mv, getTransformContext(), access, name, desc, signature, exceptions); } return mv; } /** * a method visitor used to add a rule event trigger call to a method */ private class EntryCheckMethodAdapter extends RuleCheckMethodAdapter { private int access; private String name; private String descriptor; private String signature; private String[] exceptions; EntryCheckMethodAdapter(MethodVisitor mv, TransformContext transformContext, int access, String name, String descriptor, String signature, String[] exceptions) { super(mv, transformContext, access, name, descriptor); this.access = access; this.name = name; this.descriptor = descriptor; this.signature = signature; this.exceptions = exceptions; } public void visitCode() { // any instruction counts as a trigger point setTriggerPoint(); } } }byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/adapter/EntryTriggerAdapter.java000066400000000000000000000222771414767246600317050ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2008-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent.adapter; import org.jboss.byteman.agent.TransformContext; import org.objectweb.asm.*; /** * asm Adapter class used to add a rule event trigger call to a method of som egiven class */ public class EntryTriggerAdapter extends RuleTriggerAdapter { public EntryTriggerAdapter(ClassVisitor cv, TransformContext transformContext) { super(cv, transformContext); } public MethodVisitor visitMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if (injectIntoMethod(name, desc)) { if (name.equals("")) { return new EntryTriggerConstructorAdapter(mv, getTransformContext(), access, name, desc, signature, exceptions); } else { return new EntryTriggerMethodAdapter(mv, getTransformContext(), access, name, desc, signature, exceptions); } } return mv; } /** * a method visitor used to add a rule event trigger call to a method */ private class EntryTriggerMethodAdapter extends RuleTriggerMethodAdapter { protected boolean unlatched; /** * flag which says whether a trigger has been injected into this method */ private boolean visited; /** * label generated before in */ private Label startMarker; private Label offsetMarker; EntryTriggerMethodAdapter(MethodVisitor mv, TransformContext transformContext, int access, String name, String descriptor, String signature, String[] exceptions) { super(mv, transformContext, access, name, descriptor, signature, exceptions); this.unlatched = true; // subclass can manipulate this to postponne visit visited = false; startMarker = null; offsetMarker = null; } // if possible inject a trigger as soon as we visit the code. This will precede any visit to labels defined // by the original program -- in particular the loop label for a while loop occuring at the start of the // method body. if we don't do this then we can end up injecting the trigger into the body of the while // loop @Override public void visitCode() { // call the super method first so we have a valid CFG and start label super.visitCode(); if (unlatched && !visited) { // if we inject here we keep markers for the original // and offset start of real code. that allows us to identify // parameters whose start pos has been pushed forward to the // end marker and reset it to the start marker startMarker = new Label(); visitLabel(startMarker); visited = true; injectTriggerPoint(); offsetMarker = new Label(); visitLabel(offsetMarker); } } @Override public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { if (offsetMarker != null && (offsetMarker.getOffset() == start.getOffset())) { // this local wil be a parameter whose start // offset was originally 0 and now has been // offset by the inejcted AT ENTRY code // passing startMarker will reset it's start to 0 start = startMarker; } super.visitLocalVariable(name, desc, signature, start, end, index); } // we will not be able to inject into a constructor at visitCode because we need to delay until // we have run the super constructor. so we also need to override each visitXXXINsn operation // and inject a trigger point as soon as possible after the constructor unlatches the adapter. @Override public void visitInsn(int opcode) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitInsn(opcode); } @Override public void visitIincInsn(int var, int increment) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitIincInsn(var, increment); } @Override public void visitIntInsn(int opcode, int operand) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitIntInsn(opcode, operand); } @Override public void visitLdcInsn(Object cst) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitLdcInsn(cst); } @Override public void visitVarInsn(int opcode, int var) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitVarInsn(opcode, var); } @Override public void visitTypeInsn(int opcode, String desc) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitTypeInsn(opcode, desc); } @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitFieldInsn(opcode, owner, name, desc); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitMethodInsn(opcode, owner, name, desc, itf); } @Override public void visitJumpInsn(int opcode, Label label) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitJumpInsn(opcode, label); } @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitTableSwitchInsn(min, max, dflt, labels); } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitLookupSwitchInsn(dflt, keys, labels); } @Override public void visitMultiANewArrayInsn(String desc, int dims) { if (unlatched && !visited) { visited = true; injectTriggerPoint(); } super.visitMultiANewArrayInsn(desc, dims); } } /** * a method visitor used to add a rule event trigger call to a constructor -- this has to make sure * the super constructor has been called before allowing a trigger call to be compiled */ private class EntryTriggerConstructorAdapter extends EntryTriggerMethodAdapter { EntryTriggerConstructorAdapter(MethodVisitor mv, TransformContext transformContext, int access, String name, String descriptor, String signature, String[] exceptions) { super(mv, getTransformContext(), access, name, descriptor, signature, exceptions); this.unlatched = false; } public void visitMethodInsn( final int opcode, final String owner, final String name, final String desc, final boolean itf) { super.visitMethodInsn(opcode, owner, name, desc, itf); unlatched |= isSuperOrSiblingConstructorCall(opcode, owner, name); } } }byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/adapter/ExceptionExitCheckAdapter.java000066400000000000000000000054441414767246600330030ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2008-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent.adapter; import org.objectweb.asm.*; import org.jboss.byteman.agent.TransformContext; /** * asm Adapter class used to check that the target method for a rule exists in a class */ public class ExceptionExitCheckAdapter extends RuleCheckAdapter { public ExceptionExitCheckAdapter(ClassVisitor cv, TransformContext transformContext) { super(cv, transformContext); } public MethodVisitor visitMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if (matchTargetMethod(access, name, desc)) { setVisited(); return new ExceptionExitCheckMethodAdapter(mv, getTransformContext(), access, name, desc, signature, exceptions); } return mv; } /** * a method visitor used to add a rule event trigger call to a method */ private class ExceptionExitCheckMethodAdapter extends RuleCheckMethodAdapter { private int access; private String name; private String descriptor; private String signature; private String[] exceptions; ExceptionExitCheckMethodAdapter(MethodVisitor mv, TransformContext transformContext, int access, String name, String descriptor, String signature, String[] exceptions) { super(mv, transformContext, access, name, descriptor); this.access = access; this.name = name; this.descriptor = descriptor; this.signature = signature; this.exceptions = exceptions; } public void visitEnd() { setTriggerPoint(); super.visitEnd(); } } }byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/adapter/ExceptionExitTriggerAdapter.java000066400000000000000000000107101414767246600333610ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2008-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Gary Brown */ package org.jboss.byteman.agent.adapter; import org.jboss.byteman.agent.TransformContext; import org.jboss.byteman.rule.type.TypeHelper; import org.objectweb.asm.*; /** * asm Adapter class used to add a rule event trigger call to a method of some given class */ public class ExceptionExitTriggerAdapter extends RuleTriggerAdapter { public ExceptionExitTriggerAdapter(ClassVisitor cv, TransformContext transformContext) { super(cv, transformContext); } public MethodVisitor visitMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if (injectIntoMethod(name, desc)) { if (name.equals("")) { return new ExceptionExitTriggerConstructorAdapter(mv, getTransformContext(), access, name, desc, signature, exceptions); } else { return new ExceptionExitTriggerMethodAdapter(mv, getTransformContext(), access, name, desc, signature, exceptions); } } return mv; } /** * a method visitor used to add a rule event trigger call to a method */ private class ExceptionExitTriggerMethodAdapter extends RuleTriggerMethodAdapter { private Label newStart; ExceptionExitTriggerMethodAdapter(MethodVisitor mv, TransformContext transformContext, int access, String name, String descriptor, String signature, String[] exceptions) { super(mv, transformContext, access, name, descriptor, signature, exceptions); } @Override public void visitCode() { // call the super method first so we have a valid CFG and start label super.visitCode(); newStart = newLabel(); visitLabel(newStart); } @Override public void visitMaxs(int maxStack, int maxLocals) { /** NEED TO CREATE CATCH HANDLER FOR THROWABLE THAT COVERS COMPLETE METHOD * */ Label newEnd=newLabel(); Label throwableCatch = newLabel(); visitTryCatchBlock(newStart, newEnd, throwableCatch, Type.getType(TypeHelper.externalizeType("java.lang.Throwable")).getInternalName()); visitLabel(newEnd); visitLabel(throwableCatch); injectTriggerPoint(); throwException(); super.visitMaxs(maxStack, maxLocals); } } /** * a method visitor used to add a rule event trigger call to a constructor -- this has to make sure * the super constructor has been called before allowing a trigger call to be compiled */ private class ExceptionExitTriggerConstructorAdapter extends ExceptionExitTriggerMethodAdapter { ExceptionExitTriggerConstructorAdapter(MethodVisitor mv, TransformContext transformContext, int access, String name, String descriptor, String signature, String[] exceptions) { super(mv, getTransformContext(), access, name, descriptor, signature, exceptions); } public void visitMethodInsn( final int opcode, final String owner, final String name, final String desc, final boolean itf) { super.visitMethodInsn(opcode, owner, name, desc, itf); } } }byteman-4.0.18/agent/src/main/java/org/jboss/byteman/agent/adapter/ExitCheckAdapter.java000066400000000000000000000135601414767246600311220ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source * Copyright 2009-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.agent.adapter; import org.objectweb.asm.*; import org.jboss.byteman.rule.Rule; import org.jboss.byteman.agent.RuleScript; import org.jboss.byteman.agent.TransformContext; import java.util.Vector; /** * asm Adapter class used to check that the target method for a rule exists in a class */ public class ExitCheckAdapter extends RuleCheckAdapter { public ExitCheckAdapter(ClassVisitor cv, TransformContext transformContext) { super(cv, transformContext); this.earlyReturnHandlers = new Vector