Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
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...
commit 64c8276404f4886bb9f0358f71b2d3d3129d3a00 1 parent 5435a4f
@dbussink dbussink authored
View
4 kernel/bootstrap/proc.rb
@@ -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
23 kernel/common/proc.rb
@@ -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
2  kernel/common/proc18.rb
@@ -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
2  kernel/common/proc19.rb
@@ -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
31 vm/builtin/proc.cpp
@@ -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_)) {
Please sign in to comment.
Something went wrong with that request. Please try again.