| // 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 |