class ADSL::Extract::Rails::ActionInstrumenter
Attributes
action_block[RW]
Public Class Methods
extract_stmt_from_expr(expr, method_name=nil)
click to toggle source
# File lib/adsl/extract/rails/action_instrumenter.rb, line 170 def self.extract_stmt_from_expr(expr, method_name=nil) adsl_ast = expr adsl_ast = expr.adsl_ast if adsl_ast.respond_to? :adsl_ast return nil unless adsl_ast.is_a? ::ADSL::Parser::ASTNode return adsl_ast if adsl_ast.class.is_statement? return ::ADSL::Parser::ASTObjsetStmt.new :objset => adsl_ast if adsl_ast.class.is_objset? nil end
new(ar_class_names, instrument_domain = Dir.pwd)
click to toggle source
Calls superclass method
ADSL::Extract::Instrumenter::new
# File lib/adsl/extract/rails/action_instrumenter.rb, line 218 def initialize(ar_class_names, instrument_domain = Dir.pwd) super instrument_domain @branch_index = 0 # remove respond_to and render render_stmts = [:respond_to, :render, :redirect_to, :respond_with] replace :call do |sexp| next sexp unless sexp.length >= 3 and sexp[1].nil? and render_stmts.include?(sexp[2]) s(:call, nil, :ins_mark_render_statement) end replace :iter do |sexp| next sexp unless sexp[1].length >= 3 and sexp[1][0] == :call and sexp[1][1].nil? and render_stmts.include?(sexp[1][2]) s(:call, nil, :ins_mark_render_statement) end # surround the entire method with a call to abb.explore_all_choices replace :defn, :defs do |sexp| header_elem_count = sexp.sexp_type == :defn ? 3 : 4 stmts = sexp.pop(sexp.length - header_elem_count) single_stmt = stmts.length > 1 ? s(:block, *stmts) : stmts.first explore_all = s(:iter, s(:call, nil, :ins_explore_all, s(:lit, sexp[header_elem_count - 2])), s(:args), single_stmt) sexp.push explore_all sexp end # replace raise with ins_do_raise replace :call do |sexp| next sexp unless sexp[2] == :raise s(:call, nil, :ins_do_raise) end # replace returns with ins_do_return replace :return do |sexp| s(:call, nil, :ins_do_return, *sexp.sexp_body) end # instrument assignments replace :lasgn, :iasgn, :cvasgn, :cvdecl, :gasgn, :masgn, :op_asgn_or, :unless_in => [:args, :op_asgn_or] do |sexp| next sexp if sexp.length <= 2 variables_and_prefixes = if sexp.sexp_type == :masgn sexp[1].sexp_body.map{ |asgn_type, var| [asgn_type.to_s[0..-5], var] } elsif sexp.sexp_type == :op_asgn_or [] else [[sexp[0].to_s[0..-5], sexp[1]]] end prepare_assignments = variables_and_prefixes.map{ |prefix, var| s(:op_asgn_or, s("#{prefix}var".to_sym, var), s("#{prefix}asgn".to_sym, var, s(:nil))) } var_names = if sexp.sexp_type == :masgn sexp[1].sexp_body.map{ |var| s(:str, var[1].to_s) }.to_a elsif sexp.sexp_type == :op_asgn_or [s(:str, sexp[1][1].to_s)] else [s(:str, sexp[1].to_s)] end values = if sexp.sexp_type == :masgn sexp[2] elsif sexp.sexp_type == :op_asgn_or s(:array, sexp[2][2]) else s(:array, sexp[2]) end operator = sexp.sexp_type == :op_asgn_or ? s(:str, '||=') : s(:str, '=') s(:block, *prepare_assignments, s(:call, nil, :ins_multi_assignment, s(:call, nil, :binding), s(:array, *var_names), values, operator) ) end # prepend ins_stmt to every non-return or non-if statement replace :defn, :defs, :block, :iter do |sexp| first_stmt_index = case sexp.sexp_type when :defn; 3 when :defs; 4 when :iter; 3 when :block; 1 end (first_stmt_index..sexp.length-1).each do |index| unless [:if, :return].include?(sexp[index].sexp_type) || (sexp[index][1].nil? && [:ins_push_frame, :ins_pop_frame].include?(sexp[index][2])) sexp[index] = s(:call, nil, :ins_stmt, sexp[index]) end end sexp end # instrument branches replace :if do |sexp| if sexp.may_return_or_raise? sexp[1] = s(:call, nil, :ins_branch_choice, sexp[1], s(:lit, @branch_index += 1)) sexp[2] = s(:block, sexp[2]) unless sexp[2].nil? or sexp[2].sexp_type == :block sexp[3] = s(:block, sexp[3]) unless sexp[3].nil? or sexp[3].sexp_type == :block sexp else block1_sexp = sexp[2] || s(:nil) block1 = block1_sexp.sexp_type == :block ? block1_sexp : s(:block, block1_sexp) block2_sexp = sexp[3] || s(:nil) block2 = block2_sexp.sexp_type == :block ? block2_sexp : s(:block, block2_sexp) s(:call, nil, :ins_if, sexp[1], s(:array, s(:call, nil, :ins_push_frame), s(:splat, s(:rescue, s(:array, block1, s(:call, nil, :ins_pop_frame)), s(:resbody, s(:array, s(:const, :Exception)), s(:array, s(:nil), s(:call, nil, :ins_pop_frame)) ) )) ), s(:array, s(:call, nil, :ins_push_frame), s(:splat, s(:rescue, s(:array, block2, s(:call, nil, :ins_pop_frame)), s(:resbody, s(:array, s(:const, :Exception)), s(:array, s(:nil), s(:call, nil, :ins_pop_frame)) ) )) ) ) end end # change rescue into a branch statement replace :rescue do |sexp| resbody = sexp.sexp_body.select{ |a| a.sexp_type == :resbody }.first exception_type_array = s(:array, *resbody[1].sexp_body) res_block = resbody[2..-1] res_block = res_block.length > 1 ? s(:block, *res_block) : res_block.first s(:if, s(:nil), res_block, sexp[1]) end # change attrasgn into a normal call replace :attrasgn do |sexp| s(:call, *sexp.sexp_body) end # make the implicit return explicit replace :defn, :defs do |sexp| make_returns_explicit sexp sexp end # replace calls to collection_action, member_action or page_action with a def # these represent code sugar for defining actions with the activeadmin gem replace :iter do |sexp| next sexp unless ( sexp[1].sexp_type == :call && sexp[1][1] == nil && [:collection_action, :member_action, :page_action].include?(sexp[1][2]) ) s(:defn, sexp[1][3][1], sexp[2], sexp[3]) end end
Public Instance Methods
abb()
click to toggle source
# File lib/adsl/extract/rails/action_instrumenter.rb, line 181 def abb method_locals[:abb] end
create_locals()
click to toggle source
# File lib/adsl/extract/rails/action_instrumenter.rb, line 189 def create_locals { :abb => ActionBlockBuilder.new } end
make_returns_explicit(sexp, last_stmt_index = -1)
click to toggle source
# File lib/adsl/extract/rails/action_instrumenter.rb, line 193 def make_returns_explicit(sexp, last_stmt_index = -1) last_stmt = sexp[last_stmt_index] case last_stmt.sexp_type when :block make_returns_explicit last_stmt, -1 when :if if last_stmt[2].nil? last_stmt[2] = s(:return) else make_returns_explicit last_stmt, 2 end if last_stmt[3].nil? last_stmt[3] = s(:return) else make_returns_explicit last_stmt, 3 end when :ensure make_returns_explicit last_stmt, 1 when :rescue make_returns_explicit last_stmt, 1 else sexp[last_stmt_index] = s(:return, last_stmt) unless last_stmt.sexp_type == :return end end
prev_abb()
click to toggle source
# File lib/adsl/extract/rails/action_instrumenter.rb, line 185 def prev_abb previous_locals[:abb] end
should_instrument?(object, method_name)
click to toggle source
Calls superclass method
ADSL::Extract::Instrumenter#should_instrument?
# File lib/adsl/extract/rails/action_instrumenter.rb, line 388 def should_instrument?(object, method_name) return false unless super klass = object.is_a?(Class) ? object : object.class method = object.method method_name klass.name.match(/^ADSL::.*$/).nil? && !(method.source_location[0] =~ /.*lib\/adsl\/.*/) end