@@ -65,21 +65,45 @@ njs_object_t *
6565njs_object_value_copy (njs_vm_t * vm , njs_value_t * value )
6666{
6767 size_t size ;
68- njs_object_t * object ;
68+ njs_object_t * object , * proto ;
6969
7070 object = njs_object (value );
7171
7272 if (!object -> shared ) {
7373 return object ;
7474 }
7575
76- size = njs_is_object_value (value ) ? sizeof (njs_object_value_t )
77- : sizeof (njs_object_t );
76+ switch (object -> type ) {
77+ case NJS_OBJECT :
78+ size = sizeof (njs_object_t );
79+ proto = (object -> __proto__ != NULL )
80+ ? njs_vm_proto (vm , NJS_OBJ_TYPE_OBJECT )
81+ : NULL ;
82+ break ;
83+ case NJS_ARRAY :
84+ size = sizeof (njs_array_t );
85+ njs_assert_msg (!object -> fast_array ,
86+ "shared fast_array is not supported" );
87+ proto = (object -> __proto__ != NULL )
88+ ? njs_vm_proto (vm , NJS_OBJ_TYPE_ARRAY )
89+ : NULL ;
90+ break ;
91+ case NJS_OBJECT_VALUE :
92+ size = sizeof (njs_object_value_t );
93+ proto = (object -> __proto__ != NULL )
94+ ? njs_vm_proto (vm , NJS_OBJ_TYPE_OBJECT )
95+ : NULL ;
96+ break ;
97+ default :
98+ njs_internal_error (vm , "unexpected object type to copy" );
99+ return NULL ;
100+ }
101+
78102 object = njs_mp_alloc (vm -> mem_pool , size );
79103
80104 if (njs_fast_path (object != NULL )) {
81105 memcpy (object , njs_object (value ), size );
82- object -> __proto__ = njs_vm_proto ( vm , NJS_OBJ_TYPE_OBJECT ) ;
106+ object -> __proto__ = proto ;
83107 object -> shared = 0 ;
84108 value -> data .u .object = object ;
85109 return object ;
@@ -917,6 +941,10 @@ njs_get_own_ordered_keys(njs_vm_t *vm, const njs_object_t *object,
917941 njs_lvlhsh_each_init (& lhe , & njs_object_hash_proto );
918942 hash = & object -> shared_hash ;
919943
944+ if (flags & NJS_ENUM_NON_SHARED_ONLY ) {
945+ goto local_hash ;
946+ }
947+
920948 for ( ;; ) {
921949 prop = njs_lvlhsh_each (hash , & lhe );
922950
@@ -984,6 +1012,8 @@ njs_get_own_ordered_keys(njs_vm_t *vm, const njs_object_t *object,
9841012 }
9851013 }
9861014
1015+ local_hash :
1016+
9871017 njs_lvlhsh_each_init (& lhe , & njs_object_hash_proto );
9881018 hash = & object -> hash ;
9891019
@@ -1187,6 +1217,189 @@ njs_traverse_visited(njs_arr_t *list, const njs_value_t *value)
11871217}
11881218
11891219
1220+ static njs_int_t
1221+ njs_object_copy_shared_hash (njs_vm_t * vm , njs_object_t * object )
1222+ {
1223+ njs_int_t ret ;
1224+ njs_flathsh_t new_hash , * shared_hash ;
1225+ njs_object_prop_t * prop ;
1226+ njs_flathsh_each_t fhe ;
1227+ njs_flathsh_query_t fhq ;
1228+
1229+ fhq .replace = 0 ;
1230+ fhq .proto = & njs_object_hash_proto ;
1231+ fhq .pool = vm -> mem_pool ;
1232+
1233+ njs_flathsh_init (& new_hash );
1234+ shared_hash = & object -> shared_hash ;
1235+
1236+ njs_flathsh_each_init (& fhe , & njs_object_hash_proto );
1237+
1238+ for ( ;; ) {
1239+ prop = njs_flathsh_each (shared_hash , & fhe );
1240+
1241+ if (prop == NULL ) {
1242+ break ;
1243+ }
1244+
1245+ if (njs_is_symbol (& prop -> name )) {
1246+ fhq .key_hash = njs_symbol_key (& prop -> name );
1247+ fhq .key .start = NULL ;
1248+
1249+ } else {
1250+ njs_string_get (& prop -> name , & fhq .key );
1251+ fhq .key_hash = njs_djb_hash (fhq .key .start , fhq .key .length );
1252+ }
1253+
1254+ fhq .value = prop ;
1255+
1256+ ret = njs_flathsh_insert (& new_hash , & fhq );
1257+ if (njs_slow_path (ret != NJS_OK )) {
1258+ njs_internal_error (vm , "flathsh insert failed" );
1259+ return NJS_ERROR ;
1260+ }
1261+ }
1262+
1263+ object -> shared_hash = new_hash ;
1264+
1265+ return NJS_OK ;
1266+ }
1267+
1268+
1269+ njs_int_t
1270+ njs_object_make_shared (njs_vm_t * vm , njs_object_t * object )
1271+ {
1272+ njs_int_t ret ;
1273+ njs_arr_t visited ;
1274+ njs_object_t * * start ;
1275+ njs_value_t value , * key ;
1276+ njs_traverse_t * s ;
1277+ njs_object_prop_t * prop ;
1278+ njs_property_query_t pq ;
1279+ njs_traverse_t state [NJS_TRAVERSE_MAX_DEPTH ];
1280+
1281+ s = & state [0 ];
1282+ s -> parent = NULL ;
1283+ s -> index = 0 ;
1284+ njs_set_object (& s -> value , object );
1285+
1286+ s -> keys = njs_value_own_enumerate (vm , & s -> value , NJS_ENUM_KEYS
1287+ | NJS_ENUM_STRING
1288+ | NJS_ENUM_NON_SHARED_ONLY );
1289+ if (njs_slow_path (s -> keys == NULL )) {
1290+ return NJS_ERROR ;
1291+ }
1292+
1293+ if (s -> keys -> length != 0
1294+ && !njs_flathsh_is_empty (& object -> shared_hash ))
1295+ {
1296+ /*
1297+ * object->shared_hash can be shared with other objects
1298+ * and we do not want to modify other objects.
1299+ */
1300+
1301+ ret = njs_object_copy_shared_hash (vm , object );
1302+ if (njs_slow_path (ret != NJS_OK )) {
1303+ return NJS_ERROR ;
1304+ }
1305+ }
1306+
1307+ start = njs_arr_init (vm -> mem_pool , & visited , NULL , 8 , sizeof (void * ));
1308+ if (njs_slow_path (start == NULL )) {
1309+ return NJS_ERROR ;
1310+ }
1311+
1312+ (void ) njs_traverse_visit (& visited , & s -> value );
1313+
1314+ pq .lhq .replace = 0 ;
1315+ pq .lhq .pool = vm -> mem_pool ;
1316+
1317+ for ( ;; ) {
1318+
1319+ if (s -> index >= s -> keys -> length ) {
1320+ njs_flathsh_init (& njs_object (& s -> value )-> hash );
1321+ njs_object (& s -> value )-> shared = 1 ;
1322+ njs_array_destroy (vm , s -> keys );
1323+ s -> keys = NULL ;
1324+
1325+ if (s == & state [0 ]) {
1326+ goto done ;
1327+ }
1328+
1329+ s -- ;
1330+ continue ;
1331+ }
1332+
1333+
1334+ njs_property_query_init (& pq , NJS_PROPERTY_QUERY_GET , 0 , 0 );
1335+ key = & s -> keys -> start [s -> index ++ ];
1336+
1337+ ret = njs_property_query (vm , & pq , & s -> value , key );
1338+ if (njs_slow_path (ret != NJS_OK )) {
1339+ if (ret == NJS_DECLINED ) {
1340+ continue ;
1341+ }
1342+
1343+ return NJS_ERROR ;
1344+ }
1345+
1346+
1347+ prop = pq .lhq .value ;
1348+
1349+ ret = njs_flathsh_insert (& njs_object (& s -> value )-> shared_hash , & pq .lhq );
1350+ if (njs_slow_path (ret != NJS_OK )) {
1351+ njs_internal_error (vm , "flathsh insert failed" );
1352+ return NJS_ERROR ;
1353+ }
1354+
1355+ njs_value_assign (& value , njs_prop_value (prop ));
1356+
1357+ if (njs_is_object (& value )
1358+ && !njs_object (& value )-> shared
1359+ && !njs_traverse_visited (& visited , & value ))
1360+ {
1361+ ret = njs_traverse_visit (& visited , & value );
1362+ if (njs_slow_path (ret != NJS_OK )) {
1363+ return NJS_ERROR ;
1364+ }
1365+
1366+ if (s == & state [NJS_TRAVERSE_MAX_DEPTH - 1 ]) {
1367+ njs_type_error (vm , "njs_object_traverse() recursion limit:%d" ,
1368+ NJS_TRAVERSE_MAX_DEPTH );
1369+ return NJS_ERROR ;
1370+ }
1371+
1372+ s ++ ;
1373+ s -> prop = NULL ;
1374+ s -> parent = & s [-1 ];
1375+ s -> index = 0 ;
1376+ njs_value_assign (& s -> value , & value );
1377+ s -> keys = njs_value_own_enumerate (vm , & s -> value , NJS_ENUM_KEYS
1378+ | NJS_ENUM_STRING
1379+ | NJS_ENUM_NON_SHARED_ONLY );
1380+ if (njs_slow_path (s -> keys == NULL )) {
1381+ return NJS_ERROR ;
1382+ }
1383+
1384+ if (s -> keys -> length != 0
1385+ && !njs_flathsh_is_empty (& njs_object (& s -> value )-> shared_hash ))
1386+ {
1387+ ret = njs_object_copy_shared_hash (vm , njs_object (& s -> value ));
1388+ if (njs_slow_path (ret != NJS_OK )) {
1389+ return NJS_ERROR ;
1390+ }
1391+ }
1392+ }
1393+ }
1394+
1395+ done :
1396+
1397+ njs_arr_destroy (& visited );
1398+
1399+ return NJS_OK ;
1400+ }
1401+
1402+
11901403njs_int_t
11911404njs_object_traverse (njs_vm_t * vm , njs_object_t * object , void * ctx ,
11921405 njs_object_traverse_cb_t cb )
0 commit comments