// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/inspector/v8-value-copier.h" namespace v8_inspector { namespace { static int kMaxDepth = 20; static int kMaxCalls = 1000; class V8ValueCopier { public: v8::MaybeLocal copy(v8::Local value, int depth) { if (++m_calls > kMaxCalls || depth > kMaxDepth) return v8::MaybeLocal(); if (value.IsEmpty()) return v8::MaybeLocal(); if (value->IsNull() || value->IsUndefined() || value->IsBoolean() || value->IsString() || value->IsNumber()) return value; if (!value->IsObject()) return v8::MaybeLocal(); v8::Local object = value.As(); if (object->CreationContext() != m_from) return value; if (object->IsArray()) { v8::Local array = object.As(); v8::Local result = v8::Array::New(m_isolate, array->Length()); if (!result->SetPrototype(m_to, v8::Null(m_isolate)).FromMaybe(false)) return v8::MaybeLocal(); for (uint32_t i = 0; i < array->Length(); ++i) { v8::Local item; if (!array->Get(m_from, i).ToLocal(&item)) return v8::MaybeLocal(); v8::Local copied; if (!copy(item, depth + 1).ToLocal(&copied)) return v8::MaybeLocal(); if (!createDataProperty(m_to, result, i, copied).FromMaybe(false)) return v8::MaybeLocal(); } return result; } v8::Local result = v8::Object::New(m_isolate); if (!result->SetPrototype(m_to, v8::Null(m_isolate)).FromMaybe(false)) return v8::MaybeLocal(); v8::Local properties; if (!object->GetOwnPropertyNames(m_from).ToLocal(&properties)) return v8::MaybeLocal(); for (uint32_t i = 0; i < properties->Length(); ++i) { v8::Local name; if (!properties->Get(m_from, i).ToLocal(&name) || !name->IsString()) return v8::MaybeLocal(); v8::Local property; if (!object->Get(m_from, name).ToLocal(&property)) return v8::MaybeLocal(); v8::Local copied; if (!copy(property, depth + 1).ToLocal(&copied)) return v8::MaybeLocal(); if (!createDataProperty(m_to, result, v8::Local::Cast(name), copied) .FromMaybe(false)) return v8::MaybeLocal(); } return result; } v8::Isolate* m_isolate; v8::Local m_from; v8::Local m_to; int m_calls; }; protocol::Response toProtocolValue(v8::Local context, v8::Local value, int maxDepth, std::unique_ptr* result) { using protocol::Response; if (value.IsEmpty()) { UNREACHABLE(); return Response::InternalError(); } if (!maxDepth) return Response::Error("Object reference chain is too long"); maxDepth--; if (value->IsNull() || value->IsUndefined()) { *result = protocol::Value::null(); return Response::OK(); } if (value->IsBoolean()) { *result = protocol::FundamentalValue::create(value.As()->Value()); return Response::OK(); } if (value->IsNumber()) { double doubleValue = value.As()->Value(); int intValue = static_cast(doubleValue); if (intValue == doubleValue) { *result = protocol::FundamentalValue::create(intValue); return Response::OK(); } *result = protocol::FundamentalValue::create(doubleValue); return Response::OK(); } if (value->IsString()) { *result = protocol::StringValue::create(toProtocolString(value.As())); return Response::OK(); } if (value->IsArray()) { v8::Local array = value.As(); std::unique_ptr inspectorArray = protocol::ListValue::create(); uint32_t length = array->Length(); for (uint32_t i = 0; i < length; i++) { v8::Local value; if (!array->Get(context, i).ToLocal(&value)) return Response::InternalError(); std::unique_ptr element; Response response = toProtocolValue(context, value, maxDepth, &element); if (!response.isSuccess()) return response; inspectorArray->pushValue(std::move(element)); } *result = std::move(inspectorArray); return Response::OK(); } if (value->IsObject()) { std::unique_ptr jsonObject = protocol::DictionaryValue::create(); v8::Local object = v8::Local::Cast(value); v8::Local propertyNames; if (!object->GetPropertyNames(context).ToLocal(&propertyNames)) return Response::InternalError(); uint32_t length = propertyNames->Length(); for (uint32_t i = 0; i < length; i++) { v8::Local name; if (!propertyNames->Get(context, i).ToLocal(&name)) return Response::InternalError(); // FIXME(yurys): v8::Object should support GetOwnPropertyNames if (name->IsString()) { v8::Maybe hasRealNamedProperty = object->HasRealNamedProperty( context, v8::Local::Cast(name)); if (!hasRealNamedProperty.IsJust() || !hasRealNamedProperty.FromJust()) continue; } v8::Local propertyName; if (!name->ToString(context).ToLocal(&propertyName)) continue; v8::Local property; if (!object->Get(context, name).ToLocal(&property)) return Response::InternalError(); std::unique_ptr propertyValue; Response response = toProtocolValue(context, property, maxDepth, &propertyValue); if (!response.isSuccess()) return response; jsonObject->setValue(toProtocolString(propertyName), std::move(propertyValue)); } *result = std::move(jsonObject); return Response::OK(); } return Response::Error("Object couldn't be returned by value"); } } // namespace v8::MaybeLocal copyValueFromDebuggerContext( v8::Isolate* isolate, v8::Local debuggerContext, v8::Local toContext, v8::Local value) { V8ValueCopier copier; copier.m_isolate = isolate; copier.m_from = debuggerContext; copier.m_to = toContext; copier.m_calls = 0; return copier.copy(value, 0); } v8::Maybe createDataProperty(v8::Local context, v8::Local object, v8::Local key, v8::Local value) { v8::TryCatch tryCatch(context->GetIsolate()); v8::Isolate::DisallowJavascriptExecutionScope throwJs( context->GetIsolate(), v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); return object->CreateDataProperty(context, key, value); } v8::Maybe createDataProperty(v8::Local context, v8::Local array, int index, v8::Local value) { v8::TryCatch tryCatch(context->GetIsolate()); v8::Isolate::DisallowJavascriptExecutionScope throwJs( context->GetIsolate(), v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); return array->CreateDataProperty(context, index, value); } protocol::Response toProtocolValue(v8::Local context, v8::Local value, std::unique_ptr* result) { return toProtocolValue(context, value, 1000, result); } } // namespace v8_inspector