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

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

Powered by Google App Engine
This is Rietveld 408576698