looksee 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,142 @@
1
+ /**********************************************************************
2
+
3
+ method.h -
4
+
5
+ $Author: tmm1 $
6
+ created at: Wed Jul 15 20:02:33 2009
7
+
8
+ Copyright (C) 2009 Koichi Sasada
9
+
10
+ **********************************************************************/
11
+ #ifndef METHOD_H
12
+ #define METHOD_H
13
+
14
+ #include "internal.h"
15
+
16
+ #ifndef END_OF_ENUMERATION
17
+ # if defined(__GNUC__) &&! defined(__STRICT_ANSI__)
18
+ # define END_OF_ENUMERATION(key)
19
+ # else
20
+ # define END_OF_ENUMERATION(key) END_OF_##key##_PLACEHOLDER = 0
21
+ # endif
22
+ #endif
23
+
24
+ typedef enum {
25
+ NOEX_PUBLIC = 0x00,
26
+ NOEX_NOSUPER = 0x01,
27
+ NOEX_PRIVATE = 0x02,
28
+ NOEX_PROTECTED = 0x04,
29
+ NOEX_MASK = 0x06,
30
+ NOEX_BASIC = 0x08,
31
+ NOEX_UNDEF = NOEX_NOSUPER,
32
+ NOEX_MODFUNC = 0x12,
33
+ NOEX_SUPER = 0x20,
34
+ NOEX_VCALL = 0x40,
35
+ NOEX_RESPONDS = 0x80,
36
+
37
+ NOEX_BIT_WIDTH = 8,
38
+ NOEX_SAFE_SHIFT_OFFSET = ((NOEX_BIT_WIDTH+3)/4)*4 /* round up to nibble */
39
+ } rb_method_flag_t;
40
+
41
+ #define NOEX_SAFE(n) ((int)((n) >> NOEX_SAFE_SHIFT_OFFSET) & 0x0F)
42
+ #define NOEX_WITH(n, s) (((s) << NOEX_SAFE_SHIFT_OFFSET) | (n) | (ruby_running ? 0 : NOEX_BASIC))
43
+ #define NOEX_WITH_SAFE(n) NOEX_WITH((n), rb_safe_level())
44
+
45
+ /* method data type */
46
+
47
+ typedef enum {
48
+ VM_METHOD_TYPE_ISEQ,
49
+ VM_METHOD_TYPE_CFUNC,
50
+ VM_METHOD_TYPE_ATTRSET,
51
+ VM_METHOD_TYPE_IVAR,
52
+ VM_METHOD_TYPE_BMETHOD,
53
+ VM_METHOD_TYPE_ZSUPER,
54
+ VM_METHOD_TYPE_UNDEF,
55
+ VM_METHOD_TYPE_NOTIMPLEMENTED,
56
+ VM_METHOD_TYPE_OPTIMIZED, /* Kernel#send, Proc#call, etc */
57
+ VM_METHOD_TYPE_MISSING, /* wrapper for method_missing(id) */
58
+ VM_METHOD_TYPE_REFINED,
59
+
60
+ END_OF_ENUMERATION(VM_METHOD_TYPE)
61
+ } rb_method_type_t;
62
+
63
+ struct rb_call_info_struct;
64
+
65
+ typedef struct rb_method_cfunc_struct {
66
+ VALUE (*func)(ANYARGS);
67
+ VALUE (*invoker)(VALUE (*func)(ANYARGS), VALUE recv, int argc, const VALUE *argv);
68
+ int argc;
69
+ } rb_method_cfunc_t;
70
+
71
+ typedef struct rb_method_attr_struct {
72
+ ID id;
73
+ const VALUE location;
74
+ } rb_method_attr_t;
75
+
76
+ typedef struct rb_iseq_struct rb_iseq_t;
77
+
78
+ typedef struct rb_method_definition_struct {
79
+ rb_method_type_t type; /* method type */
80
+ ID original_id;
81
+ union {
82
+ rb_iseq_t * const iseq; /* should be mark */
83
+ rb_method_cfunc_t cfunc;
84
+ rb_method_attr_t attr;
85
+ const VALUE proc; /* should be mark */
86
+ enum method_optimized_type {
87
+ OPTIMIZED_METHOD_TYPE_SEND,
88
+ OPTIMIZED_METHOD_TYPE_CALL,
89
+
90
+ OPTIMIZED_METHOD_TYPE__MAX
91
+ } optimize_type;
92
+ struct rb_method_entry_struct *orig_me;
93
+ } body;
94
+ int alias_count;
95
+ } rb_method_definition_t;
96
+
97
+ typedef struct rb_method_entry_struct {
98
+ rb_method_flag_t flag;
99
+ char mark;
100
+ rb_method_definition_t *def;
101
+ ID called_id;
102
+ VALUE klass; /* should be mark */
103
+ } rb_method_entry_t;
104
+
105
+ struct unlinked_method_entry_list_entry {
106
+ struct unlinked_method_entry_list_entry *next;
107
+ rb_method_entry_t *me;
108
+ };
109
+
110
+ #define UNDEFINED_METHOD_ENTRY_P(me) (!(me) || !(me)->def || (me)->def->type == VM_METHOD_TYPE_UNDEF)
111
+
112
+ void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_flag_t noex);
113
+ rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex);
114
+ rb_method_entry_t *rb_method_entry(VALUE klass, ID id, VALUE *define_class_ptr);
115
+ rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
116
+ void rb_add_refined_method_entry(VALUE refined_class, ID mid);
117
+ rb_method_entry_t *rb_resolve_refined_method(VALUE refinements,
118
+ const rb_method_entry_t *me,
119
+ VALUE *defined_class_ptr);
120
+ rb_method_entry_t *rb_method_entry_with_refinements(VALUE klass, ID id,
121
+ VALUE *defined_class_ptr);
122
+ rb_method_entry_t *rb_method_entry_without_refinements(VALUE klass, ID id,
123
+ VALUE *defined_class_ptr);
124
+
125
+ rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, ID id, VALUE *define_class_ptr);
126
+ rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_flag_t noex);
127
+
128
+ int rb_method_entry_arity(const rb_method_entry_t *me);
129
+ int rb_method_entry_eq(const rb_method_entry_t *m1, const rb_method_entry_t *m2);
130
+ st_index_t rb_hash_method_entry(st_index_t hash, const rb_method_entry_t *me);
131
+
132
+ VALUE rb_method_entry_location(rb_method_entry_t *me);
133
+ VALUE rb_mod_method_location(VALUE mod, ID id);
134
+ VALUE rb_obj_method_location(VALUE obj, ID id);
135
+
136
+ void rb_mark_method_entry(const rb_method_entry_t *me);
137
+ void rb_free_method_entry(rb_method_entry_t *me);
138
+ void rb_sweep_method_entry(void *vm);
139
+ void rb_free_m_tbl(st_table *tbl);
140
+ void rb_free_m_tbl_wrapper(struct method_table_wrapper *wrapper);
141
+
142
+ #endif /* METHOD_H */
Binary file
data/lib/looksee/clean.rb CHANGED
@@ -4,6 +4,10 @@ require 'set'
4
4
  module Looksee
