Ruby Issue Tracking System: Issues
https://redmine.ruby-lang.org/
https://redmine.ruby-lang.org/favicon.ico?1711330511
2020-12-26T11:04:18Z
Ruby Issue Tracking System
Redmine
Ruby master - Feature #17472 (Rejected): HashWithIndifferentAccess like Hash extension
https://redmine.ruby-lang.org/issues/17472
2020-12-26T11:04:18Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Rails has <a href="https://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html" class="external">ActiveSupport::HashWithIndifferentAccess</a>, which is widely used in Rails to handle Request, Session, ActionView's form construction, ActiveRecord's DB communication, and so on. It receives String or Symbol and normalize them to fetch the value. But it is implemented with Ruby. If we provide C implementation of that, Rails will gain the performance improvement.</p>
<p>summary of previous discussion: <a href="https://github.com/rails/rails/pull/40182#issuecomment-687607812" class="external">https://github.com/rails/rails/pull/40182#issuecomment-687607812</a></p>
Ruby master - Feature #17468 (Closed): Deprecate RUBY_DEVEL
https://redmine.ruby-lang.org/issues/17468
2020-12-25T09:32:50Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Some configuration of Ruby use RUBY_DEVEL, which depends PATCH_LEVEL.<br>
But depending PATCH_LEVEL causes issues which will become revealed on the final release.<br>
Though we release some previews and RCs, they don't contributes the quality around RUBY_DEVEL.</p>
<p>Therefore to ensure CI tests the quality of the final release, we need to deprecate RUBY_DEVEL.</p>
Ruby master - Bug #15992 (Closed): An exception breaks monitor state and cause deadlock
https://redmine.ruby-lang.org/issues/15992
2019-07-10T06:55:40Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>lib/monitor.rb provides Monitor.<br>
But its state handling is weak for interrupts caused by Thread.kill for example timeout libraries.</p>
<p>Timeout exception may happen everywhere. If it raised when the thread is executing</p>
<pre><code class="ruby syntaxhl" data-language="ruby"> <span class="k">def</span> <span class="nf">mon_exit</span>
<span class="n">mon_check_owner</span>
<span class="vi">@mon_count</span> <span class="o">-=</span><span class="mi">1</span>
<span class="k">if</span> <span class="vi">@mon_count</span> <span class="o">==</span> <span class="mi">0</span>
<span class="vi">@mon_owner</span> <span class="o">=</span> <span class="kp">nil</span>
<span class="c1"># HERE!!!</span>
<span class="vi">@mon_mutex</span><span class="p">.</span><span class="nf">unlock</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>It breaks the state of the monitor and it causes deadlock.</p>
Ruby master - Bug #15164 (Closed): mkmf doesn't work with miniruby on Windows
https://redmine.ruby-lang.org/issues/15164
2018-09-26T15:29:46Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>After r59449 <code>nmake</code> failes to run when making ripper and so on.<br>
It because miniruby sets UTF-8 encoding even though the content is CP932 (on Japanese Windows).<br>
It should set ASCII-8BIT and handle as it is.</p>
<pre><code>ripper:
" Could not be configured. It will not be installed."
" C:/ruby-trunk/lib/mkmf.rb:1560: invalid byte sequence in UTF-8"
" Check ext/ripper/mkmf.log for more details."
*** Fix the problems, then remove these directories and try again if you want.
</code></pre>
<p>Note that recent Windows sets PATH to paths under %APPDATA% for example<br>
C:\Users<UserNameInANSI>\AppData\Local\Microsoft\WindowsApps</p>
Ruby master - Misc #14861 (Closed): DevelopersMeeting20180718Japan
https://redmine.ruby-lang.org/issues/14861
2018-06-21T05:58:04Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Date: 2018/07/18 (Thu)<br>
Time: 14:00-18:00 (JST)<br>
Place: MoneyForward HQ (beware: new location) (Tokyo, Japan)<br>
Sign-up: <a href="https://ruby.connpass.com/event/92314/" class="external">https://ruby.connpass.com/event/92314/</a><br>
log: <a href="https://docs.google.com/document/d/1_cKh0LJd18y5CH1MfM6WC1fqh2rpHHZntrBCLTbwRSE/edit" class="external">https://docs.google.com/document/d/1_cKh0LJd18y5CH1MfM6WC1fqh2rpHHZntrBCLTbwRSE/edit</a><br>
pub: <a href="https://docs.google.com/document/d/1_cKh0LJd18y5CH1MfM6WC1fqh2rpHHZntrBCLTbwRSE/pub" class="external">https://docs.google.com/document/d/1_cKh0LJd18y5CH1MfM6WC1fqh2rpHHZntrBCLTbwRSE/pub</a></p>
<p>Please comment your favorite ticket numbers you want to ask to discuss with your <em>SHORT</em> comment or summary.<br>
(your summary/comment will help us because we don't need to read all of ticket comments)</p>
<p><em>DO NOT</em> discuss then on this ticket, please.</p>
<p>Past meetings: <a href="https://bugs.ruby-lang.org/projects/ruby/wiki#Developer-Meetings" class="external">https://bugs.ruby-lang.org/projects/ruby/wiki#Developer-Meetings</a></p>
<a name="NOTE"></a>
<h1 >NOTE<a href="#NOTE" class="wiki-anchor">¶</a></h1>
<p>Dev meeting <em>IS NOT</em> a decision making place. All decisions should be done at the bug tracker.<br>
Dev meeting is a place we can ask Matz, nobu, nurse and other developers directly.<br>
Matz is a very busy person. Take this opportunity to ask him. If you can not attend, other attendees can ask instead of you (if attendees can understand your issue).<br>
We will write a log about discussion to a file or to each ticket in English.<br>
All activities are best-effort (keep in mind that most of us are volunteer developers).<br>
The date, time and place is scheduled according to when/where we can reserve Matz's time.</p>
<a name="Agenda"></a>
<h1 >Agenda<a href="#Agenda" class="wiki-anchor">¶</a></h1>
<a name="Next-dev-meeting"></a>
<h2 >Next dev-meeting<a href="#Next-dev-meeting" class="wiki-anchor">¶</a></h2>
<a name="About-26-timeframe"></a>
<h2 >About 2.6 timeframe<a href="#About-26-timeframe" class="wiki-anchor">¶</a></h2>
<a name="Carry-over-from-previous-meetings"></a>
<h2 >Carry-over from previous meeting(s)<a href="#Carry-over-from-previous-meetings" class="wiki-anchor">¶</a></h2>
<ul>
<li>[Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Comparable#clamp with a range (Closed)" href="https://redmine.ruby-lang.org/issues/14784">#14784</a>] One-sided Comparable#clamp (with endless/startless ranges) (zverok)
<ul>
<li>more reasonable version of Object#enumerate proposed for the previous meeting.</li>
</ul>
</li>
<li>[Feature <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: [PATCH] implement Timeout in VM (Open)" href="https://redmine.ruby-lang.org/issues/14859">#14859</a>] Timeout in VM (normalperson)
<ul>
<li>Still needs some work, mainly wondering if the idea of moving this part of stdlib into core VM is acceptable or not. No semantic changes except speed improvement.</li>
</ul>
</li>
</ul>
<a name="From-Attendees"></a>
<h2 >From Attendees<a href="#From-Attendees" class="wiki-anchor">¶</a></h2>
<p>(will be edited later)<br>
(if you have a write access permission, please list directly)</p>
<ul>
<li>[Bug <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Array#delete_if does not use #delete (Closed)" href="https://redmine.ruby-lang.org/issues/14887">#14887</a>] Array#delete_if does not use #delete (shyouhei)
<ul>
<li>Is it by design, or a bug?</li>
</ul>
</li>
<li>[Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Readline: expose rl_completion_quote_character variable (Closed)" href="https://redmine.ruby-lang.org/issues/13050">#13050</a>] Readline: expose rl_completion_quote_character variable (nobu)</li>
<li>[Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Add official API for setting timezone on Time (Closed)" href="https://redmine.ruby-lang.org/issues/14850">#14850</a>] Add official API for setting timezone on Time (nobu)</li>
<li>[Feature <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Proposal to add Hash#=== (Open)" href="https://redmine.ruby-lang.org/issues/14869">#14869</a>] Proposal to add Hash#=== (nobu)</li>
<li>[Feature <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Calculate age in Date class (Rejected)" href="https://redmine.ruby-lang.org/issues/14877">#14877</a>] Calculate age in Date class (nobu)</li>
<li>[Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Add Range#subrange? (Closed)" href="https://redmine.ruby-lang.org/issues/14473">#14473</a>] Add Range#subrange? (tarui)</li>
<li>[Bug <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Enumerator::Lazy creates unnecessary Array objects. (Closed)" href="https://redmine.ruby-lang.org/issues/14908">#14908</a>] Enumerator::Lazy creates unnecessary Array objects. (nobu)</li>
<li>[Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Introduce pattern matching syntax (Closed)" href="https://redmine.ruby-lang.org/issues/14912">#14912</a>] Introduce pattern matching syntax (mame)
<ul>
<li>pattern matching</li>
</ul>
</li>
<li>opt_to_s (nobu)
<ul>
<li>see below for his patch</li>
</ul>
</li>
<li>[Feature <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Add String#byteslice! (Open)" href="https://redmine.ruby-lang.org/issues/13626">#13626</a>] Add String#byteslice! (aycabta)</li>
<li>[Feature <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Add display width method to String for CLI (Open)" href="https://redmine.ruby-lang.org/issues/14618">#14618</a>] Add display width method to String for CLI (aycabta)</li>
<li>[Misc <a class="issue tracker-5 status-2 priority-4 priority-default" title="Misc: Add RDoc documents to tar ball (Assigned)" href="https://redmine.ruby-lang.org/issues/14917">#14917</a>] Add RDoc documents to tar ball (aycabta)</li>
<li>[Feature <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Use Reline for fallback of ext/readline (Closed)" href="https://redmine.ruby-lang.org/issues/14918">#14918</a>] Use Reline for fallback of ext/readline (aycabta)</li>
<li>[Feature <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Add String#byteinsert (Open)" href="https://redmine.ruby-lang.org/issues/14919">#14919</a>] Add String#byteinsert (aycabta)</li>
<li>[Feature <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Proposal to add Array#=== (Open)" href="https://redmine.ruby-lang.org/issues/14916">#14916</a>] Proposal to add Array#=== (aycabta)</li>
</ul>
<a name="From-non-attendees"></a>
<h2 >From non-attendees<a href="#From-non-attendees" class="wiki-anchor">¶</a></h2>
<p>(will be edited later)<br>
(if you have a write access, please list directly)</p>
<ul>
<li>[Feature <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: ArgumentErrorが発生した時メソッドのプロトタイプをメッセージに含む (Open)" href="https://redmine.ruby-lang.org/issues/14111">#14111</a>] ArgumentErrorが発生した時メソッドのプロトタイプをメッセージに含む (esjee)
<ul>
<li>Suggestion: include method parameters when generating an ArgumentsError message. Nobu's patch in the issue provides functionality to make writing a ruby gem to provide this functionality possible.</li>
</ul>
</li>
<li>[Bug <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Add command line argument to deactivate JIT (Closed)" href="https://redmine.ruby-lang.org/issues/14878">#14878</a>] Add command line argument to deactivate JIT (k0kubun)
<ul>
<li>Please discuss the necessity of the flag and its name in the proposal.</li>
</ul>
</li>
<li>[Feature <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Implement String #blank? #present? and improve #strip and family to handle unicode (Open)" href="https://redmine.ruby-lang.org/issues/12306">#12306</a>] Implement String #blank? #present? and improve #strip and family to handle unicode (sam.saffron)</li>
<li>[Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Extend case to match several values at once (Closed)" href="https://redmine.ruby-lang.org/issues/14913">#14913</a>] Extend case to match several values at once (zverok)
<ul>
<li>some steps towards better pattern matching</li>
</ul>
</li>
<li>[Feature <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Add BasicObject#instance_exec_with_block (Rejected)" href="https://redmine.ruby-lang.org/issues/14914">#14914</a>] Add BasicObject#instance_exec_with_block (jeremyevans0)</li>
<li>[Feature <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Deprecate String#crypt (Rejected)" href="https://redmine.ruby-lang.org/issues/14915">#14915</a>] Deprecate String#crypt, move implementation to string/crypt (jeremyevans0)</li>
</ul>
Ruby master - Misc #14770 (Open): [META] DevelopersMeeting
https://redmine.ruby-lang.org/issues/14770
2018-05-17T12:28:50Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>A meta ticket to organize DevelopersMeeting tickets.<br>
<a href="https://bugs.ruby-lang.org/projects/ruby/wiki#Developer-Meetings" class="external">https://bugs.ruby-lang.org/projects/ruby/wiki#Developer-Meetings</a></p>
Ruby master - Bug #14186 (Closed): cannot build ruby with tarball on noruby environment
https://redmine.ruby-lang.org/issues/14186
2017-12-14T16:35:37Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>r61055 breaks build</p>
Ruby master - Bug #13945 (Closed): Backport r60024
https://redmine.ruby-lang.org/issues/13945
2017-09-27T21:48:05Z
naruse (Yui NARUSE)
naruse@airemix.jp
<pre><code>vm.c: fetch retval iff necessary
* vm.c (rb_vm_make_jump_tag_but_local_jump): get rid of fetching
retval when it is not used. it is necessary for local jump
state only.
</code></pre>
<p>This caused SEGV if an application which embeds Ruby and uses <code>rb_load_protect</code>.<br>
<a href="https://github.com/vim/vim/pull/2147" class="external">https://github.com/vim/vim/pull/2147</a></p>
Ruby master - Feature #13869 (Open): Filter non directories from Dir.glob
https://redmine.ruby-lang.org/issues/13869
2017-09-05T14:30:58Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Dir.glob is a tool to fetch filesystem entries with filtering.</p>
<p>On Rails, it often query files from template directories with braces (FNM_EXTGLOB)<br>
<a href="https://github.com/rails/rails/blob/6f1c18308ebffc97d51440cdeed7be71de58f26a/actionview/lib/action_view/template/resolver.rb#L247" class="external">https://github.com/rails/rails/blob/6f1c18308ebffc97d51440cdeed7be71de58f26a/actionview/lib/action_view/template/resolver.rb#L247</a></p>
<pre><code class="ruby syntaxhl" data-language="ruby"> <span class="k">def</span> <span class="nf">find_template_paths</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
<span class="no">Dir</span><span class="p">[</span><span class="n">query</span><span class="p">].</span><span class="nf">uniq</span><span class="p">.</span><span class="nf">reject</span> <span class="k">do</span> <span class="o">|</span><span class="n">filename</span><span class="o">|</span>
<span class="no">File</span><span class="p">.</span><span class="nf">directory?</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="o">||</span>
<span class="c1"># deals with case-insensitive file systems.</span>
<span class="o">!</span><span class="no">File</span><span class="p">.</span><span class="nf">fnmatch</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">filename</span><span class="p">,</span> <span class="no">File</span><span class="o">::</span><span class="no">FNM_EXTGLOB</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>By this code, File.directory?() will call lstat(2) system call for each files.</p>
<p>But if Dir.glob is extended, it can avoid calling lstat because it can fetch entry's type from struct dirent<br>
(on many platforms).</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/dir.c b/dir.c
index b7afaec4e0..5db9c2dc79 100644
</span><span class="gd">--- a/dir.c
</span><span class="gi">+++ b/dir.c
</span><span class="p">@@ -206,6 +206,7 @@</span> typedef enum {
#else
#define FNM_SHORTNAME 0
#endif
<span class="gi">+#define FNM_NONDIR 0x40
</span>
#define FNM_NOMATCH 1
#define FNM_ERROR 2
<span class="p">@@ -1408,7 +1409,7 @@</span> do_opendir(const int basefd, const char *path, int flags, rb_encoding *enc,
}
/* Globing pattern */
<span class="gd">-enum glob_pattern_type { PLAIN, ALPHA, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR };
</span><span class="gi">+enum glob_pattern_type { PLAIN, ALPHA, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR, MATCH_NONDIR };
</span>
/* Return nonzero if S has any special globbing chars in it. */
static enum glob_pattern_type
<span class="p">@@ -1581,7 +1582,7 @@</span> glob_make_pattern(const char *p, const char *e, int flags, rb_encoding *enc)
glob_free_pattern(list);
return 0;
}
<span class="gd">- tmp->type = dirsep ? MATCH_DIR : MATCH_ALL;
</span><span class="gi">+ tmp->type = dirsep ? MATCH_DIR : flags & flags & FNM_NONDIR ? MATCH_NONDIR : MATCH_ALL;
</span> tmp->str = 0;
*tail = tmp;
tmp->next = 0;
<span class="p">@@ -1893,7 +1894,7 @@</span> glob_helper(
struct stat st;
int status = 0;
struct glob_pattern **cur, **new_beg, **new_end;
<span class="gd">- int plain = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0;
</span><span class="gi">+ int plain = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0, match_nondir = 0;
</span> int escape = !(flags & FNM_NOESCAPE);
size_t pathlen = baselen + namelen;
const char *base = path;
<span class="p">@@ -1926,6 +1927,9 @@</span> glob_helper(
case MATCH_DIR:
match_dir = 1;
break;
<span class="gi">+ case MATCH_NONDIR:
+ match_nondir = 1;
+ break;
</span> case RECURSIVE:
rb_bug("continuous RECURSIVEs");
}
<span class="p">@@ -1940,7 +1944,7 @@</span> glob_helper(
pathtype = path_noent;
}
}
<span class="gd">- if (match_dir && (pathtype == path_unknown || pathtype == path_symlink)) {
</span><span class="gi">+ if ((match_dir || match_nondir) && (pathtype == path_unknown || pathtype == path_symlink)) {
</span> if (do_stat(fd, base, &st, flags, enc) == 0) {
pathtype = IFTODT(st.st_mode);
}
<span class="p">@@ -1953,6 +1957,11 @@</span> glob_helper(
status = glob_call_func(funcs->match, subpath, arg, enc);
if (status) return status;
}
<span class="gi">+ if (match_nondir && pathtype > path_noent && pathtype != path_directory) {
+ const char *subpath = path + baselen + (baselen && path[baselen] == '/');
+ status = glob_call_func(funcs->match, subpath, arg, enc);
+ if (status) return status;
+ }
</span> if (match_dir && pathtype == path_directory) {
const char *subpath = path + baselen + (baselen && path[baselen] == '/');
char *tmp = join_path(subpath, namelen, dirsep, "", 0);
<span class="p">@@ -3152,4 +3161,10 @@</span> Init_Dir(void)
* on Microsoft Windows.
*/
rb_file_const("FNM_SHORTNAME", INT2FIX(FNM_SHORTNAME));
<span class="gi">+
+ /* Document-const: File::Constants::FNM_NONDIR
+ *
+ * Makes patterns to match non directory. Valid only
+ */
+ rb_file_const("FNM_NONDIR", INT2FIX(FNM_NONDIR));
</span> }
</code></pre>
Ruby master - Bug #13852 (Closed): Backport r59693,59695 (Net::HTTP should raise error when CR/LF...
https://redmine.ruby-lang.org/issues/13852
2017-08-30T17:25:44Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>This is a ticket for backport management. The issue was already fixed on trunk.<br>
Let's treat r59693 as a bugfix and backport into stable branches.</p>
Ruby master - Feature #13608 (Rejected): Add TracePoint#thread
https://redmine.ruby-lang.org/issues/13608
2017-05-29T05:21:37Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>rb_trace_arg_t, TracePoint's internal struct, already stores the thread which the event happened at,<br>
but there's not API to fetch it.<br>
How about adding an API to get the info.</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb
index 53ee82a229..65dbc938ab 100644
</span><span class="gd">--- a/test/ruby/test_settracefunc.rb
</span><span class="gi">+++ b/test/ruby/test_settracefunc.rb
</span><span class="p">@@ -699,6 +699,23 @@</span> def test_tracepoint_enabled
assert_equal(false, trace.enabled?)
end
<span class="gi">+ def test_tracepoint_thread
+ trace = TracePoint.new(:call, :return){|tp|
+ next if !target_thread?
+ next if tp.path != __FILE__
+ assert_equal(Thread.current, tp.thread)
+ case tp.event
+ when :call
+ assert_raise(RuntimeError) {tp.return_value}
+ when :return
+ assert_equal("xyzzy", tp.return_value)
+ end
+ }
+ trace.enable{
+ foo
+ }
+ end
+
</span> def method_test_tracepoint_return_value obj
obj
end
<span class="gh">diff --git a/vm_trace.c b/vm_trace.c
index decb2c32e4..702b84e6e3 100644
</span><span class="gd">--- a/vm_trace.c
</span><span class="gi">+++ b/vm_trace.c
</span><span class="p">@@ -776,6 +776,12 @@</span> rb_tracearg_path(rb_trace_arg_t *trace_arg)
return trace_arg->path;
}
<span class="gi">+VALUE
+rb_tracearg_thread(rb_trace_arg_t *trace_arg)
+{
+ return trace_arg->th->self;
+}
+
</span> static void
fill_id_and_klass(rb_trace_arg_t *trace_arg)
{
<span class="p">@@ -913,6 +919,15 @@</span> tracepoint_attr_path(VALUE tpval)
}
/*
<span class="gi">+ * Thread of the event
+ */
+static VALUE
+tracepoint_attr_thread(VALUE tpval)
+{
+ return rb_tracearg_thread(get_trace_arg());
+}
+
+/*
</span> * Return the name at the definition of the method being called
*/
static VALUE
<span class="p">@@ -1502,6 +1517,7 @@</span> Init_vm_trace(void)
rb_define_method(rb_cTracePoint, "self", tracepoint_attr_self, 0);
rb_define_method(rb_cTracePoint, "return_value", tracepoint_attr_return_value, 0);
rb_define_method(rb_cTracePoint, "raised_exception", tracepoint_attr_raised_exception, 0);
<span class="gi">+ rb_define_method(rb_cTracePoint, "thread", tracepoint_attr_thread, 0);
</span>
rb_define_singleton_method(rb_cTracePoint, "stat", tracepoint_stat_s, 0);
</code></pre>
Ruby master - Feature #13577 (Assigned): Digest.file accidentally receives File object but uses f...
https://redmine.ruby-lang.org/issues/13577
2017-05-19T09:03:19Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Digest::SHA256.file()'s first argument is file path name but it accidentally accepts file object.<br>
But for file objects created with O_TMPFILE to_path returns the directory of the temporary file and this File.open will fail.</p>
<pre><code> class ::Digest::Class
# Creates a digest object and reads a given file, _name_.
# Optional arguments are passed to the constructor of the digest
# class.
#
# p Digest::SHA256.file("X11R6.8.2-src.tar.bz2").hexdigest
# # => "f02e3c85572dc9ad7cb77c2a638e3be24cc1b5bea9fdbb0b0299c9668475c534"
def self.file(name, *args)
new(*args).file(name)
end
end
module Instance
# Updates the digest with the contents of a given file _name_ and
# returns self.
def file(name)
File.open(name, "rb") {|f|
buf = ""
while f.read(16384, buf)
update buf
end
}
self
end
</code></pre>
Ruby master - Bug #12923 (Closed): Accessing singleton_class of fstring cause assertion failure
https://redmine.ruby-lang.org/issues/12923
2016-11-11T17:57:23Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>以下のワンライナーが「Assertion Failed: string.c:343:register_fstring:RBASIC_CLASS(ret) == rb_cString」します。</p>
<p>./miniruby -e'#encoding:us-ascii' -e'ObjectSpace.each_object{|o| o.singleton_class if o.is_a?(String)}; "hoge".intern'</p>
<p>クラッシュログは自分で走らせてもらうとして、何が起きているかというと、<br>
(1) 文字列リテラルをはじめとして、何らかのかたちでシンボルを作らずにrb_fstringからfrozenなStringを作る<br>
(2) その文字列に対してsingleton_classを呼ぶ。するとそのRVALUE->klassにsingleton_classが代入される<br>
(3) 同じ内容の文字列でString#internする(1.でシンボルを作っているとlookup_str_sym()にひっかかる)<br>
(4) register_fstringの"assert(RBASIC_CLASS(ret) == rb_cString);"で落ちる</p>
<p>しかし、どう直しましょうかね。</p>
Ruby master - Feature #12896 (Closed): Add compiler version message into rbconfig
https://redmine.ruby-lang.org/issues/12896
2016-11-04T06:30:53Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>When a extension library developer receives bug reports, they sometimes want to know what compiler is used for building the ruby binary.<br>
We can already know its compiler option but cannot know compiler name and version.</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/configure.in b/configure.in
index 3734afa..6793d7d 100644
</span><span class="gd">--- a/configure.in
</span><span class="gi">+++ b/configure.in
</span><span class="p">@@ -536,8 +536,10 @@</span> for option in --version -v -V -qversion; do
AS_CASE($cc_version_status, [0], [:], [continue])
AS_CASE($cc_version_message, [*Warning*], [continue])
cc_version='$(CC) '$option
<span class="gi">+ break
</span> done
AC_SUBST(CC_VERSION, $cc_version)
<span class="gi">+AC_SUBST(CC_VERSION_MESSAGE, $cc_version_message)
</span><span class="err">
</span> RUBY_UNIVERSAL_ARCH
if test "$target_cpu" != "$host_cpu" -a "$GCC" = yes -a "$cross_compiling" = no -a "$universal_binary" = no; then
</code></pre>
Ruby master - Bug #12791 (Closed): Don't allow ,-separator for cookie
https://redmine.ruby-lang.org/issues/12791
2016-09-27T03:11:40Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>RFC2965 allowed both ; and , as a separator for cookie, but RFC6265 only allows ;.</p>
<p>Moreover CVE-2016-7401 uses , as a separator to overwrite CSRF-token.<br>
<a href="https://gist.github.com/mala/457a25650950d4daf4144f98159802cc" class="external">https://gist.github.com/mala/457a25650950d4daf4144f98159802cc</a></p>
Ruby master - Misc #12751 (Open): Incompatibility of Ruby 3
https://redmine.ruby-lang.org/issues/12751
2016-09-12T06:38:38Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>META ticket for Ruby 3's breakages</p>
<ul>
<li>Encoding on Windows
<ul>
<li>[Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: On Windows use UTF-8 as filesystem encoding (Closed)" href="https://redmine.ruby-lang.org/issues/12654">#12654</a>]</li>
<li>[Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Use UTF-8 encoding for ENV on Windows (Closed)" href="https://redmine.ruby-lang.org/issues/12650">#12650</a>]</li>
</ul>
</li>
</ul>
Ruby master - Bug #12711 (Closed): Darwin doesn't show C backtrace correctly if iSIGSEGV is recei...
https://redmine.ruby-lang.org/issues/12711
2016-08-29T18:38:08Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Current Ruby can show C backtrace on the following case</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Process</span><span class="p">.</span><span class="nf">kill</span> <span class="ss">:SEGV</span><span class="p">,</span> <span class="vg">$$</span>
</code></pre>
<p>But can't on the following:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">require</span><span class="s2">"fiddle"</span>
<span class="no">Fiddle</span><span class="p">.</span><span class="nf">dlunwrap</span><span class="p">(</span><span class="mi">100</span><span class="p">).</span><span class="nf">class</span>
</code></pre>
Ruby master - Bug #12563 (Closed): backport 49758,50356
https://redmine.ruby-lang.org/issues/12563
2016-07-06T20:40:07Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>49758,50356 is required to run <code>make test-rubyspec</code> because without them rubyspec fails to build option/capi/ext.<br>
<a href="https://travis-ci.org/ruby/ruby/builds/142788121" class="external">https://travis-ci.org/ruby/ruby/builds/142788121</a></p>
Ruby master - Bug #12560 (Closed): backport r55602
https://redmine.ruby-lang.org/issues/12560
2016-07-06T18:20:29Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>They may fail parallel test-all.</p>
<ul>
<li>ruby_2_3: <a href="https://travis-ci.org/ruby/ruby/builds/141708115" class="external">https://travis-ci.org/ruby/ruby/builds/141708115</a>
</li>
<li>ruby_2_2: <a href="https://travis-ci.org/ruby/ruby/builds/142772798" class="external">https://travis-ci.org/ruby/ruby/builds/142772798</a>
</li>
</ul>
Ruby master - Feature #12553 (Closed): IO.readlines(filename, chomp: true)
https://redmine.ruby-lang.org/issues/12553
2016-07-05T09:11:11Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>IO.readlinesやIO.foreach、IO#each_lineなどは戻り値(またはブロックパラメータ)のそれぞれの「行」に改行を含みます。</p>
<p>POSIXの "Line" の定義は末尾の改行を含んでのものなので非常に正しい挙動なのですが、正直不便です。</p>
<blockquote>
<p><a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206" class="external">3.206 Line</a></p>
<p>A sequence of zero or more non- characters plus a terminating character.</p>
</blockquote>
<p>例えば IO.readlines(filename, chomp: true) などで改行を最初から削ってくれませんか</p>
Ruby master - Bug #12316 (Third Party's Issue): clang on Linux wrongly keep the inlined symbol
https://redmine.ruby-lang.org/issues/12316
2016-04-24T14:56:35Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Through clang-3.4 to clang-3.8 they wrongly keep the inlined symbols, and it cause compilation error.</p>
<pre><code>compiling vm.c
linking miniruby
vm.o: In function `vm_getinstancevariable':
/home/naruse/ruby-clang/./vm_insnhelper.c:876: undefined reference to `vm_getivar'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Makefile:223: recipe for target 'miniruby' failed
make: *** [miniruby] Error 1
</code></pre>
<p>Note that Apple's and FreeBSD's following versions works fine.</p>
<pre><code>Apple LLVM version 7.3.0 (clang-703.0.29)
Target: x86_64-apple-darwin15.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
FreeBSD clang version 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512
Target: x86_64-unknown-freebsd10.3
Thread model: posix
</code></pre>
Ruby master - Misc #12283 (Closed): Obsolete ChangeLog and commit message in Git-style
https://redmine.ruby-lang.org/issues/12283
2016-04-14T18:57:41Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>How about Git-style commit message instead of ChangeLog?</p>
<a name="Proposal"></a>
<h2 >Proposal<a href="#Proposal" class="wiki-anchor">¶</a></h2>
<p>After we reach the consensus</p>
<ol>
<li>Write a commit message in Git-style.</li>
</ol>
<ul>
<li>Line 1: summary of commit (around 80 characters or less, <50 is better)</li>
<li>Line 2: empty line</li>
<li>Line 3-: More detailed explanation of commit</li>
</ul>
<p>See also: <a href="https://git-scm.com/book/ch5-2.html#Commit-Guidelines" class="external">https://git-scm.com/book/ch5-2.html#Commit-Guidelines</a></p>
<ol start="2">
<li>Stop manually edit ChangeLog and auto-generate on <code>make dist</code>.</li>
</ol>
<a name="Rationale"></a>
<h2 >Rationale<a href="#Rationale" class="wiki-anchor">¶</a></h2>
<p>A good commit message should include the objective of the commit.<br>
Committers should write "what purpose the change is for".<br>
In this sense, ChangeLog is not a good format. It lists per-file changes, so committers tend to write just "how the files were changed".</p>
<p>Also, ChangeLog causes commit conflicts frequently.<br>
Though it is not related with the actual commit.<br>
People who uses svn/git should see the log with it; ChangeLog is only for tarball users.</p>
<a name="Status"></a>
<h2 >Status<a href="#Status" class="wiki-anchor">¶</a></h2>
<p>mame made this proposal at the developers' meeting (13 Apr.), and there was no objection.</p>
<p>Matz agreed with this proposal, provided that a ChangeLog-like plain-text file is generated from the commit log.<br>
The release manager will create the file when Ruby is released.</p>
<a name="Note"></a>
<h3 >Note<a href="#Note" class="wiki-anchor">¶</a></h3>
<p>This ticket does NOT propose a migration from SVN to Git. Never discuss that in this ticket.</p>
Ruby master - Feature #12244 (Open): Add a way to `integer - integer % num`
https://redmine.ruby-lang.org/issues/12244
2016-04-02T13:45:33Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>We sometimes calculates <code>integer - integer % num</code>.</p>
<p>For example time series events into time partitions, we write code like</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">event</span> <span class="c1"># {time: 1459580435, name: "hoge", text: "Rawr!"}</span>
<span class="n">partition</span> <span class="o">=</span> <span class="n">event</span><span class="p">[</span><span class="ss">:time</span><span class="p">]</span> <span class="o">-</span> <span class="n">event</span><span class="p">[</span><span class="ss">:time</span><span class="p">]</span> <span class="o">%</span> <span class="n">num</span>
<span class="n">chunk</span> <span class="o">=</span> <span class="n">get_chunk</span><span class="p">(</span><span class="n">partition</span><span class="p">)</span>
<span class="n">chunk</span><span class="p">.</span><span class="nf">write</span> <span class="n">event</span>
</code></pre>
<p><a href="https://twitter.com/tagomoris/status/715814050534461440" class="external">https://twitter.com/tagomoris/status/715814050534461440</a><br>
<a href="https://twitter.com/tagomoris/status/715814961260457985" class="external">https://twitter.com/tagomoris/status/715814961260457985</a></p>
<p>The name is always issue.<br>
There are some suggestions likes Integer#adjust](<a href="https://twitter.com/cocoatomo/status/716088708655489024" class="external">https://twitter.com/cocoatomo/status/716088708655489024</a>).</p>
<p>kosaki says <a href="https://twitter.com/kosaki55tea/status/716059296186765312" class="external">Excel's FLOOR() function is FLOOR(number, significance)</a>.<br>
Therefore Ruby should be <a href="https://twitter.com/kosaki55tea/status/716106530114768897" class="external">Integer#floor(digits=1, significance: nil)</a>.<br>
<a href="https://twitter.com/kosaki55tea/status/716107516409581568" class="external">kosaki agrees this</a>.</p>
<p>I checked the speed of a half baked implementation..., but it's 10x slow...</p>
<pre><code>% time ./miniruby -e'i=10000000;while i>0;i-=1;1459497599.floor(significance: 3600);end'
./miniruby 6.58s user 0.02s system 99% cpu 6.596 total
#3 1459604131 22:35:31 naruse@windy:~/obj/ruby
% time ./miniruby -e'i=10000000;while i>0;i-=1;t=1459497599;t-t%3600;end'
./miniruby -e'i=10000000;while i>0;i-=1;t=1459497599;t-t%3600;end' 0.52s user 0.00s system 99% cpu 0.520 total
</code></pre>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/numeric.c b/numeric.c
index 37217a1..f6acfe3 100644
</span><span class="gd">--- a/numeric.c
</span><span class="gi">+++ b/numeric.c
</span><span class="p">@@ -4123,6 +4123,42 @@</span> int_dotimes(VALUE num)
<span class="err">
</span> /*
* call-seq:
<span class="gi">+ * int.floor([ndigits]) -> integer or float
+ *
+ * Rounds +int+ to a given precision in decimal digits (default 0 digits).
+ *
+ * Precision may be negative. Returns a floating point number when +ndigits+
+ * is positive, +self+ for zero, and round down for negative.
+ *
+ * 1.round #=> 1
+ * 1.round(2) #=> 1.0
+ * 15.round(-1) #=> 20
+ */
+
+static VALUE
+int_floor(int argc, VALUE* argv, VALUE num)
+{
+ static ID keyword_ids[1];
+ VALUE kwargs[1], ndigits, opt;
+ if (!keyword_ids[0]) {
+ CONST_ID(keyword_ids[0], "significance");
+ }
+
+ rb_scan_args(argc, argv, "01:", &ndigits, &opt);
+ if (!NIL_P(opt)) {
+ VALUE factor;
+ long a, b;
+ rb_get_kwargs(opt, keyword_ids, 0, 1, kwargs);
+ factor = kwargs[0];
+ a = FIX2LONG(num);
+ b = FIX2LONG(factor);
+ return LONG2FIX(a - a % b);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
</span> * int.round([ndigits]) -> integer or float
*
* Rounds +int+ to a given precision in decimal digits (default 0 digits).
<span class="p">@@ -4321,7 +4357,7 @@</span> Init_Numeric(void)
rb_define_method(rb_cInteger, "to_i", int_to_i, 0);
rb_define_method(rb_cInteger, "to_int", int_to_i, 0);
rb_define_method(rb_cInteger, "to_f", int_to_f, 0);
<span class="gd">- rb_define_method(rb_cInteger, "floor", int_to_i, 0);
</span><span class="gi">+ rb_define_method(rb_cInteger, "floor", int_floor, -1);
</span> rb_define_method(rb_cInteger, "ceil", int_to_i, 0);
rb_define_method(rb_cInteger, "truncate", int_to_i, 0);
rb_define_method(rb_cInteger, "round", int_round, -1);
</code></pre>
Ruby master - Feature #12208 (Closed): Improve ri command
https://redmine.ruby-lang.org/issues/12208
2016-03-23T05:22:46Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Ruby has well documented in rdoc.<br>
It also has a frontend CLI.</p>
<p>But people seems not using <code>ri</code>...</p>
<p>At first, write your request here to improve ri command!</p>
Ruby master - Bug #12158 (Closed): Fixnum#% doesn't show its name on ZeroDivisionError
https://redmine.ruby-lang.org/issues/12158
2016-03-08T08:54:05Z
naruse (Yui NARUSE)
naruse@airemix.jp
<pre><code>% ruby -ve'p 12345 % 0'
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-freebsd10.2]
-e:1:in `<main>': divided by 0 (ZeroDivisionError)
</code></pre>
<p>It should behave like</p>
<pre><code>% ruby -ve'p 12345 / 0'
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-freebsd10.2]
-e:1:in `/': divided by 0 (ZeroDivisionError)
from -e:1:in `<main>'
</code></pre>
Ruby master - Bug #12011 (Closed): honor Marshal.load post proc value for TYPE_LINK
https://redmine.ruby-lang.org/issues/12011
2016-01-20T02:25:15Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Following test doesn't work.<br>
A patch also attached.<br>
Both of them are worked by nahi.</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb
index 482637f..262e7f6 100644
</span><span class="gd">--- a/test/ruby/test_marshal.rb
</span><span class="gi">+++ b/test/ruby/test_marshal.rb
</span><span class="p">@@ -712,4 +712,10 @@</span> def test_no_internal_ids
assert_predicate(status, :success?)
assert_equal(expected, out)
end
<span class="gi">+
+ def test_marshal_post_proc
+ str = 'x' # for link
+ obj = [str, str]
+ assert_equal(['X', 'X'], Marshal.load(Marshal.dump(obj), ->(v) { v == str ? v.upcase : v }))
+ end
</span> end
<span class="gh">diff --git a/marshal.c b/marshal.c
index d67ce87..d64e5ff 100644
</span><span class="gd">--- a/marshal.c
</span><span class="gi">+++ b/marshal.c
</span><span class="p">@@ -1569,7 +1569,7 @@</span> r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
rb_raise(rb_eArgError, "dump format error (unlinked)");
}
v = (VALUE)link;
<span class="gd">- r_post_proc(v, arg);
</span><span class="gi">+ v = r_post_proc(v, arg);
</span> break;
case TYPE_IVAR:
</code></pre>
<p><a href="https://github.com/ruby/ruby/pull/1204" class="external">https://github.com/ruby/ruby/pull/1204</a></p>
Ruby master - Feature #12010 (Closed): Exclude dot and dotdot from Dir#each
https://redmine.ruby-lang.org/issues/12010
2016-01-20T02:17:16Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p><code>Dir#each</code> and <code>Dir#read</code> (including <code>Dir.entries</code>, <code>Dir.foreach</code> and other methods) return <code>"."</code> and <code>".."</code> at first.<br>
But through the all real use case <code>"."</code> and <code>".."</code> are useless.<br>
How about excluding them?</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/dir.c b/dir.c
index 193b5be..4c23a2d 100644
</span><span class="gd">--- a/dir.c
</span><span class="gi">+++ b/dir.c
</span><span class="p">@@ -699,6 +699,8 @@</span> fundamental_encoding_p(rb_encoding *enc)
#else
# define READDIR(dir, enc) readdir((dir))
#endif
<span class="gi">+#define DIR_IS_DOT_OR_DOTDOT(dp) ((dp)->d_name[0] == '.' && \
+ ((dp)->d_name[1] == '\0' || ((dp)->d_name[1] == '.' && (dp)->d_name[2] == '\0')))
</span>
/*
* call-seq:
<span class="p">@@ -720,13 +722,12 @@</span> dir_read(VALUE dir)
GetDIR(dir, dirp);
errno = 0;
<span class="gd">- if ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
- return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc);
- }
- else {
- if (errno != 0) rb_sys_fail(0);
- return Qnil; /* end of stream */
</span><span class="gi">+ while ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
+ if (!DIR_IS_DOT_OR_DOTDOT(dp))
+ return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc);
</span> }
<span class="gi">+ if (errno != 0) rb_sys_fail(0);
+ return Qnil; /* end of stream */
</span> }
/*
<span class="p">@@ -764,6 +765,7 @@</span> dir_each(VALUE dir)
const char *name = dp->d_name;
size_t namlen = NAMLEN(dp);
VALUE path;
<span class="gi">+ if (DIR_IS_DOT_OR_DOTDOT(dp)) continue;
</span> #if NORMALIZE_UTF8PATH
if (norm_p && has_nonascii(name, namlen) &&
!NIL_P(path = rb_str_normalize_ospath(name, namlen))) {
<span class="gh">diff --git a/test/pathname/test_pathname.rb b/test/pathname/test_pathname.rb
index 2690a3f..33f0d44 100644
</span><span class="gd">--- a/test/pathname/test_pathname.rb
</span><span class="gi">+++ b/test/pathname/test_pathname.rb
</span><span class="p">@@ -1238,7 +1238,7 @@</span> def test_entries
with_tmpchdir('rubytest-pathname') {|dir|
open("a", "w") {}
open("b", "w") {}
<span class="gd">- assert_equal([Pathname("."), Pathname(".."), Pathname("a"), Pathname("b")], Pathname(".").entries.sort)
</span><span class="gi">+ assert_equal([Pathname("a"), Pathname("b")], Pathname(".").entries.sort)
</span> }
end
<span class="p">@@ -1248,7 +1248,7 @@</span> def test_each_entry
open("b", "w") {}
a = []
Pathname(".").each_entry {|v| a << v }
<span class="gd">- assert_equal([Pathname("."), Pathname(".."), Pathname("a"), Pathname("b")], a.sort)
</span><span class="gi">+ assert_equal([Pathname("a"), Pathname("b")], a.sort)
</span> }
end
<span class="p">@@ -1278,7 +1278,7 @@</span> def test_opendir
Pathname(".").opendir {|d|
d.each {|e| a << e }
}
<span class="gd">- assert_equal([".", "..", "a", "b"], a.sort)
</span><span class="gi">+ assert_equal(["a", "b"], a.sort)
</span> }
end
<span class="gh">diff --git a/test/ruby/test_dir.rb b/test/ruby/test_dir.rb
index 0cc5a6a..d3f6602 100644
</span><span class="gd">--- a/test/ruby/test_dir.rb
</span><span class="gi">+++ b/test/ruby/test_dir.rb
</span><span class="p">@@ -186,7 +186,7 @@</span> def test_glob_recursive
def assert_entries(entries)
entries.sort!
<span class="gd">- assert_equal(%w(. ..) + ("a".."z").to_a, entries)
</span><span class="gi">+ assert_equal(("a".."z").to_a, entries)
</span> end
def test_entries
</code></pre>
Ruby master - Feature #11987 (Open): daemons can't show the backtrace of rb_bug
https://redmine.ruby-lang.org/issues/11987
2016-01-13T10:38:57Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Ruby shows backtrace and other information into stderr on rb_bug.<br>
But daemon process redirects stderr into /dev/null.<br>
How do I get the log?</p>
<p>NOTE: if I can reproduce this, I can use strace to get it. But issues are not always reproducible.</p>
Ruby master - Bug #11669 (Closed): inconsitent behavior of refining frozen class
https://redmine.ruby-lang.org/issues/11669
2015-11-09T05:38:34Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Is this expected behavior?</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">C</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="nb">p</span> <span class="mi">1</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">module</span> <span class="nn">Foo</span>
<span class="n">refine</span> <span class="no">C</span> <span class="k">do</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="nb">p</span> <span class="mi">2</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">using</span> <span class="no">Foo</span>
<span class="no">C</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">foo</span> <span class="c1">#=> 2</span>
<span class="no">C</span><span class="p">.</span><span class="nf">freeze</span>
<span class="k">module</span> <span class="nn">Foo</span>
<span class="n">refine</span> <span class="no">C</span> <span class="k">do</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="nb">p</span> <span class="mi">3</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">bar</span> <span class="c1">#=> can't modify frozen class (RuntimeError)</span>
<span class="nb">p</span> <span class="mi">3</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">C</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">foo</span>
<span class="no">C</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">bar</span>
</code></pre>
<pre><code>ruby 2.3.0dev (2015-10-26 trunk 52291) [x86_64-darwin15]
2
test.rb:21:in `block in <module:Foo>': can't modify frozen class (RuntimeError)
from test.rb:17:in `refine'
from test.rb:17:in `<module:Foo>'
from test.rb:16:in `<main>'
</code></pre>
Ruby master - Feature #11577 (Open): Add encodeURIComponent compatible API for URI
https://redmine.ruby-lang.org/issues/11577
2015-10-09T13:40:10Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>How about adding encodeURIComponent/decodeURIComponent compatible API?</p>
<p>There's already have some methods:</p>
<ul>
<li>URI.escape: context aware but deprecated.</li>
<li>URIencode_www_form: application/x-www-form-urlencoded, which encodes spaces into '+'</li>
<li>URIencode_www_form_component: above component</li>
</ul>
<p>So it doesn't have non-form URI escape method.</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/lib/uri/common.rb b/lib/uri/common.rb
index 1444ae8..0017ae3 100644
</span><span class="gd">--- a/lib/uri/common.rb
</span><span class="gi">+++ b/lib/uri/common.rb
</span><span class="p">@@ -1,4 +1,5 @@</span>
<span class="gd">-#--
</span><span class="gi">+#
+# -*- frozen-string-literal: true -*-
</span> # = uri/common.rb
#
# Author:: Akira Yamada <akira@ruby-lang.org>
<span class="p">@@ -329,27 +330,72 @@</span> module URI
DEFAULT_PARSER.make_regexp(schemes)
end
<span class="gi">+ TBLENCURICOMP_ = {} # :nodoc:
</span> TBLENCWWWCOMP_ = {} # :nodoc:
<span class="gd">- 256.times do |i|
- TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
- end
- TBLENCWWWCOMP_[' '] = '+'
- TBLENCWWWCOMP_.freeze
</span><span class="gi">+ TBLDECURICOMP_ = {} # :nodoc:
</span> TBLDECWWWCOMP_ = {} # :nodoc:
256.times do |i|
h, l = i>>4, i&15
<span class="gd">- TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
- TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
- TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
- TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
</span><span class="gi">+ c = i.chr.freeze
+ k = sprintf('%%%X%X', h, l).freeze
+ TBLENCURICOMP_[c] = TBLENCWWWCOMP_[c] = k
+ TBLDECURICOMP_[k] = TBLDECWWWCOMP_[k] = c
+ k = sprintf('%%%x%X', h, l).freeze
+ TBLDECURICOMP_[k] = TBLDECWWWCOMP_[k] = c
+ k = sprintf('%%%X%x', h, l).freeze
+ TBLDECURICOMP_[k] = TBLDECWWWCOMP_[k] = c
+ k = sprintf('%%%x%x', h, l).freeze
+ TBLDECURICOMP_[k] = TBLDECWWWCOMP_[k] = c
</span> end
<span class="gi">+ TBLENCWWWCOMP_[' '] = '+'
+ TBLENCWWWCOMP_.freeze
+ TBLENCURICOMP_.freeze
</span> TBLDECWWWCOMP_['+'] = ' '
TBLDECWWWCOMP_.freeze
<span class="gi">+ TBLDECURICOMP_.freeze
+
+ # Encode given +str+ to URL-encoded form data.
+ #
+ # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
+ # (ASCII space) to + and converts others to %XX.
+ #
+ # If +enc+ is given, convert +str+ to the encoding before percent encoding.
+ #
+ # This is an implementation of
+ # http://www.ecma-international.org/ecma-262/6.0/#sec-encodeuricomponent-uricomponent
+ #
+ # See URI.encode_www_form_component, URI.encode_www_form
+ def self.encode_component(str)
+ str = str.to_s.dup
+ if !str.ascii_only?
+ enc = str.encoding
+ if enc != Encoding::ASCII_8BIT && enc != Encoding::UTF_8
+ str.encode!(Encoding::UTF_8, invalid: :replace, undef: :replace)
+ end
+ str.force_encoding(Encoding::ASCII_8BIT)
+ end
+ str.gsub!(/[^\-_.!~*'()0-9A-Za-z]/, TBLENCURICOMP_)
+ str.force_encoding(Encoding::US_ASCII)
+ end
+
+ # Decode given +str+ of URL-encoded form data.
+ #
+ #
+ # This doesn't decodes + to SP.
+ #
+ # This is an implementation of
+ # http://www.ecma-international.org/ecma-262/6.0/#sec-decodeuricomponent-encodeduricomponent
+ #
+ # See URI.decode_www_form_component, URI.decode_www_form
+ def self.decode_component(str, enc=Encoding::UTF_8)
+ raise ArgumentError, "invalid %-encoding (#{str})" if /%(?!\h\h)/ =~ str
+ str.b.gsub(/%\h\h/, TBLDECURICOMP_).force_encoding(enc)
+ end
</span>
HTML5ASCIIINCOMPAT = defined? Encoding::UTF_7 ? [Encoding::UTF_7, Encoding::UTF_16BE, Encoding::UTF_16LE,
Encoding::UTF_32BE, Encoding::UTF_32LE] : [] # :nodoc:
<span class="gd">- # Encode given +str+ to URL-encoded form data.
</span><span class="gi">+ # Encode given +str+ in application/x-www-form-urlencoded format.
</span> #
# This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
# (ASCII space) to + and converts others to %XX.
<span class="p">@@ -373,7 +419,7 @@</span> module URI
str.force_encoding(Encoding::US_ASCII)
end
<span class="gd">- # Decode given +str+ of URL-encoded form data.
</span><span class="gi">+ # Decode given +str+ in application/x-www-form-urlencoded format.
</span> #
# This decodes + to SP.
#
<span class="p">@@ -457,7 +503,7 @@</span> module URI
if isindex
if sep.empty?
val = key
<span class="gd">- key = ''
</span><span class="gi">+ key = String.new
</span> end
isindex = false
end
<span class="p">@@ -471,7 +517,7 @@</span> module URI
if val
val.gsub!(/\+|%\h\h/, TBLDECWWWCOMP_)
else
<span class="gd">- val = ''
</span><span class="gi">+ val = String.new
</span> end
ary << [key, val]
<span class="gh">diff --git a/test/uri/test_common.rb b/test/uri/test_common.rb
index 5620415..c6b5633 100644
</span><span class="gd">--- a/test/uri/test_common.rb
</span><span class="gi">+++ b/test/uri/test_common.rb
</span><span class="p">@@ -54,6 +54,34 @@</span> class TestCommon < Test::Unit::TestCase
assert_raise(NoMethodError) { Object.new.URI("http://www.ruby-lang.org/") }
end
<span class="gi">+ def test_encode_component
+ assert_equal("%00%20!%22%23%24%25%26'()*%2B%2C-.%2F09%3A%3B%3C%3D%3E%3F%40" \
+ "AZ%5B%5C%5D%5E_%60az%7B%7C%7D~",
+ URI.encode_component("\x00 !\"\#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~"))
+ assert_equal("%E6%9F%8A", URI.encode_component(
+ "\x95\x41".force_encoding(Encoding::Shift_JIS)))
+ assert_equal("%E3%81%82", URI.encode_component(
+ "\x30\x42".force_encoding(Encoding::UTF_16BE)))
+ assert_equal("%E3%81%82", URI.encode_component(
+ "\e$B$\"\e(B".force_encoding(Encoding::ISO_2022_JP)))
+ end
+
+ def test_decode_component
+ assert_equal(" +!\"\#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~",
+ URI.decode_component(
+ "%20+%21%22%23%24%25%26%27%28%29*%2B%2C-.%2F09%3A%3B%3C%3D%3E%3F%40" \
+ "AZ%5B%5C%5D%5E_%60az%7B%7C%7D%7E"))
+ assert_equal("\xA1\xA2".force_encoding(Encoding::EUC_JP),
+ URI.decode_component("%A1%A2", "EUC-JP"))
+ assert_equal("\xE3\x81\x82\xE3\x81\x82".force_encoding("UTF-8"),
+ URI.decode_component("\xE3\x81\x82%E3%81%82".force_encoding("UTF-8")))
+
+ assert_raise(ArgumentError){URI.decode_component("%")}
+ assert_raise(ArgumentError){URI.decode_component("%a")}
+ assert_raise(ArgumentError){URI.decode_component("x%a_")}
+ assert_nothing_raised(ArgumentError){URI.decode_component("x"*(1024*1024))}
+ end
+
</span> def test_encode_www_form_component
assert_equal("%00+%21%22%23%24%25%26%27%28%29*%2B%2C-.%2F09%3A%3B%3C%3D%3E%3F%40" \
"AZ%5B%5C%5D%5E_%60az%7B%7C%7D%7E",
</code></pre>
Ruby master - Misc #11474 (Closed): [META] Call for Feature Proposals for Ruby 2.3
https://redmine.ruby-lang.org/issues/11474
2015-08-21T05:44:04Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>This is meta ticket for Call for Feature Proposals for Ruby 2.3.<br>
After you attach your slide to the related ticket, write ticket number as a comment here to avoid I miss it.</p>
Ruby master - Bug #11457 (Closed): miniruby SEGVs on CentOS 5
https://redmine.ruby-lang.org/issues/11457
2015-08-18T11:03:58Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>minirubyがCentOS 5 (64bit) でSEGVするやつです。<br>
<a href="http://rubyci.s3.amazonaws.com/centos5-64/ruby-trunk/log/20150818T093302Z.log.html.gz#miniversion" class="external">http://rubyci.s3.amazonaws.com/centos5-64/ruby-trunk/log/20150818T093302Z.log.html.gz#miniversion</a></p>
<p>パッチ見ればわかりますが原因はr49452です。<br>
2.1と2.2にはバックポートされているようだけど、2.0.0には入っていないと思う。</p>
Ruby master - Feature #11302 (Closed): Dir.entries and Dir.foreach without [".", ".."]
https://redmine.ruby-lang.org/issues/11302
2015-06-24T06:24:58Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Dir.entries returns an array of its content with "." and "..".<br>
But as far as I met, almost all cases don't need them.</p>
<p>How about adding such new method or options?</p>
Ruby master - Feature #11218 (Closed): File.open FILE_SHARE_DELETE
https://redmine.ruby-lang.org/issues/11218
2015-06-04T05:19:41Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>fluentdという、OSSのログコレクタがあるのですが、これには<code>in_tail</code>プラグインというものがあります。<br>
これは、ログファイルを監視して、ログがファイルに追記されたらその分を読み込んでJSONにして他に流します。</p>
<p>このログファイルはfluentdではない他の誰か、ApacheだったりRailsだったりします。<br>
この手のログファイルを書いていく流儀はいくつかありますが、ご存じの通りRailsやApacheは基本的に特定のパスにひたすら追記し続け、<br>
それでは肥大化しすぎるのでlogrotateなどと組み合わせて適宜rotateしていく運用が一般的でしょう。<br>
つまり、ログファイルをrenameし、ApacheやRailsなどにsignalで再起動して新しいファイルに書くわけです。</p>
<p>ところで、Windowsは誰かが開いているファイルをrenameすることはできないので通常このようなことは出来ません。<br>
やるにはファイルを開く際の<code>CreateFile</code>に<code>FILE_SHARE_DELETE</code>フラッグをつけてやる必要があります。<br>
そんなことやってる人いるのかというご指摘もあるとは思いますが、やる人はやっているようです。<br>
<a href="https://issues.apache.org/jira/browse/HADOOP-8564" class="external">https://issues.apache.org/jira/browse/HADOOP-8564</a></p>
<p>さて、RubyのWindowsの<code>open(2)</code>ラッパー、<code>rb_w32_open</code>/<code>rb_w32_wopen</code>はテキストモードの場合は<code>_open</code>/<code>_wopen</code>を使っており、<br>
これはおいておくとして、バイナリモードの場合は<code>CreateFile</code>を使っているものの現状<code>FILE_SHARE_DELETE</code>は渡していないので、<br>
なんらかの手段で<code>FILE_SHARE_DELETE</code>を付けられるようになりませんか。</p>
Ruby master - Feature #11216 (Closed): inode for Windows
https://redmine.ruby-lang.org/issues/11216
2015-06-03T13:32:22Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>現在WindowsではFile::Stat#inodeは常に0を返しますが、<br>
例えばあるlogrotate的な運用の行われるファイルを監視・開きっぱなしにして、追記されれば差分を読み、<br>
rotateされた場合は検出して最新のログを読む…というような場合に、rotateを検出する際にinodeは使われます。</p>
<p>WindowsにはGetFileInformationByHandle()というAPIで得られるBY_HANDLE_FILE_INFORMATION構造体のメンバ、<br>
nFileIndexHigh/Lowから64bitのファイルシステムuniqueなIDがあるので、これをinodeとして代用が可能です。</p>
<p>問題はこれをFile::Statにいれるにはもとのstruct stat.inoが16bitで小さすぎる点ですが、<br>
幸いstruct stati64にはアライメントの都合で隙間があるため、そこにつっこむことにしています。<br>
(devとudevが常に同じとか、uidやgidが常に0など無意味な値は他にもありますが)<br>
<a href="https://msdn.microsoft.com/ja-jp/library/ms350241%28v=vs.71%29.aspx" class="external">https://msdn.microsoft.com/ja-jp/library/ms350241%28v=vs.71%29.aspx</a></p>
<pre><code>diff --git a/file.c b/file.c
index ac99de6..761ff1f 100644
--- a/file.c
+++ b/file.c
@@ -548,7 +548,18 @@ rb_stat_dev_minor(VALUE self)
static VALUE
rb_stat_ino(VALUE self)
{
-#if SIZEOF_STRUCT_STAT_ST_INO > SIZEOF_LONG
+#ifdef _WIN32
+ struct stat *st = get_stat(self);
+ unsigned short *p2 = (unsigned short *)st;
+ unsigned int *p4 = (unsigned int *)st;
+ uint64_t r;
+ r = p2[2];
+ r <<= 16;
+ r |= p2[7];
+ r <<= 32;
+ r |= p4[5];
+ return ULL2NUM(r);
+#elif SIZEOF_STRUCT_STAT_ST_INO > SIZEOF_LONG
return ULL2NUM(get_stat(self)->st_ino);
#else
return ULONG2NUM(get_stat(self)->st_ino);
diff --git a/win32/win32.c b/win32/win32.c
index b23e9af..fcfc0a6 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -4975,6 +4975,48 @@ static time_t filetime_to_unixtime(const FILETIME *ft);
static WCHAR *name_for_stat(WCHAR *buf, const WCHAR *path);
static DWORD stati64_handle(HANDLE h, struct stati64 *st);
+/* License: Ruby's */
+static void
+stati64_set_inode(PBY_HANDLE_FILE_INFORMATION pinfo, struct stati64 *st)
+{
+ /* struct stati64 layout
+ *
+ * dev: 0-3
+ * ino: 4-5
+ * mode: 6-7
+ * nlink: 8-9
+ * uid: 10-11
+ * gid: 12-13
+ * _: 14-15
+ * rdev: 16-19
+ * _: 20-23
+ * size: 24-31
+ * atime: 32-39
+ * mtime: 40-47
+ * ctime: 48-55
+ *
+ */
+ unsigned short *p2 = (unsigned short *)st;
+ unsigned int *p4 = (unsigned int *)st;
+ DWORD high = pinfo->nFileIndexHigh;
+ p2[2] = high >> 16;
+ p2[7] = high & 0xFFFF;
+ p4[5] = pinfo->nFileIndexLow;
+}
+
+/* License: Ruby's */
+static DWORD
+stati64_set_inode_handle(HANDLE h, struct stati64 *st)
+{
+ BY_HANDLE_FILE_INFORMATION info;
+ DWORD attr = (DWORD)-1;
+
+ if (GetFileInformationByHandle(h, &info)) {
+ stati64_set_inode(&info, st);
+ }
+ return attr;
+}
+
#undef fstat
/* License: Ruby's */
int
@@ -5000,7 +5042,11 @@ rb_w32_fstati64(int fd, struct stati64 *st)
struct stat tmp;
int ret;
- if (GetEnvironmentVariableW(L"TZ", NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) return _fstati64(fd, st);
+ if (GetEnvironmentVariableW(L"TZ", NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ ret = _fstati64(fd, st);
+ stati64_set_inode_handle((HANDLE)_get_osfhandle(fd), st);
+ return ret;
+ }
ret = fstat(fd, &tmp);
if (ret) return ret;
@@ -5022,6 +5068,7 @@ stati64_handle(HANDLE h, struct stati64 *st)
st->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime);
st->st_ctime = filetime_to_unixtime(&info.ftCreationTime);
st->st_nlink = info.nNumberOfLinks;
+ stati64_set_inode(&info, st);
attr = info.dwFileAttributes;
}
return attr;
</code></pre>
Ruby master - Bug #10397 (Closed): gcc 4.1.2 for x86 can't build trunk
https://redmine.ruby-lang.org/issues/10397
2014-10-19T07:12:15Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Recently CentOS 5.6 x86 can't build ruby-trunk.<br>
This is since r47562 and it hit <a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=16185" class="external">gcc bug</a>.</p>
<p>Do people want CRuby to continue RHEL/CentOS 5.6 support?<br>
If so, ideally it is fixed by gcc.</p>
Ruby master - Feature #9871 (Open): load a ruby library which doesn't have extension
https://redmine.ruby-lang.org/issues/9871
2014-05-28T10:07:07Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>拡張子のない Ruby スクリプトファイルを require する手段を提供しませんか。</p>
<p>Rubyスクリプトを拡張子無しで書くことはしばしばあります。<br>
例えば Unix のコマンドを Ruby で書くときがそうでしょう。</p>
<p>そのスクリプトの部品を将来再利用しそうなとき、場合によっては if $0 == <strong>FILE</strong> ハックを用いて、<br>
他のファイルから読み込まれたときはコマンドを起動を行わないようにするわけですが、<br>
拡張子がないとそもそも読み込めないので、まずファイル名を変えないといけません。</p>
<p>という具合で残念な感じなので、拡張子無しのファイルを読み込む手段を提供しませんか。<br>
require_relative は拡張子無しでも読める、辺りがいいと思うのですが。</p>
Ruby master - Feature #9772 (Rejected): IO#statfs and File::Statfs
https://redmine.ruby-lang.org/issues/9772
2014-04-24T04:03:50Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>IO#statfs and File::Statfs を追加しませんか。<br>
(テストで statfs.f_type が必要だったのでとりあえず追加してしまっていますが)</p>
<p>statfs(2) は Unix 系 OS でそこそこ普及している、あるパスが属するファイルシステムについての情報を得るためのシステムコールです。<br>
OS によって多少差がありますが、だいたい以下の様な情報が得られます。</p>
<pre><code> struct statfs {
uint32_t f_type; /* type of filesystem */
uint64_t f_bsize; /* filesystem fragment size */
uint64_t f_blocks; /* total data blocks in filesystem */
uint64_t f_bfree; /* free blocks in filesystem */
int64_t f_bavail; /* free blocks avail to non-superuser */
uint64_t f_files; /* total file nodes in filesystem */
int64_t f_ffree; /* free nodes avail to non-superuser */
char f_fstypename[MFSNAMELEN]; /* filesystem type name */
};
</code></pre>
<p>f_type の値が OS 依存だったり、Linux 以外だとそもそもどれがどの値かきちんと定義されていないとか<br>
ツッコミどころの多い API ではあるのですが、他では得られない情報が得られます。</p>
<p>たとえば、以前から CRuby で使われている用途としては、あるファイルの乗っているファイルシステムが、<br>
HFS+ かどうかがわかります。言い換えると、ファイル名が正規化されているかどうかがわかります。<br>
ありがちな反論として、書き込めば正規化されるかわかるだろうというのがありえますが、<br>
目当てのファイルと同じディレクトリに書き込めるとは限りません。<br>
違うディレクトリだと別のファイルシステムがマウントされている可能性があります。</p>
<p>なお今回の用途は、SEEK_DATA/SEEK_HOLEができるか否かを、実際にやってみる以外の方法で知りたかった、というものです。<br>
(Rubyのテストなのにやってみて調べるではテストにあまりならない)</p>
Ruby master - Feature #9647 (Closed): File::Stat#birthtimeの追加
https://redmine.ruby-lang.org/issues/9647
2014-03-17T05:13:51Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>File::Stat#birthtimeを追加しませんか。</p>
<p>以下の様なシステムの stat(2) には st_birthtimespec があり、<br>
(ctime = change time ではなく) ファイルを作成した日時を得ることができます。<br>
<a href="http://netbsd.gw.com/cgi-bin/man-cgi?stat+2+NetBSD-current" class="external">http://netbsd.gw.com/cgi-bin/man-cgi?stat+2+NetBSD-current</a><br>
<a href="http://www.freebsd.org/cgi/man.cgi?query=stat&sektion=2&apropos=0&manpath=FreeBSD+10.0-RELEASE" class="external">http://www.freebsd.org/cgi/man.cgi?query=stat&sektion=2&apropos=0&manpath=FreeBSD+10.0-RELEASE</a><br>
<a href="https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/stat.2.html" class="external">https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/stat.2.html</a></p>
<p>また、Windowsはbirthtimeがあるがctimeがないという環境ですので、birthtimeはctimeを返します。<br>
<a href="http://msdn.microsoft.com/ja-jp/library/ms350241(v=vs.71).aspx" class="external">http://msdn.microsoft.com/ja-jp/library/ms350241(v=vs.71).aspx</a></p>
<p>今回のパッチではLinuxやOpenBSDなど、struct statにbirthtimeがない環境では、<br>
Windows同様ctimeを返すようにしています。<br>
(が、意味が違うからWindows以外ではNotImpErrorの方がいいかも)</p>
<p><a href="https://github.com/nurse/ruby/compare/ruby:trunk...birthtime" class="external">https://github.com/nurse/ruby/compare/ruby:trunk...birthtime</a></p>
Ruby master - Feature #8850 (Assigned): Convert Rational to decimal string
https://redmine.ruby-lang.org/issues/8850
2013-09-02T10:02:36Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>On Ruby 2.1.0, decimal literal is introduced.<br>
It generates Rational but it cannot easily convert to decimal string.<br>
You know, Rational#to_f is related to this but</p>
<ul>
<li>
<p>Float is not exact number<br>
** 0.123456789123456789r.to_f.to_s #=> "0.12345678912345678"</p>
</li>
<li>
<p>it can't handle recursive decimal<br>
** (151/13r).to_f.to_s #=> "11.615384615384615"</p>
</li>
<li>
<p>the method name<br>
** to_decimal<br>
** to_decimal_string<br>
** to_s(format: :decimal)<br>
** extend sprintf</p>
</li>
<li>
<p>how does it express recursive decimal<br>
** (151/13r).to_decimal_string #=> "11.615384..."<br>
** (151/13r).to_decimal_string #=> "11.(615384)"</p>
</li>
</ul>
<p>Example implementation is following.<br>
Its result is<br>
** 0.123456789123456789r.to_f.to_s #=> "0.123456789123456789"<br>
** (151/13r).to_f.to_s #=> "11.(615384)"</p>
<pre><code>class Rational
def to_decimal_string(base=10)
n = numerator
d = denominator
r, n = n.divmod d
str = r.to_s(base)
return str if n == 0
h = {}
str << '.'
n *= base
str.size.upto(Float::INFINITY) do |i|
r, n = n.divmod d
if n == 0
str << r.to_s(base)
break
elsif h.key? n
str[h[n], 0] = '('
str << ')'
break
else
str << r.to_s(base)
h[n] = i
n *= base
end
end
str
end
end
</code></pre>
Ruby master - Bug #8782 (Closed): Don't set rl_getc_function on editline
https://redmine.ruby-lang.org/issues/8782
2013-08-12T16:31:55Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>r42402 以来 OS X 等の editline 環境では #define rl_getc(f) EOF が使われるようになってしまって残念なことになっていたわけですが、<br>
そもそも editline の readline wrapper は non ASCII に対応していません。<br>
(editline 自体には UTF-8 のみの対応が入ったが、readline wrapper は src/readline.c の _getc_function を経由するので non ASCII は化ける)</p>
<p>ので、いっそ rl_getc_function を使わないようにしてはどうでしょう。<br>
以下のようなパッチを当てると、readline なしの OS X の irb で日本語が使えるようになります。</p>
<p>diff --git a/ext/readline/extconf.rb b/ext/readline/extconf.rb<br>
index 0b121c1..bc0ee77 100644<br>
--- a/ext/readline/extconf.rb<br>
+++ b/ext/readline/extconf.rb<br>
@@ -94,4 +94,5 @@ readline.have_func("clear_history")<br>
readline.have_func("rl_redisplay")<br>
readline.have_func("rl_insert_text")<br>
readline.have_func("rl_delete_text")<br>
+readline.have_func("el_init")<br>
create_makefile("readline")<br>
diff --git a/ext/readline/readline.c b/ext/readline/readline.c<br>
index 0f76d1a..85109f0 100644<br>
--- a/ext/readline/readline.c<br>
+++ b/ext/readline/readline.c<br>
@@ -130,12 +130,7 @@ static VALUE readline_instream;<br>
static VALUE readline_outstream;</p>
<a name="if-defined-HAVE_RL_GETC_FUNCTION"></a>
<h2 >#if defined HAVE_RL_GETC_FUNCTION<a href="#if-defined-HAVE_RL_GETC_FUNCTION" class="wiki-anchor">¶</a></h2>
<h2>-#ifndef HAVE_RL_GETC<br>
-#define rl_getc(f) EOF<br>
-#endif</h2>
<p>-static int readline_getc(FILE *);<br>
+# ifndef HAVE_EL_INIT<br>
static int<br>
readline_getc(FILE *input)<br>
{<br>
@@ -187,6 +182,7 @@ readline_getc(FILE *input)<br>
#endif<br>
return FIX2INT(c);<br>
}<br>
+# endif<br>
#elif defined HAVE_RL_EVENT_HOOK<br>
#define BUSY_WAIT 0</p>
<p>@@ -1771,7 +1767,9 @@ Init_readline()<br>
/* libedit check rl_getc_function only when rl_initialize() is called, <em>/<br>
/</em> and using_history() call rl_initialize(). <em>/<br>
/</em> This assignment should be placed before using_history() */<br>
+# ifndef HAVE_EL_INIT<br>
rl_getc_function = readline_getc;<br>
+# endif<br>
#elif defined HAVE_RL_EVENT_HOOK<br>
rl_event_hook = readline_event;<br>
#endif</p>
Ruby master - Feature #8734 (Closed): irbに複数行履歴機能が欲しい
https://redmine.ruby-lang.org/issues/8734
2013-08-05T16:22:48Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>現在のirbはクラス定義やメソッド定義のような複数行にわたるものを書くと、<br>
実行結果は閉じるまで返ってこないのに、履歴は一行ごとにバラバラになってしまい、とても不便です。<br>
なので、zshのように、複数行をまとめて履歴として扱えるようになって欲しいです。<br>
参考: <a href="http://news.mynavi.jp/column/zsh/003/index.html" class="external">http://news.mynavi.jp/column/zsh/003/index.html</a></p>
Ruby master - Feature #8678 (Assigned): Allow invalid string to work with regexp
https://redmine.ruby-lang.org/issues/8678
2013-07-24T14:47:22Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Legacy Ruby 1.8 could regexp match with broken strings.<br>
People can find characters from binary data on the age.</p>
<p>After Ruby 1.9, Ruby raises Exception if it does regexp match with broken strings.<br>
So it became hard to work with character-wise regexp matching with binary data.</p>
<p>Following patch allows it with the constant Regexp::LOOSEENCODING.</p>
<p>commit eb0111ff7ae3f563ce201c4a5f724f121336d42d<br>
Author: NARUSE, Yui <a href="mailto:naruse@ruby-lang.org" class="email">naruse@ruby-lang.org</a><br>
Date: Mon Jul 22 05:37:44 2013 +0900</p>
<pre><code>* Regexp
* New constant:
* Regexp::ENCODINGLOOSE: declare execute matching even if the target string
is invalid byte sequence. [experimental]
</code></pre>
<p>diff --git a/NEWS b/NEWS<br>
index f5fe388..ade0b03 100644<br>
--- a/NEWS<br>
+++ b/NEWS<br>
@@ -35,6 +35,11 @@ with all sufficient information, see the ChangeLog file.</p>
<ul>
<li>misc
<ul>
<li>Mutex#owned? is no longer experimental.</li>
</ul>
</li>
</ul>
<p>+* Regexp</p>
<ul>
<li>
<ul>
<li>New constant:</li>
</ul>
</li>
<li>
<ul>
<li>Regexp::ENCODINGLOOSE: declare execute matching even if the target string</li>
</ul>
</li>
<li>
<pre><code> is invalid byte sequence. [experimental]
</code></pre>
</li>
<li>
</ul>
<ul>
<li>String
<ul>
<li>New methods:
<ul>
<li>String#scrub and String#scrub! verify and fix invalid byte sequence.<br>
diff --git a/re.c b/re.c<br>
index e5cc79d..230a2e0 100644<br>
--- a/re.c<br>
+++ b/re.c<br>
@@ -256,6 +256,7 @@ rb_memsearch(const void *x0, long m, const void *y0, long n, rb_encoding *enc)</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>#define REG_LITERAL FL_USER5<br>
#define REG_ENCODING_NONE FL_USER6<br>
+#define REG_ENCODING_LOOSE FL_USER7</p>
<p>#define KCODE_FIXED FL_USER4</p>
<p>@@ -263,6 +264,7 @@ rb_memsearch(const void *x0, long m, const void *y0, long n, rb_encoding *enc)<br>
(ONIG_OPTION_IGNORECASE|ONIG_OPTION_MULTILINE|ONIG_OPTION_EXTEND)<br>
#define ARG_ENCODING_FIXED 16<br>
#define ARG_ENCODING_NONE 32<br>
+#define ARG_ENCODING_LOOSE 64</p>
<p>static int<br>
char_to_option(int c)<br>
@@ -1251,7 +1253,8 @@ rb_reg_prepare_enc(VALUE re, VALUE str, int warn)<br>
{<br>
rb_encoding *enc = 0;</p>
<ul>
<li>if (rb_enc_str_coderange(str) == ENC_CODERANGE_BROKEN) {</li>
</ul>
<ul>
<li>if (!(RBASIC(re)->flags & REG_ENCODING_LOOSE) &&</li>
<li>
<pre><code> rb_enc_str_coderange(str) == ENC_CODERANGE_BROKEN) {
rb_raise(rb_eArgError,
"invalid byte sequence in %s",
rb_enc_name(rb_enc_get(str)));
</code></pre>
</li>
</ul>
<p>@@ -2433,6 +2436,9 @@ rb_reg_initialize(VALUE obj, const char *s, long len, rb_encoding *enc,<br>
if (options & ARG_ENCODING_NONE) {<br>
re->basic.flags |= REG_ENCODING_NONE;<br>
}</p>
<ul>
<li>
<p>if (options & ARG_ENCODING_LOOSE) {</p>
</li>
<li>
<pre><code> re->basic.flags |= REG_ENCODING_LOOSE;
</code></pre>
</li>
<li>
<p>}</p>
<p>re->ptr = make_regexp(RSTRING_PTR(unescaped), RSTRING_LEN(unescaped), enc,<br>
options & ARG_REG_OPTION_MASK, err,<br>
@@ -3091,6 +3097,7 @@ rb_reg_options(VALUE re)<br>
options = RREGEXP(re)->ptr->options & ARG_REG_OPTION_MASK;<br>
if (RBASIC(re)->flags & KCODE_FIXED) options |= ARG_ENCODING_FIXED;<br>
if (RBASIC(re)->flags & REG_ENCODING_NONE) options |= ARG_ENCODING_NONE;</p>
</li>
<li>
<p>if (RBASIC(re)->flags & REG_ENCODING_LOOSE) options |= ARG_ENCODING_LOOSE;<br>
return options;<br>
}</p>
</li>
</ul>
<p>@@ -3579,6 +3586,8 @@ Init_Regexp(void)<br>
rb_define_const(rb_cRegexp, "FIXEDENCODING", INT2FIX(ARG_ENCODING_FIXED));<br>
/* see Regexp.options and Regexp.new */<br>
rb_define_const(rb_cRegexp, "NOENCODING", INT2FIX(ARG_ENCODING_NONE));</p>
<ul>
<li>
<p>/* see Regexp.options and Regexp.new */</p>
</li>
<li>
<p>rb_define_const(rb_cRegexp, "LOOSEENCODING", INT2FIX(ARG_ENCODING_LOOSE));</p>
<p>rb_global_variable(&reg_cache);</p>
</li>
</ul>
<p>diff --git a/string.c b/string.c<br>
index 1d784e3..caf0baf 100644<br>
--- a/string.c<br>
+++ b/string.c<br>
@@ -3970,7 +3970,7 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang)<br>
cp = sp;<br>
str_enc = STR_ENC_GET(str);<br>
rb_enc_associate(dest, str_enc);</p>
<ul>
<li>ENC_CODERANGE_SET(dest, rb_enc_asciicompat(str_enc) ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID);</li>
</ul>
<ul>
<li>
<p>/<em>ENC_CODERANGE_SET(dest, rb_enc_asciicompat(str_enc) ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID);</em>/</p>
<p>do {<br>
n++;<br>
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb<br>
index 11e86ec..b8f6897 100644<br>
--- a/test/ruby/test_regexp.rb<br>
+++ b/test/ruby/test_regexp.rb<br>
@@ -8,6 +8,10 @@ class TestRegexp < Test::Unit::TestCase<br>
$VERBOSE = nil<br>
end</p>
</li>
<li>
<p>def u(str)</p>
</li>
<li>
<p>str.dup.force_encoding(Encoding::UTF_8)</p>
</li>
<li>
<p>end</p>
</li>
<li>
<p>def teardown<br>
$VERBOSE = @verbose<br>
end<br>
@@ -958,6 +962,17 @@ class TestRegexp < Test::Unit::TestCase<br>
}<br>
end</p>
</li>
<li>
<p>def test_encoding_loose</p>
</li>
<li>
<p>str = u("\x80\xE3\x81\x82\x81")</p>
</li>
<li>
<p>assert_equal(0, Regexp.new(".", Regexp::LOOSEENCODING) =~ str)</p>
</li>
<li>
<p>assert_equal(1, Regexp.new(u('\p{Any}'), Regexp::LOOSEENCODING) =~ str)</p>
</li>
<li>
<p>assert_equal(1, Regexp.new("\u3042", Regexp::LOOSEENCODING) =~ str)</p>
</li>
<li>
<p>assert_equal(1, Regexp.new(u('\p{Hiragana}'), Regexp::LOOSEENCODING) =~ str)</p>
</li>
<li>
<p>assert_equal(0, Regexp.new(u('\A.\p{Hiragana}.\z'), Regexp::LOOSEENCODING) =~ str)</p>
</li>
<li>
<p>str = u("\xf1\x80\xE3\x81\x82\x81")</p>
</li>
<li>
<p>assert_equal(0, Regexp.new(u('\A..\p{Hiragana}.\z'), Regexp::LOOSEENCODING) =~ str)</p>
</li>
<li>
<p>end</p>
</li>
<li>
<a name="This-assertion-is-for-porting-x2-tests-in-testpypy-of-Onigmo"></a>
<h1 >This assertion is for porting x2() tests in testpy.py of Onigmo.<a href="#This-assertion-is-for-porting-x2-tests-in-testpypy-of-Onigmo" class="wiki-anchor">¶</a></h1>
<p>def assert_match_at(re, str, positions, msg = nil)<br>
re = Regexp.new(re) unless re.is_a?(Regexp)</p>
</li>
</ul>
Ruby master - Feature #8675 (Closed): Add Readline.point=(pos)
https://redmine.ruby-lang.org/issues/8675
2013-07-24T03:03:13Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>r42146 でのテスト修正で \A を入力しているのですが、rl_point を直接いじった方が正しい気がするので。</p>
<p>diff --git a/ext/readline/readline.c b/ext/readline/readline.c<br>
index 714b08c..03ab724 100644<br>
--- a/ext/readline/readline.c<br>
+++ b/ext/readline/readline.c<br>
@@ -808,6 +808,12 @@ readline_s_get_point(VALUE self)<br>
{<br>
return INT2NUM(rl_point);<br>
}<br>
+static VALUE<br>
+readline_s_set_point(VALUE self, VALUE pos)<br>
+{</p>
<ul>
<li>rl_point = NUM2INT(pos);</li>
<li>return pos;<br>
+}<br>
#else<br>
#define readline_s_get_point rb_f_notimplement<br>
#endif<br>
@@ -1761,6 +1767,8 @@ Init_readline()<br>
readline_s_get_line_buffer, 0);<br>
rb_define_singleton_method(mReadline, "point",<br>
readline_s_get_point, 0);</li>
<li>rb_define_singleton_method(mReadline, "point=",</li>
<li>
<pre><code> readline_s_set_point, 1);
</code></pre>
rb_define_singleton_method(mReadline, "set_screen_size",<br>
readline_s_set_screen_size, 2);<br>
rb_define_singleton_method(mReadline, "get_screen_size",</li>
</ul>
Ruby master - Feature #8526 (Closed): gemify tk
https://redmine.ruby-lang.org/issues/8526
2013-06-14T10:22:28Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>tk を Ruby のリポジトリから外しませんか。</p>
<p>そもそも tk はサイズベースで言えば、約69MBのRubyリポジトリのうち、8.8MBを占める巨大なライブラリでかつ、<br>
ビルドに際して多くのライブラリを必要とするため、従前より周囲の目が厳しいライブラリでありましたが、<br>
これまでは精力的にメンテされている永井さんに敬意を払って、本格的な削除の提案はされて来ませんでした。</p>
<p>しかし、最近は活動が鈍り、#5199 <a class="issue tracker-4 status-5 priority-4 priority-default closed" title="Backport: Ruby 1.9.3 fails to compile with tcl/tk on s390x (Closed)" href="https://redmine.ruby-lang.org/issues/5465">#5465</a> <a class="issue tracker-1 status-6 priority-4 priority-default closed" title="Bug: compile error for ext/tk/tcltklib.c: ‘ruby_errinfo’ undeclared (Rejected)" href="https://redmine.ruby-lang.org/issues/5686">#5686</a> <a class="issue tracker-1 status-6 priority-4 priority-default closed" title="Bug: Tk::Scrollable の include が成功しない? (Rejected)" href="https://redmine.ruby-lang.org/issues/7000">#7000</a> <a class="issue tracker-1 status-6 priority-4 priority-default closed" title="Bug: Tkで,コマンドにforkを入れると,イベント実行時にクラッシュする (Rejected)" href="https://redmine.ruby-lang.org/issues/7884">#7884</a> <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: "require 'tk'" segfaults on 64-bit linux with Tk 8.6 (Closed)" href="https://redmine.ruby-lang.org/issues/8000">#8000</a> <a class="issue tracker-4 status-5 priority-4 priority-default closed" title="Backport: Tk::Canvas.create raise if type TkcItem (Closed)" href="https://redmine.ruby-lang.org/issues/8319">#8319</a> <a class="issue tracker-1 status-8 priority-4 priority-default closed" title="Bug: Can't build tcl/tk extensions after updating Debian/Ubuntu package (Third Party's Issue)" href="https://redmine.ruby-lang.org/issues/8435">#8435</a> が残タスクとして残っています。<br>
リリースマネージャとしては、このような状況下ではtkをRubyのリポジトリから外していただいて、<br>
gem等で永井さんのペースでメンテしていただくのがよいのではないでしょうか。</p>
Ruby master - Bug #8492 (Closed): ObjectSpace.after_gc_start_hook aborts with GC.stress
https://redmine.ruby-lang.org/issues/8492
2013-06-05T12:45:13Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>以下を実行すると assert(during_gc > 0) に失敗して abort します。<br>
ruby -robjspace -e'ObjectSpace.after_gc_start_hook=proc{};GC.stress=true;{}'<br>
gc.c:3818 の<br>
gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_START, 0 /* TODO: pass minor/immediate flag? */);<br>
実行後に、during_gc が 0 になっている模様。</p>
Ruby master - Bug #8408 (Closed): minitest's test may fail randomly
https://redmine.ruby-lang.org/issues/8408
2013-05-15T11:54:58Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>test/minitest/test_minitest_spec.rb may fail as following:<br>
<a href="http://c5664.rubyci.org/~chkbuild/ruby-trunk/log/20130515T013301Z.log.html.gz" class="external">http://c5664.rubyci.org/~chkbuild/ruby-trunk/log/20130515T013301Z.log.html.gz</a></p>
<ol start="9">
<li>
<p>Failure:<br>
TestMeta#test_structure_subclasses [/home/chkbuild/build/20130515T013301Z/ruby/test/minitest/test_minitest_spec.rb:751]:<br>
Expected #<#<a href="Class:0x002abd3bcfd7f8" class="external">Class:0x002abd3bcfd7f8</a>:0x002abd3bf000f0 @<strong>name</strong>=nil, @<strong>io</strong>=nil, @passed=nil> (top-level thingy::inner) to respond to #xyz.</p>
</li>
<li>
<p>Failure:<br>
TestMeta#test_name [/home/chkbuild/build/20130515T013301Z/ruby/test/minitest/test_minitest_spec.rb:668]:<br>
Expected: "ExampleA"<br>
Actual: "top-level thingy::ExampleA"</p>
</li>
<li>
<p>Failure:<br>
TestMeta#test_structure [/home/chkbuild/build/20130515T013301Z/ruby/test/minitest/test_minitest_spec.rb:687]:<br>
--- expected<br>
+++ actual<br>
@@ -1 +1 @@<br>
-"top-level thingy"<br>
+"top-level thingy::top-level thingy"</p>
</li>
<li>
<p>Failure:<br>
TestMeta#test_name2 [/home/chkbuild/build/20130515T013301Z/ruby/test/minitest/test_minitest_spec.rb:680]:<br>
Expected: "ExampleA"<br>
Actual: "top-level thingy::ExampleA"</p>
</li>
</ol>
<p>It seems because of minitest's bug.<br>
minitest's parallelize_me! make tests parallell but its describe method uses single stack.</p>
Backport200 - Backport #8266 (Closed): Backport r40216 (fiddle's mprotect)
https://redmine.ruby-lang.org/issues/8266
2013-04-14T23:23:40Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>r40216 を backport お願いします。<br>
とりあえず segv はしなくなります。</p>
Ruby master - Bug #8252 (Closed): cgiのHTML tag makerに未定義の属性を渡した場合の挙動
https://redmine.ruby-lang.org/issues/8252
2013-04-11T19:58:41Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>r40237 で以下の通り、定義されていない属性を渡したときの挙動が変化しているのですが意図していますか</p>
<ul>
<li>)<br>
+CGI::HtmlExtension#html when passed a Hash returns an 'html'-element using the passed Hash for attributes FAILED<br>
+Expected ""<br>
+to equal "<HTML BLA="TEST">"</li>
</ul>
<p>+/extdisk/chkbuild/chkbuild/tmp/build//rubyspec/library/cgi/htmlextension/html_spec.rb:<line_a>:in <code>block (3 levels) in <top (required)>' +/extdisk/chkbuild/chkbuild/tmp/build/<buildtime>/rubyspec/library/cgi/htmlextension/html_spec.rb:<line_a>:in </code><top (required)>'</p>
<p><a href="http://www.rubyist.net/~akr/chkbuild/debian/ruby-trunk/log/20130411T081100Z.diff.html.gz" class="external">http://www.rubyist.net/~akr/chkbuild/debian/ruby-trunk/log/20130411T081100Z.diff.html.gz</a></p>
Ruby master - Bug #8157 (Closed): How to write document for __LINE__, __FILE__, __END__
https://redmine.ruby-lang.org/issues/8157
2013-03-24T01:12:41Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>How do I write document for <strong>LINE</strong>, <strong>FILE</strong>, <strong>END</strong> ?</p>
<p>Moreover, __ is expressed as _ in rdoc.<br>
How can I avoid it?</p>
Backport193 - Backport #8076 (Closed): Lookbehind assertion fails with /m mode enabled
https://redmine.ruby-lang.org/issues/8076
2013-03-11T19:52:38Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Lookbehind assertions fail if they are longer than one character, and if dotall mode is set.</p>
<p>irb(main):001:0> "foo" =~ /(?<=f).<em>/m<br>
=> 1<br>
irb(main):002:0> "foo" =~ /(?<=fo).</em>/m<br>
=> nil</p>
<p>The latter should have matched the "o". This only seems to happen with dotall mode turned on (dot matches newline); without it, everything is OK:</p>
<p>irb(main):003:0> "foo" =~ /(?<=f).<em>/<br>
=> 1<br>
irb(main):004:0> "foo" =~ /(?<=fo).</em>/<br>
=> 2</p>
Backport193 - Backport #7896 (Closed): Can't test rb_iter_break with extensions
https://redmine.ruby-lang.org/issues/7896
2013-02-21T12:05:29Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>If you test rb_iter_break() with some extension library like trunk's ext/-test-/iter/break.c, it will cause SEGV on 1.9.3.<br>
It won't happen on 2.0 because r34369 is accidentally fix it with below patch.</p>
<p>I noticed this because RubySpec added a test for rb_spec_iter (4db31b04954118e66ac1d6353ebf4106cb2b419b) and hit this.</p>
<h1>% svn di<br>
Index: vm.c</h1>
<p>--- vm.c (revision 39346)<br>
+++ vm.c (working copy)<br>
@@ -1370,6 +1370,7 @@<br>
*th->cfp->sp++ = (GET_THROWOBJ_VAL(err));<br>
#endif<br>
}</p>
<ul>
<li>
<pre><code> th->state = 0;
th->errinfo = Qnil;
goto vm_loop_start;
}
</code></pre>
</li>
</ul>
Ruby master - Feature #6936 (Closed): Forbid singleton class and instance variabls for float
https://redmine.ruby-lang.org/issues/6936
2012-08-27T08:12:45Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>[Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Introduce Flonum technique to speedup floating computation on th 64bit environment (Closed)" href="https://redmine.ruby-lang.org/issues/6763">#6763</a>] などで議論されていた flonum が r36798 でが入ったわけですが、</p>
<ol>
<li>Float のオブジェクトID の仕様が変更</li>
<li>flonum な float に特異メソッドが追加不可</li>
<li>flonum な float に特異クラスが作成不可</li>
<li>flonum な float は同じ値同士でインスタンス変数が共有される</li>
</ol>
<p>といった非互換が存在します。<br>
もっとも、1. は通常意識するはずのないところですし、2. は元から禁止されています。<br>
気になるのは 3. と 4. で、これは 1.9.3 と挙動が異なるだけでなく、<br>
32bit 環境での 2.0 や、64bit環境の flonum でない float オブジェクトとも挙動が異なります。</p>
<p>実際問題として実害はないような気もしますが、このような違いが極めて実装上の問題で、<br>
Ruby 上から見えないところに存在するのは気持ち悪く感じます。</p>
<p>よって、以下のようにするとよいのではないでしょうか。</p>
<ul>
<li>flonum でない float でも特異クラスの作成を禁止</li>
<li>float へのインスタンス変数作成を禁止</li>
</ul>
<p>後者の具体的手法はいくつかあると思いますが、即値は最初から frozen にしておくとかもありかなと思っています。</p>
<p>話を発散させると、この話は true, false, nil, Fixnum, Symbol のような即値から、<br>
Bignum や Time のような immutable っぽいオブジェクトにも当てはまる気がしています。</p>
Ruby master - Feature #6910 (Rejected): Loading syck's broken yaml with psych
https://redmine.ruby-lang.org/issues/6910
2012-08-23T11:04:21Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>You know, syck outputs wrong yaml.<br>
For example, syck works as following:</p>
<p>ruby-1.9.2 > ["\u3042",Time.at(0).to_s].to_yaml<br>
=> "--- \n- "\xE3\x81\x82"\n- 1970-01-01 09:00:00 +09:00\n"</p>
<p>It should be</p>
<p>ruby-1.9.3 > ["\u3042",Time.at(0).to_s].to_yaml<br>
=> "---\n- あ\n- '1970-01-01 09:00:00 +0900'\n"</p>
<p>syck's dump of Unicode string is interpreted as "\u00E3\u0081\u0082".<br>
syck's dump of Time like string is interpreted as Time.<br>
It is hard to migrate old data to new and correct data, so it is useful if psych has a such compatibility option.</p>
Ruby master - Bug #6577 (Closed): GC中にstack overflowが発生するとSEGVする
https://redmine.ruby-lang.org/issues/6577
2012-06-12T02:18:44Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>GC中にstack overflowが発生すると、例外作成時にrb_new_objするので[BUG]ります。</p>
<p>原因<br>
(1) caller のテストで Fiber を利用するようにした<br>
(2) caller 実行中に GC が発生<br>
(3) GC 中にマシンスタックオーバーフロー(SEGV)が発生<br>
(4) スタックオーバーフローエラーを作成<br>
(5) スタックオーバーフローエラーを作るときに object allocation している<br>
(6) -> [BUG]</p>
<p>対処法:<br>
スタックオーバーフローエラーを投げるときはオブジェクト作らないようにする<br>
対症療法:<br>
caller のテストで Fiber を使わないようにする<br>
対症療法その2:<br>
callerのテストで GC.disable</p>
<p>nariさんがGC で再帰しないようにするなんて構想も先日語っておられましたが。</p>
Ruby master - Bug #6441 (Rejected): IO.pipe on ENFILE
https://redmine.ruby-lang.org/issues/6441
2012-05-16T12:59:05Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>今の Ruby は open(2) などで、errno=ENFILE が発生した場合、<br>
すなわち fd を使いきっている場合には、rb_gc() を呼び、IO オブジェクトを GC して、<br>
fd が解放されないか試み、それでもダメだったら諦めるとしています。</p>
<p>しかし、IO.pipe の場合はこれに失敗することがあります。<br>
これは、lazy sweep が上記の目的のため T_FILE の場合は直ちに sweep することにしている所、<br>
pipe の場合はその例外にあたらないからです。</p>
Ruby master - Bug #6400 (Closed): dl/callback with fiddle occurs SEGV on NetBSD amd64
https://redmine.ruby-lang.org/issues/6400
2012-05-04T21:33:37Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>On NetBSD amd64, libffi with callback occurs SEGV as following.</p>
<p>kelvena% cat p<br>
require 'dl/callback'<br>
require 'dl/func'<br>
include DL<br>
Called_with = nil<br>
addr = set_callback(TYPE_VOID, 1) do |str|<br>
called_with = dlunwrap(str)<br>
end<br>
func = CFunc.new(addr, TYPE_VOID, 'test')<br>
f = Function.new(func, [TYPE_VOIDP])<br>
arg = 'foo'<br>
f.call(dlwrap(arg))<br>
kelvena% ./ruby p<br>
/home/naruse/local/ruby/lib/ruby/2.0.0/dl/func.rb:55: [BUG] Segmentation fault<br>
ruby 2.0.0dev (2012-04-30 trunk 35500) [x86_64-netbsd6.99.5]</p>
<p>-- Control frame information -----------------------------------------------<br>
c:0005 p:---- s:0022 b:0022 l:000021 d:000021 CFUNC :call<br>
c:0004 p:0059 s:0018 b:0018 l:000017 d:000017 METHOD /home/naruse/local/ruby/lib/ruby/2.0.0/dl/func.<br>
rb:55<br>
c:0003 p:0157 s:0010 b:0010 l:001db8 d:0021a8 EVAL p:11<br>
c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH<br>
c:0001 p:0000 s:0002 b:0002 l:001db8 d:001db8 TOP</p>
<p>-- Ruby level backtrace information ----------------------------------------<br>
p:11:in <code><main>' /home/naruse/local/ruby/lib/ruby/2.0.0/dl/func.rb:55:in </code>call'<br>
/home/naruse/local/ruby/lib/ruby/2.0.0/dl/func.rb:55:in `call'</p>
<p>-- Other runtime information -----------------------------------------------</p>
<ul>
<li>
<p>Loaded script: p</p>
</li>
<li>
<p>Loaded features:</p>
<p>0 enumerator.so<br>
1 /home/naruse/local/ruby/lib/ruby/2.0.0/x86_64-netbsd6.99.5/enc/encdb.so<br>
2 /home/naruse/local/ruby/lib/ruby/2.0.0/x86_64-netbsd6.99.5/enc/trans/transdb.so<br>
3 /home/naruse/local/ruby/lib/ruby/2.0.0/rubygems/defaults.rb<br>
4 /home/naruse/local/ruby/lib/ruby/2.0.0/x86_64-netbsd6.99.5/rbconfig.rb<br>
5 /home/naruse/local/ruby/lib/ruby/2.0.0/rubygems/deprecate.rb<br>
6 /home/naruse/local/ruby/lib/ruby/2.0.0/rubygems/exceptions.rb<br>
7 /home/naruse/local/ruby/lib/ruby/2.0.0/rubygems/custom_require.rb<br>
8 /home/naruse/local/ruby/lib/ruby/2.0.0/rubygems.rb<br>
9 /home/naruse/local/ruby/lib/ruby/2.0.0/x86_64-netbsd6.99.5/dl.so<br>
10 /home/naruse/local/ruby/lib/ruby/2.0.0/x86_64-netbsd6.99.5/fiddle.so<br>
11 /home/naruse/local/ruby/lib/ruby/2.0.0/fiddle/function.rb<br>
12 /home/naruse/local/ruby/lib/ruby/2.0.0/fiddle/closure.rb<br>
13 /home/naruse/local/ruby/lib/ruby/2.0.0/fiddle.rb<br>
14 /home/naruse/local/ruby/lib/ruby/2.0.0/dl.rb<br>
15 /home/naruse/local/ruby/lib/ruby/2.0.0/thread.rb<br>
16 /home/naruse/local/ruby/lib/ruby/2.0.0/dl/callback.rb<br>
17 /home/naruse/local/ruby/lib/ruby/2.0.0/dl/stack.rb<br>
18 /home/naruse/local/ruby/lib/ruby/2.0.0/dl/value.rb<br>
19 /home/naruse/local/ruby/lib/ruby/2.0.0/dl/func.rb</p>
</li>
</ul>
<p>[NOTE]<br>
You may have encountered a bug in the Ruby interpreter or extension libraries.<br>
Bug reports are welcome.<br>
For details: <a href="http://www.ruby-lang.org/bugreport.html" class="external">http://www.ruby-lang.org/bugreport.html</a></p>
<p>zsh: abort (core dumped) ./ruby p</p>
Ruby master - Feature #6118 (Feedback): Hash#keys_of(values), returns related keys of given values
https://redmine.ruby-lang.org/issues/6118
2012-03-06T17:55:07Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Hash#key(value) の複数版がほしいです。</p>
<p>{a: 1, b: 2, c: 3, d: 1}.key(1)<br>
=> :a<br>
というメソッドはあるのですが、<br>
{a: 1, b: 2, c: 3, d: 1}.keys_of(1)<br>
=> [:a, :d]<br>
というメソッドは現状ありません。</p>
<p>Ruby での実装例は以下のような感じになります。<br>
どうでしょうか。</p>
<p>class Hash<br>
def keys_of(*a)<br>
each_with_object([]) {|(k, v), r| r << k if a.include? v}<br>
end<br>
end</p>
Ruby master - Feature #5959 (Rejected): Addrinfo#inspectname
https://redmine.ruby-lang.org/issues/5959
2012-02-02T17:16:51Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Addrinfo の導入によって、Socket.getaddrinfo での正引き結果がオブジェクト化され、<br>
元のホスト名を inspect で見れるようになってとても便利になったわけですが、<br>
現在この元のホスト名をとりだす API がありません。</p>
<p>ホスト名とIP アドレスをセットで扱うのに、つまり解決済みの名前とアドレスの組を扱うのに Addrinfo って便利なので、<br>
ホスト名を取り出す API が欲しいです。<br>
現在の C での名前、inspectname か hostname あたりでどうでしょう。</p>
Ruby master - Bug #5868 (Closed): make failed on i686-linux from Bitmap Marking GC
https://redmine.ruby-lang.org/issues/5868
2012-01-09T10:54:12Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>i686-linux に置いて、r34225 以降 make に失敗します。</p>
<p><a href="http://c5632.rubyci.org/~chkbuild/ruby-trunk/log/20120109T010103Z.log.html.gz" class="external">http://c5632.rubyci.org/~chkbuild/ruby-trunk/log/20120109T010103Z.log.html.gz</a><br>
<a href="http://u32.rubyci.org/~chkbuild/ruby-trunk/log/20120108T230102Z.log.html.gz" class="external">http://u32.rubyci.org/~chkbuild/ruby-trunk/log/20120108T230102Z.log.html.gz</a><br>
<a href="http://www.rubyist.net/~akr/chkbuild/debian/ruby-trunk/log/20120109T000400Z.log.html.gz" class="external">http://www.rubyist.net/~akr/chkbuild/debian/ruby-trunk/log/20120109T000400Z.log.html.gz</a></p>
Ruby master - Feature #5820 (Closed): Merge Onigmo to Ruby 2.0
https://redmine.ruby-lang.org/issues/5820
2011-12-29T02:42:18Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Ruby 1.9 では正規表現エンジンや M17N の基盤として Oniguruma を用いています。<br>
これを 2.0 では Oniguruma の改造版である、k-takata さんの Onigmo に置き換えようという話です。<br>
<a href="https://github.com/k-takata/Onigmo/tree/tmp/ruby-2.0.x" class="external">https://github.com/k-takata/Onigmo/tree/tmp/ruby-2.0.x</a></p>
<p>この取り込みによる影響は以下の通りです。</p>
<ul>
<li>100%互換 (既存のテストが全て無修正で通る)</li>
<li>いくつかの新機能 <a href="/issues/5208">[ruby-dev:44410]</a>
<ul>
<li>正規表現<br>
* \K, \R, \X, (?(cond)yes|no), \g<0>, \g<+n>, (?au)<br>
* Perl 5.10互換の名前参照(←Rubyには不要でしょう。)
<ul>
<li>Shift_JIS, EUC-JPで、全角アルファベットなどの大文字小文字同一視検索に対応。</li>
<li>Shift_JIS, EUC-JPで、\p{Han}, \p{Latin}, \p{Greek}, \p{Cyrillic} に対応。</li>
<li>最適化
<ul>
<li>暗黙のアンカーによる最適化を実装。</li>
<li>
<a href="http://redmine.ruby-lang.org/issues/3568" class="external">http://redmine.ruby-lang.org/issues/3568</a> で無効化された最適化を再度有効化。</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>現状は POSIX 文字クラスに非互換があり、それが解決されればマージ可能と認識しています。</p>
Ruby master - Bug #5813 (Closed): net/http's EOFError and Keep-Alive
https://redmine.ruby-lang.org/issues/5813
2011-12-27T14:56:52Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p><a href="https://blade.ruby-lang.org/ruby-dev/39421">[ruby-dev:39421]</a> describes exceptions thrown by open-uri, and raise a question why net/http raises EOFError.</p>
<p>net/http sometimes raises EOFError.<br>
I recently find it is because of Keep-Alive.<br>
On HTTP/1.1, connections are Keep-Alive and a Keep-Alive connection has a timeout.<br>
If a client of such connection doesn't send anything after some communication,<br>
server closes the connection because of Keep-Alive timeout,<br>
and the client's connection shall raise EOFError (sometimes it may be ECONNRESET).</p>
<p>HTTP/1.1 says a client should retry a request if the request is idempotent.<br>
<a href="http://tools.ietf.org/html/rfc2616#section-8.1.4" class="external">http://tools.ietf.org/html/rfc2616#section-8.1.4</a><br>
<a href="http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-17#section-6.1.5" class="external">http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-17#section-6.1.5</a><br>
<a href="http://www.studyinghttp.net/connections" class="external">http://www.studyinghttp.net/connections</a><br>
So I attached a patch to such retry to net/http.</p>
<p>FYI, this timeout of Keep-Alive, KeepAliveTimeout, is:<br>
Apache in FreeBSD ports or pkgsrc is 5 seconds,<br>
the on in Debian Packages or RPM is 15 seconds.</p>
<p>diff --git a/lib/net/http.rb b/lib/net/http.rb<br>
index 879cfe0..13bd1a7 100644<br>
--- a/lib/net/http.rb<br>
+++ b/lib/net/http.rb<br>
@@ -1332,7 +1332,10 @@ module Net #:nodoc:<br>
res<br>
end</p>
<ul>
<li>IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/ # :nodoc:</li>
<li>def transport_request(req)</li>
<li>
<pre><code> count = 0
begin_transport req
res = catch(:response) {
req.exec @socket, @curr_http_version, edit_path(req.path)
</code></pre>
</li>
</ul>
<p>@@ -1346,6 +1349,16 @@ module Net #:nodoc:<br>
}<br>
end_transport req, res<br>
res</p>
<ul>
<li>rescue EOFError, Errno::ECONNRESET => exception</li>
<li>
<pre><code> if count == 0 && IDEMPOTENT_METHODS_.include?(req.method)
</code></pre>
</li>
<li>
<pre><code> count += 1
</code></pre>
</li>
<li>
<pre><code> @socket.close if @socket and not @socket.closed?
</code></pre>
</li>
<li>
<pre><code> D "Conn close because of error #{exception}, and retry"
</code></pre>
</li>
<li>
<pre><code> retry
</code></pre>
</li>
<li>
<pre><code> end
</code></pre>
</li>
<li>
<pre><code> D "Conn close because of error #{exception}"
</code></pre>
</li>
<li>
<pre><code> @socket.close if @socket and not @socket.closed?
</code></pre>
</li>
<li>
<pre><code> raise
</code></pre>
rescue => exception<br>
D "Conn close because of error #{exception}"<br>
@socket.close if @socket and not @socket.closed?<br>
diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb<br>
index 1515854..2e7ab4e 100644<br>
--- a/test/net/http/test_http.rb<br>
+++ b/test/net/http/test_http.rb<br>
@@ -564,3 +564,29 @@ class TestNetHTTPContinue < Test::Unit::TestCase<br>
assert_not_match(/HTTP/1.1 100 continue/, @debug.string)<br>
end<br>
end</li>
<li>
</ul>
<p>+class TestNetHTTPKeepAlive < Test::Unit::TestCase</p>
<ul>
<li>CONFIG = {</li>
<li>'host' => '127.0.0.1',</li>
<li>'port' => 10081,</li>
<li>'proxy_host' => nil,</li>
<li>'proxy_port' => nil,</li>
<li>'RequestTimeout' => 0.1,</li>
<li>}</li>
<li>
<li>include TestNetHTTPUtils</li>
<li>
<li>def test_keep_alive_get</li>
<li>start {|http|</li>
<li>
<pre><code> res = http.get('/')
</code></pre>
</li>
<li>
<pre><code> assert_kind_of Net::HTTPResponse, res
</code></pre>
</li>
<li>
<pre><code> assert_kind_of String, res.body
</code></pre>
</li>
<li>
<pre><code> sleep 1
</code></pre>
</li>
<li>
<pre><code> assert_nothing_raised {
</code></pre>
</li>
<li>
<pre><code> res = http.get('/')
</code></pre>
</li>
<li>
<pre><code> }
</code></pre>
</li>
<li>
<pre><code> assert_kind_of Net::HTTPResponse, res
</code></pre>
</li>
<li>
<pre><code> assert_kind_of String, res.body
</code></pre>
</li>
<li>}</li>
<li>end<br>
+end<br>
diff --git a/test/net/http/utils.rb b/test/net/http/utils.rb<br>
index 50f616f..07e0b9f 100644<br>
--- a/test/net/http/utils.rb<br>
+++ b/test/net/http/utils.rb<br>
@@ -51,6 +51,7 @@ module TestNetHTTPUtils<br>
:ServerType => Thread,<br>
}<br>
server_config[:OutputBufferSize] = 4 if config('chunked')</li>
<li>server_config[:RequestTimeout] = config('RequestTimeout') if config('RequestTimeout')<br>
if defined?(OpenSSL) and config('ssl_enable')<br>
server_config.update({<br>
:SSLEnable => true,</li>
</ul>
Ruby master - Bug #5790 (Closed): net/http の EOFError と Keep-Alive
https://redmine.ruby-lang.org/issues/5790
2011-12-22T18:49:13Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p><a href="https://blade.ruby-lang.org/ruby-dev/39421">[ruby-dev:39421]</a> がずっと心に残っていたので、思い立って調べてみたので、<br>
(正確には自分が高頻度で踏むようになったので調べてみた)<br>
その調査結果と対策案を提案します。</p>
<p>まず、投げられる原因ですが、根本的な原因は Keep-Alive のタイムアウトです。<br>
HTTP/1.1 ではデフォルトで持続的接続を行うので、複数回のリクエストに渡って<br>
一つの socket が使い回されます。</p>
<p>しかし、リクエスト同士で時間が開いていると、サーバー側でタイムアウトする<br>
可能性があります。この時にクライアント側の read(2) が 0 を返す、<br>
つまり EOFError となることがあります。</p>
<p>HTTP/1.1 は、冪等なメソッドの場合には確認なしにリトライすべきと言っているので、<br>
そのようにするパッチを添付します。<br>
冪等でないメソッドの場合にどうするべきかは悩ましいところです。<br>
<a href="http://tools.ietf.org/html/rfc2616#section-8.1.4" class="external">http://tools.ietf.org/html/rfc2616#section-8.1.4</a><br>
<a href="http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-17#section-6.1.5" class="external">http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-17#section-6.1.5</a><br>
<a href="http://www.studyinghttp.net/connections" class="external">http://www.studyinghttp.net/connections</a></p>
<p>なお、この Keep-Alive における Timeout は、<br>
Apache の場合、FreeBSD ports や pkgsrc では 5 秒、<br>
Debian Packages や RPM では 15 秒でした。</p>
<p>diff --git a/lib/net/http.rb b/lib/net/http.rb<br>
index 879cfe0..13bd1a7 100644<br>
--- a/lib/net/http.rb<br>
+++ b/lib/net/http.rb<br>
@@ -1332,7 +1332,10 @@ module Net #:nodoc:<br>
res<br>
end</p>
<ul>
<li>IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/ # :nodoc:</li>
<li>def transport_request(req)</li>
<li>
<pre><code> count = 0
begin_transport req
res = catch(:response) {
req.exec @socket, @curr_http_version, edit_path(req.path)
</code></pre>
</li>
</ul>
<p>@@ -1346,6 +1349,16 @@ module Net #:nodoc:<br>
}<br>
end_transport req, res<br>
res</p>
<ul>
<li>rescue EOFError, Errno::ECONNRESET => exception</li>
<li>
<pre><code> if count == 0 && IDEMPOTENT_METHODS_.include?(req.method)
</code></pre>
</li>
<li>
<pre><code> count += 1
</code></pre>
</li>
<li>
<pre><code> @socket.close if @socket and not @socket.closed?
</code></pre>
</li>
<li>
<pre><code> D "Conn close because of error #{exception}, and retry"
</code></pre>
</li>
<li>
<pre><code> retry
</code></pre>
</li>
<li>
<pre><code> end
</code></pre>
</li>
<li>
<pre><code> D "Conn close because of error #{exception}"
</code></pre>
</li>
<li>
<pre><code> @socket.close if @socket and not @socket.closed?
</code></pre>
</li>
<li>
<pre><code> raise
</code></pre>
rescue => exception<br>
D "Conn close because of error #{exception}"<br>
@socket.close if @socket and not @socket.closed?<br>
diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb<br>
index 1515854..2e7ab4e 100644<br>
--- a/test/net/http/test_http.rb<br>
+++ b/test/net/http/test_http.rb<br>
@@ -564,3 +564,29 @@ class TestNetHTTPContinue < Test::Unit::TestCase<br>
assert_not_match(/HTTP/1.1 100 continue/, @debug.string)<br>
end<br>
end</li>
<li>
</ul>
<p>+class TestNetHTTPKeepAlive < Test::Unit::TestCase</p>
<ul>
<li>CONFIG = {</li>
<li>'host' => '127.0.0.1',</li>
<li>'port' => 10081,</li>
<li>'proxy_host' => nil,</li>
<li>'proxy_port' => nil,</li>
<li>'RequestTimeout' => 0.1,</li>
<li>}</li>
<li>
<li>include TestNetHTTPUtils</li>
<li>
<li>def test_keep_alive_get</li>
<li>start {|http|</li>
<li>
<pre><code> res = http.get('/')
</code></pre>
</li>
<li>
<pre><code> assert_kind_of Net::HTTPResponse, res
</code></pre>
</li>
<li>
<pre><code> assert_kind_of String, res.body
</code></pre>
</li>
<li>
<pre><code> sleep 1
</code></pre>
</li>
<li>
<pre><code> assert_nothing_raised {
</code></pre>
</li>
<li>
<pre><code> res = http.get('/')
</code></pre>
</li>
<li>
<pre><code> }
</code></pre>
</li>
<li>
<pre><code> assert_kind_of Net::HTTPResponse, res
</code></pre>
</li>
<li>
<pre><code> assert_kind_of String, res.body
</code></pre>
</li>
<li>}</li>
<li>end<br>
+end<br>
diff --git a/test/net/http/utils.rb b/test/net/http/utils.rb<br>
index 50f616f..07e0b9f 100644<br>
--- a/test/net/http/utils.rb<br>
+++ b/test/net/http/utils.rb<br>
@@ -51,6 +51,7 @@ module TestNetHTTPUtils<br>
:ServerType => Thread,<br>
}<br>
server_config[:OutputBufferSize] = 4 if config('chunked')</li>
<li>server_config[:RequestTimeout] = config('RequestTimeout') if config('RequestTimeout')<br>
if defined?(OpenSSL) and config('ssl_enable')<br>
server_config.update({<br>
:SSLEnable => true,</li>
</ul>
Backport193 - Backport #5757 (Closed): main threadがreadやselectで待っていると、^C でなかなか死なない
https://redmine.ruby-lang.org/issues/5757
2011-12-13T16:35:41Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>FreeBSD 9 にて、 ./ruby と起動して ^C を投げてもなかなか死にません。<br>
./miniruby でも -e'$stdin.read' でも同じです。</p>
<p>仕組みとしては、main thread が read や select で待つ場合、最近は blocking region で<br>
unblock.func に ubf_select を設定するわけですが、この時にシグナルが来ると、</p>
<ol>
<li>どこかのスレッドの sighandler が呼ばれて、rb_thread_wakeup_timer_thread() が呼ばれる</li>
<li>タイマースレッドが起きて、thread_timer() -> timer_thread_function() -> rb_threadptr_check_signal() -> rb_threadptr_interrupt() -> (th->unblock.func)(th->unblock.arg) -> ubf_select() -> rb_thread_wakeup_timer_thread() が呼ばれる</li>
<li>タイマースレッドが起きて、thread_timer() -> timer_thread_function() -> rb_threadptr_check_signal() -> rb_threadptr_interrupt() -> (th->unblock.func)(th->unblock.arg) -> ubf_select() -> rb_thread_wakeup_timer_thread() が呼ばれる</li>
<li>タイマースレッドが起きて、thread_timer() -> timer_thread_function() -> rb_threadptr_check_signal() -> rb_threadptr_interrupt() -> (th->unblock.func)(th->unblock.arg) -> ubf_select() -> rb_thread_wakeup_timer_thread() が呼ばれる<br>
...</li>
</ol>
<p>対策はいくつかあり得ると思うのですが、例えば、ubf_select() から rb_thread_wakeup_timer_thread() を呼ばないようにするとか</p>
Ruby master - Bug #5547 (Closed): Cleanup engine after a test
https://redmine.ruby-lang.org/issues/5547
2011-11-02T11:16:21Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>OpenSSL::Engine.load() loads engines and register them, and it may change the behavior of some existing methods.</p>
<p>For example on NetBSD 6 with cryptodev, it effects DH as folloing:<br>
./ruby -ropenssl -e'p OpenSSL::PKey::DH.new(256).public_key.private?;p OpenSSL::Engine.load;p OpenSSL::PKey::DH.new(256).public_key.private?'<br>
false<br>
true<br>
true</p>
<p>After loads cryptodev and register it (yes, it needs register. current ext/openssl can't register a engine),<br>
OpenSSL::PKey::DH#private?'s behavior seems to be changed.</p>
<p>Whether it is a bug or not, test/openssl/test_engine.rb should be fixed.</p>
<a name="Index-testopenssltest_enginerb"></a>
<h1 >Index: test/openssl/test_engine.rb<a href="#Index-testopenssltest_enginerb" class="wiki-anchor">¶</a></h1>
<p>--- test/openssl/test_engine.rb (revision 33605)<br>
+++ test/openssl/test_engine.rb (working copy)<br>
@@ -8,6 +8,7 @@<br>
OpenSSL::Engine.load<br>
OpenSSL::Engine.engines<br>
OpenSSL::Engine.engines</p>
<ul>
<li>OpenSSL::Engine.cleanup<br>
end</li>
</ul>
<p>end</p>
Ruby master - Bug #5524 (Closed): IO.wait_for_single_fd(closed fd) sticks on other than Linux
https://redmine.ruby-lang.org/issues/5524
2011-10-31T21:31:41Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>r31428 で、test_wait_for_invalid_fd ってテストを追加しており、<br>
IO.wait_for_single_fd(close 済みの fd) が EBADF になることを確認しているのですが、<br>
これ単体で動かすと FreeBSD で戻ってきません。</p>
<p>思うに、このテストって本来ポータブルに刺さる物なんじゃないでしょうか。<br>
test-allだと何かの弾みで通ってしまうだけで。</p>
<p>% cat poll.c<br>
#include <stdio.h><br>
#include <stdlib.h><br>
#include <poll.h><br>
#include <errno.h><br>
int<br>
main(void) {<br>
int pipes[2];<br>
int res = pipe(pipes);<br>
if (res != 0) abort();<br>
int r = pipes[0];<br>
int w = pipes[1];<br>
res = close(w);<br>
if (res != 0) abort();</p>
<pre><code>struct pollfd fds;
fds.fd = w;
fds.events = POLLOUT;
errno = 0;
res = poll(&fds, 1, 1000);
fprintf(stderr, "%d %d %d\n", res, errno, fds.revents);
return 0;
</code></pre>
<p>}</p>
<p>というプログラムではポータブルに POLLVAL が返り、</p>
<p>#include <stdio.h><br>
#include <stdlib.h><br>
#include <sys/select.h><br>
int<br>
main(void) {<br>
int pipes[2];<br>
int res = pipe(pipes);<br>
if (res != 0) abort();<br>
int r = pipes[0];<br>
int w = pipes[1];<br>
res = close(w);<br>
if (res != 0) abort();<br>
fd_set readfds; FD_ZERO(&readfds);<br>
fd_set writefds; FD_ZERO(&writefds);<br>
fd_set exceptfds; FD_ZERO(&exceptfds);<br>
//struct timeval *timeout = NULL;<br>
//FD_SET(r, &readfds);<br>
FD_SET(w, &writefds);<br>
res = select(1, &readfds, &writefds, &exceptfds, NULL);<br>
return 0;<br>
}</p>
<p>はポータブルにブロックされるあたり、このテストってLinux依存なんじゃ無いかという疑惑を持っているんですがどうでしょう。</p>
Backport193 - Backport #5276 (Closed): 4294967295.8.round is 4294967295 on 32bit
https://redmine.ruby-lang.org/issues/5276
2011-09-05T18:01:46Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>ruby -e'p 4294967295.8.round' must be 4294967296 but 4294967295 on 32bit environment.</p>
Ruby master - Feature #5180 (Closed): net/http の接続時に用いる IP アドレスの指定
https://redmine.ruby-lang.org/issues/5180
2011-08-10T11:45:57Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>通常 net/http を使う時は、Net::HTTP.start("ruby-lang.org") などとホスト名を使います。<br>
で、Socket がホスト名から IP アドレスを引いて、コネクションが張られます。<br>
普通の人はこれで足りるわけですが、ふつうな人はしばしば DNS で引けない IP アドレスに接続したくなります。<br>
例えば、ホスト名は "ruby-lang.org" としたいが、IP アドレスは 127.0.0.1 とか。</p>
<p>以下のパッチをあてると、<br>
Net::HTTP.start("ruby-lang.org", ipaddr: '127.0.0.1')<br>
などとできるようになります。</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/lib/net/http.rb b/lib/net/http.rb
index 7b9ec4f..6d034e0 100644
</span><span class="gd">--- a/lib/net/http.rb
</span><span class="gi">+++ b/lib/net/http.rb
</span><span class="p">@@ -524,7 +524,7 @@</span> module Net #:nodoc:
# _opt_ :: optional hash
#
# _opt_ sets following values by its accessor.
<span class="gd">- # The keys are ca_file, ca_path, cert, cert_store, ciphers,
</span><span class="gi">+ # The keys are ipaddr, ca_file, ca_path, cert, cert_store, ciphers,
</span> # close_on_empty_response, key, open_timeout, read_timeout, ssl_timeout,
# ssl_version, use_ssl, verify_callback, verify_depth and verify_mode.
# If you set :use_ssl as true, you can use https and default value of
<span class="p">@@ -542,6 +542,7 @@</span> module Net #:nodoc:
port, p_addr, p_port, p_user, p_pass = *arg
port = https_default_port if !port && opt && opt[:use_ssl]
http = new(address, port, p_addr, p_port, p_user, p_pass)
<span class="gi">+ http.ipaddr = opt[:ipaddr] if opt[:ipaddr]
</span>
if opt
if opt[:use_ssl]
<span class="p">@@ -575,6 +576,7 @@</span> module Net #:nodoc:
def initialize(address, port = nil)
@address = address
@port = (port || HTTP.default_port)
<span class="gi">+ @ipaddr = nil
</span> @curr_http_version = HTTPVersion
@no_keepalive_server = false
@close_on_empty_response = false
<span class="p">@@ -620,6 +622,17 @@</span> module Net #:nodoc:
# The port number to connect to.
attr_reader :port
<span class="gi">+ # The IP address to connect to/used to connect to
+ def ipaddr
+ started? ? @socket.io.peeraddr[3] : @ipaddr
+ end
+
+ # Set the IP address to connect to
+ def ipaddr=(addr)
+ raise IOError, "ipaddr value changed, but session already started" if started?
+ @ipaddr = addr
+ end
+
</span> # Number of seconds to wait for the connection to open. Any number
# may be used, including Floats for fractional seconds. If the HTTP
# object cannot open a connection in this many seconds, it raises a
<span class="p">@@ -945,7 +958,7 @@</span> module Net #:nodoc:
# without proxy
def conn_address
<span class="gd">- address()
</span><span class="gi">+ @ipaddr || address()
</span> end
def conn_port
</code></pre>
Ruby master - Bug #5076 (Closed): Mac OS X Lion Support
https://redmine.ruby-lang.org/issues/5076
2011-07-22T20:20:03Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Ruby doesn't work on Lion.</p>
Backport192 - Backport #5075 (Rejected): invalid *fdp in Mac OS X and FreeBSD over recvmsg with S...
https://redmine.ruby-lang.org/issues/5075
2011-07-22T17:51:06Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>Mac OS X と FreeBSD にて、存在しない fd を close してしまう問題について、<br>
現在 r32598 で応急処置が施されていますが、根本的な原因について、<br>
sys/kern/uipc_socket.c を見るに、<br>
<a href="http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/kern/uipc_socket.c?rev=1.340.2.6.2.1;content-type=text%2Fplain;only_with_tag=RELENG_8_2_0_RELEASE" class="external">http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/kern/uipc_socket.c?rev=1.340.2.6.2.1;content-type=text%2Fplain;only_with_tag=RELENG_8_2_0_RELEASE</a></p>
<pre><code> * Process one or more MT_CONTROL mbufs present before any data mbufs
* in the first mbuf chain on the socket buffer. If MSG_PEEK, we
* just copy the data; if !MSG_PEEK, we call into the protocol to
* perform externalization (or freeing if controlp == NULL).
</code></pre>
<p>とあるので、recvmsg に MSG_PEEK を与えた場合は invalid なものが返ってくると思うのですが。</p>
<p>ちなみに、以下のような printf パッチをあてて走らせると、discard_cmsg() に来たものは全て invalid になっています。</p>
<p>diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c<br>
index 61e0576..ad44fb4 100644<br>
--- a/ext/socket/ancdata.c<br>
+++ b/ext/socket/ancdata.c<br>
@@ -1379,6 +1379,7 @@ rb_recvmsg(int fd, struct msghdr *msg, int flags)<br>
static void<br>
discard_cmsg(struct cmsghdr *cmh, char *msg_end)<br>
{</p>
<ul>
<li>fprintf(stderr, "discard_cmsg-begin\n");<br>
if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_RIGHTS) {<br>
int *fdp = (int *)CMSG_DATA(cmh);<br>
int *end = (int *)((char *)cmh + cmh->cmsg_len);<br>
@@ -1391,12 +1392,18 @@ discard_cmsg(struct cmsghdr *cmh, char *msg_end)<br>
*/<br>
struct stat buf;<br>
if (fstat(*fdp, &buf) == 0) {</li>
<li>
<pre><code> fprintf(stderr, "fdp: %d is valid (%p %p %p)\n", *fdp,fdp,end,msg_end);
rb_update_max_fd(*fdp);
close(*fdp);
}
</code></pre>
</li>
<li>
<pre><code> else {
</code></pre>
</li>
<li>
<pre><code> fprintf(stderr, "fdp: %d is invalid (%p %p %p)\n", *fdp,fdp,end,msg_end);
</code></pre>
</li>
<li>
<pre><code> rb_backtrace();
</code></pre>
</li>
<li>
<pre><code> }
fdp++;
}
</code></pre>
}</li>
<li>fprintf(stderr, "discard_cmsg-end\n");<br>
}<br>
#endif</li>
</ul>
<p>@@ -1432,6 +1439,7 @@ make_io_for_unix_rights(VALUE ctl, struct cmsghdr *cmh, char *msg_end)<br>
(char *)fdp + sizeof(int) <= msg_end) {<br>
int fd = *fdp;<br>
struct stat stbuf;</p>
<ul>
<li>fprintf(stderr,"makeiounixr: %d (%p %p %p)\n", *fdp,fdp,end,msg_end);<br>
VALUE io;<br>
if (fstat(fd, &stbuf) == -1)<br>
rb_raise(rb_eSocket, "invalid fd in SCM_RIGHTS");</li>
</ul>
Ruby master - Bug #5026 (Closed): 1.9.3 allows URI(uri, parser)
https://redmine.ruby-lang.org/issues/5026
2011-07-14T07:28:08Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>r28699 introduces a new API: the second argument of URI(uri [, parser]).<br>
But I object this because such parser argument should be obsolete.</p>
Ruby master - Bug #4886 (Closed): autoload in instance_eval doesn't work
https://redmine.ruby-lang.org/issues/4886
2011-06-15T09:53:15Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>autoload を instance_eval の中で使うとうまく動きません。</p>
<p>% ./ruby -e'instance_eval{autoload :R,"a";p R}'<br>
-e:1:in <code>block in <main>': uninitialized constant R (NameError) from -e:1:in </code>instance_eval'<br>
from -e:1:in `'</p>
Ruby master - Bug #4826 (Closed): Date fails RubySpec
https://redmine.ruby-lang.org/issues/4826
2011-06-04T21:44:01Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>最近の Date の変更で、以下のように RubySpec が失敗しています。<br>
RubySpec 側を直した方がいい物もあるような気がしますが、Date 側の問題もあるようなので確認頂けますか。<br>
spec 側を直すべきものについてはあるべき挙動を教えて頂ければそう直します。</p>
<p>なお、RubySpec は <a href="http://rubyspec.org/" class="external">http://rubyspec.org/</a> です。<br>
RubySpec を実行するには、git をインストールした上で、<br>
make update-rubyspec<br>
すると、spec/rubyspec 下に rubyspec のコードが持ってこられるので、<br>
make test-rubyspec MSPECOPT='-V -j -f s /library/date/civil_spec.rb'<br>
などとすれば該当のテストだけを走らせることができます。</p>
<ol start="6">
<li>
</ol>
<p>Date#civil creats a Date for different calendar reform dates FAILED<br>
Expected 2<br>
to equal 20</p>
<p>/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/shared/civil.rb:61:in <code>block (2 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/civil_spec.rb:5:in </code><top (required)>'</p>
<ol start="7">
<li>
</ol>
<p>Date#civil doesn't blow up (illegal instruction and segfault, respectively) when fed huge numbers FAILED<br>
Expected FloatDomainError but got RangeError (float Inf out of range of integer)<br>
/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/shared/civil.rb:72:in <code>block (4 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/shared/civil.rb:71:in </code>each'<br>
/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/shared/civil.rb:71:in <code>block (3 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/civil_spec.rb:5:in </code><top (required)>'</p>
<ol start="8">
<li>
</ol>
<p>Date#gregorian? marks a day before the calendar reform as Julian FAILED<br>
Expected true<br>
to equal false</p>
<p>/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/gregorian_spec.rb:8:in <code>block (2 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/gregorian_spec.rb:4:in </code><top (required)>'</p>
<ol start="9">
<li>
</ol>
<p>Date#gregorian? marks a day after the calendar reform as Julian FAILED<br>
Expected false<br>
to equal true</p>
<p>/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/gregorian_spec.rb:13:in <code>block (2 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/gregorian_spec.rb:4:in </code><top (required)>'</p>
<ol start="10">
<li>
</ol>
<p>Date#julian? should mark a day before the calendar reform as Julian FAILED<br>
Expected false<br>
to equal true</p>
<p>/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/julian_spec.rb:20:in <code>block (2 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/julian_spec.rb:16:in </code><top (required)>'</p>
<ol start="11">
<li>
</ol>
<p>Date#julian? should mark a day after the calendar reform as Julian FAILED<br>
Expected true<br>
to equal false</p>
<p>/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/julian_spec.rb:25:in <code>block (2 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/julian_spec.rb:16:in </code><top (required)>'</p>
<ol start="12">
<li>
</ol>
<p>Date#new creats a Date for different calendar reform dates FAILED<br>
Expected 2<br>
to equal 20</p>
<p>/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/shared/civil.rb:61:in <code>block (2 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/new_spec.rb:5:in </code><top (required)>'</p>
<ol start="13">
<li>
</ol>
<p>Date#new doesn't blow up (illegal instruction and segfault, respectively) when fed huge numbers FAILED<br>
Expected FloatDomainError but got RangeError (float Inf out of range of integer)<br>
/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/shared/civil.rb:72:in <code>block (4 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/shared/civil.rb:71:in </code>each'<br>
/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/shared/civil.rb:71:in <code>block (3 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/date/new_spec.rb:5:in </code><top (required)>'</p>
Ruby master - Bug #4825 (Closed): BigDecimal#new is broken
https://redmine.ruby-lang.org/issues/4825
2011-06-04T21:32:58Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>RubySpec にて以下のテストが失敗しています。</p>
<p>BigDecimal.new creates a new object of class BigDecimal FAILED<br>
Expected #<a href="BigDecimal:445ef968,'0.1E0',9(18)" class="external">BigDecimal:445ef968,'0.1E0',9(18)</a><br>
to equal (1/10)</p>
<p>/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/bigdecimal/new_spec.rb:12:in <code>block (3 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/bigdecimal/new_spec.rb:8:in </code>each'<br>
/usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/bigdecimal/new_spec.rb:8:in <code>block (2 levels) in <top (required)>' /usr/home/chkbuild/build/ruby-trunk/20110604T110102Z/rubyspec/library/bigdecimal/new_spec.rb:4:in </code><top (required)>'</p>
Ruby master - Feature #4529 (Rejected): date_core と long 型
https://redmine.ruby-lang.org/issues/4529
2011-03-26T06:13:43Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
おそらく既にご存じの通り、最近 date_core も含めて ext 中の shorten-64-to-32 を直しています。<br>
それに際して date_core をいじっていて気づいたのですが、ユリウス日や年を long で保持したり int で保持したりしています。</p>
<p>int と long が混在していると、long から int へのキャスト時にオーバーフローが起こりえますし、<br>
そもそも long は</p>
<ul>
<li>環境によってサイズが違う (int も 理屈の上では違うはずだが、CRuby では 32bit しか確認してない)</li>
<li>long は mswin64 でだけポインタとサイズが違うので、Unix な人には気づきづらいバグの温床になる<br>
というような懸念があるので、特に理由が無いのであれば、int にするか、int64_t にするかした方がよいかと思います。<br>
(CRuby は C90 だが、int64_t を configure で定義している)</li>
</ul>
<p>例えば int に統一する場合のパッチは以下の通りです</p>
<p>=end</p>
Ruby master - Bug #4454 (Closed): Fails test by ext/date
https://redmine.ruby-lang.org/issues/4454
2011-03-01T13:34:19Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
ext/date 導入以降、以下の2テストが失敗します。</p>
<ol start="2">
<li>
<p>Failure:<br>
test_sub(TestDate) [/home/chkbuild/build/ruby-trunk/20110228T230101Z/ruby/test/date/test_date.rb:44]:<br>
<"#<DateSub: -4712-01-01 (-1/2,0,2299161)>"> expected but was<br>
<"#<DateSub[R]: -4712-01-01 (-1/2,0,2299161)>">.</p>
</li>
<li>
<p>Failure:<br>
test__attr(TestDateAttr) [/home/chkbuild/build/ruby-trunk/20110228T230101Z/ruby/test/date/test_date_attr.rb:12]:<br>
Expected /#<Date\d?: 1965-05-23 (4877807/2,0,2299161)>/ to match "#<Date[L]: 1965-05-23 (2438904j,0,2299161)>".<br>
=end</p>
</li>
</ol>
Ruby master - Bug #4395 (Closed): Can't require test/rubygems/simple_gem when tests run in differ...
https://redmine.ruby-lang.org/issues/4395
2011-02-14T09:34:46Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
It fails as following:</p>
<ol>
<li>
<p>Skipped:<br>
test_require_failed(test/rubygems/simple_gem) [/home/naruse/ruby/test/rubygems/test_gem_format.rb:8]:<br>
cannot load such file -- test/rubygems/simple_gem</p>
</li>
<li>
<p>Skipped:<br>
test_require_failed(test/rubygems/simple_gem) [/home/naruse/ruby/test/rubygems/test_gem_validator.rb:8]:<br>
cannot load such file -- test/rubygems/simple_gem</p>
</li>
</ol>
<p>Line 8 of test/rubygems/test_gem_format.rb should use require_relative.<br>
=end</p>
Ruby master - Feature #4180 (Closed): Add Zlib.deflate / Zlib.inflate
https://redmine.ruby-lang.org/issues/4180
2010-12-21T10:34:56Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
Zlib::Deflate.deflate ってムダに長いと思いんです。<br>
Rails 的な言葉で言うと、DRY じゃないと言いますか。<br>
Zlib.deflate でいいんじゃないかと。</p>
<p>同様に、Zlib::Inflate.inflate も Zlib.inflate でいいのではないでしょうか。<br>
=end</p>
Ruby master - Feature #4142 (Closed): multipart/form-data for net/http
https://redmine.ruby-lang.org/issues/4142
2010-12-10T09:17:39Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
multipart/form-data 対応を net/http に入れませんか。<br>
追加される API は Net::HTTPRequest#set_form になります。</p>
<p>akr さんからは multipart/form-data 用のデータを出力する API 案も示唆されたのですが、<br>
chunked encoding を考慮に入れるとうまくまとまらなかったので見送っています。</p>
<p>diff --git a/lib/net/http.rb b/lib/net/http.rb<br>
index 4d475b1..2751f77 100644<br>
--- a/lib/net/http.rb<br>
+++ b/lib/net/http.rb<br>
@@ -22,6 +22,7 @@<br>
require 'net/protocol'<br>
autoload :OpenSSL, 'openssl'<br>
require 'uri'<br>
+autoload :SecureRandom, 'securerandom'</p>
<p>module Net #:nodoc:</p>
<p>@@ -1772,7 +1773,8 @@ module Net #:nodoc:<br>
alias content_type= set_content_type</p>
<pre><code> # Set header fields and a body from HTML form data.
</code></pre>
<ul>
<li>
<a name="params-should-be-a-Hash-containing-HTML-form-data"></a>
<h1 >+params+ should be a Hash containing HTML form data.<a href="#params-should-be-a-Hash-containing-HTML-form-data" class="wiki-anchor">¶</a></h1>
</li>
</ul>
<ul>
<li>
<a name="params-should-be-an-Array-of-Arrays-or"></a>
<h1 >+params+ should be an Array of Arrays or<a href="#params-should-be-an-Array-of-Arrays-or" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="a-Hash-containing-HTML-form-data"></a>
<h1 >a Hash containing HTML form data.<a href="#a-Hash-containing-HTML-form-data" class="wiki-anchor">¶</a></h1>
<a name="Optional-argument-sep-means-data-record-separator"></a>
<h1 >Optional argument +sep+ means data record separator.<a href="#Optional-argument-sep-means-data-record-separator" class="wiki-anchor">¶</a></h1>
<h1></h1>
<a name="Values-are-URL-encoded-as-necessary-and-the-content-type-is-set-to"></a>
<h1 >Values are URL encoded as necessary and the content-type is set to<a href="#Values-are-URL-encoded-as-necessary-and-the-content-type-is-set-to" class="wiki-anchor">¶</a></h1>
</li>
</ul>
<p>@@ -1792,6 +1794,48 @@ module Net #:nodoc:</p>
<pre><code> alias form_data= set_form_data
</code></pre>
<ul>
<li>
<a name="Set-a-HTML-form-data-set"></a>
<h1 >Set a HTML form data set.<a href="#Set-a-HTML-form-data-set" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="params-is-the-form-data-set-it-is-an-Array-of-Arrays-or-a-Hash"></a>
<h1 >+params+ is the form data set; it is an Array of Arrays or a Hash<a href="#params-is-the-form-data-set-it-is-an-Array-of-Arrays-or-a-Hash" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="enctype-is-the-type-to-encode-the-form-data-set"></a>
<h1 >+enctype is the type to encode the form data set.<a href="#enctype-is-the-type-to-encode-the-form-data-set" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="It-is-applicationx-www-form-urlencoded-or-multipartform-data"></a>
<h1 >It is application/x-www-form-urlencoded or multipart/form-data.<a href="#It-is-applicationx-www-form-urlencoded-or-multipartform-data" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="formpot-is-an-optional-hash-to-specify-the-detail"></a>
<h1 >+formpot+ is an optional hash to specify the detail.<a href="#formpot-is-an-optional-hash-to-specify-the-detail" class="wiki-anchor">¶</a></h1>
</li>
<li>
<h1></h1>
</li>
<li>
<a name="boundary-the-boundary-of-the-multipart-message"></a>
<h1 >boundary:: the boundary of the multipart message<a href="#boundary-the-boundary-of-the-multipart-message" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="charset-the-charset-of-the-message-All-names-and-the-values-of"></a>
<h1 >charset:: the charset of the message. All names and the values of<a href="#charset-the-charset-of-the-message-All-names-and-the-values-of" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="non-file-fields-are-encoded-as-the-charset"></a>
<h1 >non-file fields are encoded as the charset.<a href="#non-file-fields-are-encoded-as-the-charset" class="wiki-anchor">¶</a></h1>
</li>
<li>
<h1></h1>
</li>
<li>
<a name="Each-item-of-params-is-an-array-and-contains-following-items"></a>
<h1 >Each item of params is an array and contains following items:<a href="#Each-item-of-params-is-an-array-and-contains-following-items" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="name-the-name-of-the-field"></a>
<h1 >+name+:: the name of the field<a href="#name-the-name-of-the-field" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="value-the-value-of-the-field-it-should-be-a-String-or-a-File"></a>
<h1 >+value+:: the value of the field, it should be a String or a File<a href="#value-the-value-of-the-field-it-should-be-a-String-or-a-File" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="opt-an-optional-hash-to-specify-additional-information"></a>
<h1 >+opt+:: an optional hash to specify additional information<a href="#opt-an-optional-hash-to-specify-additional-information" class="wiki-anchor">¶</a></h1>
</li>
<li>
<h1></h1>
</li>
<li>
<a name="Each-item-is-a-file-field-or-a-normal-field"></a>
<h1 >Each item is a file field or a normal field.<a href="#Each-item-is-a-file-field-or-a-normal-field" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="If-value-is-a-File-object-or-the-opt-have-a-filename-key"></a>
<h1 >If +value+ is a File object or the +opt+ have a filename key,<a href="#If-value-is-a-File-object-or-the-opt-have-a-filename-key" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="the-item-is-treated-as-a-file-field"></a>
<h1 >the item is treated as a file field.<a href="#the-item-is-treated-as-a-file-field" class="wiki-anchor">¶</a></h1>
</li>
<li>
<h1></h1>
</li>
<li>
<a name="If-Transfer-Encoding-is-set-as-chunked-this-send-the-request-in"></a>
<h1 >If Transfer-Encoding is set as chunked, this send the request in<a href="#If-Transfer-Encoding-is-set-as-chunked-this-send-the-request-in" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="chunked-encoding-Because-chunked-encoding-is-HTTP11-feature"></a>
<h1 >chunked encoding. Because chunked encoding is HTTP/1.1 feature,<a href="#chunked-encoding-Because-chunked-encoding-is-HTTP11-feature" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="you-must-confirm-the-server-to-support-HTTP11-before-sending-it"></a>
<h1 >you must confirm the server to support HTTP/1.1 before sending it.<a href="#you-must-confirm-the-server-to-support-HTTP11-before-sending-it" class="wiki-anchor">¶</a></h1>
</li>
<li>
<h1></h1>
</li>
<li>
<a name="Example"></a>
<h1 >Example:<a href="#Example" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="httpset_formq-ruby-lang-en"></a>
<h1 >http.set_form([["q", "ruby"], ["lang", "en"]])<a href="#httpset_formq-ruby-lang-en" class="wiki-anchor">¶</a></h1>
</li>
<li>
<h1></h1>
</li>
<li>
<a name="See-also-RFC-2388-RFC-2616-HTML-401-and-HTML5"></a>
<h1 >See also RFC 2388, RFC 2616, HTML 4.01, and HTML5<a href="#See-also-RFC-2388-RFC-2616-HTML-401-and-HTML5" class="wiki-anchor">¶</a></h1>
</li>
<li>
<h1></h1>
</li>
<li>
<p>def set_form(params, enctype='application/x-www-form-urlencoded', formopt={})</p>
</li>
<li>
<pre><code> @body_data = params
</code></pre>
</li>
<li>
<pre><code> @body = nil
</code></pre>
</li>
<li>
<pre><code> @body_stream = nil
</code></pre>
</li>
<li>
<pre><code> @form_option = formopt
</code></pre>
</li>
<li>
<pre><code> case enctype
</code></pre>
</li>
<li>
<pre><code> when /\Aapplication\/x-www-form-urlencoded\z/i,
</code></pre>
</li>
<li>
<pre><code> /\Amultipart\/form-data\z/i
</code></pre>
</li>
<li>
<pre><code> self.content_type = enctype
</code></pre>
</li>
<li>
<pre><code> else
</code></pre>
</li>
<li>
<pre><code> raise ArgumentError, "invalid enctype: #{enctype}"
</code></pre>
</li>
<li>
<pre><code> end
</code></pre>
</li>
<li>
<p>end</p>
</li>
<li>
<a name="Set-the-Authorization-header-for-Basic-authorization"></a>
<h1 >Set the Authorization: header for "Basic" authorization.<a href="#Set-the-Authorization-header-for-Basic-authorization" class="wiki-anchor">¶</a></h1>
<p>def basic_auth(account, password)<br>
@header['authorization'] = [basic_encode(account, password)]<br>
@@ -1849,6 +1893,7 @@ module Net #:nodoc:<br>
self['User-Agent'] ||= 'Ruby'<br>
@body = nil<br>
@body_stream = nil</p>
</li>
<li>
<pre><code> @body_data = nil
</code></pre>
<p>end</p>
<p>attr_reader :method<br>
@@ -1876,6 +1921,7 @@ module Net #:nodoc:<br>
def body=(str)<br>
@body = str<br>
@body_stream = nil</p>
</li>
<li>
<pre><code> @body_data = nil
str
</code></pre>
<p>end</p>
</li>
</ul>
<p>@@ -1884,6 +1930,7 @@ module Net #:nodoc:<br>
def body_stream=(input)<br>
@body = nil<br>
@body_stream = input</p>
<ul>
<li>
<pre><code> @body_data = nil
input
</code></pre>
end</li>
</ul>
<p>@@ -1901,6 +1948,8 @@ module Net #:nodoc:<br>
send_request_with_body sock, ver, path, @body<br>
elsif @body_stream<br>
send_request_with_body_stream sock, ver, path, @body_stream</p>
<ul>
<li>
<pre><code> elsif @body_data
</code></pre>
</li>
<li>
<pre><code> send_request_with_body_data sock, ver, path, @body_data
else
write_header sock, ver, path
end
</code></pre>
</li>
</ul>
<p>@@ -1935,6 +1984,92 @@ module Net #:nodoc:<br>
end<br>
end</p>
<ul>
<li>
<p>def send_request_with_body_data(sock, ver, path, params)</p>
</li>
<li>
<pre><code> if /\Amultipart\/form-data\z/i !~ self.content_type
</code></pre>
</li>
<li>
<pre><code> self.content_type = 'application/x-www-form-urlencoded'
</code></pre>
</li>
<li>
<pre><code> return send_request_with_body(sock, ver, path, URI.encode_www_form(params))
</code></pre>
</li>
<li>
<pre><code> end
</code></pre>
</li>
<li>
<li>
<pre><code> opt = @form_option.dup
</code></pre>
</li>
<li>
<pre><code> opt[:boundary] ||= SecureRandom.urlsafe_base64(40)
</code></pre>
</li>
<li>
<pre><code> self.set_content_type(self.content_type, boundary: opt[:boundary])
</code></pre>
</li>
<li>
<pre><code> if chunked?
</code></pre>
</li>
<li>
<pre><code> write_header sock, ver, path
</code></pre>
</li>
<li>
<pre><code> encode_multipart_form_data(sock, params, opt)
</code></pre>
</li>
<li>
<pre><code> else
</code></pre>
</li>
<li>
<pre><code> require 'tempfile'
</code></pre>
</li>
<li>
<pre><code> file = Tempfile.new('multipart')
</code></pre>
</li>
<li>
<pre><code> encode_multipart_form_data(file, params, opt)
</code></pre>
</li>
<li>
<pre><code> file.rewind
</code></pre>
</li>
<li>
<pre><code> self.content_length = file.size
</code></pre>
</li>
<li>
<pre><code> write_header sock, ver, path
</code></pre>
</li>
<li>
<pre><code> IO.copy_stream(file, sock)
</code></pre>
</li>
<li>
<pre><code> end
</code></pre>
</li>
<li>
<p>end</p>
</li>
<li>
<li>
<p>def encode_multipart_form_data(out, params, opt)</p>
</li>
<li>
<pre><code> charset = opt[:charset]
</code></pre>
</li>
<li>
<pre><code> boundary = opt[:boundary]
</code></pre>
</li>
<li>
<pre><code> boundary ||= SecureRandom.urlsafe_base64(40)
</code></pre>
</li>
<li>
<pre><code> chunked_p = chunked?
</code></pre>
</li>
<li>
<li>
<pre><code> buf = ''
</code></pre>
</li>
<li>
<pre><code> params.each do |key, value, h={}|
</code></pre>
</li>
<li>
<pre><code> key = quote_string(key, charset)
</code></pre>
</li>
<li>
<pre><code> filename =
</code></pre>
</li>
<li>
<pre><code> h.key?(:filename) ? h[:filename] :
</code></pre>
</li>
<li>
<pre><code> value.respond_to?(:to_path) ? File.basename(value.to_path) :
</code></pre>
</li>
<li>
<pre><code> nil
</code></pre>
</li>
<li>
<li>
<pre><code> buf << "--#{boundary}\r\n"
</code></pre>
</li>
<li>
<pre><code> if filename
</code></pre>
</li>
<li>
<pre><code> filename = quote_string(filename, charset)
</code></pre>
</li>
<li>
<pre><code> type = h[:content_type] || 'application/octet-stream'
</code></pre>
</li>
<li>
<pre><code> buf << "Content-Disposition: form-data; " \
</code></pre>
</li>
<li>
<pre><code> "name=\"#{key}\"; filename=\"#{filename}\"\r\n" \
</code></pre>
</li>
<li>
<pre><code> "Content-Type: #{type}\r\n\r\n"
</code></pre>
</li>
<li>
<pre><code> if !out.respond_to?(:write) || !value.respond_to?(:read)
</code></pre>
</li>
<li>
<pre><code> # if +out+ is not an IO or +value+ is not an IO
</code></pre>
</li>
<li>
<pre><code> buf << (value.respond_to?(:read) ? value.read : value)
</code></pre>
</li>
<li>
<pre><code> elsif value.respond_to?(:size) && chunked_p
</code></pre>
</li>
<li>
<pre><code> # if +out+ is an IO and +value+ is a File, use IO.copy_stream
</code></pre>
</li>
<li>
<pre><code> flush_buffer(out, buf, chunked_p)
</code></pre>
</li>
<li>
<pre><code> out << "%x\r\n" % value.size if chunked_p
</code></pre>
</li>
<li>
<pre><code> IO.copy_stream(value, out)
</code></pre>
</li>
<li>
<pre><code> out << "\r\n" if chunked_p
</code></pre>
</li>
<li>
<pre><code> else
</code></pre>
</li>
<li>
<pre><code> # +out+ is an IO, and +value+ is not a File but an IO
</code></pre>
</li>
<li>
<pre><code> flush_buffer(out, buf, chunked_p)
</code></pre>
</li>
<li>
<pre><code> 1 while flush_buffer(out, value.read(4096), chunked_p)
</code></pre>
</li>
<li>
<pre><code> end
</code></pre>
</li>
<li>
<pre><code> else
</code></pre>
</li>
<li>
<pre><code> # non-file field:
</code></pre>
</li>
<li>
<pre><code> # HTML5 says, "The parts of the generated multipart/form-data
</code></pre>
</li>
<li>
<pre><code> # resource that correspond to non-file fields must not have a
</code></pre>
</li>
<li>
<pre><code> # Content-Type header specified."
</code></pre>
</li>
<li>
<pre><code> buf << "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n"
</code></pre>
</li>
<li>
<pre><code> buf << (value.respond_to?(:read) ? value.read : value)
</code></pre>
</li>
<li>
<pre><code> end
</code></pre>
</li>
<li>
<pre><code> buf << "\r\n"
</code></pre>
</li>
<li>
<pre><code> end
</code></pre>
</li>
<li>
<pre><code> buf << "--#{boundary}--\r\n"
</code></pre>
</li>
<li>
<pre><code> flush_buffer(out, buf, chunked_p)
</code></pre>
</li>
<li>
<pre><code> out << "0\r\n\r\n" if chunked_p
</code></pre>
</li>
<li>
<p>end</p>
</li>
<li>
<li>
<p>def quote_string(str, charset)</p>
</li>
<li>
<pre><code> str = str.encode(charset, fallback:->(c){'&#%d;'%c.encode("UTF-8").ord}) if charset
</code></pre>
</li>
<li>
<pre><code> str = str.gsub(/[\\"]/, '\\\\\&')
</code></pre>
</li>
<li>
<p>end</p>
</li>
<li>
<li>
<p>def flush_buffer(out, buf, chunked_p)</p>
</li>
<li>
<pre><code> return unless buf
</code></pre>
</li>
<li>
<pre><code> out << "%x\r\n"%buf.bytesize if chunked_p
</code></pre>
</li>
<li>
<pre><code> out << buf
</code></pre>
</li>
<li>
<pre><code> out << "\r\n" if chunked_p
</code></pre>
</li>
<li>
<pre><code> buf.clear
</code></pre>
</li>
<li>
<p>end</p>
</li>
<li>
<p>def supply_default_content_type<br>
return if content_type()<br>
warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE<br>
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb<br>
index 2a6cfb4..a3ffa71 100644<br>
--- a/lib/net/protocol.rb<br>
+++ b/lib/net/protocol.rb<br>
@@ -168,6 +168,8 @@ module Net # :nodoc:<br>
}<br>
end</p>
</li>
<li>
<p>alias << write</p>
</li>
<li>
<p>def writeline(str)<br>
writing {<br>
write0 str + "\r\n"<br>
diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb<br>
index 76280ad..12c03a4 100644<br>
--- a/test/net/http/test_http.rb<br>
+++ b/test/net/http/test_http.rb<br>
@@ -303,6 +303,102 @@ module TestNetHTTP_version_1_2_methods<br>
assert_equal data.size, res.body.size<br>
assert_equal data, res.body<br>
end</p>
</li>
<li>
<li>
<p>def test_set_form</p>
</li>
<li>
<p>require 'tempfile'</p>
</li>
<li>
<p>file = Tempfile.new('ruby-test')</p>
</li>
<li>
<p>file << "\u{30c7}\u{30fc}\u{30bf}"</p>
</li>
<li>
<p>data = [</p>
</li>
<li>
<pre><code> ['name', 'Gonbei Nanashi'],
</code></pre>
</li>
<li>
<pre><code> ['name', "\u{540d}\u{7121}\u{3057}\u{306e}\u{6a29}\u{5175}\u{885b}"],
</code></pre>
</li>
<li>
<pre><code> ['s"i\o', StringIO.new("\u{3042 3044 4e9c 925b}")],
</code></pre>
</li>
<li>
<pre><code> ["file", file, filename: "ruby-test"]
</code></pre>
</li>
<li>
<p>]</p>
</li>
<li>
<p>expected = <<"<strong>EOM</strong>".gsub(/\n/, "\r\n")<br>
+--<br>
+Content-Disposition: form-data; name="name"</p>
</li>
<li>
</ul>
<p>+Gonbei Nanashi<br>
+--<br>
+Content-Disposition: form-data; name="name"<br>
+<br>
+\xE5\x90\x8D\xE7\x84\xA1\xE3\x81\x97\xE3\x81\xAE\xE6\xA8\xA9\xE5\x85\xB5\xE8\xA1\x9B<br>
+--<br>
+Content-Disposition: form-data; name="s\"i\\o"<br>
+<br>
+\xE3\x81\x82\xE3\x81\x84\xE4\xBA\x9C\xE9\x89\x9B<br>
+--<br>
+Content-Disposition: form-data; name="file"; filename="ruby-test"<br>
+Content-Type: application/octet-stream<br>
+<br>
+\xE3\x83\x87\xE3\x83\xBC\xE3\x82\xBF<br>
+----<br>
+<strong>EOM</strong></p>
<ul>
<li>start {|http|</li>
<li>
<pre><code> _test_set_form_urlencoded(http, data.reject{|k,v|!v.is_a?(String)})
</code></pre>
</li>
<li>
<pre><code> _test_set_form_multipart(http, false, data, expected)
</code></pre>
</li>
<li>
<pre><code> _test_set_form_multipart(http, true, data, expected)
</code></pre>
</li>
<li>}</li>
<li>end</li>
<li>
<li>def _test_set_form_urlencoded(http, data)</li>
<li>req = Net::HTTP::Post.new('/')</li>
<li>req.set_form(data)</li>
<li>res = http.request req</li>
<li>assert_equal "name=Gonbei+Nanashi&name=%E5%90%8D%E7%84%A1%E3%81%97%E3%81%AE%E6%A8%A9%E5%85%B5%E8%A1%9B", res.body</li>
<li>end</li>
<li>
<li>def _test_set_form_multipart(http, chunked_p, data, expected)</li>
<li>data.each{|k,v|v.rewind rescue nil}</li>
<li>req = Net::HTTP::Post.new('/')</li>
<li>req.set_form(data, 'multipart/form-data')</li>
<li>req['Transfer-Encoding'] = 'chunked' if chunked_p</li>
<li>res = http.request req</li>
<li>body = res.body</li>
<li>assert_match(/\A--(?\S+)/, body)</li>
<li>/\A--(?\S+)/ =~ body</li>
<li>expected = expected.gsub(//, boundary)</li>
<li>assert_equal(expected, body)</li>
<li>end</li>
<li>
<li>def test_set_form_with_file</li>
<li>require 'tempfile'</li>
<li>file = Tempfile.new('ruby-test')</li>
<li>file << $test_net_http_data</li>
<li>filename = File.basename(file.to_path)</li>
<li>data = [['file', file]]</li>
<li>expected = <<"<strong>EOM</strong>".gsub(/\n/, "\r\n")<br>
+--<br>
+Content-Disposition: form-data; name="file"; filename=""<br>
+Content-Type: application/octet-stream</li>
<li>
</ul>
<p>+<br>
+----<br>
+<strong>EOM</strong></p>
<ul>
<li>expected.sub!(//, filename)</li>
<li>expected.sub!(//, $test_net_http_data)</li>
<li>start {|http|</li>
<li>
<pre><code> data.each{|k,v|v.rewind rescue nil}
</code></pre>
</li>
<li>
<pre><code> req = Net::HTTP::Post.new('/')
</code></pre>
</li>
<li>
<pre><code> req.set_form(data, 'multipart/form-data')
</code></pre>
</li>
<li>
<pre><code> res = http.request req
</code></pre>
</li>
<li>
<pre><code> body = res.body
</code></pre>
</li>
<li>
<pre><code> header, _ = body.split(/\r\n\r\n/, 2)
</code></pre>
</li>
<li>
<pre><code> assert_match(/\A--(?<boundary>\S+)/, body)
</code></pre>
</li>
<li>
<pre><code> /\A--(?<boundary>\S+)/ =~ body
</code></pre>
</li>
<li>
<pre><code> expected = expected.gsub(/<boundary>/, boundary)
</code></pre>
</li>
<li>
<pre><code> assert_match(/^--(?<boundary>\S+)\r\n/, header)
</code></pre>
</li>
<li>
<pre><code> assert_match(
</code></pre>
</li>
<li>
<pre><code> /^Content-Disposition: form-data; name="file"; filename="#{filename}"\r\n/,
</code></pre>
</li>
<li>
<pre><code> header)
</code></pre>
</li>
<li>
<pre><code> assert_equal(expected, body)
</code></pre>
</li>
<li>
<li>
<pre><code> data.each{|k,v|v.rewind rescue nil}
</code></pre>
</li>
<li>
<pre><code> req['Transfer-Encoding'] = 'chunked'
</code></pre>
</li>
<li>
<pre><code> res = http.request req
</code></pre>
</li>
<li>
<pre><code> #assert_equal(expected, res.body)
</code></pre>
</li>
<li>}</li>
<li>end<br>
end</li>
</ul>
<p>class TestNetHTTP_version_1_1 < Test::Unit::TestCase<br>
=end</p>
Ruby master - Feature #4089 (Closed): Add addr2line for C level backtrace
https://redmine.ruby-lang.org/issues/4089
2010-11-26T09:22:36Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
最近の Ruby は SEGV や BUG 時に Ruby level backtrace を出したり、<br>
取れるときは C level backtrace を出したりしています。</p>
<p>ところが、C level backtrace だけ見ても実のところあまり助けにならないことが多いので、<br>
ソースコードのファイルや行数も可能ならば出したいところです。</p>
<p>などと言っていたら浜地さんがパッチを作ってくれたので、これを取り込みませんか。<br>
glibc 環境 (つまり Linux) や、libexecinfo を導入している FreeBSD や NetBSD など (のELFなバイナリ) で動きます。</p>
<p>diff --git a/addr2line.c b/addr2line.c<br>
new file mode 100644<br>
index 0000000..da85f4d<br>
--- /dev/null<br>
+++ b/addr2line.c<br>
@@ -0,0 +1,534 @@<br>
+/**********************************************************************<br>
+</p>
<ul>
<li>addr2line.h -</li>
<li>
<li>$Author$</li>
<li>
<li>Copyright (C) 2010 Shinichiro Hamaji</li>
<li>
</ul>
<p>+*********************************************************************<em>/<br>
+<br>
+#include "addr2line.h"<br>
+<br>
+#include <stdio.h><br>
+<br>
+#ifdef <strong>ELF</strong><br>
+<br>
+#include <elf.h><br>
+#include <fcntl.h><br>
+#include <limits.h><br>
+#include <stdio.h><br>
+#include <stdlib.h><br>
+#include <string.h><br>
+#include <sys/mman.h><br>
+#include <sys/types.h><br>
+#include <sys/stat.h><br>
+#include <unistd.h><br>
+<br>
+#ifdef HAVE_DL_ITERATE_PHDR<br>
+# ifndef _GNU_SOURCE<br>
+# define _GNU_SOURCE<br>
+# endif<br>
+# include <link.h><br>
+#endif<br>
+<br>
+#define DW_LNS_copy 0x01<br>
+#define DW_LNS_advance_pc 0x02<br>
+#define DW_LNS_advance_line 0x03<br>
+#define DW_LNS_set_file 0x04<br>
+#define DW_LNS_set_column 0x05<br>
+#define DW_LNS_negate_stmt 0x06<br>
+#define DW_LNS_set_basic_block 0x07<br>
+#define DW_LNS_const_add_pc 0x08<br>
+#define DW_LNS_fixed_advance_pc 0x09<br>
+#define DW_LNS_set_prologue_end 0x0a /</em> DWARF3 <em>/<br>
+#define DW_LNS_set_epilogue_begin 0x0b /</em> DWARF3 <em>/<br>
+#define DW_LNS_set_isa 0x0c /</em> DWARF3 <em>/<br>
+<br>
+/</em> Line number extended opcode name. <em>/<br>
+#define DW_LNE_end_sequence 0x01<br>
+#define DW_LNE_set_address 0x02<br>
+#define DW_LNE_define_file 0x03<br>
+#define DW_LNE_set_discriminator 0x04 /</em> DWARF4 */<br>
+<br>
+# if SIZEOF_VOIDP == 8<br>
+# define ElfW(x) Elf64##<em>##x<br>
+# else<br>
+# define ElfW(x) Elf32##</em>##x<br>
+# endif<br>
+<br>
+typedef struct {</p>
<ul>
<li>const char *dirname;</li>
<li>const char *filename;</li>
<li>int line;</li>
<li>
<li>int fd;</li>
<li>void *mapped;</li>
<li>size_t mapped_size;</li>
<li>unsigned long base_addr;<br>
+} line_info_t;</li>
<li>
</ul>
<p>+/* Avoid consuming stack as this module may be used from signal handler */<br>
+static char binary_filename[PATH_MAX];<br>
+<br>
+static unsigned long<br>
+uleb128(char **p) {</p>
<ul>
<li>unsigned long r = 0;</li>
<li>int s = 0;</li>
<li>for (;;) {</li>
<li>unsigned char b = *(unsigned char *)(*p)++;</li>
<li>if (b < 0x80) {</li>
<li>
<pre><code> r += b << s;
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>}</li>
<li>r += (b & 0x7f) << s;</li>
<li>s += 7;</li>
<li>}</li>
<li>return r;<br>
+}</li>
<li>
</ul>
<p>+static long<br>
+sleb128(char **p) {</p>
<ul>
<li>long r = 0;</li>
<li>int s = 0;</li>
<li>for (;;) {</li>
<li>unsigned char b = *(unsigned char *)(*p)++;</li>
<li>if (b < 0x80) {</li>
<li>
<pre><code> if (b & 0x40) {
</code></pre>
</li>
<li>
<pre><code> r -= (0x80 - b) << s;
</code></pre>
</li>
<li>
<pre><code> }
</code></pre>
</li>
<li>
<pre><code> else {
</code></pre>
</li>
<li>
<pre><code> r += (b & 0x3f) << s;
</code></pre>
</li>
<li>
<pre><code> }
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>}</li>
<li>r += (b & 0x7f) << s;</li>
<li>s += 7;</li>
<li>}</li>
<li>return r;<br>
+}</li>
<li>
</ul>
<p>+static const char *<br>
+get_nth_dirname(int dir, char *p)<br>
+{</p>
<ul>
<li>if (!dir--) {</li>
<li>return "";</li>
<li>}</li>
<li>while (dir) {</li>
<li>while (*p) p++;</li>
<li>p++;</li>
<li>if (!*p) {</li>
<li>
<pre><code> fprintf(stderr, "Unexpected directory number %d in %s\n",
</code></pre>
</li>
<li>
<pre><code> dir, binary_filename);
</code></pre>
</li>
<li>
<pre><code> return "";
</code></pre>
</li>
<li>}</li>
<li>}</li>
<li>return p;<br>
+}</li>
<li>
</ul>
<p>+static void<br>
+fill_filename(int file, char *include_directories, char *filenames,</p>
<ul>
<li>
<pre><code> line_info_t *line)
</code></pre>
</li>
</ul>
<p>+{</p>
<ul>
<li>int i;</li>
<li>char *p = filenames;</li>
<li>char *filename;</li>
<li>unsigned long dir;</li>
<li>for (i = 1; i <= file; i++) {</li>
<li>filename = p;</li>
<li>if (!*p) {</li>
<li>
<pre><code> /* Need to output binary file name? */
</code></pre>
</li>
<li>
<pre><code> fprintf(stderr, "Unexpected file number %d in %s\n",
</code></pre>
</li>
<li>
<pre><code> file, binary_filename);
</code></pre>
</li>
<li>
<pre><code> return;
</code></pre>
</li>
<li>}</li>
<li>while (*p) p++;</li>
<li>p++;</li>
<li>dir = uleb128(&p);</li>
<li>/* last modified. */</li>
<li>uleb128(&p);</li>
<li>/* size of the file. */</li>
<li>uleb128(&p);</li>
<li>
<li>if (i == file) {</li>
<li>
<pre><code> line->filename = filename;
</code></pre>
</li>
<li>
<pre><code> line->dirname = get_nth_dirname(dir, include_directories);
</code></pre>
</li>
<li>}</li>
<li>}<br>
+}</li>
<li>
</ul>
<p>+static int<br>
+get_path_from_symbol(const char *symbol, const char **p, size_t *len)<br>
+{</p>
<ul>
<li>if (symbol[0] == '0') {</li>
<li>/* libexecinfo */</li>
<li>*p = strchr(symbol, '/');</li>
<li>if (*p == NULL) return 0;</li>
<li>*len = strlen(*p);</li>
<li>}</li>
<li>else {</li>
<li>/* glibc */</li>
<li>const char *q;</li>
<li>*p = symbol;</li>
<li>q = strchr(symbol, '(');</li>
<li>if (q == NULL) return 0;</li>
<li>*len = q - symbol;</li>
<li>}</li>
<li>return 1;<br>
+}</li>
<li>
</ul>
<p>+static void<br>
+fill_line(int num_traces, void **traces,</p>
<ul>
<li>unsigned long addr, int file, int line,</li>
<li>char *include_directories, char *filenames, line_info_t *lines)<br>
+{</li>
<li>int i;</li>
<li>for (i = 0; i < num_traces; i++) {</li>
<li>unsigned long a = (unsigned long)traces[i] - lines[i].base_addr;</li>
<li>/* We assume one line code doesn't result >100 bytes of native code.</li>
<li>
<pre><code> We may want more reliable way eventually... */
</code></pre>
</li>
<li>if (addr < a && a < addr + 100) {</li>
<li>
<pre><code> fill_filename(file, include_directories, filenames, &lines[i]);
</code></pre>
</li>
<li>
<pre><code> lines[i].line = line;
</code></pre>
</li>
<li>}</li>
<li>}<br>
+}</li>
<li>
</ul>
<p>+static void<br>
+parse_debug_line_cu(int num_traces, void **traces,</p>
<ul>
<li>
<pre><code> char **debug_line, line_info_t *lines)
</code></pre>
</li>
</ul>
<p>+{</p>
<ul>
<li>char *p, *cu_end, *cu_start, *include_directories, *filenames;</li>
<li>unsigned long unit_length;</li>
<li>int default_is_stmt, line_base;</li>
<li>unsigned int header_length, minimum_instruction_length, line_range,</li>
<li>
<pre><code> opcode_base;
</code></pre>
</li>
<li>unsigned char *standard_opcode_lengths;</li>
<li>
<li>/* The registers. */</li>
<li>unsigned long addr = 0;</li>
<li>unsigned int file = 1;</li>
<li>unsigned int line = 1;</li>
<li>unsigned int column = 0;</li>
<li>int is_stmt = default_is_stmt;</li>
<li>int basic_block = 0;</li>
<li>int end_sequence = 0;</li>
<li>int prologue_end = 0;</li>
<li>int epilogue_begin = 0;</li>
<li>unsigned int isa = 0;</li>
<li>
<li>p = *debug_line;</li>
<li>
<li>unit_length = *(unsigned int *)p;</li>
<li>p += sizeof(unsigned int);</li>
<li>if (unit_length == 0xffffffff) {</li>
<li>unit_length = *(unsigned long *)p;</li>
<li>p += sizeof(unsigned long);</li>
<li>}</li>
<li>
<li>cu_end = p + unit_length;</li>
<li>
<li>/*dwarf_version = *(unsigned short <em>)p;</em>/</li>
<li>p += 2;</li>
<li>
<li>header_length = *(unsigned int *)p;</li>
<li>p += sizeof(unsigned int);</li>
<li>
<li>cu_start = p + header_length;</li>
<li>
<li>minimum_instruction_length = *(unsigned char *)p;</li>
<li>p++;</li>
<li>
<li>default_is_stmt = *(unsigned char *)p;</li>
<li>p++;</li>
<li>
<li>line_base = *(char *)p;</li>
<li>p++;</li>
<li>
<li>line_range = *(unsigned char *)p;</li>
<li>p++;</li>
<li>
<li>opcode_base = *(unsigned char *)p;</li>
<li>p++;</li>
<li>
<li>standard_opcode_lengths = (unsigned char *)p - 1;</li>
<li>p += opcode_base - 1;</li>
<li>
<li>include_directories = p;</li>
<li>
<li>/* skip include directories */</li>
<li>while (*p) {</li>
<li>while (*p) p++;</li>
<li>p++;</li>
<li>}</li>
<li>p++;</li>
<li>
<li>filenames = p;</li>
<li>
<li>p = cu_start;</li>
<li>
</ul>
<p>+#define FILL_LINE() \</p>
<ul>
<li>do { \</li>
<li>fill_line(num_traces, traces, addr, file, line, \</li>
<li>
<pre><code> include_directories, filenames, lines); \
</code></pre>
</li>
<li>basic_block = prologue_end = epilogue_begin = 0; \</li>
<li>} while (0)</li>
<li>
<li>while (p < cu_end) {</li>
<li>unsigned long a;</li>
<li>unsigned char op = *p++;</li>
<li>switch (op) {</li>
<li>case DW_LNS_copy:</li>
<li>
<pre><code> FILL_LINE();
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>case DW_LNS_advance_pc:</li>
<li>
<pre><code> a = uleb128(&p);
</code></pre>
</li>
<li>
<pre><code> addr += a;
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>case DW_LNS_advance_line: {</li>
<li>
<pre><code> long a = sleb128(&p);
</code></pre>
</li>
<li>
<pre><code> line += a;
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>}</li>
<li>case DW_LNS_set_file:</li>
<li>
<pre><code> file = uleb128(&p);
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>case DW_LNS_set_column:</li>
<li>
<pre><code> column = uleb128(&p);
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>case DW_LNS_negate_stmt:</li>
<li>
<pre><code> is_stmt = !is_stmt;
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>case DW_LNS_set_basic_block:</li>
<li>
<pre><code> basic_block = 1;
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>case DW_LNS_const_add_pc:</li>
<li>
<pre><code> a = ((255 - opcode_base) / line_range) *
</code></pre>
</li>
<li>
<pre><code> minimum_instruction_length;
</code></pre>
</li>
<li>
<pre><code> addr += a;
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>case DW_LNS_fixed_advance_pc:</li>
<li>
<pre><code> a = *(unsigned char *)p++;
</code></pre>
</li>
<li>
<pre><code> addr += a;
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>case DW_LNS_set_prologue_end:</li>
<li>
<pre><code> prologue_end = 1;
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>case DW_LNS_set_epilogue_begin:</li>
<li>
<pre><code> epilogue_begin = 1;
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>case DW_LNS_set_isa:</li>
<li>
<pre><code> isa = uleb128(&p);
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>case 0:</li>
<li>
<pre><code> a = *(unsigned char *)p++;
</code></pre>
</li>
<li>
<pre><code> op = *p++;
</code></pre>
</li>
<li>
<pre><code> switch (op) {
</code></pre>
</li>
<li>
<pre><code> case DW_LNE_end_sequence:
</code></pre>
</li>
<li>
<pre><code> end_sequence = 1;
</code></pre>
</li>
<li>
<pre><code> FILL_LINE();
</code></pre>
</li>
<li>
<pre><code> addr = 0;
</code></pre>
</li>
<li>
<pre><code> file = 1;
</code></pre>
</li>
<li>
<pre><code> line = 1;
</code></pre>
</li>
<li>
<pre><code> column = 0;
</code></pre>
</li>
<li>
<pre><code> is_stmt = default_is_stmt;
</code></pre>
</li>
<li>
<pre><code> end_sequence = 0;
</code></pre>
</li>
<li>
<pre><code> isa = 0;
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>
<pre><code> case DW_LNE_set_address:
</code></pre>
</li>
<li>
<pre><code> addr = *(unsigned long *)p;
</code></pre>
</li>
<li>
<pre><code> p += sizeof(unsigned long);
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>
<pre><code> case DW_LNE_define_file:
</code></pre>
</li>
<li>
<pre><code> fprintf(stderr, "Unsupported operation in %s\n",
</code></pre>
</li>
<li>
<pre><code> binary_filename);
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>
<pre><code> default:
</code></pre>
</li>
<li>
<pre><code> fprintf(stderr, "Unknown extended opcode: %d in %s\n",
</code></pre>
</li>
<li>
<pre><code> op, binary_filename);
</code></pre>
</li>
<li>
<pre><code> }
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>default: {</li>
<li>
<pre><code> unsigned int addr_incr;
</code></pre>
</li>
<li>
<pre><code> int line_incr;
</code></pre>
</li>
<li>
<pre><code> a = op - opcode_base;
</code></pre>
</li>
<li>
<pre><code> addr_incr = (a / line_range) * minimum_instruction_length;
</code></pre>
</li>
<li>
<pre><code> line_incr = line_base + (a % line_range);
</code></pre>
</li>
<li>
<pre><code> addr += addr_incr;
</code></pre>
</li>
<li>
<pre><code> line += line_incr;
</code></pre>
</li>
<li>
<pre><code> FILL_LINE();
</code></pre>
</li>
<li>}</li>
<li>}</li>
<li>}</li>
<li>*debug_line = p;<br>
+}</li>
<li>
</ul>
<p>+static void<br>
+parse_debug_line(int num_traces, void **traces,</p>
<ul>
<li>
<pre><code> char *debug_line, unsigned long size, line_info_t *lines)
</code></pre>
</li>
</ul>
<p>+{</p>
<ul>
<li>char *debug_line_end = debug_line + size;</li>
<li>while (debug_line < debug_line_end) {</li>
<li>parse_debug_line_cu(num_traces, traces, &debug_line, lines);</li>
<li>}</li>
<li>if (debug_line != debug_line_end) {</li>
<li>fprintf(stderr, "Unexpected size of .debug_line in %s\n",</li>
<li>
<pre><code> binary_filename);
</code></pre>
</li>
<li>}<br>
+}</li>
<li>
</ul>
<p>+/* read file and fill lines */<br>
+static void<br>
+fill_lines(int num_traces, void **traces, char **syms,</p>
<ul>
<li>
<pre><code>char *file, line_info_t *lines)
</code></pre>
</li>
</ul>
<p>+{</p>
<ul>
<li>int i;</li>
<li>char *shstr;</li>
<li>char *section_name;</li>
<li>ElfW(Ehdr) *ehdr;</li>
<li>ElfW(Shdr) *shdr, *shstr_shdr, *debug_line_shdr = NULL;</li>
<li>
<li>for (i = 0; i < num_traces; i++) {</li>
<li>const char *path;</li>
<li>size_t len;</li>
<li>if (get_path_from_symbol(syms[i], &path, &len) &&</li>
<li>
<pre><code> !strncmp(path, binary_filename, len)) {
</code></pre>
</li>
<li>
<pre><code> lines[i].line = -1;
</code></pre>
</li>
<li>}</li>
<li>}</li>
<li>
<li>ehdr = (ElfW(Ehdr) *)file;</li>
<li>shdr = (ElfW(Shdr) *)(file + ehdr->e_shoff);</li>
<li>
<li>shstr_shdr = shdr + ehdr->e_shstrndx;</li>
<li>shstr = file + shstr_shdr->sh_offset;</li>
<li>
<li>for (i = 0; i < ehdr->e_shnum; i++) {</li>
<li>section_name = shstr + shdr[i].sh_name;</li>
<li>if (!strcmp(section_name, ".debug_line")) {</li>
<li>
<pre><code> debug_line_shdr = shdr + i;
</code></pre>
</li>
<li>
<pre><code> break;
</code></pre>
</li>
<li>}</li>
<li>}</li>
<li>
<li>if (!debug_line_shdr) {</li>
<li>/* this file doesn't have .debug_line section */</li>
<li>return;</li>
<li>}</li>
<li>
<li>parse_debug_line(num_traces, traces,</li>
<li>
<pre><code> file + debug_line_shdr->sh_offset,
</code></pre>
</li>
<li>
<pre><code> debug_line_shdr->sh_size,
</code></pre>
</li>
<li>
<pre><code> lines);
</code></pre>
</li>
</ul>
<p>+}<br>
+<br>
+#ifdef HAVE_DL_ITERATE_PHDR<br>
+<br>
+typedef struct {</p>
<ul>
<li>int num_traces;</li>
<li>char **syms;</li>
<li>line_info_t *lines;<br>
+} fill_base_addr_state_t;</li>
<li>
</ul>
<p>+static int<br>
+fill_base_addr(struct dl_phdr_info *info, size_t size, void *data)<br>
+{</p>
<ul>
<li>int i;</li>
<li>fill_base_addr_state_t *st = (fill_base_addr_state_t *)data;</li>
<li>for (i = 0; i < st->num_traces; i++) {</li>
<li>const char *path;</li>
<li>size_t len;</li>
<li>size_t name_len = strlen(info->dlpi_name);</li>
<li>
<li>if (get_path_from_symbol(st->syms[i], &path, &len) &&</li>
<li>
<pre><code> (len == name_len || (len > name_len && path[len-name_len-1] == '/')) &&
</code></pre>
</li>
<li>
<pre><code> !strncmp(path+len-name_len, info->dlpi_name, name_len)) {
</code></pre>
</li>
<li>
<pre><code> st->lines[i].base_addr = info->dlpi_addr;
</code></pre>
</li>
<li>}</li>
<li>}</li>
<li>return 0;<br>
+}</li>
<li>
</ul>
<p>+#endif /* HAVE_DL_ITERATE_PHDR */<br>
+<br>
+void<br>
+rb_dump_backtrace_with_lines(int num_traces, void **trace, char **syms)<br>
+{</p>
<ul>
<li>int i;</li>
<li>int fd;</li>
<li>/* async-signal unsafe */</li>
<li>line_info_t *lines = (line_info_t *)calloc(num_traces,</li>
<li>
<pre><code> sizeof(line_info_t));
</code></pre>
</li>
<li>off_t filesize;</li>
<li>char *file;</li>
<li>
<li>/* Note that line info of shared objects might not be shown</li>
<li>
<pre><code> if we don't have dl_iterate_phdr */
</code></pre>
</li>
</ul>
<p>+#ifdef HAVE_DL_ITERATE_PHDR</p>
<ul>
<li>fill_base_addr_state_t fill_base_addr_state;</li>
<li>
<li>fill_base_addr_state.num_traces = num_traces;</li>
<li>fill_base_addr_state.syms = syms;</li>
<li>fill_base_addr_state.lines = lines;</li>
<li>/* maybe async-signal unsafe */</li>
<li>dl_iterate_phdr(fill_base_addr, &fill_base_addr_state);<br>
+#endif /* HAVE_DL_ITERATE_PHDR */</li>
<li>
<li>for (i = 0; i < num_traces; i++) {</li>
<li>const char *path;</li>
<li>size_t len;</li>
<li>if (lines[i].line) {</li>
<li>
<pre><code> continue;
</code></pre>
</li>
<li>}</li>
<li>
<li>if (!get_path_from_symbol(syms[i], &path, &len)) {</li>
<li>
<pre><code> continue;
</code></pre>
</li>
<li>}</li>
<li>
<li>strncpy(binary_filename, path, len);</li>
<li>binary_filename[len] = '\0';</li>
<li>
<li>fd = open(binary_filename, O_RDONLY);</li>
<li>filesize = lseek(fd, 0, SEEK_END);</li>
<li>lseek(fd, 0, SEEK_SET);</li>
<li>/* async-signal unsafe */</li>
<li>file = (char *)mmap(NULL, filesize, PROT_READ, MAP_SHARED, fd, 0);</li>
<li>
<li>lines[i].fd = fd;</li>
<li>lines[i].mapped = file;</li>
<li>lines[i].mapped_size = filesize;</li>
<li>
<li>fill_lines(num_traces, trace, syms, file, lines);</li>
<li>}</li>
<li>
<li>/* fprintf may not be async-signal safe */</li>
<li>for (i = 0; i < num_traces; i++) {</li>
<li>line_info_t *line = &lines[i];</li>
<li>
<li>if (line->line > 0) {</li>
<li>
<pre><code> fprintf(stderr, "%s ", syms[i]);
</code></pre>
</li>
<li>
<pre><code> if (line->filename) {
</code></pre>
</li>
<li>
<pre><code> if (line->dirname && line->dirname[0]) {
</code></pre>
</li>
<li>
<pre><code> fprintf(stderr, "%s/", line->dirname);
</code></pre>
</li>
<li>
<pre><code> }
</code></pre>
</li>
<li>
<pre><code> fprintf(stderr, "%s", line->filename);
</code></pre>
</li>
<li>
<pre><code> } else {
</code></pre>
</li>
<li>
<pre><code> fprintf(stderr, "???");
</code></pre>
</li>
<li>
<pre><code> }
</code></pre>
</li>
<li>
<pre><code> fprintf(stderr, ":%d\n", line->line);
</code></pre>
</li>
<li>} else {</li>
<li>
<pre><code> fprintf(stderr, "%s\n", syms[i]);
</code></pre>
</li>
<li>}</li>
<li>}</li>
<li>
<li>for (i = 0; i < num_traces; i++) {</li>
<li>line_info_t *line = &lines[i];</li>
<li>if (line->fd) {</li>
<li>
<pre><code> munmap(line->mapped, line->mapped_size);
</code></pre>
</li>
<li>
<pre><code> close(line->fd);
</code></pre>
</li>
<li>}</li>
<li>}</li>
<li>free(lines);<br>
+}</li>
<li>
</ul>
<p>+#endif /* defined(<strong>ELF</strong>) <em>/<br>
diff --git a/addr2line.h b/addr2line.h<br>
new file mode 100644<br>
index 0000000..cbb18e5<br>
--- /dev/null<br>
+++ b/addr2line.h<br>
@@ -0,0 +1,21 @@<br>
+/</em>*********************************************************************<br>
+</p>
<ul>
<li>addr2line.h -</li>
<li>
<li>$Author$</li>
<li>
<li>Copyright (C) 2010 Shinichiro Hamaji</li>
<li>
</ul>
<p>+**********************************************************************/<br>
+<br>
+#ifndef RUBY_ADDR2LINE_H<br>
+#define RUBY_ADDR2LINE_H<br>
+<br>
+#ifdef <strong>ELF</strong><br>
+<br>
+void<br>
+rb_dump_backtrace_with_lines(int num_traces, void **traces, char *<em>syms);<br>
+<br>
+#endif /</em> <strong>ELF</strong> <em>/<br>
+<br>
+#endif /</em> RUBY_ADDR2LINE_H */<br>
diff --git a/common.mk b/common.mk<br>
index c45c3e1..cf66e42 100644<br>
--- a/common.mk<br>
+++ b/common.mk<br>
@@ -91,6 +91,7 @@ COMMONOBJS = array.$(OBJEXT) <br>
vm_dump.$(OBJEXT) <br>
thread.$(OBJEXT) <br>
cont.$(OBJEXT) \</p>
<ul>
<li>
<pre><code> addr2line.$(OBJEXT) \
$(BUILTIN_ENCOBJS) \
$(BUILTIN_TRANSOBJS) \
$(MISSING)
</code></pre>
</li>
</ul>
<p>diff --git a/configure.in b/configure.in<br>
index 36a58d4..ef2ee2d 100644<br>
--- a/configure.in<br>
+++ b/configure.in<br>
@@ -1300,7 +1300,7 @@ AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall chroot getcwd eacce<br>
setsid telldir seekdir fchmod cosh sinh tanh log2 round<br>
setuid setgid daemon select_large_fdset setenv unsetenv<br>
mktime timegm gmtime_r clock_gettime gettimeofday\</p>
<ul>
<li>
<pre><code> pread sendfile shutdown sigaltstack)
</code></pre>
</li>
</ul>
<ul>
<li>
<pre><code> pread sendfile shutdown sigaltstack dl_iterate_phdr)
</code></pre>
</li>
</ul>
<p>AC_CACHE_CHECK(for unsetenv returns a value, rb_cv_unsetenv_return_value,<br>
[AC_TRY_COMPILE([<br>
diff --git a/vm_dump.c b/vm_dump.c<br>
index 2975001..b22c041 100644<br>
--- a/vm_dump.c<br>
+++ b/vm_dump.c<br>
@@ -10,6 +10,7 @@</p>
<p>#include "ruby/ruby.h"<br>
+#include "addr2line.h"<br>
#include "vm_core.h"</p>
<p>#define MAX_POSBUF 128<br>
@@ -785,9 +786,13 @@ rb_vm_bugreport(void)<br>
int i;</p>
<pre><code>if (syms) {
</code></pre>
<p>+#ifdef <strong>ELF</strong></p>
<ul>
<li>
<pre><code> rb_dump_backtrace_with_lines(n, trace, syms);
</code></pre>
</li>
</ul>
<p>+#else<br>
for (i=0; i<n; i++) {<br>
fprintf(stderr, "%s\n", syms[i]);<br>
}<br>
+#endif<br>
free(syms);<br>
}<br>
#elif defined(_WIN32)<br>
=end</p>
Ruby master - Feature #3947 (Closed): Array#packのにエンディアン指定修飾子</>を追加
https://redmine.ruby-lang.org/issues/3947
2010-10-14T15:46:42Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
Ruby の伏魔殿 Array#pack ですが、機種依存な部分をいじる際にはしばしば強力かつ唯一の手段になる事があります。<br>
具体的には RubySpec 書く時とか。</p>
<p>さて、pack のようにバイト列を扱う場合、しばしば問題になるのがエンディアンです。<br>
ここで、Ruby の Array#pack は 16bit/32bit 固定の整数に対してはエンディアン固定の<br>
n/N/v/V 指定子を用意していますが、short/int/long/long long のように、<br>
マシン依存の型をエンディアン固定で pack する手段を提供していません。<br>
というわけで、これが欲しいです。</p>
<p>ここで問題になるのがどのように指定するかなのですが、pack のネタ元である Perl さんでは既に </> 修飾子を<br>
この目的のために提供しています。<br>
<a href="http://perldoc.perl.org/functions/pack.html" class="external">http://perldoc.perl.org/functions/pack.html</a><br>
というわけで、これに追従するのがよろしいと思います。</p>
<pre><code> > sSiIlLqQ Force big-endian byte-order on the type.
jJfFdDpP (The "big end" touches the construct.)
< sSiIlLqQ Force little-endian byte-order on the type.
jJfFdDpP (The "little end" touches the construct.)
</code></pre>
<p>なお、「</> とかきもい」という意見もあるかとは思いますが、pack は機種依存とかに触るきもいメソッドなので、<br>
そこを気にするよりは長い物に巻かれておいた方が無難ではないかなと思うため、</> 修飾子がベストかと思います。<br>
=end</p>
Ruby master - Feature #3946 (Closed): Array#packのqQ指定子に機種依存サイズフラグ!を追加
https://redmine.ruby-lang.org/issues/3946
2010-10-14T15:36:56Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
Ruby の伏魔殿 Array#pack ですが、機種依存な部分をいじる際にはしばしば強力かつ唯一の手段になる事があります。<br>
具体的には RubySpec 書く時とか。</p>
<p>さて、q/Q は 64bit signed/unsigned int なのですが、機種依存の long long (厳密には LONG_LONG) のサイズが必要な場合、<br>
現状 Ruby レベルから取る手段が一切ありません。<br>
すでに s/S/i/I/l/L (16bit/32bit/32bit) は、! をつけることでその機種の short/int/long のサイズになるため、<br>
これを q/Q に拡張すると唯一の手段が提供される事になります。</p>
<p>というわけで、Array#packのqQ指定子に機種依存サイズフラグ!を追加したいです。<br>
=end</p>
Ruby master - Bug #3945 (Closed): Numeric#step with infinity unit
https://redmine.ruby-lang.org/issues/3945
2010-10-14T04:15:58Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
Numeric#step with infinity unit doesn't works well:</p>
<blockquote>
<p>1.step(0, Float::INFINITY) {|x| p x }<br>
1.0<br>
=> 1<br>
1.step(0, -Float::INFINITY) {|x| p x }<br>
=> 1</p>
</blockquote>
<p>Expected result is:</p>
<blockquote>
<p>1.step(0, Float::INFINITY) {|x| p x }<br>
=> 1<br>
1.step(0, -Float::INFINITY) {|x| p x }<br>
1.0<br>
=> 1<br>
=end</p>
</blockquote>
Ruby master - Bug #3593 (Closed): cont.cで"PAGE_SIZE" redefined
https://redmine.ruby-lang.org/issues/3593
2010-07-21T17:20:27Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
cont.cで以下のようなwarningがでます。<br>
ifndefを使うべきか、RB_PAGE_SIZEなどと別の名前を使うべきか迷ったので、対処お願いします。<br>
../../ruby/cont.c:45:1: warning: "PAGE_SIZE" redefined<br>
In file included from /usr/include/sys/param.h:110,<br>
from /usr/include/pthread_np.h:34,<br>
from ../../ruby/thread_pthread.h:16,<br>
from ../../ruby/vm_core.h:29,<br>
from ../../ruby/cont.c:13:<br>
/usr/include/machine/param.h:100:1: warning: this is the location of the previous definition<br>
../../ruby/cont.c:46:1: warning: "PAGE_MASK" redefined<br>
/usr/include/machine/param.h:101:1: warning: this is the location of the previous definition<br>
../../ruby/cont.c:45:1: warning: "PAGE_SIZE" redefined<br>
In file included from /usr/include/sys/param.h:111,<br>
from /usr/include/pthread_np.h:35,<br>
from ../../ruby/thread_pthread.h:17,<br>
from ../../ruby/vm_core.h:30,<br>
from ../../ruby/cont.c:14:<br>
/usr/include/machine/param.h:100:1: warning: this is the location of the previous definition<br>
../../ruby/cont.c:46:1: warning: "PAGE_MASK" redefined<br>
/usr/include/machine/param.h:101:1: warning: this is the location of the previous definition<br>
../../ruby/cont.c: In function 'fiber_initialize_machine_stack_context':<br>
../../ruby/cont.c:567: warning: assignment from incompatible pointer type<br>
=end</p>
Ruby master - Bug #3568 (Closed): /(?<=a).*b/ =~ "aab" doesn't match
https://redmine.ruby-lang.org/issues/3568
2010-07-14T12:09:42Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
「1.9.1で /(?<=a).*b/ =~ "aab" がマッチしないのはなぜ? 」<br>
<a href="http://pc12.2ch.net/test/read.cgi/tech/1272248179/735" class="external">http://pc12.2ch.net/test/read.cgi/tech/1272248179/735</a><br>
=end</p>
Ruby master - Bug #3515 (Closed): FreeBSD wrongly raises ECONNRESET on close(2)
https://redmine.ruby-lang.org/issues/3515
2010-07-02T12:20:40Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
FreeBSD 8 では現在以下のようなテストに失敗しています。</p>
<ol>
<li>
<p>Error:<br>
test_idle(IMAPTest):<br>
Errno::ECONNRESET: Connection reset by peer<br>
/home/naruse/ruby/test/net/imap/test_imap.rb:189:in `test_idle'</p>
</li>
<li>
<p>Failure:<br>
test_03(TestDRbSSLCore) [/home/naruse/ruby/test/drb/drbtest.rb:138]:<br>
[DRb::DRbConnError] exception expected, not<br>
Class: <a href="Errno::ECONNRESET" class="external">Errno::ECONNRESET</a><br>
Message: <"Connection reset by peer"><br>
---Backtrace---<br>
/home/naruse/ruby/test/drb/drbtest.rb:139:in <code>block in test_03' /home/naruse/ruby/test/drb/drbtest.rb:138:in </code>test_03'</p>
</li>
</ol>
<hr>
<ol start="3">
<li>Failure:<br>
test_07_public_private_protected_missing(TestDRbSSLCore) [/home/naruse/ruby/test/drb/drbtest.rb:182]:<br>
Exception raised:<br>
<#<Errno::ECONNRESET: Connection reset by peer>>.</li>
</ol>
<p>これらに共通するのは「Errno::ECONNRESET: Connection reset by peer」という例外が発生している点です。<br>
この例外は socket の close(2) を呼んだ際に errno に ECONNRESET がセットされたときに発生します。<br>
しかし、この挙動は POSIX 仕様外であり、FreeBSD 独自のものです。</p>
<p><a href="http://www.freebsd.org/cgi/man.cgi?query=close&apropos=0&sektion=0&manpath=FreeBSD+8.0-RELEASE&format=html" class="external">http://www.freebsd.org/cgi/man.cgi?query=close&apropos=0&sektion=0&manpath=FreeBSD+8.0-RELEASE&format=html</a><br>
<a href="http://www.opengroup.org/onlinepubs/9699919799/functions/close.html" class="external">http://www.opengroup.org/onlinepubs/9699919799/functions/close.html</a><br>
<a href="http://netbsd.gw.com/cgi-bin/man-cgi?close++NetBSD-current" class="external">http://netbsd.gw.com/cgi-bin/man-cgi?close++NetBSD-current</a><br>
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=close&apropos=0&sektion=0&manpath=OpenBSD+Current&arch=i386&format=html" class="external">http://www.openbsd.org/cgi-bin/man.cgi?query=close&apropos=0&sektion=0&manpath=OpenBSD+Current&arch=i386&format=html</a><br>
<a href="http://leaf.dragonflybsd.org/cgi/web-man?command=close&section=ANY" class="external">http://leaf.dragonflybsd.org/cgi/web-man?command=close&section=ANY</a><br>
<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html" class="external">http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html</a><br>
<a href="http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man2/close.2.html" class="external">http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man2/close.2.html</a></p>
<p>これが結果的に、他の OS では例外が投げられない状況で例外が発生するという現象を生み出しています。<br>
以下は関連する議論です。<br>
<a href="http://old.nabble.com/close()-failing-with-ECONNRESET-td28817716.html" class="external">http://old.nabble.com/close()-failing-with-ECONNRESET-td28817716.html</a><br>
<a href="http://old.nabble.com/Re:-kern-146845:--libc--close(2)-returns-error-54-(connection-reset-by-peer)-wrongly-td28649525.html" class="external">http://old.nabble.com/Re:-kern-146845:--libc--close(2)-returns-error-54-(connection-reset-by-peer)-wrongly-td28649525.html</a></p>
<p>で、Ruby における対策ですが、close(2) で errno に ECONNRESET がセットされた場合、<br>
それを無視するべきだと思います。<br>
いかがそのパッチなのですがいかがでしょうか。</p>
<p>diff --git a/io.c b/io.c<br>
index 05b2d45..a1b49d2 100644<br>
--- a/io.c<br>
+++ b/io.c<br>
@@ -3436,7 +3436,7 @@ fptr_finalize(rb_io_t <em>fptr, int noraise)<br>
/</em> fptr->fd may be closed even if close fails.<br>
* POSIX doesn't specify it.<br>
* We assumes it is closed. */</p>
<ul>
<li>
<pre><code> if (close(fptr->fd) < 0 && NIL_P(err))
</code></pre>
</li>
</ul>
<ul>
<li>
<pre><code> if (close(fptr->fd) < 0 && NIL_P(err) && errno != ECONNRESET)
err = noraise ? Qtrue : INT2NUM(errno);
</code></pre>
}<br>
skip_fd_close:<br>
=end</li>
</ul>
Backport186 - Backport #3403 (Closed): A bug related to ruby's regular expression!!!!!!
https://redmine.ruby-lang.org/issues/3403
2010-06-07T16:01:37Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
Backport r28192</p>
<p>I found a bug related to ruby's regular expression!<br>
puts(/wo{0,3}?/.match("woo"))<br>
This line of code should show a "w" on the screen, but my ruby shows "wo" on then screen.<br>
According to the principles of regular expression, this is a non-greedy match, so it should match 0 "o", but it matches at least 1 "o". I think this is a bug!<br>
I also tried this in the javascript language, it works well!("w" is shown)</p>
<p>My ruby version is v1.8.7-p249.<br>
=end</p>
Backport187 - Backport #3402 (Closed): A bug related to ruby's regular expression!!!!!!
https://redmine.ruby-lang.org/issues/3402
2010-06-07T16:00:55Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
Backport r28192</p>
<p>I found a bug related to ruby's regular expression!<br>
puts(/wo{0,3}?/.match("woo"))<br>
This line of code should show a "w" on the screen, but my ruby shows "wo" on then screen.<br>
According to the principles of regular expression, this is a non-greedy match, so it should match 0 "o", but it matches at least 1 "o". I think this is a bug!<br>
I also tried this in the javascript language, it works well!("w" is shown)</p>
<p>My ruby version is v1.8.7-p249.<br>
=end</p>
Ruby master - Bug #2748 (Closed): fix for READ_CHECK causes failures on FreeBSD 8.0
https://redmine.ruby-lang.org/issues/2748
2010-02-16T03:41:08Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
間を置いてからの報告で申し訳ないのですが、例の READ_CHECK の修正 (r26625) が、<br>
FreeBSD での test-all の失敗を増やします。<br>
r26625 を revert するとこれらのテストは成功するようになります。</p>
<ol>
<li>
<p>Failure:<br>
test_read_error(TestIO) [/home/naruse/ruby/test/ruby/test_io.rb:869]:<br>
RuntimeError expected but nothing was raised.</p>
</li>
<li>
<p>Failure:<br>
test_readpartial_error(TestIO) [/home/naruse/ruby/test/ruby/test_io.rb:834]:<br>
RuntimeError expected but nothing was raised.<br>
=end</p>
</li>
</ol>
Ruby master - Bug #2724 (Third Party's Issue): fork from other than the main thread causes wrong ...
https://redmine.ruby-lang.org/issues/2724
2010-02-09T03:04:20Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>NetBSD 5.0.[01] において、main thread 以外の pthread から fork すると、<br>
pthread とカーネルスレッド (lwp) との関連が壊れるという現象が確認されています。</p>
<p>後述のパッチがあまりにアレなのでこの問題は Third Party's Issue とし、<br>
Ruby 側では修正を入れない事としますが、情報の共有と記録のために<br>
ここにチケットを切っておきます。</p>
<p>なお、この workaround の作成には @_enamiさんの助けがありました。</p>
<p>追記:<br>
NetBSD 側では kern/42772 として報告、修正されています。<br>
<a href="http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=42772" class="external">http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=42772</a></p>
<a name="Index-thread_pthreadc"></a>
<h1 >Index: thread_pthread.c<a href="#Index-thread_pthreadc" class="wiki-anchor">¶</a></h1>
<p>--- thread_pthread.c (revision 26615)<br>
+++ thread_pthread.c (working copy)<br>
@@ -17,6 +17,93 @@<br>
#include <sys/resource.h><br>
#endif</p>
<p>+#if defined(<strong>NetBSD_Version</strong>) && <strong>NetBSD_Version</strong> >= 500000000<br>
+/* Hack for NetBSD 5.0.x's broken pthread->pt_lid <em>/<br>
+/</em> Copied from /src/lib/libpthread/pthread_int.h <em>/<br>
+#define BROKEN_PTHREAD_T_PT_LID<br>
+#include <lwp.h><br>
+#include <pthread_queue.h><br>
+#include <sys/tree.h><br>
+<br>
+#define PTHREAD_KEYS_MAX 256<br>
+#define PTHREAD__UNPARK_MAX 32<br>
+<br>
+/</em></p>
<ul>
<li>
<ul>
<li>The size of this structure needs to be no larger than struct</li>
</ul>
</li>
<li>
<ul>
<li>__pthread_cleanup_store, defined in pthread.h.</li>
</ul>
</li>
<li>*/<br>
+struct pt_clean_t {</li>
<li>
<pre><code> PTQ_ENTRY(pt_clean_t) ptc_next;
</code></pre>
</li>
<li>
<pre><code> void (*ptc_cleanup)(void *);
</code></pre>
</li>
<li>
<pre><code> void *ptc_arg;
</code></pre>
</li>
</ul>
<p>+};<br>
+<br>
+struct pthread_lock_ops {</p>
<ul>
<li>
<pre><code> void (*plo_init)(__cpu_simple_lock_t *);
</code></pre>
</li>
<li>
<pre><code> int (*plo_try)(__cpu_simple_lock_t *);
</code></pre>
</li>
<li>
<pre><code> void (*plo_unlock)(__cpu_simple_lock_t *);
</code></pre>
</li>
<li>
<pre><code> void (*plo_lock)(__cpu_simple_lock_t *);
</code></pre>
</li>
</ul>
<p>+};<br>
+<br>
+struct __pthread_st {</p>
<ul>
<li>
<pre><code> pthread_t pt_self; /* Must be first. */
</code></pre>
</li>
<li>
<pre><code> unsigned int pt_magic; /* Magic number */
</code></pre>
</li>
<li>
<pre><code> int pt_state; /* running, blocked, etc. */
</code></pre>
</li>
<li>
<pre><code> pthread_mutex_t pt_lock; /* lock on state */
</code></pre>
</li>
<li>
<pre><code> int pt_flags; /* see PT_FLAG_* below */
</code></pre>
</li>
<li>
<pre><code> int pt_cancel; /* Deferred cancellation */
</code></pre>
</li>
<li>
<pre><code> int pt_errno; /* Thread-specific errno. */
</code></pre>
</li>
<li>
<pre><code> stack_t pt_stack; /* Our stack */
</code></pre>
</li>
<li>
<pre><code> void *pt_exitval; /* Read by pthread_join() */
</code></pre>
</li>
<li>
<pre><code> char *pt_name; /* Thread's name, set by the app. */
</code></pre>
</li>
<li>
<pre><code> int pt_willpark; /* About to park */
</code></pre>
</li>
<li>
<pre><code> lwpid_t pt_unpark; /* Unpark this when parking */
</code></pre>
</li>
<li>
<pre><code> struct pthread_lock_ops pt_lockops;/* Cached to avoid PIC overhead */
</code></pre>
</li>
<li>
<pre><code> pthread_mutex_t *pt_droplock; /* Drop this lock if cancelled */
</code></pre>
</li>
<li>
<pre><code> pthread_cond_t pt_joiners; /* Threads waiting to join. */
</code></pre>
</li>
<li>
<li>
<pre><code> /* Threads to defer waking, usually until pthread_mutex_unlock(). */
</code></pre>
</li>
<li>
<pre><code> lwpid_t pt_waiters[PTHREAD__UNPARK_MAX];
</code></pre>
</li>
<li>
<pre><code> size_t pt_nwaiters;
</code></pre>
</li>
<li>
<li>
<pre><code> /* Stack of cancellation cleanup handlers and their arguments */
</code></pre>
</li>
<li>
<pre><code> PTQ_HEAD(, pt_clean_t) pt_cleanup_stack;
</code></pre>
</li>
<li>
<li>
<pre><code> /* LWP ID and entry on the list of all threads. */
</code></pre>
</li>
<li>
<pre><code> lwpid_t pt_lid;
</code></pre>
</li>
<li>
<pre><code> RB_ENTRY(__pthread_st) pt_alltree;
</code></pre>
</li>
<li>
<pre><code> PTQ_ENTRY(__pthread_st) pt_allq;
</code></pre>
</li>
<li>
<pre><code> PTQ_ENTRY(__pthread_st) pt_deadq;
</code></pre>
</li>
<li>
<li>
<pre><code> /*
</code></pre>
</li>
<li>
<pre><code> * General synchronization data. We try to align, as threads
</code></pre>
</li>
<li>
<pre><code> * on other CPUs will access this data frequently.
</code></pre>
</li>
<li>
<pre><code> */
</code></pre>
</li>
<li>
<pre><code> int pt_dummy1 __aligned(128);
</code></pre>
</li>
<li>
<pre><code> struct lwpctl *pt_lwpctl; /* Kernel/user comms area */
</code></pre>
</li>
<li>
<pre><code> volatile int pt_blocking; /* Blocking in userspace */
</code></pre>
</li>
<li>
<pre><code> volatile int pt_rwlocked; /* Handed rwlock successfully */
</code></pre>
</li>
<li>
<pre><code> volatile int pt_signalled; /* Received pthread_cond_signal() */
</code></pre>
</li>
<li>
<pre><code> volatile int pt_mutexwait; /* Waiting to acquire mutex */
</code></pre>
</li>
<li>
<pre><code> void * volatile pt_mutexnext; /* Next thread in chain */
</code></pre>
</li>
<li>
<pre><code> void * volatile pt_sleepobj; /* Object slept on */
</code></pre>
</li>
<li>
<pre><code> PTQ_ENTRY(__pthread_st) pt_sleep;
</code></pre>
</li>
<li>
<pre><code> void (*pt_early)(void *);
</code></pre>
</li>
<li>
<pre><code> int pt_dummy2 __aligned(128);
</code></pre>
</li>
<li>
<li>
<pre><code> /* Thread-specific data. Large so it sits close to the end. */
</code></pre>
</li>
<li>
<pre><code> int pt_havespecific;
</code></pre>
</li>
<li>
<pre><code> void *pt_specific[PTHREAD_KEYS_MAX];
</code></pre>
</li>
<li>
<li>
<pre><code> /*
</code></pre>
</li>
<li>
<pre><code> * Context for thread creation. At the end as it's cached
</code></pre>
</li>
<li>
<pre><code> * and then only ever passed to _lwp_create().
</code></pre>
</li>
<li>
<pre><code> */
</code></pre>
</li>
<li>
<pre><code> ucontext_t pt_uc;
</code></pre>
</li>
</ul>
<p>+};<br>
+#endif /* <strong>NetBSD</strong> */<br>
+<br>
+<br>
static void native_mutex_lock(pthread_mutex_t *lock);<br>
static void native_mutex_unlock(pthread_mutex_t *lock);<br>
static int native_mutex_trylock(pthread_mutex_t *lock);<br>
@@ -833,6 +920,9 @@<br>
native_reset_timer_thread(void)<br>
{<br>
timer_thread_id = 0;<br>
+#ifdef BROKEN_PTHREAD_T_PT_LID</p>
<ul>
<li>((struct __pthread_st *)pthread_self())->pt_lid = _lwp_self();<br>
+#endif<br>
}</li>
</ul>
<p>#ifdef HAVE_SIGALTSTACK</p>
Ruby master - Feature #2579 (Closed): Net::HTTP.start("www.ruby-lang.org", use_ssl: true) で SSL 利...
https://redmine.ruby-lang.org/issues/2579
2010-01-09T20:34:21Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
従来、Net::HTTP で https アクセスをするためには</p>
<p>http = Net::HTTP.new("www.ruby-lang.org", 443) # port を指定<br>
http.use_ssl = true # use_ssl を true に<br>
http.verify_mode = OpenSSL::SSL::VERIFY_PEER # デフォルトだと検証してくれないので変更<br>
http.start{|h| } # やっと本題<br>
http.finish # 破棄</p>
<p>などとする必要がありました。</p>
<p>この提案では、Net::HTTP(addr, opt){..} という呼び出し方を追加します。<br>
opt はハッシュで、アクセサのある項目を設定する事が出来ます。</p>
<p>なお、折角の新 API なので、この API を使った場合、verify_mode のデフォルトが VERIFY_PEER になっています。<br>
つまり、この API を用いた場合デフォルトで SSL 署名の有効性を検査するため、<br>
期限切れやオレオレ証明書の場合例外が出ます。</p>
<p>これにより、さっきのは以下のように書けます。<br>
Net::HTTP.start("www.ruby-lang.org", use_ssl: true){|h| }</p>
<p>P.S.<br>
なお、以上の例で用いている <a href="http://www.ruby-lang.org" class="external">www.ruby-lang.org</a> は、現在 SSL 証明書が期限切れのため、<br>
この例を今実行すると検証に失敗して例外が出ます。<br>
今は StartCom とかだと無料で 主要環境でルート証明書が入っている Web サーバ用の SSL 証明書が取得できますから、<br>
そういうのに入れ替えた方がいいのではないでしょうか。<br>
=end</p>
Ruby master - Feature #2574 (Closed): merging net/https
https://redmine.ruby-lang.org/issues/2574
2010-01-08T09:40:06Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
現在 net/https はほとんど抜け殻で、ほぼ require 'openssl' するためだけに存在するのですが、<br>
残っているロジックも net/http に移してしまって、net/http だけで https も扱えるようにしませんか。<br>
autoload を使って https に実際にアクセスしたときに openssl ライブラリをロードするようにしたので、<br>
無駄に読み込むこともありません。</p>
<p>なお、net/https は require を使うままにしているので、こちらだともし openssl ライブラリがない場合、<br>
net/https ロード時にエラーが出るという現在の挙動をそのまま利用できます。<br>
=end</p>
Ruby master - Feature #2470 (Closed): Encoding#new doesn't undef_method-ed
https://redmine.ruby-lang.org/issues/2470
2009-12-10T08:03:34Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
Fixnum や Encoding のような、rb_undef_alloc_func されているクラスでは、<br>
Hoge.new がエラーになりますが、<br>
Fixnum などでは Fixnum.new が undef されているのに、<br>
Encoding ではなされていないため、呼んだ際の挙動が異なります。</p>
<p>irb(main):001:0> Fixnum.new<br>
NoMethodError: undefined method <code>new' for Fixnum:Class from (irb):1 from /usr/local/bin/irb_1_9_1:12:in </code>'<br>
irb(main):002:0> Encoding.new<br>
TypeError: allocator undefined for Encoding<br>
from (irb):2:in <code>new' from (irb):2 from /usr/local/bin/irb_1_9_1:12:in </code>'</p>
<p>以下がパッチです。</p>
<p>diff --git a/encoding.c b/encoding.c<br>
index 38d81b8..f9d2f20 100644<br>
--- a/encoding.c<br>
+++ b/encoding.c<br>
@@ -1484,6 +1484,7 @@ Init_Encoding(void)</p>
<pre><code> rb_cEncoding = rb_define_class("Encoding", rb_cObject);
rb_undef_alloc_func(rb_cEncoding);
</code></pre>
<ul>
<li>
<p>rb_undef_method(CLASS_OF(rb_cEncoding), "new");<br>
rb_define_method(rb_cEncoding, "to_s", enc_name, 0);<br>
rb_define_method(rb_cEncoding, "inspect", enc_inspect, 0);<br>
rb_define_method(rb_cEncoding, "name", enc_name, 0);<br>
diff --git a/object.c b/object.c<br>
index 10eb983..704cb2a 100644<br>
--- a/object.c<br>
+++ b/object.c<br>
@@ -2680,6 +2680,7 @@ Init_Object(void)</p>
<p>rb_cData = rb_define_class("Data", rb_cObject);<br>
rb_undef_alloc_func(rb_cData);</p>
</li>
<li>
<p>rb_undef_method(CLASS_OF(rb_cData), "new");</p>
<p>rb_cTrueClass = rb_define_class("TrueClass", rb_cObject);<br>
rb_define_method(rb_cTrueClass, "to_s", true_to_s, 0);<br>
diff --git a/vm.c b/vm.c<br>
index fa028fd..e522c9f 100644<br>
--- a/vm.c<br>
+++ b/vm.c<br>
@@ -1944,6 +1944,7 @@ Init_VM(void)<br>
/* ::VM */<br>
rb_cRubyVM = rb_define_class("RubyVM", rb_cObject);<br>
rb_undef_alloc_func(rb_cRubyVM);</p>
</li>
<li>
<p>rb_undef_method(CLASS_OF(rb_cRubyVM), "new");</p>
<p>/* ::VM::FrozenCore <em>/<br>
fcore = rb_class_new(rb_cBasicObject);<br>
@@ -1962,6 +1963,7 @@ Init_VM(void)<br>
/</em> ::VM::Env */<br>
rb_cEnv = rb_define_class_under(rb_cRubyVM, "Env", rb_cObject);<br>
rb_undef_alloc_func(rb_cEnv);</p>
</li>
<li>
<p>rb_undef_method(CLASS_OF(rb_cEnv), "new");</p>
<p>/* ::Thread */<br>
rb_cThread = rb_define_class("Thread", rb_cObject);<br>
=end</p>
</li>
</ul>
Ruby master - Bug #2208 (Rejected): Exception#inspect の message 部が inspect されていない
https://redmine.ruby-lang.org/issues/2208
2009-10-14T17:38:54Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
通常 Object#inspect の結果にでてくる文字列は inspect されています。<br>
しかし、Exception#inspect はそのままになっています。<br>
<a href="http://shinh.skr.jp/m/?date=20091014#c02" class="external">http://shinh.skr.jp/m/?date=20091014#c02</a></p>
<p>class Foo;def initialize;@a="\t";end;end<br>
Foo.new.inspect #=> #<Foo:0x000008012dffd8 <a class="user active user-mention" href="https://redmine.ruby-lang.org/users/52980">@A (A A)</a>="\t"></p>
<p>Exception.new("\t") #=> #<Exception: " "></p>
<p>単なるかけ忘れな気がするのですがどうでしょう。</p>
<p>diff --git a/error.c b/error.c<br>
index a7342de..3a5580a 100644<br>
--- a/error.c<br>
+++ b/error.c<br>
@@ -458,7 +458,7 @@ exc_exception(int argc, VALUE *argv, VALUE self)<br>
static VALUE<br>
exc_to_s(VALUE exc)<br>
{</p>
<ul>
<li>VALUE mesg = rb_attr_get(exc, rb_intern("mesg"));</li>
</ul>
<ul>
<li>
<p>VALUE mesg = rb_inspect(rb_attr_get(exc, rb_intern("mesg")));</p>
<p>if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));<br>
if (OBJ_TAINTED(exc)) OBJ_TAINT(mesg);<br>
=end</p>
</li>
</ul>
Ruby master - Feature #2166 (Rejected): Add CreationError
https://redmine.ruby-lang.org/issues/2166
2009-10-01T00:33:34Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin</p>
<blockquote>
<p>Here is a patch which implements the CreationError exception class,<br>
as documented in the Tempfile API documentation. However,<br>
I could not write a unit test for it because there seems<br>
to be no mocking/stubbing framework that I could use.</p>
</blockquote>
<p>Originally <a href="/issues/1999">[ruby-core:25131]</a> and patch is there.<br>
=end</p>
Ruby master - Feature #2032 (Closed): Change the license to "GPLv2+ or Ruby's original".
https://redmine.ruby-lang.org/issues/2032
2009-09-02T17:44:13Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
This is moved ticked from ruby-dev.<br>
Original post and ticket is <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Change the license to "GPLv2+ or Ruby's original". (Closed)" href="https://redmine.ruby-lang.org/issues/2000">#2000</a> in English.</p>
<p>----- Original Post -----</p>
<p>Hello.</p>
<p>Recently readline 6.0 was released and its license was changed from<br>
GPLv2+ (GPL version 2 and any later) to GPLv3+ [1][2]<br>
Unfortunately Ruby's license is still under GPLv2 and Ruby's original license [3],<br>
which is incompatible with GPLv3 [4]. So unless Ruby's license is changed<br>
to "GPLv2+ or Ruby's original license" or so , Ruby's readline module cannot be shipped<br>
any more. Note that "Ruby's original license" is regarded as incompatible with<br>
GPL [5].</p>
<p>So please change the Ruby's license to GPLv3 (and GPLv2) compat.</p>
<p>[1] <a href="http://tiswww.case.edu/php/chet/readline/rltop.html" class="external">http://tiswww.case.edu/php/chet/readline/rltop.html</a><br>
[2] <a href="https://www.redhat.com/archives/fedora-devel-list/2009-July/msg00192.html" class="external">https://www.redhat.com/archives/fedora-devel-list/2009-July/msg00192.html</a><br>
[3] <a href="http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/COPYING?view=co" class="external">http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/COPYING?view=co</a><br>
[4] <a href="https://fedoraproject.org/wiki/Licensing#GPL_Compatibility_Matrix" class="external">https://fedoraproject.org/wiki/Licensing#GPL_Compatibility_Matrix</a><br>
[5] <a href="https://fedoraproject.org/wiki/Licensing" class="external">https://fedoraproject.org/wiki/Licensing</a><br>
=end</p>
Ruby master - Feature #1951 (Closed): openのBOM指定拡張
https://redmine.ruby-lang.org/issues/1951
2009-08-18T23:47:15Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>#747と#802で議論された、openのBOM指定拡張ですが、現在の仕様は、</p>
<ul>
<li>BOMを捨てる</li>
<li>BOMを見てencodingを設定する<br>
という2つの機能が混在しています。</li>
</ul>
<p>このために、たとえば「<code>UTF-8-BOM</code>」という指定でも、<br>
BOMがUTF-16LEを示していた場合には実際に返ってくるStringはUTF-16LEになってしまいます。</p>
<p>この問題に対する解決案として、</p>
<ul>
<li>UTF-*-BOM はBOMを捨てるだけ。別のencodingだった場合は例外</li>
<li>
<code>BOM|UTF-*</code>を追加、これが現在の<code>UTF-*-BOM</code>相当の動作 (BOMを見る OR <code>UTF-*</code>と指定、というイメージ)<br>
というものを考えています。</li>
</ul>
<p>皆さんはどのように思われますか?</p>
Backport191 - Backport #1938 (Closed): FreeBSDでmakeに失敗する
https://redmine.ruby-lang.org/issues/1938
2009-08-14T16:56:45Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
common.mkに$(YACC) -d $(YFLAGS) -o y.tab.c $(<:=/)という行がありまして、<br>
これはLinuxとかWindowsでは動くんですが、BSD makeだとバックスラッシュがエスケープだと解釈され、<br>
「Unclosed substitution for < (= missing)」<br>
というエラーが出ます。</p>
<p>この問題はr22964で対処されています。<br>
=end</p>
Ruby master - Bug #941 (Closed): ignores SIGQUIT on FreeBSD 7.1
https://redmine.ruby-lang.org/issues/941
2008-12-29T11:14:42Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
FreeBSD 7.1-PRERELEASE amd64 にて、SIGQUIT を無視してしまうようです。</p>
<ol>
<li>Failure:<br>
test_status_kill(TestProcess) [test/ruby/test_process.rb:941]:<br>
Expected ["#<Process::Status: pid 53404 SIGQUIT (signal )>",<br>
"#<Process::Status: pid 53404 SIGQUIT (signal ) (core dumped)>"].include?(*["#<Process::Status: pid 53404 exit 0>"]) to return true.</li>
</ol>
<p>書き換えると以下の通り</p>
<p>% cat wait.rb<br>
sleep 10;<br>
print "finished\n";<br>
% ruby19 -e'pid=spawn("ruby19","wait.rb"); Thread.new{sleep 3; Process.kill(:SIGQUIT, pid) }; Process.wait(pid);;p $?'<br>
finished<br>
#<Process::Status: pid 53220 exit 0></p>
<p>なお、spawn("perl" に書き換えるとちゃんと殺せます<br>
=end</p>
Ruby master - Bug #624 (Closed): ArgumentError on "%c" % 0x80
https://redmine.ruby-lang.org/issues/624
2008-10-09T19:59:42Z
naruse (Yui NARUSE)
naruse@airemix.jp
<p>=begin<br>
成瀬です。</p>
<p>Tanaka Akira wrote:</p>
<blockquote>
<p>In article <a href="mailto:874p3mqm1s.fsf@fsij.org" class="email">874p3mqm1s.fsf@fsij.org</a>,<br>
Tanaka Akira <a href="mailto:akr@fsij.org" class="email">akr@fsij.org</a> writes:</p>
<blockquote>
<p>"%c" % 0x80 が例外になります。</p>
</blockquote>
<p>うぅむ。ロケールに依存しているようですね。</p>
<p>EUC-JP ではおきます。</p>
<p>% LANG=ja_JP.EUC-JP ./ruby -ve '"%c" % 0x80'<br>
ruby 1.9.0 (2008-10-09 revision 19725) [i686-linux]<br>
-e:1: warning: useless use of % in void context<br>
-e:1:in <code>%': negative string size (or size too big) (ArgumentError) from -e:1:in </code>'<br>
zsh: exit 1 LANG=ja_JP.EUC-JP ./ruby -ve '"%c" % 0x80'</p>
<p>UTF-8 ではおきません。</p>
<p>% LANG=ja_JP.UTF-8 ./ruby -ve '"%c" % 0x80'<br>
ruby 1.9.0 (2008-10-09 revision 19725) [i686-linux]<br>
-e:1: warning: useless use of % in void context</p>
<p>7bit な文字列のエンコーディングを US-ASCII にしなくなったこ<br>
との影響ともいえるかなぁ。</p>
</blockquote>
<p>printf("%c", codepoint) という解釈になりますので、<br>
EUC-JP で例外というのは妥当な動作だと思います。</p>
<p>例外の内容が誤っている気はしますね。</p>
<p>--<br>
NARUSE, Yui <a href="mailto:naruse@airemix.jp" class="email">naruse@airemix.jp</a><br>
=end</p>