1247 | | class ValueCloner |
1248 | | { |
1249 | | public: |
1250 | | ValueCloner(ScriptInterface& from, ScriptInterface& to) : |
1251 | | scriptInterfaceFrom(from), cxFrom(from.GetContext()), cxTo(to.GetContext()), m_RooterFrom(from), m_RooterTo(to) |
1252 | | { |
1253 | | } |
1254 | | |
1255 | | // Return the cloned object (or an already-computed object if we've cloned val before) |
1256 | | jsval GetOrClone(jsval val) |
1257 | | { |
1258 | | if (!JSVAL_IS_GCTHING(val) || JSVAL_IS_NULL(val)) |
1259 | | return val; |
1260 | | |
1261 | | std::map<void*, jsval>::iterator it = m_Mapping.find(JSVAL_TO_GCTHING(val)); |
1262 | | if (it != m_Mapping.end()) |
1263 | | return it->second; |
1264 | | |
1265 | | m_RooterFrom.Push(val); // root it so our mapping doesn't get invalidated |
1266 | | |
1267 | | return Clone(val); |
1268 | | } |
1269 | | |
1270 | | private: |
1271 | | |
1272 | | #define CLONE_REQUIRE(expr, msg) if (!(expr)) { debug_warn(L"Internal error in CloneValueFromOtherContext: " msg); return JSVAL_VOID; } |
1273 | | |
1274 | | // Clone a new value (and root it and add it to the mapping) |
1275 | | jsval Clone(jsval val) |
1276 | | { |
1277 | | if (JSVAL_IS_DOUBLE(val)) |
1278 | | { |
1279 | | jsval rval; |
1280 | | CLONE_REQUIRE(JS_NewNumberValue(cxTo, JSVAL_TO_DOUBLE(val), &rval), L"JS_NewNumberValue"); |
1281 | | m_RooterTo.Push(rval); |
1282 | | return rval; |
1283 | | } |
1284 | | |
1285 | | if (JSVAL_IS_STRING(val)) |
1286 | | { |
1287 | | size_t len; |
1288 | | const jschar* chars = JS_GetStringCharsAndLength(cxFrom, JSVAL_TO_STRING(val), &len); |
1289 | | CLONE_REQUIRE(chars, L"JS_GetStringCharsAndLength"); |
1290 | | JSString* str = JS_NewUCStringCopyN(cxTo, chars, len); |
1291 | | CLONE_REQUIRE(str, L"JS_NewUCStringCopyN"); |
1292 | | jsval rval = STRING_TO_JSVAL(str); |
1293 | | m_Mapping[JSVAL_TO_GCTHING(val)] = rval; |
1294 | | m_RooterTo.Push(rval); |
1295 | | return rval; |
1296 | | } |
1297 | | |
1298 | | ENSURE(JSVAL_IS_OBJECT(val)); |
1299 | | |
1300 | | JSObject* newObj; |
1301 | | if (JS_IsArrayObject(cxFrom, JSVAL_TO_OBJECT(val))) |
1302 | | { |
1303 | | jsuint length; |
1304 | | CLONE_REQUIRE(JS_GetArrayLength(cxFrom, JSVAL_TO_OBJECT(val), &length), L"JS_GetArrayLength"); |
1305 | | newObj = JS_NewArrayObject(cxTo, length, NULL); |
1306 | | CLONE_REQUIRE(newObj, L"JS_NewArrayObject"); |
1307 | | } |
1308 | | else |
1309 | | { |
1310 | | newObj = JS_NewObject(cxTo, NULL, NULL, NULL); |
1311 | | CLONE_REQUIRE(newObj, L"JS_NewObject"); |
1312 | | } |
1313 | | |
1314 | | m_Mapping[JSVAL_TO_GCTHING(val)] = OBJECT_TO_JSVAL(newObj); |
1315 | | m_RooterTo.Push(newObj); |
1316 | | |
1317 | | AutoJSIdArray ida (cxFrom, JS_Enumerate(cxFrom, JSVAL_TO_OBJECT(val))); |
1318 | | CLONE_REQUIRE(ida.get(), L"JS_Enumerate"); |
1319 | | |
1320 | | AutoGCRooter idaRooter(scriptInterfaceFrom); |
1321 | | idaRooter.Push(ida.get()); |
1322 | | |
1323 | | for (size_t i = 0; i < ida.length(); ++i) |
1324 | | { |
1325 | | jsid id = ida[i]; |
1326 | | jsval idval, propval; |
1327 | | CLONE_REQUIRE(JS_IdToValue(cxFrom, id, &idval), L"JS_IdToValue"); |
1328 | | CLONE_REQUIRE(JS_GetPropertyById(cxFrom, JSVAL_TO_OBJECT(val), id, &propval), L"JS_GetPropertyById"); |
1329 | | jsval newPropval = GetOrClone(propval); |
1330 | | |
1331 | | if (JSVAL_IS_INT(idval)) |
1332 | | { |
1333 | | // int jsids are portable across runtimes |
1334 | | CLONE_REQUIRE(JS_SetPropertyById(cxTo, newObj, id, &newPropval), L"JS_SetPropertyById"); |
1335 | | } |
1336 | | else if (JSVAL_IS_STRING(idval)) |
1337 | | { |
1338 | | // string jsids are runtime-specific, so we need to copy the string content |
1339 | | JSString* idstr = JS_ValueToString(cxFrom, idval); |
1340 | | CLONE_REQUIRE(idstr, L"JS_ValueToString (id)"); |
1341 | | size_t len; |
1342 | | const jschar* chars = JS_GetStringCharsAndLength(cxFrom, idstr, &len); |
1343 | | CLONE_REQUIRE(idstr, L"JS_GetStringCharsAndLength (id)"); |
1344 | | CLONE_REQUIRE(JS_SetUCProperty(cxTo, newObj, chars, len, &newPropval), L"JS_SetUCProperty"); |
1345 | | } |
1346 | | else |
1347 | | { |
1348 | | // this apparently could be an XML object; ignore it |
1349 | | } |
1350 | | } |
1351 | | |
1352 | | return OBJECT_TO_JSVAL(newObj); |
1353 | | } |
1354 | | |
1355 | | ScriptInterface& scriptInterfaceFrom; |
1356 | | JSContext* cxFrom; |
1357 | | JSContext* cxTo; |
1358 | | std::map<void*, jsval> m_Mapping; |
1359 | | AutoGCRooter m_RooterFrom; |
1360 | | AutoGCRooter m_RooterTo; |
1361 | | }; |
1362 | | |