99using namespace v8 ;
1010using namespace node ;
1111
12+ static Persistent<String> msgpack_tag_symbol;
13+
14+ #define CHECK_CIRCULAR_REFS (o, t ) \
15+ do { \
16+ Local<Value> ov = (o)->GetHiddenValue (msgpack_tag_symbol); \
17+ if (!ov.IsEmpty () && ov->Equals (t)) { \
18+ return false ; \
19+ } \
20+ (o)->SetHiddenValue (msgpack_tag_symbol, (t)); \
21+ } while (0 )
22+
1223// Convert a V8 object to a MessagePack object.
1324//
1425// This method is recursive. It will probably blow out the stack on objects
1526// with extremely deep nesting.
16- static void
17- v8_to_msgpack (Handle<Value> v8obj, msgpack_object *mo, msgpack_zone *mz) {
27+ //
28+ // This method can detect circular references in provided objects. It utilizes
29+ // the v8::Object::SetHiddenValue() facility to tag each encountered object
30+ // with a value unique to this serialization run. We leave the tags attached to
31+ // each object for ease-of-implementation (otherwise we'd have to track each
32+ // tagged v8::Object instance and un-tag at the end). This decision can be
33+ // revisited in the future if this proves problematic. I'd expect most objects
34+ // being packed to be short-lived anyway.
35+ //
36+ // Returns false if we detected a circular reference; true otherwise.
37+ static bool
38+ v8_to_msgpack (Handle<Value> v8obj, msgpack_object *mo, msgpack_zone *mz, Handle<Value> tag) {
1839 if (v8obj->IsUndefined () || v8obj->IsNull ()) {
1940 mo->type = MSGPACK_OBJECT_NIL ;
2041 } else if (v8obj->IsBoolean ()) {
@@ -39,7 +60,10 @@ v8_to_msgpack(Handle<Value> v8obj, msgpack_object *mo, msgpack_zone *mz) {
3960
4061 DecodeWrite ((char *) mo->via .raw .ptr , mo->via .raw .size , v8obj, UTF8 );
4162 } else if (v8obj->IsArray ()) {
42- Local<Array> a = Local<Array>::Cast (v8obj->ToObject ());
63+ Local<Object> o = v8obj->ToObject ();
64+ Local<Array> a = Local<Array>::Cast (o);
65+
66+ CHECK_CIRCULAR_REFS (o, tag);
4367
4468 mo->type = MSGPACK_OBJECT_ARRAY ;
4569 mo->via .array .size = a->Length ();
@@ -50,12 +74,16 @@ v8_to_msgpack(Handle<Value> v8obj, msgpack_object *mo, msgpack_zone *mz) {
5074
5175 for (int i = 0 ; i < a->Length (); i++) {
5276 Local<Value> v = a->Get (i);
53- v8_to_msgpack (v, &mo->via .array .ptr [i], mz);
77+ if (!v8_to_msgpack (v, &mo->via .array .ptr [i], mz, tag)) {
78+ return false ;
79+ }
5480 }
5581 } else {
5682 Local<Object> o = v8obj->ToObject ();
5783 Local<Array> a = o->GetPropertyNames ();
5884
85+ CHECK_CIRCULAR_REFS (o, tag);
86+
5987 mo->type = MSGPACK_OBJECT_MAP ;
6088 mo->via .map .size = a->Length ();
6189 mo->via .map .ptr = (msgpack_object_kv*) msgpack_zone_malloc (
@@ -66,10 +94,17 @@ v8_to_msgpack(Handle<Value> v8obj, msgpack_object *mo, msgpack_zone *mz) {
6694 for (int i = 0 ; i < a->Length (); i++) {
6795 Local<Value> k = a->Get (i);
6896
69- v8_to_msgpack (k, &mo->via .map .ptr [i].key , mz);
70- v8_to_msgpack (o->Get (k), &mo->via .map .ptr [i].val , mz);
97+ if (!v8_to_msgpack (k, &mo->via .map .ptr [i].key , mz, tag)) {
98+ return false ;
99+ }
100+
101+ if (!v8_to_msgpack (o->Get (k), &mo->via .map .ptr [i].val , mz, tag)) {
102+ return false ;
103+ }
71104 }
72105 }
106+
107+ return true ;
73108}
74109
75110// Convert a MessagePack object to a V8 object.
@@ -138,11 +173,14 @@ msgpack_to_v8(msgpack_object *mo) {
138173// serialized to the same bytestream, back-ty-back.
139174static Handle<Value>
140175pack (const Arguments &args) {
176+ static int64_t tag_i = 0 ;
177+
141178 HandleScope scope;
142179
143180 msgpack_packer pk;
144181 msgpack_sbuffer sbuf;
145182 msgpack_zone mz;
183+ Local<Value> tag = Integer::New (++tag_i);
146184
147185 msgpack_sbuffer_init (&sbuf);
148186 msgpack_packer_init (&pk, &sbuf, msgpack_sbuffer_write);
@@ -152,7 +190,12 @@ pack(const Arguments &args) {
152190 for (int i = 0 ; i < args.Length (); i++) {
153191 msgpack_object mo;
154192
155- v8_to_msgpack (args[0 ], &mo, &mz);
193+ if (!v8_to_msgpack (args[0 ], &mo, &mz, tag)) {
194+ return ThrowException (Exception::TypeError (String::New (
195+ " Cowardly refusing to pack object with circular reference"
196+ )));
197+ }
198+
156199 if (msgpack_pack_object (&pk, mo)) {
157200 return ThrowException (Exception::Error (
158201 String::New (" Error serializaing object" )));
@@ -211,6 +254,8 @@ init(Handle<Object> target) {
211254
212255 NODE_SET_METHOD (target, " pack" , pack);
213256 NODE_SET_METHOD (target, " unpack" , unpack);
257+
258+ msgpack_tag_symbol = NODE_PSYMBOL (" msgpack::tag" );
214259}
215260
216261// vim:ts=4 sw=4 et
0 commit comments