static RefPtr<InspectorValue> jsToInspectorValue(ExecState* scriptState, JSValue value, int maxDepth)
{
    if (!value) {
        ASSERT_NOT_REACHED();
        return nullptr;
    }

    if (!maxDepth)
        return nullptr;

    maxDepth--;

    if (value.isNull() || value.isUndefined())
        return InspectorValue::null();
    if (value.isBoolean())
        return InspectorValue::create(value.asBoolean());
    if (value.isNumber() && value.isDouble())
        return InspectorValue::create(value.asNumber());
    if (value.isNumber() && value.isMachineInt())
        return InspectorValue::create(static_cast<int>(value.asMachineInt()));
    if (value.isString())
        return InspectorValue::create(value.getString(scriptState));

    if (value.isObject()) {
        if (isJSArray(value)) {
            Ref<InspectorArray> inspectorArray = InspectorArray::create();
            JSArray* array = asArray(value);
            unsigned length = array->length();
            for (unsigned i = 0; i < length; i++) {
                JSValue element = array->getIndex(scriptState, i);
                RefPtr<InspectorValue> elementValue = jsToInspectorValue(scriptState, element, maxDepth);
                if (!elementValue)
                    return nullptr;
                inspectorArray->pushValue(WTFMove(elementValue));
            }
            return WTFMove(inspectorArray);
        }
        Ref<InspectorObject> inspectorObject = InspectorObject::create();
        JSObject* object = value.getObject();
        PropertyNameArray propertyNames(scriptState, PropertyNameMode::Strings);
        object->methodTable()->getOwnPropertyNames(object, scriptState, propertyNames, EnumerationMode());
        for (size_t i = 0; i < propertyNames.size(); i++) {
            const Identifier& name = propertyNames[i];
            JSValue propertyValue = object->get(scriptState, name);
            RefPtr<InspectorValue> inspectorValue = jsToInspectorValue(scriptState, propertyValue, maxDepth);
            if (!inspectorValue)
                return nullptr;
            inspectorObject->setValue(name.string(), WTFMove(inspectorValue));
        }
        return WTFMove(inspectorObject);
    }

    ASSERT_NOT_REACHED();
    return nullptr;
}
static PassRefPtr<InspectorValue> jsToInspectorValue(ScriptState* scriptState, JSValue value, int maxDepth)
{
    if (!value) {
        ASSERT_NOT_REACHED();
        return 0;
    }

    if (!maxDepth)
        return 0;
    maxDepth--;

    if (value.isNull() || value.isUndefined())
        return InspectorValue::null();
    if (value.isBoolean())
        return InspectorBasicValue::create(value.asBoolean());
    if (value.isNumber())
        return InspectorBasicValue::create(value.asNumber());
    if (value.isString()) {
        String s = value.getString(scriptState);
        return InspectorString::create(String(s.characters(), s.length()));
    }
    if (value.isObject()) {
        if (isJSArray(value)) {
            RefPtr<InspectorArray> inspectorArray = InspectorArray::create();
            JSArray* array = asArray(value);
            unsigned length = array->length();
            for (unsigned i = 0; i < length; i++) {
                // FIXME: What if the array is in sparse mode? https://bugs.webkit.org/show_bug.cgi?id=95610
                JSValue element = array->getIndexQuickly(i);
                RefPtr<InspectorValue> elementValue = jsToInspectorValue(scriptState, element, maxDepth);
                if (!elementValue)
                    return 0;
                inspectorArray->pushValue(elementValue);
            }
            return inspectorArray;
        }
        RefPtr<InspectorObject> inspectorObject = InspectorObject::create();
        JSObject* object = value.getObject();
        PropertyNameArray propertyNames(scriptState);
        object->methodTable()->getOwnPropertyNames(object, scriptState, propertyNames, ExcludeDontEnumProperties);
        for (size_t i = 0; i < propertyNames.size(); i++) {
            const Identifier& name =  propertyNames[i];
            JSValue propertyValue = object->get(scriptState, name);
            RefPtr<InspectorValue> inspectorValue = jsToInspectorValue(scriptState, propertyValue, maxDepth);
            if (!inspectorValue)
                return 0;
            inspectorObject->setValue(String(name.characters(), name.length()), inspectorValue);
        }
        return inspectorObject;
    }
    ASSERT_NOT_REACHED();
    return 0;
}
static PassRefPtr<InspectorValue> jsToInspectorValue(ScriptState* scriptState, JSValue value)
{
    if (!value) {
        ASSERT_NOT_REACHED();
        return 0;
    }
    if (value.isNull() || value.isUndefined())
        return InspectorValue::null();
    if (value.isBoolean())
        return InspectorBasicValue::create(value.getBoolean());
    if (value.isNumber())
        return InspectorBasicValue::create(value.uncheckedGetNumber());
    if (value.isString()) {
        UString s = value.getString(scriptState);
        return InspectorString::create(String(s.characters(), s.length()));
    }
    if (value.isObject()) {
        if (isJSArray(&scriptState->globalData(), value)) {
            RefPtr<InspectorArray> inspectorArray = InspectorArray::create();
            JSArray* array = asArray(value);
            unsigned length = array->length();
            for (unsigned i = 0; i < length; i++) {
                JSValue element = array->getIndex(i);
                RefPtr<InspectorValue> elementValue = jsToInspectorValue(scriptState, element);
                if (!elementValue) {
                    ASSERT_NOT_REACHED();
                    elementValue = InspectorValue::null();
                }
                inspectorArray->pushValue(elementValue);
            }
            return inspectorArray;
        }
        RefPtr<InspectorObject> inspectorObject = InspectorObject::create();
        JSObject* object = value.getObject();
        PropertyNameArray propertyNames(scriptState);
        object->getOwnPropertyNames(scriptState, propertyNames);
        for (size_t i = 0; i < propertyNames.size(); i++) {
            const Identifier& name =  propertyNames[i];
            JSValue propertyValue = object->get(scriptState, name);
            RefPtr<InspectorValue> inspectorValue = jsToInspectorValue(scriptState, propertyValue);
            if (!inspectorValue) {
                ASSERT_NOT_REACHED();
                inspectorValue = InspectorValue::null();
            }
            inspectorObject->setValue(String(name.characters(), name.length()), inspectorValue);
        }
        return inspectorObject;
    }
    ASSERT_NOT_REACHED();
    return 0;
}
PassRefPtr<InspectorValue> ScriptValue::toInspectorValue(ScriptState* scriptState) const
{
    JSC::JSLockHolder holder(scriptState);
    return jsToInspectorValue(scriptState, m_value.get(), InspectorValue::maxDepth);
}
PassRefPtr<InspectorValue> ScriptValue::toInspectorValue(ScriptState* scriptState) const
{
    return jsToInspectorValue(scriptState, m_value.get());
}