Permalink
Browse files

Use separate allocation for Proc

This makes sure the regular Proc.allocate isn't usable like on MRI so
stuff like YAML won't deserialize it. We also add guards for Proc's that
have no block or method bound to it, so that it doesn't crash in that
case but gives a proper exception.

Fixes #1819
  • Loading branch information...
1 parent 5435a4f commit 64c8276404f4886bb9f0358f71b2d3d3129d3a00 @dbussink dbussink committed Jul 17, 2012
Showing with 51 additions and 11 deletions.
  1. +4 −0 kernel/bootstrap/proc.rb
  2. +23 −0 kernel/common/proc.rb
  3. +1 −1 kernel/common/proc18.rb
  4. +1 −1 kernel/common/proc19.rb
  5. +22 −9 vm/builtin/proc.cpp
View
@@ -2,6 +2,10 @@
class Proc
def self.allocate
+ raise TypeError, "allocator undefined for Proc"
+ end
+
+ def self.__allocate__
Rubinius.primitive :proc_allocate
raise PrimitiveFailure, "Proc#allocate failed"
end
View
@@ -109,6 +109,29 @@ def to_proc
alias_method :[], :call
alias_method :yield, :call
+ def clone
+ copy = self.class.__allocate__
+ Rubinius.invoke_primitive :object_copy_object, copy, self
+ Rubinius.invoke_primitive :object_copy_singleton_class, copy, self
+
+ Rubinius.privately do
+ copy.initialize_copy self
+ end
+
+ copy.freeze if frozen?
+ copy
+ end
+
+ def dup
+ copy = self.class.__allocate__
+ Rubinius.invoke_primitive :object_copy_object, copy, self
+
+ Rubinius.privately do
+ copy.initialize_copy self
+ end
+ copy
+ end
+
class Method < Proc
attr_accessor :bound_method
View
@@ -9,7 +9,7 @@ def to_s
class Method < Proc
def self.__from_method__(meth)
- obj = allocate()
+ obj = __allocate__
obj.bound_method = meth
return obj
View
@@ -62,7 +62,7 @@ def to_s
class Method < Proc
def self.__from_method__(meth)
- obj = allocate()
+ obj = __allocate__
obj.bound_method = meth
obj.lambda_style!
View
@@ -115,25 +115,38 @@ namespace rubinius {
}
}
- Object* ret;
if(self->bound_method_->nil_p()) {
- ret = self->block_->call(state, call_frame, args, flags);
+ if(self->block_->nil_p()) {
+ Exception* exc =
+ Exception::make_type_error(state, BlockEnvironment::type, self->block_, "No code bound to proc");
+ exc->locations(state, Location::from_call_stack(state, call_frame));
+ state->raise_exception(exc);
+ return NULL;
+ } else {
+ return self->block_->call(state, call_frame, args, flags);
+ }
} else if(NativeMethod* nm = try_as<NativeMethod>(self->bound_method_)) {
- ret = nm->execute(state, call_frame, nm, G(object), args);
+ return nm->execute(state, call_frame, nm, G(object), args);
} else if(NativeFunction* nf = try_as<NativeFunction>(self->bound_method_)) {
- ret = nf->call(state, args, call_frame);
+ return nf->call(state, args, call_frame);
} else {
Dispatch dis(state->symbol("__yield__"));
- ret = dis.send(state, call_frame, args);
+ return dis.send(state, call_frame, args);
}
-
- return ret;
}
Object* Proc::yield(STATE, CallFrame* call_frame, Arguments& args) {
if(bound_method_->nil_p()) {
- // NOTE! To match MRI semantics, this explicitely ignores lambda_.
- return block_->call(state, call_frame, args, 0);
+ if(block_->nil_p()) {
+ Exception* exc =
+ Exception::make_type_error(state, BlockEnvironment::type, block_, "No code bound to proc");
+ exc->locations(state, Location::from_call_stack(state, call_frame));
+ state->raise_exception(exc);
+ return NULL;
+ } else {
+ // NOTE! To match MRI semantics, this explicitely ignores lambda_.
+ return block_->call(state, call_frame, args, 0);
+ }
} else if(NativeMethod* nm = try_as<NativeMethod>(bound_method_)) {
return nm->execute(state, call_frame, nm, G(object), args);
} else if(NativeFunction* nf = try_as<NativeFunction>(bound_method_)) {

0 comments on commit 64c8276

Please sign in to comment.