5
5
  Config = Object.const_defined?(:RbConfig) ? ::RbConfig : ::Config
6
6
 
7
+ NoMethodError = Class.new(RuntimeError)
8
+ NoSourceLocationError = Class.new(RuntimeError)
9
+ NoSourceFileError = Class.new(RuntimeError)
10
+
7
11
  autoload :VERSION, 'looksee/version'
8
12
  autoload :Adapter, 'looksee/adapter'
9
13
  autoload :Columnizer, 'looksee/columnizer'
@@ -42,38 +42,31 @@ module Looksee
42
42
  Inspector.new(lookup_path, options)
43
43
  end
44
44
 
45
- #
46
- # Open an editor at the named method's definition.
47
- #
48
- # Uses Looksee.editor to determine the editor command to run.
49
- #
50
- # Only works for methods for which file and line numbers are
51
- # accessible.
52
- #
53
- def edit(name)
54
- Editor.new(Looksee.editor).edit(self, name)
55
- end
56
-
57
- def self.rename(renamings) # :nodoc:
58
- renamings.each do |old_name, new_name|
59
- alias_method new_name, old_name
60
- remove_method old_name
61
- end
45
+ def self.rename(name) # :nodoc:
46
+ name = name[:ls] if name.is_a?(Hash)
47
+ alias_method name, :ls
48
+ remove_method :ls
62
49
  end
