1 // Copyright 2015 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 "src/inspector/v8-injected-script-host.h"
6 
7 #include "src/base/macros.h"
8 #include "src/debug/debug-interface.h"
9 #include "src/inspector/injected-script.h"
10 #include "src/inspector/string-util.h"
11 #include "src/inspector/v8-debugger.h"
12 #include "src/inspector/v8-inspector-impl.h"
13 #include "src/inspector/v8-internal-value-type.h"
14 #include "src/inspector/v8-value-utils.h"
15 
16 #include "include/v8-inspector.h"
17 
18 namespace v8_inspector {
19 
20 namespace {
21 
setFunctionProperty(v8::Local<v8::Context> context,v8::Local<v8::Object> obj,const char * name,v8::FunctionCallback callback,v8::Local<v8::External> external)22 void setFunctionProperty(v8::Local<v8::Context> context,
23                          v8::Local<v8::Object> obj, const char* name,
24                          v8::FunctionCallback callback,
25                          v8::Local<v8::External> external) {
26   v8::Local<v8::String> funcName =
27       toV8StringInternalized(context->GetIsolate(), name);
28   v8::Local<v8::Function> func;
29   if (!v8::Function::New(context, callback, external, 0,
30                          v8::ConstructorBehavior::kThrow)
31            .ToLocal(&func))
32     return;
33   func->SetName(funcName);
34   createDataProperty(context, obj, funcName, func);
35 }
36 
unwrapInspector(const v8::FunctionCallbackInfo<v8::Value> & info)37 V8InspectorImpl* unwrapInspector(
38     const v8::FunctionCallbackInfo<v8::Value>& info) {
39   DCHECK(!info.Data().IsEmpty());
40   DCHECK(info.Data()->IsExternal());
41   V8InspectorImpl* inspector =
42       static_cast<V8InspectorImpl*>(info.Data().As<v8::External>()->Value());
43   DCHECK(inspector);
44   return inspector;
45 }
46 
47 template <typename TypedArray>
addTypedArrayProperty(std::vector<v8::Local<v8::Value>> * props,v8::Isolate * isolate,v8::Local<v8::ArrayBuffer> arraybuffer,String16 name,size_t length)48 void addTypedArrayProperty(std::vector<v8::Local<v8::Value>>* props,
49                            v8::Isolate* isolate,
50                            v8::Local<v8::ArrayBuffer> arraybuffer,
51                            String16 name, size_t length) {
52   props->push_back(toV8String(isolate, name));
53   props->push_back(TypedArray::New(arraybuffer, 0, length));
54 }
55 
56 }  // namespace
57 
create(v8::Local<v8::Context> context,V8InspectorImpl * inspector)58 v8::Local<v8::Object> V8InjectedScriptHost::create(
59     v8::Local<v8::Context> context, V8InspectorImpl* inspector) {
60   v8::Isolate* isolate = inspector->isolate();
61   v8::Local<v8::Object> injectedScriptHost = v8::Object::New(isolate);
62   bool success = injectedScriptHost->SetPrototype(context, v8::Null(isolate))
63                      .FromMaybe(false);
64   DCHECK(success);
65   USE(success);
66   v8::Local<v8::External> debuggerExternal =
67       v8::External::New(isolate, inspector);
68   setFunctionProperty(context, injectedScriptHost, "nullifyPrototype",
69                       V8InjectedScriptHost::nullifyPrototypeCallback,
70                       debuggerExternal);
71   setFunctionProperty(context, injectedScriptHost, "getProperty",
72                       V8InjectedScriptHost::getPropertyCallback,
73                       debuggerExternal);
74   setFunctionProperty(context, injectedScriptHost, "internalConstructorName",
75                       V8InjectedScriptHost::internalConstructorNameCallback,
76                       debuggerExternal);
77   setFunctionProperty(
78       context, injectedScriptHost, "formatAccessorsAsProperties",
79       V8InjectedScriptHost::formatAccessorsAsProperties, debuggerExternal);
80   setFunctionProperty(context, injectedScriptHost, "subtype",
81                       V8InjectedScriptHost::subtypeCallback, debuggerExternal);
82   setFunctionProperty(context, injectedScriptHost, "getInternalProperties",
83                       V8InjectedScriptHost::getInternalPropertiesCallback,
84                       debuggerExternal);
85   setFunctionProperty(context, injectedScriptHost, "objectHasOwnProperty",
86                       V8InjectedScriptHost::objectHasOwnPropertyCallback,
87                       debuggerExternal);
88   setFunctionProperty(context, injectedScriptHost, "bind",
89                       V8InjectedScriptHost::bindCallback, debuggerExternal);
90   setFunctionProperty(context, injectedScriptHost, "proxyTargetValue",
91                       V8InjectedScriptHost::proxyTargetValueCallback,
92                       debuggerExternal);
93   setFunctionProperty(context, injectedScriptHost, "nativeAccessorDescriptor",
94                       V8InjectedScriptHost::nativeAccessorDescriptorCallback,
95                       debuggerExternal);
96   setFunctionProperty(context, injectedScriptHost, "typedArrayProperties",
97                       V8InjectedScriptHost::typedArrayPropertiesCallback,
98                       debuggerExternal);
99   createDataProperty(context, injectedScriptHost,
100                      toV8StringInternalized(isolate, "keys"),
101                      v8::debug::GetBuiltin(isolate, v8::debug::kObjectKeys));
102   createDataProperty(
103       context, injectedScriptHost,
104       toV8StringInternalized(isolate, "getPrototypeOf"),
105       v8::debug::GetBuiltin(isolate, v8::debug::kObjectGetPrototypeOf));
106   createDataProperty(
107       context, injectedScriptHost,
108       toV8StringInternalized(isolate, "getOwnPropertyDescriptor"),
109       v8::debug::GetBuiltin(isolate,
110                             v8::debug::kObjectGetOwnPropertyDescriptor));
111   createDataProperty(
112       context, injectedScriptHost,
113       toV8StringInternalized(isolate, "getOwnPropertyNames"),
114       v8::debug::GetBuiltin(isolate, v8::debug::kObjectGetOwnPropertyNames));
115   createDataProperty(
116       context, injectedScriptHost,
117       toV8StringInternalized(isolate, "getOwnPropertySymbols"),
118       v8::debug::GetBuiltin(isolate, v8::debug::kObjectGetOwnPropertySymbols));
119   return injectedScriptHost;
120 }
121 
nullifyPrototypeCallback(const v8::FunctionCallbackInfo<v8::Value> & info)122 void V8InjectedScriptHost::nullifyPrototypeCallback(
123     const v8::FunctionCallbackInfo<v8::Value>& info) {
124   CHECK_EQ(1, info.Length());
125   DCHECK(info[0]->IsObject());
126   if (!info[0]->IsObject()) return;
127   v8::Isolate* isolate = info.GetIsolate();
128   info[0]
129       .As<v8::Object>()
130       ->SetPrototype(isolate->GetCurrentContext(), v8::Null(isolate))
131       .ToChecked();
132 }
133 
getPropertyCallback(const v8::FunctionCallbackInfo<v8::Value> & info)134 void V8InjectedScriptHost::getPropertyCallback(
135     const v8::FunctionCallbackInfo<v8::Value>& info) {
136   CHECK(info.Length() == 2 && info[1]->IsString());
137   if (!info[0]->IsObject()) return;
138   v8::Isolate* isolate = info.GetIsolate();
139   v8::Local<v8::Context> context = isolate->GetCurrentContext();
140   v8::TryCatch tryCatch(isolate);
141   v8::Isolate::DisallowJavascriptExecutionScope throwJs(
142       isolate, v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
143   v8::Local<v8::Value> property;
144   if (info[0]
145           .As<v8::Object>()
146           ->Get(context, v8::Local<v8::String>::Cast(info[1]))
147           .ToLocal(&property)) {
148     info.GetReturnValue().Set(property);
149   }
150 }
151 
internalConstructorNameCallback(const v8::FunctionCallbackInfo<v8::Value> & info)152 void V8InjectedScriptHost::internalConstructorNameCallback(
153     const v8::FunctionCallbackInfo<v8::Value>& info) {
154   if (info.Length() < 1 || !info[0]->IsObject()) return;
155 
156   v8::Local<v8::Object> object = info[0].As<v8::Object>();
157   info.GetReturnValue().Set(object->GetConstructorName());
158 }
159 
formatAccessorsAsProperties(const v8::FunctionCallbackInfo<v8::Value> & info)160 void V8InjectedScriptHost::formatAccessorsAsProperties(
161     const v8::FunctionCallbackInfo<v8::Value>& info) {
162   DCHECK_EQ(info.Length(), 2);
163   info.GetReturnValue().Set(false);
164   if (!info[1]->IsFunction()) return;
165   // Check that function is user-defined.
166   if (info[1].As<v8::Function>()->ScriptId() != v8::UnboundScript::kNoScriptId)
167     return;
168   info.GetReturnValue().Set(
169       unwrapInspector(info)->client()->formatAccessorsAsProperties(info[0]));
170 }
171 
subtypeCallback(const v8::FunctionCallbackInfo<v8::Value> & info)172 void V8InjectedScriptHost::subtypeCallback(
173     const v8::FunctionCallbackInfo<v8::Value>& info) {
174   if (info.Length() < 1) return;
175 
176   v8::Isolate* isolate = info.GetIsolate();
177   v8::Local<v8::Value> value = info[0];
178   if (value->IsObject()) {
179     v8::Local<v8::Value> internalType = v8InternalValueTypeFrom(
180         isolate->GetCurrentContext(), v8::Local<v8::Object>::Cast(value));
181     if (internalType->IsString()) {
182       info.GetReturnValue().Set(internalType);
183       return;
184     }
185   }
186   if (value->IsArray() || value->IsArgumentsObject()) {
187     info.GetReturnValue().Set(toV8StringInternalized(isolate, "array"));
188     return;
189   }
190   if (value->IsTypedArray()) {
191     info.GetReturnValue().Set(toV8StringInternalized(isolate, "typedarray"));
192     return;
193   }
194   if (value->IsDate()) {
195     info.GetReturnValue().Set(toV8StringInternalized(isolate, "date"));
196     return;
197   }
198   if (value->IsRegExp()) {
199     info.GetReturnValue().Set(toV8StringInternalized(isolate, "regexp"));
200     return;
201   }
202   if (value->IsMap()) {
203     info.GetReturnValue().Set(toV8StringInternalized(isolate, "map"));
204     return;
205   }
206   if (value->IsWeakMap()) {
207     info.GetReturnValue().Set(toV8StringInternalized(isolate, "weakmap"));
208     return;
209   }
210   if (value->IsSet()) {
211     info.GetReturnValue().Set(toV8StringInternalized(isolate, "set"));
212     return;
213   }
214   if (value->IsWeakSet()) {
215     info.GetReturnValue().Set(toV8StringInternalized(isolate, "weakset"));
216     return;
217   }
218   if (value->IsMapIterator() || value->IsSetIterator()) {
219     info.GetReturnValue().Set(toV8StringInternalized(isolate, "iterator"));
220     return;
221   }
222   if (value->IsGeneratorObject()) {
223     info.GetReturnValue().Set(toV8StringInternalized(isolate, "generator"));
224     return;
225   }
226   if (value->IsNativeError()) {
227     info.GetReturnValue().Set(toV8StringInternalized(isolate, "error"));
228     return;
229   }
230   if (value->IsProxy()) {
231     info.GetReturnValue().Set(toV8StringInternalized(isolate, "proxy"));
232     return;
233   }
234   if (value->IsPromise()) {
235     info.GetReturnValue().Set(toV8StringInternalized(isolate, "promise"));
236     return;
237   }
238   if (value->IsArrayBuffer() || value->IsSharedArrayBuffer()) {
239     info.GetReturnValue().Set(toV8StringInternalized(isolate, "arraybuffer"));
240     return;
241   }
242   if (value->IsDataView()) {
243     info.GetReturnValue().Set(toV8StringInternalized(isolate, "dataview"));
244     return;
245   }
246   std::unique_ptr<StringBuffer> subtype =
247       unwrapInspector(info)->client()->valueSubtype(value);
248   if (subtype) {
249     info.GetReturnValue().Set(toV8String(isolate, subtype->string()));
250     return;
251   }
252 }
253 
getInternalPropertiesCallback(const v8::FunctionCallbackInfo<v8::Value> & info)254 void V8InjectedScriptHost::getInternalPropertiesCallback(
255     const v8::FunctionCallbackInfo<v8::Value>& info) {
256   if (info.Length() < 1) return;
257 
258   std::unordered_set<String16> allowedProperties;
259   if (info[0]->IsBooleanObject() || info[0]->IsNumberObject() ||
260       info[0]->IsStringObject() || info[0]->IsSymbolObject() ||
261       info[0]->IsBigIntObject()) {
262     allowedProperties.insert(String16("[[PrimitiveValue]]"));
263   } else if (info[0]->IsPromise()) {
264     allowedProperties.insert(String16("[[PromiseStatus]]"));
265     allowedProperties.insert(String16("[[PromiseValue]]"));
266   } else if (info[0]->IsGeneratorObject()) {
267     allowedProperties.insert(String16("[[GeneratorStatus]]"));
268   } else if (info[0]->IsMap() || info[0]->IsWeakMap() || info[0]->IsSet() ||
269              info[0]->IsWeakSet() || info[0]->IsMapIterator() ||
270              info[0]->IsSetIterator()) {
271     allowedProperties.insert(String16("[[Entries]]"));
272   }
273   if (!allowedProperties.size()) return;
274 
275   v8::Isolate* isolate = info.GetIsolate();
276   v8::Local<v8::Array> allProperties;
277   if (!unwrapInspector(info)
278            ->debugger()
279            ->internalProperties(isolate->GetCurrentContext(), info[0])
280            .ToLocal(&allProperties) ||
281       !allProperties->IsArray() || allProperties->Length() % 2 != 0)
282     return;
283 
284   {
285     v8::Local<v8::Context> context = isolate->GetCurrentContext();
286     v8::TryCatch tryCatch(isolate);
287     v8::Isolate::DisallowJavascriptExecutionScope throwJs(
288         isolate,
289         v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
290 
291     v8::Local<v8::Array> properties = v8::Array::New(isolate);
292     if (tryCatch.HasCaught()) return;
293 
294     uint32_t outputIndex = 0;
295     for (uint32_t i = 0; i < allProperties->Length(); i += 2) {
296       v8::Local<v8::Value> key;
297       if (!allProperties->Get(context, i).ToLocal(&key)) continue;
298       if (tryCatch.HasCaught()) {
299         tryCatch.Reset();
300         continue;
301       }
302       String16 keyString = toProtocolStringWithTypeCheck(key);
303       if (keyString.isEmpty() ||
304           allowedProperties.find(keyString) == allowedProperties.end())
305         continue;
306       v8::Local<v8::Value> value;
307       if (!allProperties->Get(context, i + 1).ToLocal(&value)) continue;
308       if (tryCatch.HasCaught()) {
309         tryCatch.Reset();
310         continue;
311       }
312       createDataProperty(context, properties, outputIndex++, key);
313       createDataProperty(context, properties, outputIndex++, value);
314     }
315     info.GetReturnValue().Set(properties);
316   }
317 }
318 
objectHasOwnPropertyCallback(const v8::FunctionCallbackInfo<v8::Value> & info)319 void V8InjectedScriptHost::objectHasOwnPropertyCallback(
320     const v8::FunctionCallbackInfo<v8::Value>& info) {
321   if (info.Length() < 2 || !info[0]->IsObject() || !info[1]->IsString()) return;
322   bool result = info[0]
323                     .As<v8::Object>()
324                     ->HasOwnProperty(info.GetIsolate()->GetCurrentContext(),
325                                      v8::Local<v8::String>::Cast(info[1]))
326                     .FromMaybe(false);
327   info.GetReturnValue().Set(v8::Boolean::New(info.GetIsolate(), result));
328 }
329 
bindCallback(const v8::FunctionCallbackInfo<v8::Value> & info)330 void V8InjectedScriptHost::bindCallback(
331     const v8::FunctionCallbackInfo<v8::Value>& info) {
332   if (info.Length() < 2 || !info[1]->IsString()) return;
333   InjectedScript* injectedScript =
334       InjectedScript::fromInjectedScriptHost(info.GetIsolate(), info.Holder());
335   if (!injectedScript) return;
336 
337   v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
338   v8::Local<v8::String> v8groupName =
339       info[1]->ToString(context).ToLocalChecked();
340   String16 groupName = toProtocolStringWithTypeCheck(v8groupName);
341   int id = injectedScript->bindObject(info[0], groupName);
342   info.GetReturnValue().Set(id);
343 }
344 
proxyTargetValueCallback(const v8::FunctionCallbackInfo<v8::Value> & info)345 void V8InjectedScriptHost::proxyTargetValueCallback(
346     const v8::FunctionCallbackInfo<v8::Value>& info) {
347   if (info.Length() != 1 || !info[0]->IsProxy()) {
348     UNREACHABLE();
349     return;
350   }
351   v8::Local<v8::Value> target = info[0].As<v8::Proxy>();
352   while (target->IsProxy())
353     target = v8::Local<v8::Proxy>::Cast(target)->GetTarget();
354   info.GetReturnValue().Set(target);
355 }
356 
nativeAccessorDescriptorCallback(const v8::FunctionCallbackInfo<v8::Value> & info)357 void V8InjectedScriptHost::nativeAccessorDescriptorCallback(
358     const v8::FunctionCallbackInfo<v8::Value>& info) {
359   v8::Isolate* isolate = info.GetIsolate();
360   if (info.Length() != 2 || !info[0]->IsObject() || !info[1]->IsName()) {
361     info.GetReturnValue().Set(v8::Undefined(isolate));
362     return;
363   }
364   v8::Local<v8::Context> context = isolate->GetCurrentContext();
365   int flags = v8::debug::GetNativeAccessorDescriptor(
366       context, v8::Local<v8::Object>::Cast(info[0]),
367       v8::Local<v8::Name>::Cast(info[1]));
368   if (flags == static_cast<int>(v8::debug::NativeAccessorType::None)) {
369     info.GetReturnValue().Set(v8::Undefined(isolate));
370     return;
371   }
372 
373   bool isBuiltin =
374       flags & static_cast<int>(v8::debug::NativeAccessorType::IsBuiltin);
375   bool hasGetter =
376       flags & static_cast<int>(v8::debug::NativeAccessorType::HasGetter);
377   bool hasSetter =
378       flags & static_cast<int>(v8::debug::NativeAccessorType::HasSetter);
379   v8::Local<v8::Object> result = v8::Object::New(isolate);
380   result->SetPrototype(context, v8::Null(isolate)).ToChecked();
381   createDataProperty(context, result, toV8String(isolate, "isBuiltin"),
382                      v8::Boolean::New(isolate, isBuiltin));
383   createDataProperty(context, result, toV8String(isolate, "hasGetter"),
384                      v8::Boolean::New(isolate, hasGetter));
385   createDataProperty(context, result, toV8String(isolate, "hasSetter"),
386                      v8::Boolean::New(isolate, hasSetter));
387   info.GetReturnValue().Set(result);
388 }
389 
typedArrayPropertiesCallback(const v8::FunctionCallbackInfo<v8::Value> & info)390 void V8InjectedScriptHost::typedArrayPropertiesCallback(
391     const v8::FunctionCallbackInfo<v8::Value>& info) {
392   v8::Isolate* isolate = info.GetIsolate();
393   if (info.Length() != 1 || !info[0]->IsArrayBuffer()) return;
394 
395   v8::TryCatch tryCatch(isolate);
396   v8::Isolate::DisallowJavascriptExecutionScope throwJs(
397       isolate, v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
398   v8::Local<v8::ArrayBuffer> arrayBuffer = info[0].As<v8::ArrayBuffer>();
399   size_t length = arrayBuffer->ByteLength();
400   if (length == 0) return;
401   std::vector<v8::Local<v8::Value>> arrays_vector;
402   addTypedArrayProperty<v8::Int8Array>(&arrays_vector, isolate, arrayBuffer,
403                                        "[[Int8Array]]", length);
404   addTypedArrayProperty<v8::Uint8Array>(&arrays_vector, isolate, arrayBuffer,
405                                         "[[Uint8Array]]", length);
406 
407   if (length % 2 == 0) {
408     addTypedArrayProperty<v8::Int16Array>(&arrays_vector, isolate, arrayBuffer,
409                                           "[[Int16Array]]", length / 2);
410   }
411   if (length % 4 == 0) {
412     addTypedArrayProperty<v8::Int32Array>(&arrays_vector, isolate, arrayBuffer,
413                                           "[[Int32Array]]", length / 4);
414   }
415 
416   if (tryCatch.HasCaught()) return;
417   v8::Local<v8::Context> context = isolate->GetCurrentContext();
418   v8::Local<v8::Array> arrays =
419       v8::Array::New(isolate, static_cast<uint32_t>(arrays_vector.size()));
420   for (uint32_t i = 0; i < static_cast<uint32_t>(arrays_vector.size()); i++)
421     createDataProperty(context, arrays, i, arrays_vector[i]);
422   if (tryCatch.HasCaught()) return;
423   info.GetReturnValue().Set(arrays);
424 }
425 
426 }  // namespace v8_inspector
427