Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(281)

Side by Side Diff: test/unittests/value-serializer-unittest.cc

Issue 2246093003: Blink-compatible serialization of dictionary-like objects. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: OptionalRescheduleException Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« src/value-serializer.cc ('K') | « src/value-serializer.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 20 matching lines...) Expand all
31 31
32 template <typename InputFunctor, typename OutputFunctor> 32 template <typename InputFunctor, typename OutputFunctor>
33 void RoundTripTest(const InputFunctor& input_functor, 33 void RoundTripTest(const InputFunctor& input_functor,
34 const OutputFunctor& output_functor) { 34 const OutputFunctor& output_functor) {
35 EncodeTest(input_functor, 35 EncodeTest(input_functor,
36 [this, &output_functor](const std::vector<uint8_t>& data) { 36 [this, &output_functor](const std::vector<uint8_t>& data) {
37 DecodeTest(data, output_functor); 37 DecodeTest(data, output_functor);
38 }); 38 });
39 } 39 }
40 40
41 // Variant for the common case where a script is used to build the original
42 // value.
43 template <typename OutputFunctor>
44 void RoundTripTest(const char* source, const OutputFunctor& output_functor) {
45 RoundTripTest([this, source]() { return EvaluateScriptForInput(source); },
46 output_functor);
47 }
48
49 Maybe<std::vector<uint8_t>> DoEncode(Local<Value> value) {
50 // This approximates what the API implementation would do.
51 // TODO(jbroman): Use the public API once it exists.
52 i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate());
53 i::HandleScope handle_scope(internal_isolate);
54 i::ValueSerializer serializer(internal_isolate);
55 serializer.WriteHeader();
56 if (serializer.WriteObject(Utils::OpenHandle(*value)).FromMaybe(false))
57 return Just(serializer.ReleaseBuffer());
Jakob Kummerow 2016/08/17 13:00:15 nit: braces please
jbroman 2016/08/17 14:05:09 Done.
58 if (internal_isolate->has_pending_exception())
59 internal_isolate->OptionalRescheduleException(true);
Jakob Kummerow 2016/08/17 13:00:15 nit: braces please
jbroman 2016/08/17 14:05:09 Done. (I wish clang-format fixed this.)
60 return Nothing<std::vector<uint8_t>>();
61 }
62
41 template <typename InputFunctor, typename EncodedDataFunctor> 63 template <typename InputFunctor, typename EncodedDataFunctor>
42 void EncodeTest(const InputFunctor& input_functor, 64 void EncodeTest(const InputFunctor& input_functor,
43 const EncodedDataFunctor& encoded_data_functor) { 65 const EncodedDataFunctor& encoded_data_functor) {
44 Context::Scope scope(serialization_context()); 66 Context::Scope scope(serialization_context());
45 TryCatch try_catch(isolate()); 67 TryCatch try_catch(isolate());
46 // TODO(jbroman): Use the public API once it exists.
47 Local<Value> input_value = input_functor(); 68 Local<Value> input_value = input_functor();
48 i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate()); 69 std::vector<uint8_t> buffer;
49 i::HandleScope handle_scope(internal_isolate); 70 ASSERT_TRUE(DoEncode(input_value).To(&buffer));
50 i::ValueSerializer serializer;
51 serializer.WriteHeader();
52 ASSERT_TRUE(serializer.WriteObject(Utils::OpenHandle(*input_value))
53 .FromMaybe(false));
54 ASSERT_FALSE(try_catch.HasCaught()); 71 ASSERT_FALSE(try_catch.HasCaught());
55 encoded_data_functor(serializer.ReleaseBuffer()); 72 encoded_data_functor(buffer);
73 }
74
75 template <typename MessageFunctor>
76 void InvalidEncodeTest(const char* source, const MessageFunctor& functor) {
77 Context::Scope scope(serialization_context());
78 TryCatch try_catch(isolate());
79 Local<Value> input_value = EvaluateScriptForInput(source);
80 ASSERT_TRUE(DoEncode(input_value).IsNothing());
81 functor(try_catch.Message());
56 } 82 }
57 83
58 template <typename OutputFunctor> 84 template <typename OutputFunctor>
59 void DecodeTest(const std::vector<uint8_t>& data, 85 void DecodeTest(const std::vector<uint8_t>& data,
60 const OutputFunctor& output_functor) { 86 const OutputFunctor& output_functor) {
61 Context::Scope scope(deserialization_context()); 87 Context::Scope scope(deserialization_context());
62 TryCatch try_catch(isolate()); 88 TryCatch try_catch(isolate());
63 // TODO(jbroman): Use the public API once it exists. 89 // TODO(jbroman): Use the public API once it exists.
64 i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate()); 90 i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate());
65 i::HandleScope handle_scope(internal_isolate); 91 i::HandleScope handle_scope(internal_isolate);
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after
381 [](const std::vector<uint8_t>& data) { 407 [](const std::vector<uint8_t>& data) {
382 // This is a sufficient but not necessary condition to be aligned. 408 // This is a sufficient but not necessary condition to be aligned.
383 // Note that the third byte (0x00) is padding. 409 // Note that the third byte (0x00) is padding.
384 const uint8_t expected_prefix[] = {0xff, 0x09, 0x00, 0x63, 0x94, 0x03}; 410 const uint8_t expected_prefix[] = {0xff, 0x09, 0x00, 0x63, 0x94, 0x03};
385 ASSERT_GT(data.size(), sizeof(expected_prefix) / sizeof(uint8_t)); 411 ASSERT_GT(data.size(), sizeof(expected_prefix) / sizeof(uint8_t));
386 EXPECT_TRUE(std::equal(std::begin(expected_prefix), 412 EXPECT_TRUE(std::equal(std::begin(expected_prefix),
387 std::end(expected_prefix), data.begin())); 413 std::end(expected_prefix), data.begin()));
388 }); 414 });
389 } 415 }
390 416
417 TEST_F(ValueSerializerTest, RoundTripDictionaryObject) {
418 // Empty object.
419 RoundTripTest("({})", [this](Local<Value> value) {
420 ASSERT_TRUE(value->IsObject());
421 EXPECT_TRUE(EvaluateScriptForResultBool(
422 "Object.getPrototypeOf(result) === Object.prototype"));
423 EXPECT_TRUE(EvaluateScriptForResultBool(
424 "Object.getOwnPropertyNames(result).length === 0"));
425 });
426 // String key.
427 RoundTripTest("({ a: 42 })", [this](Local<Value> value) {
428 ASSERT_TRUE(value->IsObject());
429 EXPECT_TRUE(EvaluateScriptForResultBool("result.hasOwnProperty('a')"));
430 EXPECT_TRUE(EvaluateScriptForResultBool("result.a === 42"));
431 EXPECT_TRUE(EvaluateScriptForResultBool(
432 "Object.getOwnPropertyNames(result).length === 1"));
433 });
434 // Integer key (treated as a string, but may be encoded differently).
435 RoundTripTest("({ 42: 'a' })", [this](Local<Value> value) {
436 ASSERT_TRUE(value->IsObject());
437 EXPECT_TRUE(EvaluateScriptForResultBool("result.hasOwnProperty('42')"));
438 EXPECT_TRUE(EvaluateScriptForResultBool("result[42] === 'a'"));
439 EXPECT_TRUE(EvaluateScriptForResultBool(
440 "Object.getOwnPropertyNames(result).length === 1"));
441 });
442 // Key order must be preserved.
443 RoundTripTest("({ x: 1, y: 2, a: 3 })", [this](Local<Value> value) {
444 EXPECT_TRUE(EvaluateScriptForResultBool(
445 "Object.getOwnPropertyNames(result).toString() === 'x,y,a'"));
446 });
447 // A harder case of enumeration order.
448 // Indexes first, in order (but not 2^31 - 1, which is not an index), then the
Jakob Kummerow 2016/08/17 13:00:15 nit: you mean "2^32 - 1".
jbroman 2016/08/17 14:05:09 Indeed. Done.
449 // remaining (string) keys, in the order they were defined.
450 RoundTripTest(
451 "({ a: 2, 0xFFFFFFFF: 1, 0xFFFFFFFE: 3, 1: 0 })",
452 [this](Local<Value> value) {
453 EXPECT_TRUE(EvaluateScriptForResultBool(
454 "Object.getOwnPropertyNames(result).toString() === "
455 "'1,4294967294,a,4294967295'"));
456 EXPECT_TRUE(EvaluateScriptForResultBool("result.a === 2"));
457 EXPECT_TRUE(EvaluateScriptForResultBool("result[0xFFFFFFFF] === 1"));
458 EXPECT_TRUE(EvaluateScriptForResultBool("result[0xFFFFFFFE] === 3"));
459 EXPECT_TRUE(EvaluateScriptForResultBool("result[1] === 0"));
460 });
461 // This detects a fairly subtle case: the object itself must be in the map
462 // before its properties are deserialized, so that references to it can be
463 // resolved.
464 RoundTripTest(
465 "(() => { var y = {}; y.self = y; return y; })()",
466 [this](Local<Value> value) {
467 ASSERT_TRUE(value->IsObject());
468 EXPECT_TRUE(EvaluateScriptForResultBool("result === result.self"));
469 });
470 }
471
472 TEST_F(ValueSerializerTest, DecodeDictionaryObject) {
473 // Empty object.
474 DecodeTest({0xff, 0x09, 0x3f, 0x00, 0x6f, 0x7b, 0x00, 0x00},
475 [this](Local<Value> value) {
476 ASSERT_TRUE(value->IsObject());
477 EXPECT_TRUE(EvaluateScriptForResultBool(
478 "Object.getPrototypeOf(result) === Object.prototype"));
479 EXPECT_TRUE(EvaluateScriptForResultBool(
480 "Object.getOwnPropertyNames(result).length === 0"));
481 });
482 // String key.
483 DecodeTest(
484 {0xff, 0x09, 0x3f, 0x00, 0x6f, 0x3f, 0x01, 0x53, 0x01, 0x61, 0x3f, 0x01,
485 0x49, 0x54, 0x7b, 0x01},
486 [this](Local<Value> value) {
487 ASSERT_TRUE(value->IsObject());
488 EXPECT_TRUE(EvaluateScriptForResultBool("result.hasOwnProperty('a')"));
489 EXPECT_TRUE(EvaluateScriptForResultBool("result.a === 42"));
490 EXPECT_TRUE(EvaluateScriptForResultBool(
491 "Object.getOwnPropertyNames(result).length === 1"));
492 });
493 // Integer key (treated as a string, but may be encoded differently).
494 DecodeTest(
495 {0xff, 0x09, 0x3f, 0x00, 0x6f, 0x3f, 0x01, 0x49, 0x54, 0x3f, 0x01, 0x53,
496 0x01, 0x61, 0x7b, 0x01},
497 [this](Local<Value> value) {
498 ASSERT_TRUE(value->IsObject());
499 EXPECT_TRUE(EvaluateScriptForResultBool("result.hasOwnProperty('42')"));
500 EXPECT_TRUE(EvaluateScriptForResultBool("result[42] === 'a'"));
501 EXPECT_TRUE(EvaluateScriptForResultBool(
502 "Object.getOwnPropertyNames(result).length === 1"));
503 });
504 // Key order must be preserved.
505 DecodeTest(
506 {0xff, 0x09, 0x3f, 0x00, 0x6f, 0x3f, 0x01, 0x53, 0x01, 0x78, 0x3f, 0x01,
507 0x49, 0x02, 0x3f, 0x01, 0x53, 0x01, 0x79, 0x3f, 0x01, 0x49, 0x04, 0x3f,
508 0x01, 0x53, 0x01, 0x61, 0x3f, 0x01, 0x49, 0x06, 0x7b, 0x03},
509 [this](Local<Value> value) {
510 EXPECT_TRUE(EvaluateScriptForResultBool(
511 "Object.getOwnPropertyNames(result).toString() === 'x,y,a'"));
512 });
513 // A harder case of enumeration order.
514 DecodeTest(
515 {0xff, 0x09, 0x3f, 0x00, 0x6f, 0x3f, 0x01, 0x49, 0x02, 0x3f, 0x01,
516 0x49, 0x00, 0x3f, 0x01, 0x55, 0xfe, 0xff, 0xff, 0xff, 0x0f, 0x3f,
517 0x01, 0x49, 0x06, 0x3f, 0x01, 0x53, 0x01, 0x61, 0x3f, 0x01, 0x49,
518 0x04, 0x3f, 0x01, 0x53, 0x0a, 0x34, 0x32, 0x39, 0x34, 0x39, 0x36,
519 0x37, 0x32, 0x39, 0x35, 0x3f, 0x01, 0x49, 0x02, 0x7b, 0x04},
520 [this](Local<Value> value) {
521 EXPECT_TRUE(EvaluateScriptForResultBool(
522 "Object.getOwnPropertyNames(result).toString() === "
523 "'1,4294967294,a,4294967295'"));
524 EXPECT_TRUE(EvaluateScriptForResultBool("result.a === 2"));
525 EXPECT_TRUE(EvaluateScriptForResultBool("result[0xFFFFFFFF] === 1"));
526 EXPECT_TRUE(EvaluateScriptForResultBool("result[0xFFFFFFFE] === 3"));
527 EXPECT_TRUE(EvaluateScriptForResultBool("result[1] === 0"));
528 });
529 // This detects a fairly subtle case: the object itself must be in the map
530 // before its properties are deserialized, so that references to it can be
531 // resolved.
532 DecodeTest(
533 {0xff, 0x09, 0x3f, 0x00, 0x6f, 0x3f, 0x01, 0x53, 0x04, 0x73,
534 0x65, 0x6c, 0x66, 0x3f, 0x01, 0x5e, 0x00, 0x7b, 0x01, 0x00},
535 [this](Local<Value> value) {
536 ASSERT_TRUE(value->IsObject());
537 EXPECT_TRUE(EvaluateScriptForResultBool("result === result.self"));
538 });
539 }
540
541 TEST_F(ValueSerializerTest, RoundTripOnlyOwnEnumerableStringKeys) {
542 // Only "own" properties should be serialized, not ones on the prototype.
543 RoundTripTest("(() => { var x = {}; x.__proto__ = {a: 4}; return x; })()",
544 [this](Local<Value> value) {
545 EXPECT_TRUE(EvaluateScriptForResultBool("!('a' in result)"));
546 });
547 // Only enumerable properties should be serialized.
548 RoundTripTest(
549 "(() => {"
550 " var x = {};"
551 " Object.defineProperty(x, 'a', {value: 1, enumerable: false});"
552 " return x;"
553 "})()",
554 [this](Local<Value> value) {
555 EXPECT_TRUE(EvaluateScriptForResultBool("!('a' in result)"));
556 });
557 // Symbol keys should not be serialized.
558 RoundTripTest("({ [Symbol()]: 4 })", [this](Local<Value> value) {
559 EXPECT_TRUE(EvaluateScriptForResultBool(
560 "Object.getOwnPropertySymbols(result).length === 0"));
561 });
562 }
563
564 TEST_F(ValueSerializerTest, RoundTripTrickyGetters) {
565 // Keys are enumerated before any setters are called, but if there is no own
566 // property when the value is to be read, then it should not be serialized.
567 RoundTripTest("({ get a() { delete this.b; return 1; }, b: 2 })",
568 [this](Local<Value> value) {
569 EXPECT_TRUE(EvaluateScriptForResultBool("!('b' in result)"));
570 });
571 // Keys added after the property enumeration should not be serialized.
572 RoundTripTest("({ get a() { this.b = 3; }})", [this](Local<Value> value) {
573 EXPECT_TRUE(EvaluateScriptForResultBool("!('b' in result)"));
574 });
575 // But if you remove a key and add it back, that's fine. But it will appear in
576 // the original place in enumeration order.
577 RoundTripTest(
578 "({ get a() { delete this.b; this.b = 4; }, b: 2, c: 3 })",
579 [this](Local<Value> value) {
580 EXPECT_TRUE(EvaluateScriptForResultBool(
581 "Object.getOwnPropertyNames(result).toString() === 'a,b,c'"));
582 EXPECT_TRUE(EvaluateScriptForResultBool("result.b === 4"));
583 });
584 // Similarly, it only matters if a property was enumerable when the
585 // enumeration happened.
586 RoundTripTest(
587 "({ get a() {"
588 " Object.defineProperty(this, 'b', {value: 2, enumerable: false});"
589 "}, b: 1})",
590 [this](Local<Value> value) {
591 EXPECT_TRUE(EvaluateScriptForResultBool("result.b === 2"));
592 });
593 RoundTripTest(
594 "(() => {"
595 " var x = {"
596 " get a() {"
597 " Object.defineProperty(this, 'b', {value: 2, enumerable: true});"
598 " }"
599 " };"
600 " Object.defineProperty(x, 'b',"
601 " {value: 1, enumerable: false, configurable: true});"
602 " return x;"
603 "})()",
604 [this](Local<Value> value) {
605 EXPECT_TRUE(EvaluateScriptForResultBool("!('b' in result)"));
606 });
607 // The property also should not be read if it can only be found on the
608 // prototype chain (but not as an own property) after enumeration.
609 RoundTripTest(
610 "(() => {"
611 " var x = { get a() { delete this.b; }, b: 1 };"
612 " x.__proto__ = { b: 0 };"
613 " return x;"
614 "})()",
615 [this](Local<Value> value) {
616 EXPECT_TRUE(EvaluateScriptForResultBool("!('b' in result)"));
617 });
618 // If an exception is thrown by script, encoding must fail and the exception
619 // must be thrown.
620 InvalidEncodeTest("({ get a() { throw new Error('sentinel'); } })",
621 [](Local<Message> message) {
622 ASSERT_FALSE(message.IsEmpty());
623 EXPECT_NE(std::string::npos,
624 Utf8Value(message->Get()).find("sentinel"));
625 });
626 }
627
391 } // namespace 628 } // namespace
392 } // namespace v8 629 } // namespace v8
OLDNEW
« src/value-serializer.cc ('K') | « src/value-serializer.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698