63
50
  end
64
51
 
65
52
  #
66
- # Rename the methods added to every object. Example:
53
+ # Rename the #ls method, added to every object. Example:
54
+ #
55
+ # rename :_ls
56
+ #
57
+ # This renames Looksee's #ls method to #_ls.
67
58
  #
68
- # rename :ls => :_ls, :edit => :_edit
59
+ # For backward compatibility, the old-style invocation is also
60
+ # supported. Please note this is deprecated.
69
61
  #
70
- def self.rename(renamings)
71
- ObjectMixin.rename(renamings)
62
+ # rename :ls => :_ls
63
+ #
64
+ def self.rename(name)
65
+ ObjectMixin.rename(name)
72
66
  end
73
67
 
74
- (ENV['LOOKSEE_METHODS'] || '').scan(/([\w_]+)=([\w_]+)/) do
75
- rename $1.to_sym => $2.to_sym
76
- end
68
+ name = ENV['LOOKSEE_METHOD'] and
69
+ rename name
77
70
 
78
71
  Object.send :include, ObjectMixin
79
72
  end
@@ -14,9 +14,15 @@ module Looksee
14
14
  #
15
15
  def edit(object, method_name)
16
16
  method = LookupPath.new(object).find(method_name.to_s) or
17
- return
17
+ raise NoMethodError, "no method `#{method_name}' in lookup path of #{object.class} instance"
18
18
  file, line = Looksee.adapter.source_location(method)
19
- run(file, line) unless line.nil?
19
+ if !file
20
+ raise NoSourceLocationError, "no source location for #{method.owner}##{method.name}"
21
+ elsif !File.exist?(file)
22
+ raise NoSourceFileError, "cannot find source file: #{file}"
23
+ else
24
+ run(file, line)
25
+ end
20
26
  end
21
27
 
22
28
  #
data/lib/looksee/help.rb CHANGED
@@ -40,7 +40,7 @@ module Looksee
40
40
  | ...
41
41
  | }
42
42
  |
43
- | object.edit(method)
43
+ | object.ls.edit(method)
44
44
  |
45
45
  | Jump to the source of the given method. Set your editor
46
46
  | with Looksee.editor or the LOOKSEE_EDITOR environment
@@ -11,12 +11,27 @@ module Looksee
11
11
  attr_reader :visibilities
12
12
  attr_reader :filters
13
13
 
14
+ #
15
+ # Print the method lookup path of self. See the README for details.
16
+ #
14
17
  def inspect
15
18
  lookup_path.entries.reverse.map do |entry|
16
19
  inspect_entry(entry)
17
20
  end.join("\n")
18
21
  end
19
22
 
23
+ #
24
+ # Open an editor at the named method's definition.
25
+ #
26
+ # Uses Looksee.editor to determine the editor command to run.
27
+ #
28
+ # Only works for methods for which file and line numbers are
29
+ # accessible.
30
+ #
31
+ def edit(name)
32
+ Editor.new(Looksee.editor).edit(lookup_path.object, name)
33
+ end
34
+
20
35
  private
21
36
 
22
37
  def inspect_entry(entry)
@@ -1,5 +1,5 @@
1
1
  module Looksee
