tcopeland/pippi

TypeError: singleton can't be dumped

Closed this issue · 6 comments

Hi,

first of all: Thanks for this library :)

While trying pippi on one of our projects I discovered several TypeErrors after activating pippi in tests.

A simple reproduction:

require 'bundler/setup'
require 'minitest/autorun'
require 'pippi'

Pippi::AutoRunner.new(:checkset => ENV['PIPPI_CHECKSET'] || "basic")

class MarshalDumpTest < Minitest::Test
  def test_dump_broken
    Marshal.dump([].select(&:odd?))
  end

  def test_dump_works
    Marshal.dump([].select(&:odd?).dup)
  end
end

Output

Run options: --seed 39192

# Running:

E.

Finished in 0.002342s, 853.9608 runs/s, 0.0000 assertions/s.

  1) Error:
MarshalDumpTest#test_dump_broken:
TypeError: singleton can't be dumped
    test/marshal_dump_test.rb:9:in `dump'
    test/marshal_dump_test.rb:9:in `test_dump_broken'

2 runs, 0 assertions, 0 failures, 1 errors, 0 skips

Any pointers how to solve this issue?

@splattael thanks for the report. Hm, I wonder if I introduced bugs with aea9123 - can you please try pippi-0.0.9.gem and see if you get the same errors in your app?

@tcopeland Thanks for your fast response!

I did a git bisect to find the first "bad" commit:
Git says it's 35871c7

I've run

$ git bisect start master c71de64
$ git bisect run ./test.sh


Already on 'master'
Your branch is up-to-date with 'origin/master'.
35871c7c1afbb1801160767e4b2ea652b9600a61 is the first bad commit
commit 35871c7c1afbb1801160767e4b2ea652b9600a61
Author: Tom Copeland <tom.copeland@livingsocial.com>
Date:   Wed Oct 8 23:28:29 2014 -0400

    Added SelectFollowedByFirst

    And called out the DFA issue on the front page.

:100644 100644 76c0a4829b1df74fa7aec1fd4c5415a3a32101f0 215bbbebfdb8ee5fa0b81a4424f44839f39871a6 M  README.md
:040000 040000 8c8afbe057e0d9940ef139b2c9ce0600990e75b1 9b3203b12747a9583a7717820b647e0983fd7de3 M  lib
:040000 040000 46ccc3a6d58f492850e39abd1a676b5d1b5406cc 70aa2e7a14956fd6b9f80727360a08cc2a8669ee M  test

More info

bisect.log

git bisect start
# good: [c71de64e859a3be05cfd53aabae1bf431ea2e0db] New rule - DetectFollowedByNil
git bisect good c71de64e859a3be05cfd53aabae1bf431ea2e0db
# bad: [5981c8776a3ee46703e00d4698c50c2dbdec609a] Heritage restored
git bisect bad 5981c8776a3ee46703e00d4698c50c2dbdec609a
# bad: [b85fb6cd67839a9fe371e3e228370619810f1214] Docs / gitignore fixes from Igor Kapkov
git bisect bad b85fb6cd67839a9fe371e3e228370619810f1214
# bad: [470c644d7eaee85115981516c65ad4b1973abba9] Detect subsequent method invocations which negate a positive result
git bisect bad 470c644d7eaee85115981516c65ad4b1973abba9
# good: [59d3c023c76326578a9a0bdd129d1eaeb0ca68a6] Remove test script
git bisect good 59d3c023c76326578a9a0bdd129d1eaeb0ca68a6
# good: [4265e35cca32831b8b731cdaaa42f3f00e62806e] Comment out the halting problem
git bisect good 4265e35cca32831b8b731cdaaa42f3f00e62806e
# bad: [e40296385edd4272a9e77fb3db0652fd2210fbb5] Allow clients to specify checksets to autorunner
git bisect bad e40296385edd4272a9e77fb3db0652fd2210fbb5
# bad: [35871c7c1afbb1801160767e4b2ea652b9600a61] Added SelectFollowedByFirst
git bisect bad 35871c7c1afbb1801160767e4b2ea652b9600a61
# good: [1158881bfb5f1cd672fbe996f838696bd8e8c584] Added ReverseFollowedByEach
git bisect good 1158881bfb5f1cd672fbe996f838696bd8e8c584
# first bad commit: [35871c7c1afbb1801160767e4b2ea652b9600a61] Added SelectFollowedByFirst

test.sh

#!/bin/bash
bundle
ruby -Ilib test/marshal_dump_test.rb

test/marshal_dump_test.rb

require 'bundler/setup'
require 'minitest/autorun'
require 'pippi'
require 'pippi/auto_runner'

Pippi::AutoRunner.new

p :VERSION => Pippi::VERSION

class MarshalDumpTest < Minitest::Test
  def test_dump_broken
    Marshal.dump([].select(&:odd?))
  end

  def test_dump_works
    Marshal.dump([].select(&:odd?).dup)
  end
end

I hope this helps 😺

I see, thank you @splattael . Yes, the problem is that the technique I'm using for observing method calls is to add a singleton method to the object that's returned from select. So when attempting to serialize that object, as discussed here http://www.ruby-doc.org/core-2.1.5/Marshal.html:

irb(main):005:0> Marshal.dump(Object.new.tap {|o| o.define_singleton_method(:foo, proc {}) } )
TypeError: singleton can't be dumped

Hmmm I wonder, maybe we can implement marshal_dump and remove the singleton method then?

@tcopeland I'll try to work on this in the next couple of days. Thanks!

Mh, obviously I didn't manage to fix this. Sorry for that :-(

No worries @splattael !

I have to say, this project has not turned out to be as productive as I thought... the interprocedural checks haven't been as effective as I thought they'd be; too many false positives. Still, I should revisit this and see if I can come up with a few more checks.