lib/objcthin.rb in objcthin-0.1.0 vs lib/objcthin.rb in objcthin-0.2.0

- old
+ new

@@ -5,27 +5,27 @@ module Objcthin class Command < Thor desc 'findsel','find unused method sel' def findsel(path) - Imp::Objc.find_unused_sel(path) + Imp::UnusedClass.find_unused_sel(path) end desc 'findclass', 'find unused class list' def findclass(path) - Imp::Objc.find_unused_class(path) + Imp::UnusedClass.find_unused_class(path) end desc'version','print version' def version puts Rainbow(Objcthin::VERSION).green end end end module Imp - class Objc + class UnusedClass def self.find_unused_sel(path) check_file_type(path) all_sels = find_impl_methods(path) used_sel = reference_selectors(path) @@ -40,14 +40,10 @@ puts Rainbow('below selector is unused:\n').red puts unused_sel end - def self.find_unused_class(path) - check_file_type(path) - end - def self.check_file_type(path) pathname = Pathname.new(path) unless pathname.exist? raise "#{path} not exit!" end @@ -154,8 +150,110 @@ end end sels end - end end + + +module Imp + class UnusedClass + + def self.check_file_type(path) + pathname = Pathname.new(path) + unless pathname.exist? + raise "#{path} not exit!" + end + + cmd = "/usr/bin/file -b #{path}" + output = `#{cmd}` + + unless output.include?('Mach-O') + raise 'input file not mach-o file type' + end + puts Rainbow('will begin process...').green + pathname + end + + def self.split_segment_and_find(path) + command = "/usr/bin/otool -arch arm64 -V -o #{path}" + output = `#{command}` + + class_list_identifier = 'Contents of (__DATA,__objc_classlist) section' + class_refs_identifier = 'Contents of (__DATA,__objc_classrefs) section' + + unless output.include? class_list_identifier + raise Rainbow('only support iphone target, please use iphone build...').red + end + + patten = /Contents of \(.*\) section/ + class_refs_patten = /^\d*\w*\s(0x\d*\w*).*/ + + class_list = [] + class_refs = [] + + can_add_to_list = false + can_add_to_refs = false + + output.each_line do |line| + if patten.match?(line) + if line.include? class_list_identifier + can_add_to_list = true + next + elsif line.include? class_refs_identifier + can_add_to_list = false + can_add_to_refs = true + else + break + end + end + + if can_add_to_list + class_list << line + end + + if can_add_to_refs && line + class_refs_patten.match(line) do |m| + class_refs << m[1] + end + end + end + + + class_list_address_patten = /^(\d*\w*)\s(0x\d*\w*)/ + class_name_patten = /name\s0x\d*\w*\s(.*)/ + + current_key = nil + class_name_address_hash = {} + + class_list.each do |line| + if class_list_address_patten.match?(line) + current_key = class_list_address_patten.match(line)[2] + end + + if class_name_patten.match?(line) && current_key + value = class_name_patten.match(line)[1] + class_name_address_hash[current_key] = value + current_key = nil + end + end + + result = class_name_address_hash + class_refs.each do |line| + if class_name_address_hash.keys.include?(line) + result.delete(line) + end + end + + result + end + + def self.find_unused_class(path) + check_file_type(path) + result = split_segment_and_find(path) + puts result.values + end + + end + +end \ No newline at end of file