2
- VERSION = [1, 1, 0]
2
+ VERSION = [2, 0, 0]
3
3
 
4
4
  class << VERSION
5
5
  include Comparable
data/spec/adapter_spec.rb CHANGED
@@ -95,33 +95,33 @@ describe "Looksee.adapter" do
95
95
  def self.it_should_list_methods_with_visibility(visibility)
96
96
  it "should return the list of #{visibility} instance methods defined directly on a class" do
97
97
  temporary_class :C
98
- replace_methods C, visibility => [:one, :two]
98
+ add_methods C, visibility => [:one, :two]
99
99
  @adapter.send(target_method, C).to_set.should == Set[:one, :two]
100
100
  end
101
101
 
102
102
  it "should return the list of #{visibility} instance methods defined directly on a module" do
103
103
  temporary_module :M
104
- replace_methods M, visibility => [:one, :two]
104
+ add_methods M, visibility => [:one, :two]
105
105
  @adapter.send(target_method, M).to_set.should == Set[:one, :two]
106
106
  end
107
107
 
108
108
  it "should return the list of #{visibility} instance methods defined directly on a singleton class" do
109
109
  temporary_class :C
110
110
  c = C.new
111
- replace_methods c.singleton_class, visibility => [:one, :two]
111
+ add_methods c.singleton_class, visibility => [:one, :two]
112
112
  @adapter.send(target_method, c.singleton_class).to_set.should == Set[:one, :two]
113
113
  end
114
114
 
115
115
  it "should return the list of #{visibility} instance methods defined directly on a class' singleton class" do
116
116
  temporary_class :C
117
- replace_methods C.singleton_class, visibility => [:one, :two], :class_singleton => true
117
+ add_methods C.singleton_class, visibility => [:one, :two], :class_singleton => true
118
118
  @adapter.send(target_method, C.singleton_class).to_set.should == Set[:one, :two]
119
119
  end
120
120
 
121
121
  # Worth checking as ruby keeps undef'd methods in method tables.
122
122
  it "should not return undefined methods" do
123
123
  temporary_class :C
124
- replace_methods C, visibility => [:removed]
124
+ add_methods C, visibility => [:removed]
125
125
  C.send(:undef_method, :removed)
126
126
  @adapter.send(target_method, C).to_set.should == Set[]
127
127
  end
@@ -130,7 +130,7 @@ describe "Looksee.adapter" do
130
130
  def self.it_should_not_list_methods_with_visibility(visibility1, visibility2)
131
131
  it "should not return any #{visibility1} or #{visibility2} instance methods" do
132
132
  temporary_class :C
133
- replace_methods C, {visibility1 => [:a], visibility2 => [:b]}
133
+ add_methods C, {visibility1 => [:a], visibility2 => [:b]}
134
134
  @adapter.send(target_method, C).to_set.should == Set[]
135
135
  end
136
136
  end
data/spec/editor_spec.rb CHANGED
@@ -1,128 +1,110 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Looksee::Editor do
4
- def editor_command(command)
5
- Looksee::Editor.new(command).command_for('FILE', 'LINE')
6
- end
4
+ describe "#command_for" do
5
+ def editor_command(command)
6
+ Looksee::Editor.new(command).command_for('FILE', 'LINE')
7
+ end
7
8
 
8
- it "should infer the file and line arguments for 'vi'" do
9
- editor_command('vi').should == ['vi', '+LINE', 'FILE']
10
- end
9
+ it "should infer the file and line arguments for 'vi'" do
10
+ editor_command('vi').should == ['vi', '+LINE', 'FILE']
11
+ end
11
12
 
12
- it "should infer the file and line arguments for 'vim'" do
13
- editor_command('vim').should == ['vim', '+LINE', 'FILE']
14
- end
13
+ it "should infer the file and line arguments for 'vim'" do
14
+ editor_command('vim').should == ['vim', '+LINE', 'FILE']
15
+ end
15
16
 
