1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <memory>
6
7 #include "include/v8-inspector.h"
8 #include "include/v8-local-handle.h"
9 #include "include/v8-primitive.h"
10 #include "src/inspector/protocol/Runtime.h"
11 #include "src/inspector/string-util.h"
12 #include "src/inspector/v8-inspector-impl.h"
13 #include "test/cctest/cctest.h"
14
15 using v8_inspector::StringBuffer;
16 using v8_inspector::StringView;
17 using v8_inspector::V8ContextInfo;
18 using v8_inspector::V8Inspector;
19 using v8_inspector::V8InspectorSession;
20
21 namespace {
22
23 class NoopChannel : public V8Inspector::Channel {
24 public:
25 ~NoopChannel() override = default;
sendResponse(int callId,std::unique_ptr<StringBuffer> message)26 void sendResponse(int callId,
27 std::unique_ptr<StringBuffer> message) override {}
sendNotification(std::unique_ptr<StringBuffer> message)28 void sendNotification(std::unique_ptr<StringBuffer> message) override {}
flushProtocolNotifications()29 void flushProtocolNotifications() override {}
30 };
31
WrapOnInterrupt(v8::Isolate * isolate,void * data)32 void WrapOnInterrupt(v8::Isolate* isolate, void* data) {
33 const char* object_group = "";
34 StringView object_group_view(reinterpret_cast<const uint8_t*>(object_group),
35 strlen(object_group));
36 reinterpret_cast<V8InspectorSession*>(data)->wrapObject(
37 isolate->GetCurrentContext(), v8::Null(isolate), object_group_view,
38 false);
39 }
40
41 } // namespace
42
TEST(WrapInsideWrapOnInterrupt)43 TEST(WrapInsideWrapOnInterrupt) {
44 LocalContext env;
45 v8::Isolate* isolate = env->GetIsolate();
46 v8::HandleScope handle_scope(isolate);
47
48 v8_inspector::V8InspectorClient default_client;
49 std::unique_ptr<V8Inspector> inspector =
50 V8Inspector::create(isolate, &default_client);
51 const char* name = "";
52 StringView name_view(reinterpret_cast<const uint8_t*>(name), strlen(name));
53 V8ContextInfo context_info(env.local(), 1, name_view);
54 inspector->contextCreated(context_info);
55
56 NoopChannel channel;
57 const char* state = "{}";
58 StringView state_view(reinterpret_cast<const uint8_t*>(state), strlen(state));
59 std::unique_ptr<V8InspectorSession> session =
60 inspector->connect(1, &channel, state_view);
61
62 const char* object_group = "";
63 StringView object_group_view(reinterpret_cast<const uint8_t*>(object_group),
64 strlen(object_group));
65 isolate->RequestInterrupt(&WrapOnInterrupt, session.get());
66 session->wrapObject(env.local(), v8::Null(isolate), object_group_view, false);
67 }
68
TEST(BinaryFromBase64)69 TEST(BinaryFromBase64) {
70 auto checkBinary = [](const v8_inspector::protocol::Binary& binary,
71 const std::vector<uint8_t>& values) {
72 std::vector<uint8_t> binary_vector(binary.data(),
73 binary.data() + binary.size());
74 CHECK_EQ(binary_vector, values);
75 };
76
77 {
78 bool success;
79 auto binary = v8_inspector::protocol::Binary::fromBase64("", &success);
80 CHECK(success);
81 checkBinary(binary, {});
82 }
83 {
84 bool success;
85 auto binary = v8_inspector::protocol::Binary::fromBase64("YQ==", &success);
86 CHECK(success);
87 checkBinary(binary, {'a'});
88 }
89 {
90 bool success;
91 auto binary = v8_inspector::protocol::Binary::fromBase64("YWI=", &success);
92 CHECK(success);
93 checkBinary(binary, {'a', 'b'});
94 }
95 {
96 bool success;
97 auto binary = v8_inspector::protocol::Binary::fromBase64("YWJj", &success);
98 CHECK(success);
99 checkBinary(binary, {'a', 'b', 'c'});
100 }
101 {
102 bool success;
103 // Wrong input length:
104 auto binary = v8_inspector::protocol::Binary::fromBase64("Y", &success);
105 CHECK(!success);
106 }
107 {
108 bool success;
109 // Invalid space:
110 auto binary = v8_inspector::protocol::Binary::fromBase64("=AAA", &success);
111 CHECK(!success);
112 }
113 {
114 bool success;
115 // Invalid space in a non-final block of four:
116 auto binary =
117 v8_inspector::protocol::Binary::fromBase64("AAA=AAAA", &success);
118 CHECK(!success);
119 }
120 {
121 bool success;
122 // Invalid invalid space in second to last position:
123 auto binary = v8_inspector::protocol::Binary::fromBase64("AA=A", &success);
124 CHECK(!success);
125 }
126 {
127 bool success;
128 // Invalid character:
129 auto binary = v8_inspector::protocol::Binary::fromBase64(" ", &success);
130 CHECK(!success);
131 }
132 }
133
TEST(BinaryToBase64)134 TEST(BinaryToBase64) {
135 uint8_t input[] = {'a', 'b', 'c'};
136 {
137 auto binary = v8_inspector::protocol::Binary::fromSpan(input, 0);
138 v8_inspector::protocol::String base64 = binary.toBase64();
139 CHECK_EQ(base64.utf8(), "");
140 }
141 {
142 auto binary = v8_inspector::protocol::Binary::fromSpan(input, 1);
143 v8_inspector::protocol::String base64 = binary.toBase64();
144 CHECK_EQ(base64.utf8(), "YQ==");
145 }
146 {
147 auto binary = v8_inspector::protocol::Binary::fromSpan(input, 2);
148 v8_inspector::protocol::String base64 = binary.toBase64();
149 CHECK_EQ(base64.utf8(), "YWI=");
150 }
151 {
152 auto binary = v8_inspector::protocol::Binary::fromSpan(input, 3);
153 v8_inspector::protocol::String base64 = binary.toBase64();
154 CHECK_EQ(base64.utf8(), "YWJj");
155 }
156 }
157
TEST(BinaryBase64RoundTrip)158 TEST(BinaryBase64RoundTrip) {
159 std::array<uint8_t, 256> values;
160 for (uint16_t b = 0x0; b <= 0xFF; ++b) values[b] = b;
161 auto binary =
162 v8_inspector::protocol::Binary::fromSpan(values.data(), values.size());
163 v8_inspector::protocol::String base64 = binary.toBase64();
164 bool success = false;
165 auto roundtrip_binary =
166 v8_inspector::protocol::Binary::fromBase64(base64, &success);
167 CHECK(success);
168 CHECK_EQ(values.size(), roundtrip_binary.size());
169 for (size_t i = 0; i < values.size(); ++i) {
170 CHECK_EQ(values[i], roundtrip_binary.data()[i]);
171 }
172 }
173
TEST(NoInterruptOnGetAssociatedData)174 TEST(NoInterruptOnGetAssociatedData) {
175 LocalContext env;
176 v8::Isolate* isolate = env->GetIsolate();
177 v8::HandleScope handle_scope(isolate);
178
179 v8_inspector::V8InspectorClient default_client;
180 std::unique_ptr<v8_inspector::V8InspectorImpl> inspector(
181 new v8_inspector::V8InspectorImpl(isolate, &default_client));
182
183 v8::Local<v8::Context> context = env->GetIsolate()->GetCurrentContext();
184 v8::Local<v8::Value> error = v8::Exception::Error(v8_str("custom error"));
185 v8::Local<v8::Name> key = v8_str("key");
186 v8::Local<v8::Value> value = v8_str("value");
187 inspector->associateExceptionData(context, error, key, value);
188
189 struct InterruptRecorder {
190 static void handler(v8::Isolate* isolate, void* data) {
191 reinterpret_cast<InterruptRecorder*>(data)->WasInvoked = true;
192 }
193
194 bool WasInvoked = false;
195 } recorder;
196
197 isolate->RequestInterrupt(&InterruptRecorder::handler, &recorder);
198
199 v8::Local<v8::Object> data =
200 inspector->getAssociatedExceptionData(error).ToLocalChecked();
201 CHECK(!recorder.WasInvoked);
202
203 CHECK_EQ(data->Get(context, key).ToLocalChecked(), value);
204
205 CompileRun("0");
206 CHECK(recorder.WasInvoked);
207 }
208