Skip to content
This repository was archived by the owner on Dec 4, 2023. It is now read-only.

Commit 02630d7

Browse files
committed
Allowing to quickly duplicates contexts
With a new `deep_clone` method. Comes in handy for caching base contexts then used to evalaute further scripts. With proper unit tests on it.
1 parent fbbce3d commit 02630d7

4 files changed

Lines changed: 143 additions & 10 deletions

File tree

ext/v8/context.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ void Context::Init() {
1010
defineSingletonMethod("GetCalling", &GetCalling).
1111
defineSingletonMethod("InContext", &InContext).
1212
defineMethod("Dispose", &Dispose).
13+
defineMethod("Clone", &Clone).
1314
defineMethod("Global", &Global).
1415
defineMethod("DetachGlobal", &Global).
1516
defineMethod("ReattachGlobal", &ReattachGlobal).
@@ -33,6 +34,53 @@ VALUE Context::Dispose(VALUE self) {
3334
Void(Context(self).dispose())
3435
}
3536

37+
VALUE Context::Clone(VALUE self, VALUE new_rr_context) {
38+
// acquire the lock
39+
v8::Locker locker;
40+
v8::HandleScope handle_scope;
41+
42+
// unwrap V8 contexts
43+
v8::Context* original_context = *((v8::Handle<v8::Context>) Context(self));
44+
v8::Context* new_context = *((v8::Handle<v8::Context>) Context(new_rr_context));
45+
46+
// get the original global context
47+
v8::Local<v8::Value> key, value;
48+
49+
original_context->Enter();
50+
51+
v8::Local<v8::Object> original_global(original_context->Global());
52+
v8::Local<v8::Array> property_names = original_global->GetPropertyNames();
53+
uint32_t i, length = property_names->Length();
54+
std::vector< v8::Local<v8::Value> > values;
55+
56+
for (i = 0; i < length; i++) {
57+
key = property_names->Get(i);
58+
value = original_global->Get(key);
59+
values.push_back(value);
60+
}
61+
62+
original_context->Exit();
63+
64+
// and copy everything to the new context
65+
new_context->Enter();
66+
67+
v8::Local<v8::Object> new_global(new_context->Global());
68+
std::vector< v8::Local<v8::Value> >::iterator it;
69+
70+
for (i = 0, it = values.begin(); i < length; i++, it++) {
71+
key = property_names->Get(i);
72+
value = *it;
73+
new_global->Set(key, value);
74+
}
75+
76+
new_context->Exit();
77+
78+
// unlock
79+
v8::Unlocker unlocker;
80+
81+
return Qnil;
82+
}
83+
3684
VALUE Context::Global(VALUE self) {
3785
return Object(Context(self)->Global());
3886
}

ext/v8/rr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ class Context : public Ref<v8::Context> {
306306
static void Init();
307307
static VALUE New(int argc, VALUE argv[], VALUE self);
308308
static VALUE Dispose(VALUE self);
309+
static VALUE Clone(VALUE self, VALUE new_context);
309310
static VALUE Enter(VALUE self);
310311
static VALUE Exit(VALUE self);
311312
static VALUE Global(VALUE self);

lib/v8/context.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,12 @@ class Context
6262
# * :with scope serves as the global scope of the new context
6363
# @yield [V8::Context] the newly created context
6464
def initialize(options = {})
65+
@options = options
66+
6567
@conversion = Conversion.new
6668
@access = Access.new
6769
@timeout = options[:timeout]
70+
6871
if global = options[:with]
6972
Context.new.enter do
7073
global_template = global.class.to_template.InstanceTemplate()
@@ -137,6 +140,14 @@ def self.enter
137140
end
138141
end
139142

143+
def deep_clone
144+
fail ArgumentError, 'cannot clone an empty context' unless @native
145+
146+
self.class.new(@options).tap do |new_context|
147+
native.Clone(new_context.native)
148+
end
149+
end
150+
140151
# Returns this context's global object. This will be a `V8::Object`
141152
# if no scope was provided or just an `Object` if a Ruby object
142153
# is serving as the global scope.

spec/v8/context_spec.rb

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,92 @@
11
require 'spec_helper'
22

33
describe V8::Context do
4-
it "can be disposed of" do
5-
cxt = V8::Context.new
6-
cxt.enter do
7-
cxt['object'] = V8::Object.new
4+
describe '.dispose' do
5+
it "can be disposed of" do
6+
cxt = V8::Context.new
7+
cxt.enter do
8+
cxt['object'] = V8::Object.new
9+
end
10+
cxt.dispose()
11+
12+
lambda {cxt.eval('1 + 1')}.should raise_error
13+
lambda {cxt['object']}.should raise_error
814
end
9-
cxt.dispose()
1015

11-
lambda {cxt.eval('1 + 1')}.should raise_error
12-
lambda {cxt['object']}.should raise_error
16+
it "can be disposed of any number of times" do
17+
cxt = V8::Context.new
18+
10.times {cxt.dispose()}
19+
end
1320
end
1421

15-
it "can be disposed of any number of times" do
16-
cxt = V8::Context.new
17-
10.times {cxt.dispose()}
22+
describe '.deep_clone' do
23+
let(:js) do
24+
<<-js
25+
if (typeof x === 'undefined') {
26+
x = 1;
27+
} else {
28+
x = x + 1;
29+
}
30+
js
31+
end
32+
33+
it 'duplicates the underlying native object' do
34+
cxt = V8::Context.new
35+
36+
cxt.eval(js)
37+
38+
cloned_cxt = cxt.deep_clone
39+
40+
cxt.eval(js)
41+
cxt["x"].should == 2
42+
43+
cloned_cxt["x"].should == 1
44+
cloned_cxt.eval(js)
45+
cloned_cxt["x"].should == 2
46+
47+
cxt.eval(js)
48+
cxt["x"].should == 3
49+
end
50+
51+
it 'works on empty contexts' do
52+
cxt = V8::Context.new
53+
54+
cloned_cxt = cxt.deep_clone
55+
56+
cxt.eval(js)
57+
cxt["x"].should == 1
58+
59+
cloned_cxt["x"].should be(nil)
60+
end
61+
62+
it 'keeps original options' do
63+
cxt = V8::Context.new(:timeout => 10)
64+
65+
cloned_cxt = cxt.deep_clone
66+
67+
cloned_cxt.timeout.should == 10
68+
end
69+
70+
context 'when the original context has been disposed of' do
71+
it 'should not allow cloning any more' do
72+
cxt = V8::Context.new
73+
74+
cxt.dispose
75+
76+
lambda { cxt.deep_clone }.should raise_error
77+
end
78+
79+
it 'should not affect previously cloned contexts' do
80+
cxt = V8::Context.new
81+
cxt.eval(js)
82+
83+
cloned_cxt = cxt.deep_clone
84+
85+
cxt.dispose
86+
87+
cloned_cxt.eval(js)
88+
cloned_cxt["x"].should == 2
89+
end
90+
end
1891
end
1992
end

0 commit comments

Comments
 (0)