16
- it "should infer the file and line arguments for 'gvim'" do
17
- editor_command('gvim').should == ['gvim', '+LINE', 'FILE']
18
- end
17
+ it "should infer the file and line arguments for 'gvim'" do
18
+ editor_command('gvim').should == ['gvim', '+LINE', 'FILE']
19
+ end
19
20
 
20
- it "should infer the file and line arguments for 'emacs'" do
21
- editor_command('emacs').should == ['emacs', '+LINE', 'FILE']
22
- end
21
+ it "should infer the file and line arguments for 'emacs'" do
22
+ editor_command('emacs').should == ['emacs', '+LINE', 'FILE']
23
+ end
23
24
 
24
- it "should infer the file and line arguments for 'xemacs'" do
25
- editor_command('xemacs').should == ['xemacs', '+LINE', 'FILE']
26
- end
25
+ it "should infer the file and line arguments for 'xemacs'" do
26
+ editor_command('xemacs').should == ['xemacs', '+LINE', 'FILE']
27
+ end
27
28
 
28
- it "should infer the file and line arguments for 'aquamacs'" do
29
- editor_command('aquamacs').should == ['aquamacs', '+LINE', 'FILE']
30
- end
29
+ it "should infer the file and line arguments for 'aquamacs'" do
30
+ editor_command('aquamacs').should == ['aquamacs', '+LINE', 'FILE']
31
+ end
31
32
 
32
- it "should infer the file and line arguments for 'pico'" do
33
- editor_command('pico').should == ['pico', '+LINE', 'FILE']
34
- end
33
+ it "should infer the file and line arguments for 'pico'" do
34
+ editor_command('pico').should == ['pico', '+LINE', 'FILE']
35
+ end
35
36
 
36
- it "should infer the file and line arguments for 'nano'" do
37
- editor_command('nano').should == ['nano', '+LINE', 'FILE']
38
- end
37
+ it "should infer the file and line arguments for 'nano'" do
38
+ editor_command('nano').should == ['nano', '+LINE', 'FILE']
39
+ end
39
40
 
40
- it "should infer the file and line arguments for 'mate'" do
41
- editor_command('mate').should == ['mate', '-lLINE', 'FILE']
42
- end
41
+ it "should infer the file and line arguments for 'mate'" do
42
+ editor_command('mate').should == ['mate', '-lLINE', 'FILE']
43
+ end
43
44
 
44
- it "should support escaped '%'-signs" do
45
- editor_command('%% %f %l').should == ['%', 'FILE', 'LINE']
46
- end
45
+ it "should support escaped '%'-signs" do
46
+ editor_command('%% %f %l').should == ['%', 'FILE', 'LINE']
47
+ end
47
48
 
48
- it "should not infer file and line arguments for unknown editors" do
49
- editor_command('wtfbbq').should == ['wtfbbq']
49
+ it "should not infer file and line arguments for unknown editors" do
50
+ editor_command('wtfbbq').should == ['wtfbbq']
51
+ end
50
52
  end
51
53
 
52
54
  describe "#edit" do
53
- TMP = "#{ROOT}/spec/tmp"
55
+ let(:tmp) { "#{ROOT}/spec/tmp" }
56
+ let(:editor_path) { "#{tmp}/edit" }
57
+ let(:editor_output) { "#{tmp}/edit.out" }
58
+ let(:editor) { Looksee::Editor.new("#{editor_path} %f %l") }
59
+ let(:editor_invocation) { File.exist?(editor_output) ? File.read(editor_output) : nil }
60
+ let(:source_location) { ["#{tmp}/c.rb", 2] }
61
+ let(:object) { C.new }
54
62
 
55
63
  before do
