[#34204] 「プログラミング Ruby 」 p.257 defined? はメソッド? — Noritsugu Nakamura <nnakamur@...>

15 messages 2002/03/05
[#34205] Re: 「プログラミング Ruby 」 p.257 defined? はメソッド? — nobu.nakada@... 2002/03/05

なかだです。

[#34244] しぶらぐっ議事録。 — rubiben <ozaki@...>

48 messages 2002/03/08
[#34250] Re: しぶらぐっ議事録。 — Minero Aoki <aamine@...> 2002/03/09

あおきです。

[#34246] Re: しぶらぐっ議事録。 — rubikitch <rubikitch@...> 2002/03/08

From: rubiben <ozaki@ruby-lang.org>

[#34448] 文字コード混在スクリプトの実行 — Noritsugu Nakamura <nnakamur@...>

14 messages 2002/03/16

[#34453] カレンダー文字列を作るメソッド — Take_tk <ggb03124@...>

たけ(tk)です。

13 messages 2002/03/16

[#34534] スクリプティング言語における GPL — Taku Nakajima <tnakajima@...>

中島@ブレーンです。

26 messages 2002/03/21

[#34552] Re: 1.7 に関して — "井上 浩一" <kyoui32@...>

井上です。

22 messages 2002/03/22
[#34556] Re: 1.7 に関して — "U.Nakamura" <usa@...> 2002/03/22

こんにちは、なかむら(う)です。

[#34590] Ruby FAQ (Re: 1.7 に関して) — TAKAHASHI Masayoshi <maki@...> 2002/03/22

高橋征義です。

[#34570] cgi.rb+ イテレータ — FUJIOKA Takeyuki <fujioka@...>

藤岡です。

15 messages 2002/03/22

[#34676] 安心できる Marshal と、 Ruby バージョンの検査法 — MALTA Koji <malta@...>

丸田と申します。

62 messages 2002/03/30
[#34678] Re: 安心できる Marshal と、 Ruby バージョンの検査法 — Koji Arai <JCA02266@...> 2002/03/31

新井です。

[#34682] Ruby バージョンの検査法 — MALTA Koji <malta@...> 2002/03/31

丸田です。 MarshalとRUBY_VERSIONの話題を分離します。

[#34694] Re: Ruby バージョンの検査法 — WATANABE Hirofumi <eban@...> 2002/04/01

わたなべです。

[#34695] Re: Ruby バージョンの検査法 — shukaku@... 2002/04/01

At Mon, 1 Apr 2002 16:06:23 +0900,

[#34697] Re: Ruby バージョンの検査法 — WATANABE Hirofumi <eban@...> 2002/04/01

わたなべです。

[#34714] Re: Ruby バージョンの検査法 — MALTA Koji <malta@...> 2002/04/01

丸田です。

[#34719] Re: Ruby バージョンの検査法 — HIDAKA Takahiro <cv8t-hdk@...> 2002/04/02

 ひだかです。

[#34738] Re: Ruby バージョンの検査法 — MALTA Koji <malta@...> 2002/04/02

丸田です。

[#34743] Re: Ruby バージョンの検査法 — matz@... (Yukihiro Matsumoto) 2002/04/02

まつもと ゆきひろです

[#34748] Re: Ruby バージョンの検査法 — "NAKAMURA, Hiroshi" <nakahiro@...> 2002/04/03

なひです。

[#34760] Re: Ruby バージョンの検査法 — Koji Arai <JCA02266@...> 2002/04/03

新井です。

[#34765] Re: Ruby バージョンの検査法 — "U.Nakamura" <usa@...> 2002/04/04

こんにちは、なかむら(う)です。

[#34776] Re: Ruby バージョンの検査法 — Tanaka Akira <akr@...17n.org> 2002/04/04

In article <20020404095542.7155.USA@osb.att.ne.jp>,

[#34681] 書籍に関する質問です — Nobuhide Kanagawa <nobuhide@...>

こんにちわ。

17 messages 2002/03/31

[ruby-list:34679] Re: 安心できる Marshal と、 Ruby バージョンの検査法

From: Koji Arai <JCA02266@...>
Date: 2002-03-31 14:07:50 UTC
List: ruby-list #34679
新井です。

In message "[ruby-list:34678] Re: 安心できる Marshalと、 Rubyバージョンの検査法"
  on 31 Mar 2002 17:55:19 +0900,
  Koji Arai <JCA02266@nifty.ne.jp> wrote:
> 新井です。
> 
> In message "[ruby-list:34676] 安心できる Marshalと、 Ruby バージョンの検査法"
>   on 31 Mar 2002 01:15:53 +0900,
>   MALTA Koji <malta@zn.xdsl.ne.jp> wrote:
> >  丸田と申します。
> > 
> > (1)
> >  「安心できないMarshalのバグ」が修正されたRubyのバージョンはいくつでしょ
> > うか?

> 1.6.7 Marshal.load にバグあり(Marshal のフォーマットとは関係ない)
> 1.6.3 Float をダンプしたときの精度が変わった。
>       Bignum の dump -> load が元に戻らない
> 
> となっています。他にも特異クラス関係で変化(バグ修正)があった
> ような覚えがあります。ダンプフォーマットの履歴は独立してマニュ
> アルに用意した方が良さそうですね。しばし時間をください頑張っ
> てみようと思います。

ひとまず、RubyUnit によるテストを書いて動作確認だけで違いを
みてみました。これでは、古いバージョンのフォーマットを新しい
バージョンで確実に読めるかどうか等はわからないのですが、まあ
それはおいおいソースでも見て調べることにします。

対象は version 1.6.0 〜 1.6.7 と 1.7 latest (2002-03-29) で
す。テストスクリプトの $version_dependent_behavior で条件分
岐してあるところは振るまいがバージョンにより異なる箇所です。

この違いをまとめると、(()は 1.7 の振るまい)

<= 1.6.7
  o クラスを clone したもののインスタンスはダンプできるが、ロー
    ドできない(無名クラスのオブジェクトになるのでダンプできない)

<= 1.6.5
  o クラスを clone したもののインスタンスはダンプできるが、ロー
    ドすると変なオブジェクトができる(?)
  o 特異クラスは、特異でないクラスにダンプされる(特異クラスはダンプできない)
  o 無名クラスは、ダンプできるがロードできない(無名クラスはダンプできない)
  
<= 1.6.4
  o モジュールはダンプできるがロードできない(ロードできる)
  o 無名モジュールは、ダンプできるがロードできない(無名モジュー
    ルはダンプできない)

<= 1.6.3
  o Float をダンプしたときに保存する精度が低い

<= 1.6.2
  o 正規表現の /m, /x オプションの有無がダンプ時に保存されない

1.6.2, 1.6.3
  o 1.6.2, 1.6.3のみBignum をダンプしたものをロードできない
    この関連バグは他にもあったと思うが再現スクリプトが不明。

<= 1.6.1
  o Range が終端を含むかどうかのフラグがダンプ時に保存されない

古いバージョンでダンプできるがロードできないものが、新しい
バージョンでどうロードされるかを調べる必要がありますね。

この他、気づいた点ありましたら教えて頂ければ幸いです。
そのうち、マニュアルにも反映させようと思います。

--
新井康司 (Koji Arai)


# test for Marshal for ruby version 1.6
require 'runit/testcase'
require 'runit/cui/testrunner'

$version_dependent_behavior = true
# for test_userClass, test_userModule
module UserModule
  def foo
  end
end
class UserClass
  def foo
  end
end

class TestMarshal < RUNIT::TestCase

  def assert_no_dumpable(obj)
    assert_exception(TypeError) {
      begin
	Marshal.dump obj
      rescue ArgumentError
	if /can't dump anonymous/ =~ $!.message
	  raise TypeError
	else
	  raise "unknown error"
	end
      end
    }
  end
  def assert_dumpable_but_not_equal(obj)
    obj2 = Marshal.load(Marshal.dump(obj))
    assert(obj != obj2)
    assert_equals(obj2.type, obj2.type)
  end
  def assert_dumpable_and_equal(obj)
    obj2 = Marshal.load(Marshal.dump(obj))
    assert_equals(obj, obj2)
    assert_equals(obj.type, obj2.type)
  end

  def test_Object
    assert_dumpable_but_not_equal Object.new
  end

=begin
  # object with singleton method
  def test_singletonObject
    obj = Object.new
    # On ruby version 1.6.0 - 1.6.2, cause parse error (nested method)
    class <<obj
      def foo
      end
    end
 
    # object has singleton method can't be dumped
    assert_no_dumpable obj
  end

  # singleton class
  def test_singletonClass
    obj = Object.new
    # On ruby version 1.6.0 - 1.6.2, cause parse error (nested method)
    singleton_class = class <<obj
			def foo
			end
			self
		      end
 
    # singleton class can't be dumped
    # On ruby version 1.6.0 - 1.6.5, singleton class be able to dumped
    # as normal class.
    if $version_dependent_behavior and RUBY_VERSION <= "1.6.5"
      assert_equals(Object, Marshal.load(Marshal.dump(singleton_class)))
    else
      assert_no_dumpable singleton_class
    end
  end
=end

  def test_Array
    assert_dumpable_and_equal [1,"foo", :foo]
  end

  def test_Binding
    assert_no_dumpable binding
  end

  def test_Continuation
    assert_no_dumpable callcc {|c| c}
  end

  def test_Data
    # assert_fail("")
  end

  def test_Exception
    assert_dumpable_but_not_equal Exception.new("hoge")
  end

  def test_Dir
    assert_no_dumpable Dir.open("/")
  end

  def test_FalseClass
    assert_dumpable_and_equal false
  end

  def test_File__Stat
    assert_no_dumpable File.stat("/")
  end

  def test_Hash
    assert_dumpable_and_equal(1=>"1",2=>"2")

    # 1.7 feature.
    if $version_dependent_behavior and RUBY_VERSION >= '1.7.0'
      # On ruby version 1.7, hash with default Proc cannot be able to dumped
      # see [ruby-dev:15417]
      assert_no_dumpable(Hash.new { })
    end      
  end

  def test_IO
    assert_no_dumpable IO.new(0)
  end
  
  def test_File
    assert_no_dumpable File.open("/")
  end

  def test_MatchData
    assert_no_dumpable(/foo/ =~ "foo" && $~)
  end

  def test_Method
    assert_no_dumpable Object.method(:method)
  end

  def test_UnboundMethod
    assert_no_dumpable Object.instance_method(:id)
  end

  def test_Module
    # On ruby version 1.6.0 - 1.6.4, loaded module is not a module.
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.4'
      dump = Marshal.dump Enumerable
      assert_exception(TypeError) {
	begin
	  Marshal.load dump
	rescue TypeError
	  if /is not a module/ =~ $!.message
	    raise
	  else
	    raise 'unknown error'
	  end
	end
      } 
    else
      assert_dumpable_and_equal Enumerable
    end
  end

  def test_userModule
    # On ruby version 1.6.0 - 1.6.4, loaded module is not a module.
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.4'
      # same as test_Module
    else
      # Note: this module must be defineed for Marshal.load.
      assert_dumpable_and_equal(UserModule)
    end
  end

  def test_anonymousModule
    # On ruby version 1.6.0 - 1.6.4, anonymous class is able to be dumped,
    # but loaded object is not identical.
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.4'
      dump = Marshal.dump(Module.new)
      assert_exception(ArgumentError) {
	begin
	  Marshal.load(dump)
	rescue ArgumentError
	  if /can't retrieve anonymous class/ =~ $!.message
	    raise
	  else
	    raise "unknown error"
	  end
	end
      }
    else
      assert_no_dumpable Module.new
    end
  end

  def test_Class
    assert_dumpable_and_equal Class
  end

  def test_userClass
    # Note: this class must be defineed for Marshal.load.
    assert_dumpable_and_equal(UserClass)
  end
  def test_anonymousClass
    # On ruby version 1.6.0 - 1.6.5, anonymous class able to be dumped,
    # but can't load it.
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.5'
      dump = Marshal.dump(Class.new)
      assert_exception(ArgumentError) {
	begin
	  Marshal.load(dump)
	rescue ArgumentError
	  if /can't retrieve anonymous class/ =~ $!.message
	    raise
	  else
	    raise "unknown error"
	  end
	end
      }
    else
      assert_no_dumpable Class.new
    end
  end

  def test_clonedClass
    # On ruby version 1.6.0 - 1.6.7, instance of cloned class is able to
    # dumped, but loaded object is not identical.
    # see [ruby-dev:14961]
    if $version_dependent_behavior
      if RUBY_VERSION <= '1.6.5'
	obj = String.clone.new("foo")
	dump = Marshal.dump(obj)
	obj2 = Marshal.load dump
	assert(obj == obj2)
	assert(obj.type != obj2.type)
	assert(obj.type.inspect == obj2.type.inspect)
      elsif RUBY_VERSION <= '1.6.7'
	dump = Marshal.dump(String.clone.new("foo"))
	assert_exception(ArgumentError) {
	  Marshal.load dump
	}
      else
	assert_no_dumpable String.clone.new("foo")
      end
    else
      # anonymous class can't be dumped
      assert_no_dumpable String.clone.new("foo")
    end
  end

  def test_Numeric
    # assert_fail("")
  end

  def test_Integer
    # assert_fail("")
  end

  def test_Fixnum
    assert_dumpable_and_equal 100
  end

  def test_Bignum
    # derived from Rubicon
    assert_dumpable_and_equal 123456789012345678901234567890
    assert_dumpable_and_equal -123**99
    if $version_dependent_behavior and %w(1.6.2 1.6.3).member?(RUBY_VERSION)
      dump = Marshal.dump 2**32
      assert_exception(ArgumentError) {
	begin
	  Marshal.load(dump)
	rescue ArgumentError
	  if /marshal data too short/ =~ $!.message
	    raise
	  else
	    raise "unknown error"
	  end
	end
      }
    else
      assert_dumpable_and_equal 2**32
    end
  end

  def test_Float
    assert_dumpable_and_equal 1.0

    # On ruby version 1.6.4, dumped format changed from "%.12g" to "%.16g"
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.3'
      assert_dumpable_but_not_equal Math::PI
    else
      assert_dumpable_and_equal Math::PI
    end
  end

  def test_Proc
    assert_no_dumpable proc { }
  end

  def test_Process__Status
    assert_dumpable_and_equal system("true") && $?
  end

  def test_Range
    # Range#== is changed from 1.6.2
    # On ruby version 1.6.0 - 1.6.1, Range.new(1,2) != Range.new(1,2)
    # assert_dumpable_and_equal 1..2
    # assert_dumpable_and_equal 1...2

    obj = Marshal.load(Marshal.dump 1..2)
    assert_equals(1, obj.begin)
    assert_equals(2, obj.end)
    assert_equals(false, obj.exclude_end?)

    obj = Marshal.load(Marshal.dump 1...2)
    assert_equals(1, obj.begin)
    assert_equals(2, obj.end)

    # On ruby version 1.6.0 - 1.6.1, the attribute exclude_end? is not saved.
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.1'
      assert_equals(false, obj.exclude_end?)
    else
      assert_equals(true, obj.exclude_end?)
    end
  end

  def test_Regexp
    # this test is no consider the /foo/p
    assert_dumpable_and_equal /foo/
    assert_dumpable_and_equal /foo/i
    assert_dumpable_and_equal /foo/m
    assert_dumpable_and_equal /foo/x
    assert_dumpable_and_equal /foo/e
    assert_dumpable_and_equal /foo/s
    assert_dumpable_and_equal /foo/u

    # On ruby version 1.6.0 - 1.6.2, Regexp#== is ignore the option.
    for obj in [/foo/, /foo/i, /foo/m, /foo/x, /foo/e, /foo/s, /foo/u]
      obj2 = Marshal.load(Marshal.dump obj)

      if $version_dependent_behavior and RUBY_VERSION <= '1.6.2' and
	  %w(/foo/m /foo/x).member?(obj.inspect)
	# On ruby version 1.6.0 - 1.6.2,
	# //m options is not saved.
	assert_equals('/foo/', obj2.inspect)
      else
	assert_equals(obj.inspect, obj2.inspect)
      end
    end
  end

  def test_String
    assert_dumpable_and_equal "foo"
  end

  def test_Struct
    assert_dumpable_and_equal Struct.new("Foo", :foo, :bar)

    Object.const_set('Foo', Struct.new(:foo, :bar))
    assert_dumpable_and_equal Foo
  end

  def test_aStruct
    assert_dumpable_and_equal Struct.new("Bar", :foo, :bar).new("foo", "bar")

    # see [ruby-dev:14961]
  end

  def test_Symbol
    assert_dumpable_and_equal :foo
  end

  def test_Thread
    assert_no_dumpable Thread.new { sleep }
  end

  def test_ThreadGroup
    assert_no_dumpable ThreadGroup::Default
  end

  def test_Time
    assert_dumpable_and_equal Time.now
    assert_dumpable_and_equal Time.now.gmtime

    # time zone is not saved.
    assert_equals(false, Marshal.load(Marshal.dump(Time.now)).utc?)
    assert_equals(false, Marshal.load(Marshal.dump(Time.now.gmtime)).utc?)
  end

  def test_TrueClass
    assert_dumpable_and_equal true
  end

  def test_NilClass
    assert_dumpable_and_equal nil
  end
end

if $0 == __FILE__
  if ARGV.size == 0
    suite = RUNIT::TestSuite.new
    suite.add_test(TestMarshal.suite)
  else
    suite = RUNIT::TestSuite.new
    ARGV.each do |testmethod|
      suite.add_test(TestMarshal.new(testmethod))
    end
  end

  RUNIT::CUI::TestRunner.run(suite)
end

In This Thread