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