blob: b9912263f6c7084677f4b8caf628f14f75ae4c66 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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/developer/shell/josh/lib/object_converter.h"
#include "src/developer/shell/josh/lib/zx.h"
#include "src/lib/fidl_codec/wire_object.h"
namespace shell::fidl {
bool ObjectConverter::HandleNull(const fidl_codec::Type* type) {
if (!JS_IsNull(value_) && !JS_IsUndefined(value_)) {
return false;
} else if (!type->Nullable()) {
JS_ThrowTypeError(ctx_, "Type is not nullable.");
} else {
result_ = std::make_unique<fidl_codec::NullValue>();
}
return true;
}
void ObjectConverter::VisitType(const fidl_codec::Type* type) {
JS_ThrowTypeError(ctx_, "Unknown FIDL type '%s'.", type->Name().c_str());
}
void ObjectConverter::VisitTableType(const fidl_codec::TableType* type) {
if (!JS_IsObject(value_)) {
JS_ThrowTypeError(ctx_, "Expected object.");
return;
}
auto ret = std::make_unique<fidl_codec::TableValue>(type->table_definition());
for (const auto& member : type->table_definition().members()) {
auto value = JS_GetPropertyStr(ctx_, value_, member->name().c_str());
if (JS_IsUndefined(value)) {
continue;
}
auto child = ObjectConverter::Convert(ctx_, member->type(), value);
if (!child) {
return;
}
ret->AddMember(member.get(), std::move(child));
}
result_ = std::move(ret);
}
void ObjectConverter::VisitStringType(const fidl_codec::StringType* type) {
if (HandleNull(type)) {
return;
}
size_t len;
const char* str = JS_ToCStringLen(ctx_, &len, value_);
if (str) {
result_ = std::make_unique<fidl_codec::StringValue>(std::string(str, len));
}
}
void ObjectConverter::VisitBoolType(const fidl_codec::BoolType* type) {
int got = JS_ToBool(ctx_, value_);
// -1 indicates a problem.
if (got >= 0) {
result_ = std::make_unique<fidl_codec::BoolValue>(got ? 1 : 0);
}
}
void ObjectConverter::VisitStructType(const fidl_codec::StructType* type) {
if (HandleNull(type)) {
return;
}
std::function<JSValue(const std::string&)> get_item = [this](const std::string& name) mutable {
return JS_GetPropertyStr(ctx_, value_, name.c_str());
};
uint32_t idx = 0;
if (JS_IsArray(ctx_, value_)) {
get_item = [this, &idx](const std::string& /*name*/) mutable {
return JS_GetPropertyUint32(ctx_, value_, idx++);
};
} else if (!JS_IsObject(value_)) {
JS_ThrowTypeError(ctx_, "Expected object.");
return;
}
auto ret = std::make_unique<fidl_codec::StructValue>(type->struct_definition());
for (const auto& member : type->struct_definition().members()) {
auto child = ObjectConverter::Convert(ctx_, member->type(), get_item(member->name()));
if (!child) {
return;
}
ret->AddField(member.get(), std::move(child));
}
result_ = std::move(ret);
}
void ObjectConverter::VisitUnionType(const fidl_codec::UnionType* type) {
if (HandleNull(type)) {
return;
}
if (!JS_IsObject(value_)) {
JS_ThrowTypeError(ctx_, "Expected object.");
return;
}
for (const auto& member : type->union_definition().members()) {
auto result = JS_GetPropertyStr(ctx_, value_, member->name().c_str());
if (JS_IsUndefined(result)) {
continue;
}
auto result_converted = ObjectConverter::Convert(ctx_, member->type(), result);
if (result_converted) {
result_ = std::make_unique<fidl_codec::UnionValue>(*member, std::move(result_converted));
}
return;
}
JS_ThrowTypeError(ctx_, "Unknown union variant.");
}
template <typename T>
void ObjectConverter::VisitAnyList(const T* type, std::optional<size_t> count) {
if (!count && HandleNull(type)) {
return;
}
int32_t length;
if (!JS_IsArray(ctx_, value_)) {
JS_ThrowTypeError(ctx_, "Expected array.");
return;
}
// It's an array, so assume this works...
JS_ToInt32(ctx_, &length, JS_GetPropertyStr(ctx_, value_, "length"));
if (count && static_cast<uint32_t>(length) != *count) {
JS_ThrowTypeError(ctx_, "Expected array of size %lu", *count);
}
auto ret = std::make_unique<fidl_codec::VectorValue>();
for (int32_t i = 0; i < length; i++) {
JSValue val = JS_GetPropertyUint32(ctx_, value_, i);
auto got = ObjectConverter::Convert(ctx_, type->component_type(), val);
if (!got) {
return;
}
ret->AddValue(std::move(got));
}
result_ = std::move(ret);
}
void ObjectConverter::VisitArrayType(const fidl_codec::ArrayType* type) {
VisitAnyList(type, type->count());
}
void ObjectConverter::VisitVectorType(const fidl_codec::VectorType* type) {
VisitAnyList(type, std::nullopt);
}
void ObjectConverter::VisitEnumType(const fidl_codec::EnumType* type) {
size_t len;
const char* str = JS_ToCStringLen(ctx_, &len, value_);
if (str) {
auto name = std::string(str, len);
for (const auto& member : type->enum_definition().members()) {
if (name == member.name()) {
result_ =
std::make_unique<fidl_codec::IntegerValue>(member.absolute_value(), member.negative());
}
}
JS_ThrowTypeError(ctx_, "Unexpected enum value: '%s'", name.c_str());
}
}
void ObjectConverter::VisitBitsType(const fidl_codec::BitsType* type) {
size_t len;
const char* str = JS_ToCStringLen(ctx_, &len, value_);
if (!str) {
return;
}
auto value_string = std::string(str, len);
std::map<std::string, std::unique_ptr<fidl_codec::Value>> values;
size_t pos = 0;
while (pos < value_string.size()) {
std::string next_key;
size_t next = value_string.find('|', pos);
if (next == std::string::npos) {
next_key = value_string.substr(pos);
pos = value_string.size();
} else {
next_key = value_string.substr(pos, next - pos);
pos = next + 1;
}
values[next_key] = std::unique_ptr<fidl_codec::Value>();
}
uint64_t out = 0;
for (const auto& member : type->bits_definition().members()) {
if (values.find(member.name()) != values.end()) {
out |= member.absolute_value();
}
}
result_ = std::make_unique<fidl_codec::IntegerValue>(out, false);
}
void ObjectConverter::VisitHandleType(const fidl_codec::HandleType* type) {
if (HandleNull(type)) {
return;
}
JSValue handle = JS_GetPropertyStr(ctx_, value_, "_handle");
result_ = std::make_unique<fidl_codec::HandleValue>(zx::HandleFromJsval(handle));
}
void ObjectConverter::VisitAnyInteger(bool is_signed) {
int64_t got;
if (JS_ToInt64(ctx_, &got, value_) != -1) {
bool negate = is_signed && got < 0;
if (negate) {
got = -got;
}
result_ = std::make_unique<fidl_codec::IntegerValue>(static_cast<uint64_t>(got), negate);
}
}
void ObjectConverter::VisitUint8Type(const fidl_codec::Uint8Type* type) { VisitAnyInteger(false); }
void ObjectConverter::VisitUint16Type(const fidl_codec::Uint16Type* type) {
VisitAnyInteger(false);
}
void ObjectConverter::VisitUint32Type(const fidl_codec::Uint32Type* type) {
VisitAnyInteger(false);
}
void ObjectConverter::VisitUint64Type(const fidl_codec::Uint64Type* type) {
VisitAnyInteger(false);
}
void ObjectConverter::VisitInt8Type(const fidl_codec::Int8Type* type) { VisitAnyInteger(true); }
void ObjectConverter::VisitInt16Type(const fidl_codec::Int16Type* type) { VisitAnyInteger(true); }
void ObjectConverter::VisitInt32Type(const fidl_codec::Int32Type* type) { VisitAnyInteger(true); }
void ObjectConverter::VisitInt64Type(const fidl_codec::Int64Type* type) { VisitAnyInteger(true); }
void ObjectConverter::VisitAnyFloat() {
double got;
if (JS_ToFloat64(ctx_, &got, value_) != -1) {
result_ = std::make_unique<fidl_codec::DoubleValue>(got);
}
}
void ObjectConverter::VisitFloat32Type(const fidl_codec::Float32Type* type) { VisitAnyFloat(); }
void ObjectConverter::VisitFloat64Type(const fidl_codec::Float64Type* type) { VisitAnyFloat(); }
} // namespace shell::fidl