OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/value-serializer.h" | 5 #include "src/value-serializer.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "include/v8.h" | 10 #include "include/v8.h" |
(...skipping 689 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
700 DecodeTestForVersion0( | 700 DecodeTestForVersion0( |
701 {0x49, 0x54, 0x53, 0x01, 0x61, 0x53, 0x01, 0x61, 0x49, 0x54, 0x7b, 0x02}, | 701 {0x49, 0x54, 0x53, 0x01, 0x61, 0x53, 0x01, 0x61, 0x49, 0x54, 0x7b, 0x02}, |
702 [this](Local<Value> value) { | 702 [this](Local<Value> value) { |
703 EXPECT_TRUE(EvaluateScriptForResultBool( | 703 EXPECT_TRUE(EvaluateScriptForResultBool( |
704 "Object.getOwnPropertyNames(result).toString() === '42,a'")); | 704 "Object.getOwnPropertyNames(result).toString() === '42,a'")); |
705 EXPECT_TRUE(EvaluateScriptForResultBool("result[42] === 'a'")); | 705 EXPECT_TRUE(EvaluateScriptForResultBool("result[42] === 'a'")); |
706 EXPECT_TRUE(EvaluateScriptForResultBool("result.a === 42")); | 706 EXPECT_TRUE(EvaluateScriptForResultBool("result.a === 42")); |
707 }); | 707 }); |
708 } | 708 } |
709 | 709 |
710 TEST_F(ValueSerializerTest, RoundTripArray) { | |
711 // A simple array of integers. | |
712 RoundTripTest("[1, 2, 3, 4, 5]", [this](Local<Value> value) { | |
713 ASSERT_TRUE(value->IsArray()); | |
714 EXPECT_EQ(5, Array::Cast(*value)->Length()); | |
715 EXPECT_TRUE(EvaluateScriptForResultBool( | |
716 "Object.getPrototypeOf(result) === Array.prototype")); | |
717 EXPECT_TRUE( | |
718 EvaluateScriptForResultBool("result.toString() === '1,2,3,4,5'")); | |
719 }); | |
720 // A long (sparse) array. | |
721 RoundTripTest( | |
722 "(() => { var x = new Array(1000); x[500] = 42; return x; })()", | |
723 [this](Local<Value> value) { | |
724 ASSERT_TRUE(value->IsArray()); | |
725 EXPECT_EQ(1000, Array::Cast(*value)->Length()); | |
726 EXPECT_TRUE(EvaluateScriptForResultBool("result[500] === 42")); | |
727 }); | |
728 // Duplicate reference. | |
729 RoundTripTest( | |
730 "(() => { var y = {}; return [y, y]; })()", [this](Local<Value> value) { | |
731 ASSERT_TRUE(value->IsArray()); | |
732 ASSERT_EQ(2, Array::Cast(*value)->Length()); | |
733 EXPECT_TRUE(EvaluateScriptForResultBool("result[0] === result[1]")); | |
734 }); | |
735 // Duplicate reference in a sparse array. | |
736 RoundTripTest( | |
737 "(() => { var x = new Array(1000); x[1] = x[500] = {}; return x; })()", | |
738 [this](Local<Value> value) { | |
739 ASSERT_TRUE(value->IsArray()); | |
740 ASSERT_EQ(1000, Array::Cast(*value)->Length()); | |
741 EXPECT_TRUE( | |
742 EvaluateScriptForResultBool("typeof result[1] === 'object'")); | |
743 EXPECT_TRUE(EvaluateScriptForResultBool("result[1] === result[500]")); | |
744 }); | |
745 // Self reference. | |
746 RoundTripTest( | |
747 "(() => { var y = []; y[0] = y; return y; })()", | |
748 [this](Local<Value> value) { | |
749 ASSERT_TRUE(value->IsArray()); | |
750 ASSERT_EQ(1, Array::Cast(*value)->Length()); | |
751 EXPECT_TRUE(EvaluateScriptForResultBool("result[0] === result")); | |
752 }); | |
753 // Self reference in a sparse array. | |
754 RoundTripTest( | |
755 "(() => { var y = new Array(1000); y[519] = y; return y; })()", | |
756 [this](Local<Value> value) { | |
757 ASSERT_TRUE(value->IsArray()); | |
758 ASSERT_EQ(1000, Array::Cast(*value)->Length()); | |
759 EXPECT_TRUE(EvaluateScriptForResultBool("result[519] === result")); | |
760 }); | |
761 // Array with additional properties. | |
762 RoundTripTest( | |
763 "(() => { var y = [1, 2]; y.foo = 'bar'; return y; })()", | |
764 [this](Local<Value> value) { | |
765 ASSERT_TRUE(value->IsArray()); | |
766 ASSERT_EQ(2, Array::Cast(*value)->Length()); | |
767 EXPECT_TRUE(EvaluateScriptForResultBool("result.toString() === '1,2'")); | |
768 EXPECT_TRUE(EvaluateScriptForResultBool("result.foo === 'bar'")); | |
769 }); | |
770 // Sparse array with additional properties. | |
771 RoundTripTest( | |
772 "(() => { var y = new Array(1000); y.foo = 'bar'; return y; })()", | |
773 [this](Local<Value> value) { | |
774 ASSERT_TRUE(value->IsArray()); | |
775 ASSERT_EQ(1000, Array::Cast(*value)->Length()); | |
776 EXPECT_TRUE(EvaluateScriptForResultBool( | |
777 "result.toString() === ','.repeat(999)")); | |
778 EXPECT_TRUE(EvaluateScriptForResultBool("result.foo === 'bar'")); | |
779 }); | |
780 // The distinction between holes and undefined elements must be maintained. | |
781 RoundTripTest("[,undefined]", [this](Local<Value> value) { | |
782 ASSERT_TRUE(value->IsArray()); | |
783 ASSERT_EQ(2, Array::Cast(*value)->Length()); | |
784 EXPECT_TRUE( | |
785 EvaluateScriptForResultBool("typeof result[0] === 'undefined'")); | |
786 EXPECT_TRUE( | |
787 EvaluateScriptForResultBool("typeof result[1] === 'undefined'")); | |
788 EXPECT_TRUE(EvaluateScriptForResultBool("!result.hasOwnProperty(0)")); | |
789 EXPECT_TRUE(EvaluateScriptForResultBool("result.hasOwnProperty(1)")); | |
790 }); | |
791 } | |
792 | |
793 TEST_F(ValueSerializerTest, DecodeArray) { | |
794 // A simple array of integers. | |
795 DecodeTest({0xff, 0x09, 0x3f, 0x00, 0x41, 0x05, 0x3f, 0x01, 0x49, 0x02, | |
796 0x3f, 0x01, 0x49, 0x04, 0x3f, 0x01, 0x49, 0x06, 0x3f, 0x01, | |
797 0x49, 0x08, 0x3f, 0x01, 0x49, 0x0a, 0x24, 0x00, 0x05, 0x00}, | |
798 [this](Local<Value> value) { | |
799 ASSERT_TRUE(value->IsArray()); | |
800 EXPECT_EQ(5, Array::Cast(*value)->Length()); | |
801 EXPECT_TRUE(EvaluateScriptForResultBool( | |
802 "Object.getPrototypeOf(result) === Array.prototype")); | |
803 EXPECT_TRUE(EvaluateScriptForResultBool( | |
804 "result.toString() === '1,2,3,4,5'")); | |
805 }); | |
806 // A long (sparse) array. | |
807 DecodeTest({0xff, 0x09, 0x3f, 0x00, 0x61, 0xe8, 0x07, 0x3f, 0x01, 0x49, | |
808 0xe8, 0x07, 0x3f, 0x01, 0x49, 0x54, 0x40, 0x01, 0xe8, 0x07}, | |
809 [this](Local<Value> value) { | |
810 ASSERT_TRUE(value->IsArray()); | |
811 EXPECT_EQ(1000, Array::Cast(*value)->Length()); | |
812 EXPECT_TRUE(EvaluateScriptForResultBool("result[500] === 42")); | |
813 }); | |
814 // Duplicate reference. | |
815 DecodeTest( | |
816 {0xff, 0x09, 0x3f, 0x00, 0x41, 0x02, 0x3f, 0x01, 0x6f, 0x7b, 0x00, 0x3f, | |
817 0x02, 0x5e, 0x01, 0x24, 0x00, 0x02}, | |
818 [this](Local<Value> value) { | |
819 ASSERT_TRUE(value->IsArray()); | |
820 ASSERT_EQ(2, Array::Cast(*value)->Length()); | |
821 EXPECT_TRUE(EvaluateScriptForResultBool("result[0] === result[1]")); | |
822 }); | |
823 // Duplicate reference in a sparse array. | |
824 DecodeTest( | |
825 {0xff, 0x09, 0x3f, 0x00, 0x61, 0xe8, 0x07, 0x3f, 0x01, 0x49, | |
826 0x02, 0x3f, 0x01, 0x6f, 0x7b, 0x00, 0x3f, 0x02, 0x49, 0xe8, | |
827 0x07, 0x3f, 0x02, 0x5e, 0x01, 0x40, 0x02, 0xe8, 0x07, 0x00}, | |
828 [this](Local<Value> value) { | |
829 ASSERT_TRUE(value->IsArray()); | |
830 ASSERT_EQ(1000, Array::Cast(*value)->Length()); | |
831 EXPECT_TRUE( | |
832 EvaluateScriptForResultBool("typeof result[1] === 'object'")); | |
833 EXPECT_TRUE(EvaluateScriptForResultBool("result[1] === result[500]")); | |
834 }); | |
835 // Self reference. | |
836 DecodeTest({0xff, 0x09, 0x3f, 0x00, 0x41, 0x01, 0x3f, 0x01, 0x5e, 0x00, 0x24, | |
837 0x00, 0x01, 0x00}, | |
838 [this](Local<Value> value) { | |
839 ASSERT_TRUE(value->IsArray()); | |
840 ASSERT_EQ(1, Array::Cast(*value)->Length()); | |
841 EXPECT_TRUE(EvaluateScriptForResultBool("result[0] === result")); | |
842 }); | |
843 // Self reference in a sparse array. | |
844 DecodeTest( | |
845 {0xff, 0x09, 0x3f, 0x00, 0x61, 0xe8, 0x07, 0x3f, 0x01, 0x49, | |
846 0x8e, 0x08, 0x3f, 0x01, 0x5e, 0x00, 0x40, 0x01, 0xe8, 0x07}, | |
847 [this](Local<Value> value) { | |
848 ASSERT_TRUE(value->IsArray()); | |
849 ASSERT_EQ(1000, Array::Cast(*value)->Length()); | |
850 EXPECT_TRUE(EvaluateScriptForResultBool("result[519] === result")); | |
851 }); | |
852 // Array with additional properties. | |
853 DecodeTest( | |
854 {0xff, 0x09, 0x3f, 0x00, 0x41, 0x02, 0x3f, 0x01, 0x49, 0x02, 0x3f, | |
855 0x01, 0x49, 0x04, 0x3f, 0x01, 0x53, 0x03, 0x66, 0x6f, 0x6f, 0x3f, | |
856 0x01, 0x53, 0x03, 0x62, 0x61, 0x72, 0x24, 0x01, 0x02, 0x00}, | |
857 [this](Local<Value> value) { | |
858 ASSERT_TRUE(value->IsArray()); | |
859 ASSERT_EQ(2, Array::Cast(*value)->Length()); | |
860 EXPECT_TRUE(EvaluateScriptForResultBool("result.toString() === '1,2'")); | |
861 EXPECT_TRUE(EvaluateScriptForResultBool("result.foo === 'bar'")); | |
862 }); | |
863 // Sparse array with additional properties. | |
864 DecodeTest({0xff, 0x09, 0x3f, 0x00, 0x61, 0xe8, 0x07, 0x3f, 0x01, | |
865 0x53, 0x03, 0x66, 0x6f, 0x6f, 0x3f, 0x01, 0x53, 0x03, | |
866 0x62, 0x61, 0x72, 0x40, 0x01, 0xe8, 0x07, 0x00}, | |
867 [this](Local<Value> value) { | |
868 ASSERT_TRUE(value->IsArray()); | |
869 ASSERT_EQ(1000, Array::Cast(*value)->Length()); | |
870 EXPECT_TRUE(EvaluateScriptForResultBool( | |
871 "result.toString() === ','.repeat(999)")); | |
872 EXPECT_TRUE(EvaluateScriptForResultBool("result.foo === 'bar'")); | |
873 }); | |
874 // The distinction between holes and undefined elements must be maintained. | |
875 // Note that since the previous output from Chrome fails this test, an | |
876 // encoding using the sparse format was constructed instead. | |
877 DecodeTest( | |
878 {0xff, 0x09, 0x61, 0x02, 0x49, 0x02, 0x5f, 0x40, 0x01, 0x02}, | |
879 [this](Local<Value> value) { | |
880 ASSERT_TRUE(value->IsArray()); | |
881 ASSERT_EQ(2, Array::Cast(*value)->Length()); | |
882 EXPECT_TRUE( | |
883 EvaluateScriptForResultBool("typeof result[0] === 'undefined'")); | |
884 EXPECT_TRUE( | |
885 EvaluateScriptForResultBool("typeof result[1] === 'undefined'")); | |
886 EXPECT_TRUE(EvaluateScriptForResultBool("!result.hasOwnProperty(0)")); | |
887 EXPECT_TRUE(EvaluateScriptForResultBool("result.hasOwnProperty(1)")); | |
888 }); | |
889 } | |
890 | |
891 TEST_F(ValueSerializerTest, RoundTripArrayWithNonEnumerableElement) { | |
892 // Even though this array looks like [1,5,3], the 5 should be missing from the | |
893 // perspective of structured clone, which only clones properties that were | |
894 // enumerable. | |
895 RoundTripTest( | |
896 "(() => {" | |
897 " var x = [1,2,3];" | |
898 " Object.defineProperty(x, '1', {enumerable:false, value:5});" | |
899 " return x;" | |
900 "})()", | |
901 [this](Local<Value> value) { | |
902 ASSERT_TRUE(value->IsArray()); | |
903 ASSERT_EQ(3, Array::Cast(*value)->Length()); | |
904 EXPECT_TRUE(EvaluateScriptForResultBool("!result.hasOwnProperty('1')")); | |
905 }); | |
906 } | |
907 | |
908 TEST_F(ValueSerializerTest, RoundTripArrayWithTrickyGetters) { | |
909 // If an element is deleted before it is serialized, then it's deleted. | |
910 RoundTripTest( | |
911 "(() => {" | |
912 " var x = [{ get a() { delete x[1]; }}, 42];" | |
913 " return x;" | |
914 "})()", | |
915 [this](Local<Value> value) { | |
916 ASSERT_TRUE(value->IsArray()); | |
917 ASSERT_EQ(2, Array::Cast(*value)->Length()); | |
918 EXPECT_TRUE( | |
919 EvaluateScriptForResultBool("typeof result[1] === 'undefined'")); | |
920 EXPECT_TRUE(EvaluateScriptForResultBool("!result.hasOwnProperty(1)")); | |
921 }); | |
922 // Same for sparse arrays. | |
923 RoundTripTest( | |
924 "(() => {" | |
925 " var x = [{ get a() { delete x[1]; }}, 42];" | |
926 " x.length = 1000;" | |
927 " return x;" | |
928 "})()", | |
929 [this](Local<Value> value) { | |
930 ASSERT_TRUE(value->IsArray()); | |
931 ASSERT_EQ(1000, Array::Cast(*value)->Length()); | |
932 EXPECT_TRUE( | |
933 EvaluateScriptForResultBool("typeof result[1] === 'undefined'")); | |
934 EXPECT_TRUE(EvaluateScriptForResultBool("!result.hasOwnProperty(1)")); | |
935 }); | |
936 // If the length is changed, then the resulting array still has the original | |
937 // length, but elements that were not yet serialized are gone. | |
938 RoundTripTest( | |
939 "(() => {" | |
940 " var x = [1, { get a() { x.length = 0; }}, 3, 4];" | |
941 " return x;" | |
942 "})()", | |
943 [this](Local<Value> value) { | |
944 ASSERT_TRUE(value->IsArray()); | |
945 ASSERT_EQ(4, Array::Cast(*value)->Length()); | |
946 EXPECT_TRUE(EvaluateScriptForResultBool("result[0] === 1")); | |
947 EXPECT_TRUE(EvaluateScriptForResultBool("!result.hasOwnProperty(2)")); | |
948 }); | |
949 // Same for sparse arrays. | |
950 RoundTripTest( | |
951 "(() => {" | |
952 " var x = [1, { get a() { x.length = 0; }}, 3, 4];" | |
953 " x.length = 1000;" | |
954 " return x;" | |
955 "})()", | |
956 [this](Local<Value> value) { | |
957 ASSERT_TRUE(value->IsArray()); | |
958 ASSERT_EQ(1000, Array::Cast(*value)->Length()); | |
959 EXPECT_TRUE(EvaluateScriptForResultBool("result[0] === 1")); | |
960 EXPECT_TRUE(EvaluateScriptForResultBool("!result.hasOwnProperty(2)")); | |
961 }); | |
962 // If a getter makes a property non-enumerable, it should still be enumerated | |
963 // as enumeration happens once before getters are invoked. | |
964 RoundTripTest( | |
965 "(() => {" | |
966 " var x = [{ get a() {" | |
967 " Object.defineProperty(x, '1', { value: 3, enumerable: false });" | |
968 " }}, 2];" | |
969 " return x;" | |
970 "})()", | |
971 [this](Local<Value> value) { | |
972 ASSERT_TRUE(value->IsArray()); | |
973 ASSERT_EQ(2, Array::Cast(*value)->Length()); | |
974 EXPECT_TRUE(EvaluateScriptForResultBool("result[1] === 3")); | |
975 }); | |
976 // Same for sparse arrays. | |
977 RoundTripTest( | |
978 "(() => {" | |
979 " var x = [{ get a() {" | |
980 " Object.defineProperty(x, '1', { value: 3, enumerable: false });" | |
981 " }}, 2];" | |
982 " x.length = 1000;" | |
983 " return x;" | |
984 "})()", | |
985 [this](Local<Value> value) { | |
986 ASSERT_TRUE(value->IsArray()); | |
987 ASSERT_EQ(1000, Array::Cast(*value)->Length()); | |
988 EXPECT_TRUE(EvaluateScriptForResultBool("result[1] === 3")); | |
989 }); | |
990 // Getters on the array itself must also run. | |
991 RoundTripTest( | |
992 "(() => {" | |
993 " var x = [1, 2, 3];" | |
994 " Object.defineProperty(x, '1', { enumerable: true, get: () => 4 });" | |
995 " return x;" | |
996 "})()", | |
997 [this](Local<Value> value) { | |
998 ASSERT_TRUE(value->IsArray()); | |
999 ASSERT_EQ(3, Array::Cast(*value)->Length()); | |
1000 EXPECT_TRUE(EvaluateScriptForResultBool("result[1] === 4")); | |
1001 }); | |
1002 // Same for sparse arrays. | |
1003 RoundTripTest( | |
1004 "(() => {" | |
1005 " var x = [1, 2, 3];" | |
1006 " Object.defineProperty(x, '1', { enumerable: true, get: () => 4 });" | |
1007 " x.length = 1000;" | |
1008 " return x;" | |
1009 "})()", | |
1010 [this](Local<Value> value) { | |
1011 ASSERT_TRUE(value->IsArray()); | |
1012 ASSERT_EQ(1000, Array::Cast(*value)->Length()); | |
1013 EXPECT_TRUE(EvaluateScriptForResultBool("result[1] === 4")); | |
1014 }); | |
1015 // Even with a getter that deletes things, we don't read from the prototype. | |
1016 RoundTripTest( | |
1017 "(() => {" | |
1018 " var x = [{ get a() { delete x[1]; } }, 2];" | |
1019 " x.__proto__ = Object.create(Array.prototype, { 1: { value: 6 } });" | |
1020 " return x;" | |
1021 "})()", | |
1022 [this](Local<Value> value) { | |
1023 ASSERT_TRUE(value->IsArray()); | |
1024 ASSERT_EQ(2, Array::Cast(*value)->Length()); | |
1025 EXPECT_TRUE(EvaluateScriptForResultBool("!(1 in result)")); | |
1026 }); | |
1027 // Same for sparse arrays. | |
1028 RoundTripTest( | |
1029 "(() => {" | |
1030 " var x = [{ get a() { delete x[1]; } }, 2];" | |
1031 " x.__proto__ = Object.create(Array.prototype, { 1: { value: 6 } });" | |
1032 " x.length = 1000;" | |
1033 " return x;" | |
1034 "})()", | |
1035 [this](Local<Value> value) { | |
1036 ASSERT_TRUE(value->IsArray()); | |
1037 ASSERT_EQ(1000, Array::Cast(*value)->Length()); | |
1038 EXPECT_TRUE(EvaluateScriptForResultBool("!(1 in result)")); | |
1039 }); | |
1040 } | |
1041 | |
1042 } // namespace | 710 } // namespace |
1043 } // namespace v8 | 711 } // namespace v8 |
OLD | NEW |