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/inspector/injected-script-native.h" 9 #include "src/inspector/string-util.h" 10 #include "src/inspector/v8-debugger.h" 11 #include "src/inspector/v8-inspector-impl.h" 12 #include "src/inspector/v8-internal-value-type.h" 13 #include "src/inspector/v8-value-copier.h" 14 15 #include "include/v8-inspector.h" 16 17 namespace v8_inspector { 18 19 namespace { 20 21 void setFunctionProperty(v8::Local<v8::Context> context, 22 v8::Local<v8::Object> obj, const char* name, 23 v8::FunctionCallback callback, 24 v8::Local<v8::External> external) { 25 v8::Local<v8::String> funcName = 26 toV8StringInternalized(context->GetIsolate(), name); 27 v8::Local<v8::Function> func; 28 if (!v8::Function::New(context, callback, external, 0, 29 v8::ConstructorBehavior::kThrow) 30 .ToLocal(&func)) 31 return; 32 func->SetName(funcName); 33 createDataProperty(context, obj, funcName, func); 34 } 35 36 V8InspectorImpl* unwrapInspector( 37 const v8::FunctionCallbackInfo<v8::Value>& info) { 38 DCHECK(!info.Data().IsEmpty()); 39 DCHECK(info.Data()->IsExternal()); 40 V8InspectorImpl* inspector = 41 static_cast<V8InspectorImpl*>(info.Data().As<v8::External>()->Value()); 42 DCHECK(inspector); 43 return inspector; 44 } 45 46 } // namespace 47 48 v8::Local<v8::Object> V8InjectedScriptHost::create( 49 v8::Local<v8::Context> context, V8InspectorImpl* inspector) { 50 v8::Isolate* isolate = inspector->isolate(); 51 v8::Local<v8::Object> injectedScriptHost = v8::Object::New(isolate); 52 bool success = injectedScriptHost->SetPrototype(context, v8::Null(isolate)) 53 .FromMaybe(false); 54 DCHECK(success); 55 USE(success); 56 v8::Local<v8::External> debuggerExternal = 57 v8::External::New(isolate, inspector); 58 setFunctionProperty(context, injectedScriptHost, "internalConstructorName", 59 V8InjectedScriptHost::internalConstructorNameCallback, 60 debuggerExternal); 61 setFunctionProperty( 62 context, injectedScriptHost, "formatAccessorsAsProperties", 63 V8InjectedScriptHost::formatAccessorsAsProperties, debuggerExternal); 64 setFunctionProperty(context, injectedScriptHost, "subtype", 65 V8InjectedScriptHost::subtypeCallback, debuggerExternal); 66 setFunctionProperty(context, injectedScriptHost, "getInternalProperties", 67 V8InjectedScriptHost::getInternalPropertiesCallback, 68 debuggerExternal); 69 setFunctionProperty(context, injectedScriptHost, "objectHasOwnProperty", 70 V8InjectedScriptHost::objectHasOwnPropertyCallback, 71 debuggerExternal); 72 setFunctionProperty(context, injectedScriptHost, "bind", 73 V8InjectedScriptHost::bindCallback, debuggerExternal); 74 setFunctionProperty(context, injectedScriptHost, "proxyTargetValue", 75 V8InjectedScriptHost::proxyTargetValueCallback, 76 debuggerExternal); 77 return injectedScriptHost; 78 } 79 80 void V8InjectedScriptHost::internalConstructorNameCallback( 81 const v8::FunctionCallbackInfo<v8::Value>& info) { 82 if (info.Length() < 1 || !info[0]->IsObject()) return; 83 84 v8::Local<v8::Object> object = info[0].As<v8::Object>(); 85 info.GetReturnValue().Set(object->GetConstructorName()); 86 } 87 88 void V8InjectedScriptHost::formatAccessorsAsProperties( 89 const v8::FunctionCallbackInfo<v8::Value>& info) { 90 DCHECK_EQ(info.Length(), 2); 91 info.GetReturnValue().Set(false); 92 if (!info[1]->IsFunction()) return; 93 // Check that function is user-defined. 94 if (info[1].As<v8::Function>()->ScriptId() != v8::UnboundScript::kNoScriptId) 95 return; 96 info.GetReturnValue().Set( 97 unwrapInspector(info)->client()->formatAccessorsAsProperties(info[0])); 98 } 99 100 void V8InjectedScriptHost::subtypeCallback( 101 const v8::FunctionCallbackInfo<v8::Value>& info) { 102 if (info.Length() < 1) return; 103 104 v8::Isolate* isolate = info.GetIsolate(); 105 v8::Local<v8::Value> value = info[0]; 106 if (value->IsObject()) { 107 v8::Local<v8::Value> internalType = v8InternalValueTypeFrom( 108 isolate->GetCurrentContext(), v8::Local<v8::Object>::Cast(value)); 109 if (internalType->IsString()) { 110 info.GetReturnValue().Set(internalType); 111 return; 112 } 113 } 114 if (value->IsArray() || value->IsArgumentsObject()) { 115 info.GetReturnValue().Set(toV8StringInternalized(isolate, "array")); 116 return; 117 } 118 if (value->IsTypedArray()) { 119 info.GetReturnValue().Set(toV8StringInternalized(isolate, "typedarray")); 120 return; 121 } 122 if (value->IsDate()) { 123 info.GetReturnValue().Set(toV8StringInternalized(isolate, "date")); 124 return; 125 } 126 if (value->IsRegExp()) { 127 info.GetReturnValue().Set(toV8StringInternalized(isolate, "regexp")); 128 return; 129 } 130 if (value->IsMap() || value->IsWeakMap()) { 131 info.GetReturnValue().Set(toV8StringInternalized(isolate, "map")); 132 return; 133 } 134 if (value->IsSet() || value->IsWeakSet()) { 135 info.GetReturnValue().Set(toV8StringInternalized(isolate, "set")); 136 return; 137 } 138 if (value->IsMapIterator() || value->IsSetIterator()) { 139 info.GetReturnValue().Set(toV8StringInternalized(isolate, "iterator")); 140 return; 141 } 142 if (value->IsGeneratorObject()) { 143 info.GetReturnValue().Set(toV8StringInternalized(isolate, "generator")); 144 return; 145 } 146 if (value->IsNativeError()) { 147 info.GetReturnValue().Set(toV8StringInternalized(isolate, "error")); 148 return; 149 } 150 if (value->IsProxy()) { 151 info.GetReturnValue().Set(toV8StringInternalized(isolate, "proxy")); 152 return; 153 } 154 if (value->IsPromise()) { 155 info.GetReturnValue().Set(toV8StringInternalized(isolate, "promise")); 156 return; 157 } 158 std::unique_ptr<StringBuffer> subtype = 159 unwrapInspector(info)->client()->valueSubtype(value); 160 if (subtype) { 161 info.GetReturnValue().Set(toV8String(isolate, subtype->string())); 162 return; 163 } 164 } 165 166 void V8InjectedScriptHost::getInternalPropertiesCallback( 167 const v8::FunctionCallbackInfo<v8::Value>& info) { 168 if (info.Length() < 1) return; 169 170 std::unordered_set<String16> allowedProperties; 171 if (info[0]->IsBooleanObject() || info[0]->IsNumberObject() || 172 info[0]->IsStringObject() || info[0]->IsSymbolObject()) { 173 allowedProperties.insert(String16("[[PrimitiveValue]]")); 174 } else if (info[0]->IsPromise()) { 175 allowedProperties.insert(String16("[[PromiseStatus]]")); 176 allowedProperties.insert(String16("[[PromiseValue]]")); 177 } else if (info[0]->IsGeneratorObject()) { 178 allowedProperties.insert(String16("[[GeneratorStatus]]")); 179 } else if (info[0]->IsMapIterator() || info[0]->IsSetIterator()) { 180 allowedProperties.insert(String16("[[IteratorHasMore]]")); 181 allowedProperties.insert(String16("[[IteratorIndex]]")); 182 allowedProperties.insert(String16("[[IteratorKind]]")); 183 allowedProperties.insert(String16("[[Entries]]")); 184 } else if (info[0]->IsMap() || info[0]->IsWeakMap() || info[0]->IsSet() || 185 info[0]->IsWeakSet()) { 186 allowedProperties.insert(String16("[[Entries]]")); 187 } 188 if (!allowedProperties.size()) return; 189 190 v8::Isolate* isolate = info.GetIsolate(); 191 v8::Local<v8::Array> allProperties; 192 if (!unwrapInspector(info) 193 ->debugger() 194 ->internalProperties(isolate->GetCurrentContext(), info[0]) 195 .ToLocal(&allProperties) || 196 !allProperties->IsArray() || allProperties->Length() % 2 != 0) 197 return; 198 199 { 200 v8::Local<v8::Context> context = isolate->GetCurrentContext(); 201 v8::TryCatch tryCatch(isolate); 202 v8::Isolate::DisallowJavascriptExecutionScope throwJs( 203 isolate, 204 v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); 205 206 v8::Local<v8::Array> properties = v8::Array::New(isolate); 207 if (tryCatch.HasCaught()) return; 208 209 uint32_t outputIndex = 0; 210 for (uint32_t i = 0; i < allProperties->Length(); i += 2) { 211 v8::Local<v8::Value> key; 212 if (!allProperties->Get(context, i).ToLocal(&key)) continue; 213 if (tryCatch.HasCaught()) { 214 tryCatch.Reset(); 215 continue; 216 } 217 String16 keyString = toProtocolStringWithTypeCheck(key); 218 if (keyString.isEmpty() || 219 allowedProperties.find(keyString) == allowedProperties.end()) 220 continue; 221 v8::Local<v8::Value> value; 222 if (!allProperties->Get(context, i + 1).ToLocal(&value)) continue; 223 if (tryCatch.HasCaught()) { 224 tryCatch.Reset(); 225 continue; 226 } 227 createDataProperty(context, properties, outputIndex++, key); 228 createDataProperty(context, properties, outputIndex++, value); 229 } 230 info.GetReturnValue().Set(properties); 231 } 232 } 233 234 void V8InjectedScriptHost::objectHasOwnPropertyCallback( 235 const v8::FunctionCallbackInfo<v8::Value>& info) { 236 if (info.Length() < 2 || !info[0]->IsObject() || !info[1]->IsString()) return; 237 bool result = info[0] 238 .As<v8::Object>() 239 ->HasOwnProperty(info.GetIsolate()->GetCurrentContext(), 240 v8::Local<v8::String>::Cast(info[1])) 241 .FromMaybe(false); 242 info.GetReturnValue().Set(v8::Boolean::New(info.GetIsolate(), result)); 243 } 244 245 void V8InjectedScriptHost::bindCallback( 246 const v8::FunctionCallbackInfo<v8::Value>& info) { 247 if (info.Length() < 2 || !info[1]->IsString()) return; 248 InjectedScriptNative* injectedScriptNative = 249 InjectedScriptNative::fromInjectedScriptHost(info.GetIsolate(), 250 info.Holder()); 251 if (!injectedScriptNative) return; 252 253 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); 254 v8::Local<v8::String> v8groupName = 255 info[1]->ToString(context).ToLocalChecked(); 256 String16 groupName = toProtocolStringWithTypeCheck(v8groupName); 257 int id = injectedScriptNative->bind(info[0], groupName); 258 info.GetReturnValue().Set(id); 259 } 260 261 void V8InjectedScriptHost::proxyTargetValueCallback( 262 const v8::FunctionCallbackInfo<v8::Value>& info) { 263 if (info.Length() != 1 || !info[0]->IsProxy()) { 264 UNREACHABLE(); 265 return; 266 } 267 v8::Local<v8::Object> target = info[0].As<v8::Proxy>(); 268 while (target->IsProxy()) 269 target = v8::Local<v8::Proxy>::Cast(target)->GetTarget(); 270 info.GetReturnValue().Set(target); 271 } 272 273 } // namespace v8_inspector 274