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:
copy(v8::Local<v8::Value> value,int depth)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
toProtocolValue(v8::Local<v8::Context> context,v8::Local<v8::Value> value,int maxDepth,std::unique_ptr<protocol::Value> * result)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
copyValueFromDebuggerContext(v8::Isolate * isolate,v8::Local<v8::Context> debuggerContext,v8::Local<v8::Context> toContext,v8::Local<v8::Value> value)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
createDataProperty(v8::Local<v8::Context> context,v8::Local<v8::Object> object,v8::Local<v8::Name> key,v8::Local<v8::Value> value)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
createDataProperty(v8::Local<v8::Context> context,v8::Local<v8::Array> array,int index,v8::Local<v8::Value> value)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
toProtocolValue(v8::Local<v8::Context> context,v8::Local<v8::Value> value,std::unique_ptr<protocol::Value> * result)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