Home | History | Annotate | Download | only in inspector
      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