@@ -23,6 +23,9 @@ import { version } from './version';
2323 * 8. Respect `toJSON` and `fromJSON` method, if the object has a `toJSON` method, it will be called to
2424 * get the serialized value. If the object has a `fromJSON` method, it will be called with
2525 * serialized json to restore the original value.
26+ * 9. Make sure you trust the source of the serialized string, because the deserialization need to
27+ * evaluate script codes. A carefully crafted strings may embed malicious code, thus posing a
28+ * security threat.
2629 */
2730/**
2831 * Advantages:
@@ -597,8 +600,8 @@ function generateDeserializationCode(result: SerializedResult, options: Internal
597600 const symbolKeySuffixRegExp = new RegExp('${ escapeRegExp ( SymbolKeySuffixRegExp , { escapeTwice : isPrinting } ) } $');
598601
599602 // 1. Should be the first step.
600- // Restore to the original types.
601- restoreOriginalTypes(deserializeResult, types);
603+ // Restore to the original types, except the root object .
604+ restoreOriginalTypes(deserializeResult, types.filter((t) => t.path.length > 0) );
602605
603606 // 2. Should be before restoreSymbolKeys, because the symbol-strings may be broken to Symbols.
604607 // Restore patches
@@ -613,62 +616,89 @@ function generateDeserializationCode(result: SerializedResult, options: Internal
613616 // 4-2. Should be before restoreDescriptors, because related fields may be changed to readonly.
614617 // Restore references to solve circular dependencies
615618 restoreRefs(deserializeResult, refs);
616-
617- // 5. Should be the last step .
619+
620+ // 5. Should be after restoreRefs .
618621 // Restore custom property descriptors
619622 restoreDescriptors(deserializeResult, descriptors);
620623
624+ // 6. Should be the last step.
625+ // Restore the root object type.
626+ if (types.some((t) => t.path.length === 0)) {
627+ const rootResult = restoreOriginalTypes(deserializeResult, types.filter((t) => t.path.length === 0));
628+ const newRoot = rootResult.root;
629+ if (refs.some((t) => t.from.length === 0)) {
630+ // Remap the refs to the root
631+ const rootRefs = refs.filter((t) => t.from.length === 0);
632+ rootRefs.forEach(({ path, from }) => {
633+ const parent = getParent(deserializeResult, path);
634+ const keyName = getLastKey(path);
635+ if (parent != null && keyName) {
636+ parent[keyName] = newRoot;
637+ }
638+ });
639+ }
640+ return newRoot;
641+ }
642+
621643 function restoreOriginalTypes(root, types = []) {
644+ const returnResult = {};
622645 // Apply types to the deserialized object
623646 types.forEach(({ path, type, metadata }) => {
624- // todo: path = [] 时,下面的逻辑会有问题
625- // todo: 测试支持BigInt64Array的序列化
626647 // todo: 支持URL、URLSearchParams、支持Buffer
627- const keyName = getLastKey(path);
628- const parent = getParent(root, path);
629648 const value = get(root, path);
630- console.log('Restoring type:', type, 'at', path, 'with value:', value, parent[keyName] === value);
631- if (value && parent && parent[keyName] === value) {
632- if (type === 'Map' && typeof value === 'object') {
633- // Convert array to Map
634- const map = new Map();
635- Object.keys(value).forEach((key) => {
636- map.set(key, value[key]);
637- });
638- parent[keyName] = map;
639- }
640- else if (type === 'Set' && Array.isArray(value)) {
641- // Convert array to Set
642- const set = new Set(value);
643- parent[keyName] = set;
644- }
645- else if ([${ TypedArrays . map ( ( t ) => `'${ t . name } '` ) . join ( ', ' ) } ].includes(type) &&
646- typeof globalThis[type] === 'function' &&
647- Array.isArray(value)) {
648- console.log('Creating TypedArray:', type, value);
649- parent[keyName] = new globalThis[type](value);
650- }
651- else if (type === 'ArrayBuffer' && typeof ArrayBuffer === 'function' && typeof Uint8Array === 'function' && Array.isArray(value)) {
652- const buffer = new ArrayBuffer(value.length);
653- const view = new Uint8Array(buffer);
654- value.forEach((item, index) => {
655- view[index] = item;
656- });
657- parent[keyName] = buffer;
658- }
659- else if (type === 'DataView' && typeof DataView === 'function' && typeof ArrayBuffer === 'function' && typeof Uint8Array === 'function' && Array.isArray(value)) {
660- const buffer = new ArrayBuffer(value.length);
661- const view = new Uint8Array(buffer);
662- value.forEach((item, index) => {
663- view[index] = item;
664- });
665- parent[keyName] = new DataView(buffer);
649+ let newResult;
650+ if (type === 'Map' && typeof value === 'object') {
651+ // Convert array to Map
652+ const map = new Map();
653+ Object.keys(value).forEach((key) => {
654+ map.set(key, value[key]);
655+ });
656+ newResult = map;
657+ }
658+ else if (type === 'Set' && Array.isArray(value)) {
659+ // Convert array to Set
660+ const set = new Set(value);
661+ newResult = set;
662+ }
663+ else if ([${ TypedArrays . map ( ( t ) => `'${ t . name } '` ) . join ( ', ' ) } ].includes(type) &&
664+ typeof globalThis[type] === 'function' &&
665+ Array.isArray(value)) {
666+ newResult = new globalThis[type](value);
667+ }
668+ else if (type === 'ArrayBuffer' && typeof ArrayBuffer === 'function' && typeof Uint8Array === 'function' && Array.isArray(value)) {
669+ const buffer = new ArrayBuffer(value.length);
670+ const view = new Uint8Array(buffer);
671+ value.forEach((item, index) => {
672+ view[index] = item;
673+ });
674+ newResult = buffer;
675+ }
676+ else if (type === 'DataView' && typeof DataView === 'function' && typeof ArrayBuffer === 'function' && typeof Uint8Array === 'function' && Array.isArray(value)) {
677+ const buffer = new ArrayBuffer(value.length);
678+ const view = new Uint8Array(buffer);
679+ value.forEach((item, index) => {
680+ view[index] = item;
681+ });
682+ newResult = new DataView(buffer);
683+ }
684+ else if (type === 'Blob' && typeof Blob === 'function' && Array.isArray(value)) {
685+ newResult = new Blob(value, { type: metadata && metadata.type ? metadata.type : '' });
686+ }
687+
688+ if (newResult) {
689+ if (path.length === 0) {
690+ returnResult.root = newResult;
666691 }
667- else if (type === 'Blob' && typeof Blob === 'function' && Array.isArray(value)) {
668- parent[keyName] = new Blob(value, { type: metadata && metadata.type ? metadata.type : '' });
692+ else {
693+ const keyName = getLastKey(path);
694+ const parent = getParent(root, path);
695+ if (parent) {
696+ parent[keyName] = newResult;
697+ }
669698 }
670699 }
671700 });
701+ return returnResult;
672702 }
673703
674704 function restorePatches(root, patches = []) {
@@ -749,7 +779,10 @@ function generateDeserializationCode(result: SerializedResult, options: Internal
749779 }
750780
751781 function getParent(root, path) {
752- return path && path.length ? get(root, path.slice(0, -1)) : null;
782+ if (path.length <= 1) {
783+ return root;
784+ }
785+ return get(root, path.slice(0, -1));
753786 }
754787
755788 function isSymbolFieldName(key) {
@@ -834,7 +867,14 @@ export function pickPrototype(
834867 const target : Record < string | symbol , any > = Object . create ( null ) ;
835868 const ignoredKeys = [ preserveClassConstructor ? undefined : 'constructor' ] . filter ( Boolean ) as ( string | symbol ) [ ] ;
836869 let proto = Object . getPrototypeOf ( source ) ;
837- while ( proto != null && proto !== Object . prototype && proto !== Array . prototype ) {
870+ while (
871+ proto != null &&
872+ proto !== Object . prototype &&
873+ proto !== Array . prototype &&
874+ proto !== Function . prototype &&
875+ proto !== Map . prototype &&
876+ proto !== Set . prototype
877+ ) {
838878 const protoKeys = getFullKeys ( proto ) ;
839879 for ( const key of protoKeys ) {
840880 if ( ! ( key in target ) && ! ignoredKeys . includes ( key ) ) {
0 commit comments