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 3 // found in the LICENSE file. 4 5 #include "src/inspector/v8-value-copier.h" 6 7 namespace v8_inspector { 8 9 namespace { 10 11 static int kMaxDepth = 20; 12 static int kMaxCalls = 1000; 13 14 class V8ValueCopier { 15 public: 16 v8::MaybeLocal<v8::Value> copy(v8::Local<v8::Value> value, int depth) { 17 if (++m_calls > kMaxCalls || depth > kMaxDepth) 18 return v8::MaybeLocal<v8::Value>(); 19 20 if (value.IsEmpty()) return v8::MaybeLocal<v8::Value>(); 21 if (value->IsNull() || value->IsUndefined() || value->IsBoolean() || 22 value->IsString() || value->IsNumber()) 23 return value; 24 if (!value->IsObject()) return v8::MaybeLocal<v8::Value>(); 25 v8::Local<v8::Object> object = value.As<v8::Object>(); 26 if (object->CreationContext() != m_from) return value; 27 28 if (object->IsArray()) { 29 v8::Local<v8::Array> array = object.As<v8::Array>(); 30 v8::Local<v8::Array> result = v8::Array::New(m_isolate, array->Length()); 31 if (!result->SetPrototype(m_to, v8::Null(m_isolate)).FromMaybe(false)) 32 return v8::MaybeLocal<v8::Value>(); 33 for (uint32_t i = 0; i < array->Length(); ++i) { 34 v8::Local<v8::Value> item; 35 if (!array->Get(m_from, i).ToLocal(&item)) 36 return v8::MaybeLocal<v8::Value>(); 37 v8::Local<v8::Value> copied; 38 if (!copy(item, depth + 1).ToLocal(&copied)) 39 return v8::MaybeLocal<v8::Value>(); 40 if (!createDataProperty(m_to, result, i, copied).FromMaybe(false)) 41 return v8::MaybeLocal<v8::Value>(); 42 } 43 return result; 44 } 45 46 v8::Local<v8::Object> result = v8::Object::New(m_isolate); 47 if (!result->SetPrototype(m_to, v8::Null(m_isolate)).FromMaybe(false)) 48 return v8::MaybeLocal<v8::Value>(); 49 v8::Local<v8::Array> properties; 50 if (!object->GetOwnPropertyNames(m_from).ToLocal(&properties)) 51 return v8::MaybeLocal<v8::Value>(); 52 for (uint32_t i = 0; i < properties->Length(); ++i) { 53 v8::Local<v8::Value> name; 54 if (!properties->Get(m_from, i).ToLocal(&name) || !name->IsString()) 55 return v8::MaybeLocal<v8::Value>(); 56 v8::Local<v8::Value> property; 57 if (!object->Get(m_from, name).ToLocal(&property)) 58 return v8::MaybeLocal<v8::Value>(); 59 v8::Local<v8::Value> copied; 60 if (!copy(property, depth + 1).ToLocal(&copied)) 61 return v8::MaybeLocal<v8::Value>(); 62 if (!createDataProperty(m_to, result, v8::Local<v8::String>::Cast(name), 63 copied) 64 .FromMaybe(false)) 65 return v8::MaybeLocal<v8::Value>(); 66 } 67 return result; 68 } 69 70 v8::Isolate* m_isolate; 71 v8::Local<v8::Context> m_from; 72 v8::Local<v8::Context> m_to; 73 int m_calls; 74 }; 75 76 protocol::Response toProtocolValue(v8::Local<v8::Context> context, 77 v8::Local<v8::Value> value, int maxDepth, 78 std::unique_ptr<protocol::Value>* result) { 79 using protocol::Response; 80 if (value.IsEmpty()) { 81 UNREACHABLE(); 82 return Response::InternalError(); 83 } 84 85 if (!maxDepth) return Response::Error("Object reference chain is too long"); 86 maxDepth--; 87 88 if (value->IsNull() || value->IsUndefined()) { 89 *result = protocol::Value::null(); 90 return Response::OK(); 91 } 92 if (value->IsBoolean()) { 93 *result = 94 protocol::FundamentalValue::create(value.As<v8::Boolean>()->Value()); 95 return Response::OK(); 96 } 97 if (value->IsNumber()) { 98 double doubleValue = value.As<v8::Number>()->Value(); 99 int intValue = static_cast<int>(doubleValue); 100 if (intValue == doubleValue) { 101 *result = protocol::FundamentalValue::create(intValue); 102 return Response::OK(); 103 } 104 *result = protocol::FundamentalValue::create(doubleValue); 105 return Response::OK(); 106 } 107 if (value->IsString()) { 108 *result = 109 protocol::StringValue::create(toProtocolString(value.As<v8::String>())); 110 return Response::OK(); 111 } 112 if (value->IsArray()) { 113 v8::Local<v8::Array> array = value.As<v8::Array>(); 114 std::unique_ptr<protocol::ListValue> inspectorArray = 115 protocol::ListValue::create(); 116 uint32_t length = array->Length(); 117 for (uint32_t i = 0; i < length; i++) { 118 v8::Local<v8::Value> value; 119 if (!array->Get(context, i).ToLocal(&value)) 120 return Response::InternalError(); 121 std::unique_ptr<protocol::Value> element; 122 Response response = toProtocolValue(context, value, maxDepth, &element); 123 if (!response.isSuccess()) return response; 124 inspectorArray->pushValue(std::move(element)); 125 } 126 *result = std::move(inspectorArray); 127 return Response::OK(); 128 } 129 if (value->IsObject()) { 130 std::unique_ptr<protocol::DictionaryValue> jsonObject = 131 protocol::DictionaryValue::create(); 132 v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value); 133 v8::Local<v8::Array> propertyNames; 134 if (!object->GetPropertyNames(context).ToLocal(&propertyNames)) 135 return Response::InternalError(); 136 uint32_t length = propertyNames->Length(); 137 for (uint32_t i = 0; i < length; i++) { 138 v8::Local<v8::Value> name; 139 if (!propertyNames->Get(context, i).ToLocal(&name)) 140 return Response::InternalError(); 141 // FIXME(yurys): v8::Object should support GetOwnPropertyNames 142 if (name->IsString()) { 143 v8::Maybe<bool> hasRealNamedProperty = object->HasRealNamedProperty( 144 context, v8::Local<v8::String>::Cast(name)); 145 if (!hasRealNamedProperty.IsJust() || !hasRealNamedProperty.FromJust()) 146 continue; 147 } 148 v8::Local<v8::String> propertyName; 149 if (!name->ToString(context).ToLocal(&propertyName)) continue; 150 v8::Local<v8::Value> property; 151 if (!object->Get(context, name).ToLocal(&property)) 152 return Response::InternalError(); 153 std::unique_ptr<protocol::Value> propertyValue; 154 Response response = 155 toProtocolValue(context, property, maxDepth, &propertyValue); 156 if (!response.isSuccess()) return response; 157 jsonObject->setValue(toProtocolString(propertyName), 158 std::move(propertyValue)); 159 } 160 *result = std::move(jsonObject); 161 return Response::OK(); 162 } 163 return Response::Error("Object couldn't be returned by value"); 164 } 165 166 } // namespace 167 168 v8::MaybeLocal<v8::Value> copyValueFromDebuggerContext( 169 v8::Isolate* isolate, v8::Local<v8::Context> debuggerContext, 170 v8::Local<v8::Context> toContext, v8::Local<v8::Value> value) { 171 V8ValueCopier copier; 172 copier.m_isolate = isolate; 173 copier.m_from = debuggerContext; 174 copier.m_to = toContext; 175 copier.m_calls = 0; 176 return copier.copy(value, 0); 177 } 178 179 v8::Maybe<bool> createDataProperty(v8::Local<v8::Context> context, 180 v8::Local<v8::Object> object, 181 v8::Local<v8::Name> key, 182 v8::Local<v8::Value> value) { 183 v8::TryCatch tryCatch(context->GetIsolate()); 184 v8::Isolate::DisallowJavascriptExecutionScope throwJs( 185 context->GetIsolate(), 186 v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); 187 return object->CreateDataProperty(context, key, value); 188 } 189 190 v8::Maybe<bool> createDataProperty(v8::Local<v8::Context> context, 191 v8::Local<v8::Array> array, int index, 192 v8::Local<v8::Value> value) { 193 v8::TryCatch tryCatch(context->GetIsolate()); 194 v8::Isolate::DisallowJavascriptExecutionScope throwJs( 195 context->GetIsolate(), 196 v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); 197 return array->CreateDataProperty(context, index, value); 198 } 199 200 protocol::Response toProtocolValue(v8::Local<v8::Context> context, 201 v8::Local<v8::Value> value, 202 std::unique_ptr<protocol::Value>* result) { 203 return toProtocolValue(context, value, 1000, result); 204 } 205 206 } // namespace v8_inspector 207