1
0
forked from 0ad/0ad

Fix script serializer to cope with spliced array enumeration quirk

This was SVN commit r7637.
This commit is contained in:
Ykkrosh 2010-06-27 11:57:00 +00:00
parent 97a1b7dc2d
commit 1edb23e7a4
2 changed files with 27 additions and 16 deletions

View File

@ -102,6 +102,11 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
// JS_Enumerate is a bit slow (lots of memory allocation), so do the enumeration manually
// (based on the code from JS_Enumerate, using the probably less stable JSObject API):
//
// Correction: Actually array_enumerate returns an incorrect num_properties so we can't
// trust it and have to iterate over all ids before we know how many there are, so
// actually this probably isn't any faster than JS_Enumerate. Also it's less compatible
// with future JSAPI updates. This probably wasn't a great idea.
// (Note that we don't do any rooting, because we assume nothing is going to trigger GC.
// I'm not absolute certain that's necessarily a valid assumption.)
@ -109,24 +114,29 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
jsval iter_state, num_properties;
if (!obj->enumerate(cx, JSENUMERATE_INIT, &iter_state, &num_properties))
throw PSERROR_Serialize_ScriptError("enumerate INIT failed");
debug_assert(JSVAL_IS_INT(num_properties));
debug_assert(JSVAL_TO_INT(num_properties) >= 0);
size_t n = (size_t)JSVAL_TO_INT(num_properties);
// Note: num_properties might be 0 if the object doesn't know in advance how many to enumerate;
// we can't distinguish that from really having 0 properties without performing the actual iteration,
// so just assume the object always returns the correct count
std::vector<jsid> ids;
ids.reserve(JSVAL_TO_INT(num_properties));
m_Serializer.NumberU32_Unbounded("num props", (uint32_t)n);
jsid id;
for (size_t i = 0; i < n; ++i)
while (true)
{
jsval id;
if (!obj->enumerate(cx, JSENUMERATE_NEXT, &iter_state, &id))
throw PSERROR_Serialize_ScriptError("enumerate NEXT failed");
if (JSVAL_IS_NULL(iter_state))
throw PSERROR_Serialize_ScriptError("enumerate NEXT gave unexpected null");
break;
ids.push_back(id);
}
m_Serializer.NumberU32_Unbounded("num props", (uint32_t)ids.size());
for (size_t i = 0; i < ids.size(); ++i)
{
jsval id = ids[i];
jsval idval, propval;
@ -147,12 +157,6 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
HandleScriptVal(propval);
}
// Check we really reached the end of the iteration
if (!obj->enumerate(cx, JSENUMERATE_NEXT, &iter_state, &id))
throw PSERROR_Serialize_ScriptError("enumerate NEXT failed");
if (!JSVAL_IS_NULL(iter_state))
throw PSERROR_Serialize_ScriptError("enumerate NEXT didn't give unexpected null");
break;
}
case JSTYPE_FUNCTION:

View File

@ -402,6 +402,13 @@ public:
TS_ASSERT_THROWS(serialize.ScriptVal("script", obj), PSERROR_Serialize_InvalidScriptValue);
}
void test_script_splice()
{
helper_script_roundtrip("splice 1", "var a=[10,20]; a.splice(0, 1); a", "[20]");
helper_script_roundtrip("splice 1", "var a=[10,20]; a.splice(0, 2); a", "[]");
helper_script_roundtrip("splice 1", "var a=[10,20]; a.splice(0, 0, 5); a", "[5, 10, 20]");
}
// TODO: test deserializing invalid streams
// TODO: test non-tree script structures