Make mutant run under MRI 1.9.
* Add a Mutant::Loader::Eval that loads code via Kernel#eval * Move Mutnat::Loader to Mutant::Loader::Rubinius (currently not used)
This commit is contained in:
parent
d77042c872
commit
001f3c7d99
14 changed files with 154 additions and 86 deletions
|
@ -4,6 +4,8 @@ language: ruby
|
||||||
script: 'bundle exec rake spec'
|
script: 'bundle exec rake spec'
|
||||||
rvm:
|
rvm:
|
||||||
# - 1.8.7 disabled while spiking mutations
|
# - 1.8.7 disabled while spiking mutations
|
||||||
|
- 1.9.2
|
||||||
|
- 1.9.3
|
||||||
- rbx-18mode
|
- rbx-18mode
|
||||||
- rbx-19mode
|
- rbx-19mode
|
||||||
notifications:
|
notifications:
|
||||||
|
|
1
Gemfile
1
Gemfile
|
@ -7,6 +7,7 @@ gem 'abstract_class', :git => 'https://github.com/dkubb/abstract_class.git'
|
||||||
gem 'equalizer', :git => 'https://github.com/dkubb/equalizer.git'
|
gem 'equalizer', :git => 'https://github.com/dkubb/equalizer.git'
|
||||||
gem 'anima', :git => 'https://github.com/mbj/anima.git'
|
gem 'anima', :git => 'https://github.com/mbj/anima.git'
|
||||||
gem 'to_source', :git => 'https://github.com/mbj/to_source.git'
|
gem 'to_source', :git => 'https://github.com/mbj/to_source.git'
|
||||||
|
gem 'melbourne', :git => 'https://github.com/mbj/melbourne.git'
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem 'rake', '~> 0.9.2'
|
gem 'rake', '~> 0.9.2'
|
||||||
|
|
|
@ -5,8 +5,6 @@ module Mutant
|
||||||
|
|
||||||
# Return root ast node
|
# Return root ast node
|
||||||
#
|
#
|
||||||
# @param [Rubinius::AST::Node] node
|
|
||||||
#
|
|
||||||
# @return [Rubinis::AST::Script]
|
# @return [Rubinis::AST::Script]
|
||||||
#
|
#
|
||||||
# @api private
|
# @api private
|
||||||
|
@ -34,19 +32,5 @@ module Mutant
|
||||||
def initialize(source_path)
|
def initialize(source_path)
|
||||||
@source_path = source_path
|
@source_path = source_path
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return script node
|
|
||||||
#
|
|
||||||
# @param [Rubinius::AST::Node] node
|
|
||||||
#
|
|
||||||
# @return [Rubinius::AST::Script]
|
|
||||||
#
|
|
||||||
# @api private
|
|
||||||
#
|
|
||||||
def script(node)
|
|
||||||
Rubinius::AST::Script.new(node).tap do |script|
|
|
||||||
script.file = source_path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,7 +48,7 @@ module Mutant
|
||||||
root = root_ast
|
root = root_ast
|
||||||
block = Rubinius::AST::Block.new(1, [node])
|
block = Rubinius::AST::Block.new(1, [node])
|
||||||
root.body = scope_class.new(1, root.name, block)
|
root.body = scope_class.new(1, root.name, block)
|
||||||
script(root)
|
root
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return unqualified name of scope
|
# Return unqualified name of scope
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
module Mutant
|
module Mutant
|
||||||
# A method object for inserting an AST into the Rubinius VM
|
# Base class for code loaders
|
||||||
#
|
|
||||||
# The idea is to split the steps for a mutation into documented
|
|
||||||
# methods. Also subclasses can override the steps. Also passing
|
|
||||||
# around the root node is not needed with a method object.
|
|
||||||
#
|
|
||||||
# As the initializer does the work there is no need for the
|
|
||||||
# instances of this class to be used outside of this class, hence
|
|
||||||
# the Loader.new method is private and the Loader.run method
|
|
||||||
# returns self.
|
|
||||||
#
|
|
||||||
class Loader
|
class Loader
|
||||||
|
include AbstractClass
|
||||||
extend MethodObject
|
extend MethodObject
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Run the loader
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
abstract_method :run
|
||||||
|
|
||||||
# Initialize and insert mutation into vm
|
# Initialize and insert mutation into vm
|
||||||
#
|
#
|
||||||
# @param [Rubinius::AST::Script] root
|
# @param [Rubinius::AST::Script] root
|
||||||
|
@ -25,51 +24,105 @@ module Mutant
|
||||||
#
|
#
|
||||||
def initialize(root)
|
def initialize(root)
|
||||||
@root = Helper.deep_clone(root)
|
@root = Helper.deep_clone(root)
|
||||||
Rubinius.run_script(compiled_code)
|
run
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return compiled code
|
# Eval based loader
|
||||||
#
|
class Eval < self
|
||||||
# @return [Rubinius::CompiledCode]
|
private
|
||||||
#
|
|
||||||
# @api private
|
# Run loader
|
||||||
#
|
#
|
||||||
# FIXME: rbx on travis is older than on my devbox.
|
# @return [undefined]
|
||||||
#
|
#
|
||||||
def compiled_code
|
# @api private
|
||||||
_script = script
|
#
|
||||||
_script.respond_to?(:compiled_code) ? _script.compiled_code : _script.compiled_method
|
def run
|
||||||
|
eval(source, TOPLEVEL_BINDING)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return source
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def source
|
||||||
|
ToSource.to_source(@root)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return code script
|
# Rubinius script node based loaded
|
||||||
#
|
class Rubinius < self
|
||||||
# @return [Rubinius::CompiledCode::Script]
|
private
|
||||||
#
|
|
||||||
# @api private
|
|
||||||
#
|
|
||||||
def script
|
|
||||||
compiled_code_raw.create_script
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return compiled code for node
|
# Run loader
|
||||||
#
|
#
|
||||||
# @return [Rubinius::CompiledCode]
|
# @return [undefined]
|
||||||
#
|
#
|
||||||
# @api private
|
# @api private
|
||||||
#
|
#
|
||||||
def compiled_code_raw
|
def run(root)
|
||||||
compiler.run
|
Rubinius.run_script(compiled_code)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return compiler loaded with mutated ast
|
# Return compiled code
|
||||||
#
|
#
|
||||||
# @return [Rubinius::Compiler]
|
# @return [Rubinius::CompiledCode]
|
||||||
#
|
#
|
||||||
# @api private
|
# @api private
|
||||||
#
|
#
|
||||||
def compiler
|
# FIXME: rbx on travis is older than on my devbox.
|
||||||
Rubinius::Compiler.new(:bytecode, :compiled_method).tap do |compiler|
|
#
|
||||||
compiler.generator.input(@root)
|
def compiled_code
|
||||||
|
_script = script
|
||||||
|
_script.respond_to?(:compiled_code) ? _script.compiled_code : _script.compiled_method
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return code script
|
||||||
|
#
|
||||||
|
# @return [Rubinius::CompiledCode::Script]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def script
|
||||||
|
compiled_code_raw.create_script
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return compiled code for node
|
||||||
|
#
|
||||||
|
# @return [Rubinius::CompiledCode]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def compiled_code_raw
|
||||||
|
compiler.run
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return compiler loaded with mutated ast
|
||||||
|
#
|
||||||
|
# @return [Rubinius::Compiler]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def compiler
|
||||||
|
Rubinius::Compiler.new(:bytecode, :compiled_method).tap do |compiler|
|
||||||
|
compiler.generator.input(@root)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return script node
|
||||||
|
#
|
||||||
|
# @param [Rubinius::AST::Node] node
|
||||||
|
#
|
||||||
|
# @return [Rubinius::AST::Script]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def script(node)
|
||||||
|
Rubinius::AST::Script.new(node).tap do |script|
|
||||||
|
script.file = source_path
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -75,7 +75,7 @@ module Mutant
|
||||||
def method
|
def method
|
||||||
scope.instance_method(method_name)
|
scope.instance_method(method_name)
|
||||||
end
|
end
|
||||||
memoize :method
|
memoize :method, :freezer => :noop
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -61,7 +61,7 @@ module Mutant
|
||||||
def method
|
def method
|
||||||
scope.method(method_name)
|
scope.method(method_name)
|
||||||
end
|
end
|
||||||
memoize :method
|
memoize :method, :freezer => :noop
|
||||||
|
|
||||||
# Test for node match
|
# Test for node match
|
||||||
#
|
#
|
||||||
|
|
|
@ -21,7 +21,7 @@ module Mutant
|
||||||
|
|
||||||
# Return mutated root node
|
# Return mutated root node
|
||||||
#
|
#
|
||||||
# @return [Rubinius::AST::Script]
|
# @return [Rubinius::AST::Node]
|
||||||
#
|
#
|
||||||
# @api private
|
# @api private
|
||||||
#
|
#
|
||||||
|
@ -37,7 +37,7 @@ module Mutant
|
||||||
# @api private
|
# @api private
|
||||||
#
|
#
|
||||||
def insert
|
def insert
|
||||||
Loader.run(root)
|
Loader::Eval.run(root)
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,8 @@ module Mutant
|
||||||
# @api private
|
# @api private
|
||||||
#
|
#
|
||||||
def reset
|
def reset
|
||||||
Loader.run(original_root)
|
Loader::Eval.run(original_root)
|
||||||
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -11,7 +11,7 @@ describe Mutant, 'code loading' do
|
||||||
let(:node) { 'def foo; :bar; end'.to_ast }
|
let(:node) { 'def foo; :bar; end'.to_ast }
|
||||||
let(:root) { context.root(node) }
|
let(:root) { context.root(node) }
|
||||||
|
|
||||||
subject { Mutant::Loader.run(root) }
|
subject { Mutant::Loader::Eval.run(root) }
|
||||||
|
|
||||||
before { subject }
|
before { subject }
|
||||||
|
|
||||||
|
|
|
@ -51,13 +51,7 @@ module Zombie
|
||||||
node.body = Rubinius::AST::ModuleScope.new(scope.line, node.name, scope.body)
|
node.body = Rubinius::AST::ModuleScope.new(scope.line, node.name, scope.body)
|
||||||
end
|
end
|
||||||
|
|
||||||
script = Rubinius::AST::Script.new(root)
|
::Mutant::Loader::Eval.run(root)
|
||||||
script.file = path
|
|
||||||
|
|
||||||
# For some reason loading is not the same as eval...?
|
|
||||||
# eval(root.to_source)
|
|
||||||
|
|
||||||
::Mutant::Loader.run(script)
|
|
||||||
end
|
end
|
||||||
private_class_method :zombify
|
private_class_method :zombify
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,9 @@ describe Mutant::Context::Scope, '#root' do
|
||||||
let(:path) { mock('Path') }
|
let(:path) { mock('Path') }
|
||||||
let(:node) { mock('Node') }
|
let(:node) { mock('Node') }
|
||||||
|
|
||||||
let(:constant) { subject.body }
|
let(:scope) { subject.body }
|
||||||
let(:scope) { constant.body }
|
|
||||||
let(:scope_body) { scope.body }
|
let(:scope_body) { scope.body }
|
||||||
|
|
||||||
it { should be_a(Rubinius::AST::Script) }
|
|
||||||
its(:file) { should be(path) }
|
|
||||||
|
|
||||||
it 'should wrap the ast under constant' do
|
it 'should wrap the ast under constant' do
|
||||||
scope.should be_kind_of(Rubinius::AST::ClassScope)
|
scope.should be_kind_of(Rubinius::AST::ClassScope)
|
||||||
end
|
end
|
||||||
|
|
33
spec/unit/mutant/loader/eval/class_methods/run_spec.rb
Normal file
33
spec/unit/mutant/loader/eval/class_methods/run_spec.rb
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Mutant::Loader::Eval, '.run' do
|
||||||
|
|
||||||
|
subject { object.run(node) }
|
||||||
|
|
||||||
|
let(:object) { described_class }
|
||||||
|
|
||||||
|
let(:source) do
|
||||||
|
# This test case will blow up when not executed
|
||||||
|
# under toplevel binding.
|
||||||
|
<<-RUBY
|
||||||
|
class SomeNamespace
|
||||||
|
class Bar
|
||||||
|
end
|
||||||
|
|
||||||
|
class SomeOther
|
||||||
|
class Foo < Bar
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:node) do
|
||||||
|
source.to_ast
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should load nodes into vm' do
|
||||||
|
subject
|
||||||
|
::SomeNamespace::SomeOther::Foo
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,10 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Mutant::Loader, '.run' do
|
describe Mutant::Loader::Rubinius, '.run' do
|
||||||
|
before do
|
||||||
|
pending
|
||||||
|
end
|
||||||
|
|
||||||
subject { object.run(node) }
|
subject { object.run(node) }
|
||||||
|
|
||||||
let(:object) { described_class }
|
let(:object) { described_class }
|
Loading…
Add table
Reference in a new issue