56
- FileUtils.mkdir_p TMP
57
- make_editor "#{TMP}/edit"
58
- @editor = Looksee::Editor.new("#{TMP}/edit %f %l")
64
+ FileUtils.mkdir_p tmp
65
+ set_up_editor
66
+
67
+ file, line = *source_location
68
+ FileUtils.touch file
69
+ Object.const_set(:C, Class.new { def f; end })
70
+ Looksee.adapter.set_methods(C, [:f], [], [], [])
71
+ Looksee.adapter.set_source_location(C, :f, source_location)
72
+ Looksee.adapter.ancestors[object] = [C, Object]
59
73
  end
60
74
 
61
75
  after do
62
- FileUtils.rm_rf TMP
76
+ Object.send(:remove_const, :C)
77
+ FileUtils.rm_rf tmp
63
78
  end
64
79
 
65
- def make_editor(path)
66
- open(path, 'w') { |f| f.puts <<-EOS.demargin }
80
+ def set_up_editor
81
+ open(editor_path, 'w') { |f| f.puts <<-EOS.demargin }
67
82
  |#!/bin/sh
68
- |echo $# $1 $2 > "#{TMP}/editor.out"
83
+ |echo $# $1 $2 > "#{editor_output}"
69
84
  EOS
70
- File.chmod 0755, path
85
+ File.chmod 0755, editor_path
71
86
  end
72
87
 
73
- def with_source_file
74
- path = "#{TMP}/c.rb"
75
- open(path, 'w') { |f| f.puts <<-EOS.demargin }
76
- |class C
77
- | def f
78
- | end
79
- |end
80
- EOS
81
- begin
82
- load path
83
- Looksee.adapter.set_methods(C, [:f], [], [], [])
84
- yield path
85
- ensure
86
- Object.send(:remove_const, :C)
87
- end
88
+ it "should run the editor on the method's source location if available" do
89
+ editor.edit(object, :f)
90
+ editor_invocation.should == "2 #{source_location.join(' ')}\n"
88
91
  end
89
92
 
90
- it "should run the editor on the method's source location if available" do
91
- with_source_file do |path|
92
- c = C.new
93
- Looksee.adapter.ancestors[c] = [C, Object]
94
- Looksee.adapter.set_source_location(C, :f, [path, 2])
95
- @editor.edit(c, :f)
96
- File.read("#{TMP}/editor.out").should == "2 #{path} 2\n"
97
- end
98
- end
99
-
100
- it "should not run the editor if the source file does not exist" do
101
- with_source_file do |path|
102
- FileUtils.rm_f path
103
- @editor.edit(C.new, :f)
104
- File.should_not exist("#{TMP}/editor.out")
105
- end
106
- end
107
-
108
- it "should not run the editor for methods defined with eval where no source file specified" do
109
- eval <<-EOS.demargin
110
- |class ::C
111
- | def f
112
- | end
113
- |end
114
- EOS
115
- begin
116
- @editor.edit(C.new, :f)
117
- File.should_not exist("#{TMP}/editor.out")
118
- ensure
119
- Object.send(:remove_const, :C)
120
- end
93
+ it "should raise NoMethodError and not run the editor if the method does not exist" do
94
+ expect { editor.edit(object, :x) }.to raise_error(Looksee::NoMethodError)
95
+ editor_invocation.should be_nil
96
+ end
97
+
98
+ it "should raise NoSourceFileError and not run the editor if the source file does not exist" do
99
+ FileUtils.rm_f source_location.first
100
+ expect { editor.edit(object, :f) }.to raise_error(Looksee::NoSourceFileError)
101
+ editor_invocation.should be_nil
121
102
  end
122
103
 
123
- it "should not run the editor for primitives" do
124
- @editor.edit('', :size)
125
- File.should_not exist("#{TMP}/editor.out")
104
+ it "should raise NoSourceLocationError and not run the editor if no source location is available" do
105
+ Looksee.adapter.set_source_location(C, :f, nil)
106
+ expect { editor.edit(object, :f) }.to raise_error(Looksee::NoSourceLocationError)
107
+ editor_invocation.should be_nil
126
108
  end
127
109
  end
128
110
  end