Last active
July 24, 2019 02:30
-
-
Save dalehamel/4aafc5436105937c0e0ad4316e583f70 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# First install https://github.com/postmodern/ruby-install | |
# Patches are below just download them | |
# After you have installed with ruby-install, you can chruby to use it | |
ruby-install ruby 2.6.3 -p 0001-RubyVM-InstructionSequence-eval_with.patch -p 0002-Update-iseq.eval-to-accept-optional-binding-Feature-.patch |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 5d0c8c83d8fdea16c403abbc80c160ddddb92ef8 Mon Sep 17 00:00:00 2001 | |
From: Nobuyoshi Nakada <nobu@ruby-lang.org> | |
Date: Mon, 22 Feb 2016 16:54:57 +0900 | |
Subject: [PATCH 1/2] RubyVM::InstructionSequence#eval_with | |
* iseq.c (iseq_eval_with): RubyVM::InstructionSequence#eval_with. | |
[Feature #12093] | |
* vm.c (rb_iseq_eval_in_scope): evaluate an iseq in the given | |
scope. | |
--- | |
iseq.c | 18 ++++++++++++++++++ | |
vm.c | 19 +++++++++++++++++++ | |
vm_core.h | 1 + | |
3 files changed, 38 insertions(+) | |
diff --git a/iseq.c b/iseq.c | |
index fa4e72d2cd..62cc562a13 100644 | |
--- a/iseq.c | |
+++ b/iseq.c | |
@@ -1345,6 +1345,23 @@ iseqw_eval(VALUE self) | |
return rb_iseq_eval(iseqw_check(self)); | |
} | |
+/* | |
+ * call-seq: | |
+ * iseq.eval_with(binding) -> obj | |
+ * | |
+ * Evaluates the instruction sequence and returns the result. | |
+ * | |
+ * obj = Struct.new(:a, :b).new(1, 2) | |
+ * bind = obj.instance_eval {binding} | |
+ * RubyVM::InstructionSequence.compile("a + b").eval_with(bind) #=> 3 | |
+ */ | |
+static VALUE | |
+iseq_eval_with(VALUE self, VALUE scope) | |
+{ | |
+ rb_secure(1); | |
+ return rb_iseq_eval_in_scope(iseqw_check(self), scope); | |
+} | |
+ | |
/* | |
* Returns a human-readable string representation of this instruction | |
* sequence, including the #label and #path. | |
@@ -3483,6 +3500,7 @@ Init_ISeq(void) | |
rb_define_method(rb_cISeq, "disassemble", iseqw_disasm, 0); | |
rb_define_method(rb_cISeq, "to_a", iseqw_to_a, 0); | |
rb_define_method(rb_cISeq, "eval", iseqw_eval, 0); | |
+ rb_define_method(rb_cISeq, "eval_with", iseq_eval_with, 1); | |
rb_define_method(rb_cISeq, "to_binary", iseqw_to_binary, -1); | |
rb_define_singleton_method(rb_cISeq, "load_from_binary", iseqw_s_load_from_binary, 1); | |
diff --git a/vm.c b/vm.c | |
index 86b0e6075f..83a5d70beb 100644 | |
--- a/vm.c | |
+++ b/vm.c | |
@@ -2138,6 +2138,25 @@ rb_iseq_eval(const rb_iseq_t *iseq) | |
return val; | |
} | |
+VALUE | |
+rb_iseq_eval_in_scope(const rb_iseq_t *iseq, VALUE scope) | |
+{ | |
+ rb_thread_t *th = GET_THREAD(); | |
+ rb_binding_t *bind = rb_check_typeddata(scope, &ruby_binding_data_type); | |
+ struct rb_block *base_block = &bind->block; | |
+ | |
+ if (iseq->body->local_table_size > 0) { | |
+ vm_bind_update_env(bind, vm_make_env_object(th, th->cfp)); | |
+ } | |
+#if 0 | |
+ iseq_set_parent_block(iseq, base_block); | |
+ iseq_set_local_table(iseq, rb_vm_cref()->nd_tbl); | |
+#endif | |
+ vm_set_eval_stack(th, iseq, 0, base_block); | |
+ | |
+ return vm_exec(th); | |
+} | |
+ | |
VALUE | |
rb_iseq_eval_main(const rb_iseq_t *iseq) | |
{ | |
diff --git a/vm_core.h b/vm_core.h | |
index 0120f21831..443aee895c 100644 | |
--- a/vm_core.h | |
+++ b/vm_core.h | |
@@ -1616,6 +1616,7 @@ NORETURN(void rb_bug_context(const void *, const char *fmt, ...)); | |
/* functions about thread/vm execution */ | |
RUBY_SYMBOL_EXPORT_BEGIN | |
VALUE rb_iseq_eval(const rb_iseq_t *iseq); | |
+VALUE rb_iseq_eval_in_scope(const rb_iseq_t *iseq, VALUE scope); | |
VALUE rb_iseq_eval_main(const rb_iseq_t *iseq); | |
VALUE rb_iseq_path(const rb_iseq_t *iseq); | |
VALUE rb_iseq_realpath(const rb_iseq_t *iseq); | |
-- | |
2.21.0 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From d35112bf47119fdd920b527c8c5667add6b4cf86 Mon Sep 17 00:00:00 2001 | |
From: Dale Hamel <dale.hamel@srvthe.net> | |
Date: Mon, 22 Jul 2019 23:05:46 -0400 | |
Subject: [PATCH 2/2] Update iseq.eval to accept optional binding [Feature | |
#12093] | |
This implements a solution to [Feature #12093], and builts upon Nobu's | |
patch from 2016 that started this support. | |
This makes the RubyVM::InstructionSequence eval API and Kernel.eval | |
be more consistent: | |
* Using Kernel.eval, you can specify source code and a binding | |
to evaluate it within | |
* Using RubyVM::InstructionSequence, you can compile source code to an | |
instruction sequence | |
* You can execute source code within a binding, but you cannot execute | |
compiled instruction sequences against a binding, so the features of | |
Kernel.eval differ from iseq.eval | |
To fix this, the signature for iseq.eval is changed to allow for 0-1 | |
arguments to be passed. I see this as keeping the same signature as | |
Kernel.eval, but just dropping the first parameter as the target to | |
evaluate is implicit as self. This allows preserving the original | |
functionality and performance characteristics of evaluating an instruction | |
sequence, while also matching the functionality of Kernel.eval for evaluating | |
compiled ruby code in the context of a specified binding. | |
RubyVM::InstructionSequence#eval | |
* iseq.c (iseqw_eval): RubyVM::InstructionSequence#eval. | |
[Feature #12093], updated from iseq_eval_with to be | |
consistent with Kernel.eval | |
* vm.c (rb_iseq_eval_in_scope): evaluate an iseq in the given | |
scope, updated for Ruby 2.7 rebase | |
* test/ruby/test_iseq.rb: Added tests for evaluating an iseq | |
within the context of a binding | |
--- | |
iseq.c | 36 +++++++++++++++++------------------- | |
test/ruby/test_iseq.rb | 7 +++++++ | |
vm.c | 17 +++++++---------- | |
3 files changed, 31 insertions(+), 29 deletions(-) | |
diff --git a/iseq.c b/iseq.c | |
index 62cc562a13..40fb09e888 100644 | |
--- a/iseq.c | |
+++ b/iseq.c | |
@@ -1332,34 +1332,33 @@ rb_iseqw_to_iseq(VALUE iseqw) | |
/* | |
* call-seq: | |
- * iseq.eval -> obj | |
+ * iseq.eval([binding]) -> obj | |
* | |
* Evaluates the instruction sequence and returns the result. | |
* | |
* RubyVM::InstructionSequence.compile("1 + 2").eval #=> 3 | |
- */ | |
-static VALUE | |
-iseqw_eval(VALUE self) | |
-{ | |
- rb_secure(1); | |
- return rb_iseq_eval(iseqw_check(self)); | |
-} | |
- | |
-/* | |
- * call-seq: | |
- * iseq.eval_with(binding) -> obj | |
* | |
- * Evaluates the instruction sequence and returns the result. | |
+ * If <em>binding</em> is given, which must be a Binding object, the | |
+ * evaluation is performed in its context. | |
* | |
* obj = Struct.new(:a, :b).new(1, 2) | |
* bind = obj.instance_eval {binding} | |
- * RubyVM::InstructionSequence.compile("a + b").eval_with(bind) #=> 3 | |
+ * RubyVM::InstructionSequence.compile("a + b").eval(bind) #=> 3 | |
*/ | |
static VALUE | |
-iseq_eval_with(VALUE self, VALUE scope) | |
+iseqw_eval(int argc, const VALUE *argv, VALUE self) | |
{ | |
- rb_secure(1); | |
- return rb_iseq_eval_in_scope(iseqw_check(self), scope); | |
+ VALUE scope; | |
+ | |
+ if (argc == 0) { | |
+ rb_secure(1); | |
+ return rb_iseq_eval(iseqw_check(self)); | |
+ } | |
+ else { | |
+ rb_scan_args(argc, argv, "01", &scope); | |
+ rb_secure(1); | |
+ return rb_iseq_eval_in_scope(iseqw_check(self), scope); | |
+ } | |
} | |
/* | |
@@ -3499,8 +3498,7 @@ Init_ISeq(void) | |
rb_define_method(rb_cISeq, "disasm", iseqw_disasm, 0); | |
rb_define_method(rb_cISeq, "disassemble", iseqw_disasm, 0); | |
rb_define_method(rb_cISeq, "to_a", iseqw_to_a, 0); | |
- rb_define_method(rb_cISeq, "eval", iseqw_eval, 0); | |
- rb_define_method(rb_cISeq, "eval_with", iseq_eval_with, 1); | |
+ rb_define_method(rb_cISeq, "eval", iseqw_eval, -1); | |
rb_define_method(rb_cISeq, "to_binary", iseqw_to_binary, -1); | |
rb_define_singleton_method(rb_cISeq, "load_from_binary", iseqw_s_load_from_binary, 1); | |
diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb | |
index b4d44e5f28..aedf935e84 100644 | |
--- a/test/ruby/test_iseq.rb | |
+++ b/test/ruby/test_iseq.rb | |
@@ -281,6 +281,13 @@ def translate | |
end; | |
end | |
+ def test_eval_with_binding | |
+ obj = Struct.new(:a, :b).new(1, 2) | |
+ bind = obj.instance_eval {binding} | |
+ val = RubyVM::InstructionSequence.compile("a + b").eval(bind) | |
+ assert_equal(3, val) | |
+ end | |
+ | |
def test_inspect | |
%W[foo \u{30d1 30b9}].each do |name| | |
assert_match(/@#{name}/, ISeq.compile("", name).inspect, name) | |
diff --git a/vm.c b/vm.c | |
index 83a5d70beb..e96a272dcc 100644 | |
--- a/vm.c | |
+++ b/vm.c | |
@@ -2141,20 +2141,17 @@ rb_iseq_eval(const rb_iseq_t *iseq) | |
VALUE | |
rb_iseq_eval_in_scope(const rb_iseq_t *iseq, VALUE scope) | |
{ | |
- rb_thread_t *th = GET_THREAD(); | |
- rb_binding_t *bind = rb_check_typeddata(scope, &ruby_binding_data_type); | |
- struct rb_block *base_block = &bind->block; | |
+ rb_execution_context_t *ec = GET_EC(); | |
+ rb_binding_t *bind = Check_TypedStruct(scope, &ruby_binding_data_type); | |
+ | |
+ vm_set_eval_stack(ec, iseq, NULL, &bind->block); | |
+ /* save new env */ | |
if (iseq->body->local_table_size > 0) { | |
- vm_bind_update_env(bind, vm_make_env_object(th, th->cfp)); | |
+ vm_bind_update_env(scope, bind, vm_make_env_object(ec, ec->cfp)); | |
} | |
-#if 0 | |
- iseq_set_parent_block(iseq, base_block); | |
- iseq_set_local_table(iseq, rb_vm_cref()->nd_tbl); | |
-#endif | |
- vm_set_eval_stack(th, iseq, 0, base_block); | |
- return vm_exec(th); | |
+ return vm_exec(ec, TRUE); | |
} | |
VALUE | |
-- | |
2.21.0 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment