Skip to content

Commit 02f7d7a

Browse files
authored
Fix object serialization (#340)
Use of ser_array_begin in two cases caused an off-by-one error in the object back-reference count that is used to stop an object from being encoded more than once (and thus maintains object identity, allowing recursive structures.)
1 parent a61c537 commit 02f7d7a

File tree

3 files changed

+26
-32
lines changed

3 files changed

+26
-32
lines changed

ext/mini_racer_extension/mini_racer_extension.c

+16-20
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,13 @@ static int serialize1(Ser *s, VALUE refs, VALUE v)
611611
return 0;
612612
}
613613

614+
// don't mix with ser_array_begin/ser_object_begin because
615+
// that will throw off the object reference count
616+
static int serialize(Ser *s, VALUE v)
617+
{
618+
return serialize1(s, rb_hash_new(), v);
619+
}
620+
614621
static struct timespec deadline_ms(int ms)
615622
{
616623
static const int64_t ns_per_sec = 1000*1000*1000;
@@ -859,18 +866,11 @@ static void *rendezvous_callback(void *arg)
859866
goto fail;
860867
}
861868
ser_init1(&s, 'c'); // callback reply
862-
ser_array_begin(&s, 2);
863-
// either [result, undefined] or [undefined, err]
864-
if (exc)
865-
ser_undefined(&s);
866-
if (serialize1(&s, rb_hash_new(), r)) { // should not happen
869+
if (serialize(&s, r)) { // should not happen
867870
c->exception = rb_exc_new_cstr(internal_error, s.err);
868871
ser_reset(&s);
869872
goto fail;
870873
}
871-
if (!exc)
872-
ser_undefined(&s);
873-
ser_array_end(&s, 2);
874874
out:
875875
buf_move(&s.b, a->req);
876876
return NULL;
@@ -1202,25 +1202,21 @@ static VALUE context_stop(VALUE self)
12021202

12031203
static VALUE context_call(int argc, VALUE *argv, VALUE self)
12041204
{
1205-
VALUE a, e, h;
1205+
VALUE name, args;
1206+
VALUE a, e;
12061207
Context *c;
1207-
int i;
12081208
Ser s;
12091209

12101210
TypedData_Get_Struct(self, Context, &context_type, c);
1211-
rb_scan_args(argc, argv, "1*", &a, &e);
1212-
Check_Type(a, T_STRING);
1211+
rb_scan_args(argc, argv, "1*", &name, &args);
1212+
Check_Type(name, T_STRING);
1213+
rb_ary_unshift(args, name);
12131214
// request is (C)all, [name, args...] array
12141215
ser_init1(&s, 'C');
1215-
ser_array_begin(&s, argc);
1216-
h = rb_hash_new();
1217-
for (i = 0; i < argc; i++) {
1218-
if (serialize1(&s, h, argv[i])) {
1219-
ser_reset(&s);
1220-
rb_raise(runtime_error, "Context.call: %s", s.err);
1221-
}
1216+
if (serialize(&s, args)) {
1217+
ser_reset(&s);
1218+
rb_raise(runtime_error, "Context.call: %s", s.err);
12221219
}
1223-
ser_array_end(&s, argc);
12241220
// response is [result, err] array
12251221
a = rendezvous(c, &s.b); // takes ownership of |s.b|
12261222
e = rb_ary_pop(a);

ext/mini_racer_extension/mini_racer_v8.cc

+1-12
Original file line numberDiff line numberDiff line change
@@ -378,18 +378,7 @@ void v8_api_callback(const v8::FunctionCallbackInfo<v8::Value>& info)
378378
des.ReadHeader(st.context).Check();
379379
v8::Local<v8::Value> result;
380380
if (!des.ReadValue(st.context).ToLocal(&result)) return; // exception pending
381-
v8::Local<v8::Object> response; // [result, err]
382-
if (!result->ToObject(st.context).ToLocal(&response)) return;
383-
v8::Local<v8::Value> err;
384-
if (!response->Get(st.context, 1).ToLocal(&err)) return;
385-
if (err->IsUndefined()) {
386-
if (!response->Get(st.context, 0).ToLocal(&result)) return;
387-
info.GetReturnValue().Set(result);
388-
} else {
389-
v8::Local<v8::String> message;
390-
if (!err->ToString(st.context).ToLocal(&message)) return;
391-
st.isolate->ThrowException(v8::Exception::Error(message));
392-
}
381+
info.GetReturnValue().Set(result);
393382
}
394383

395384
// response is err or empty string

test/mini_racer_test.rb

+9
Original file line numberDiff line numberDiff line change
@@ -1126,4 +1126,13 @@ def test_string_encoding
11261126
assert_equal Encoding::UTF_16LE, context.eval("'ok\\uD800'").encoding
11271127
end
11281128
end
1129+
1130+
def test_object_ref
1131+
context = MiniRacer::Context.new
1132+
context.eval("function f(o) { return o }")
1133+
expected = {}
1134+
expected["a"] = expected["b"] = {"x" => 42}
1135+
actual = context.call("f", expected)
1136+
assert_equal actual, expected
1137+
end
11291138
end

0 commit comments

Comments
 (0)