blob: e56e986124b3c02842c3b2cb5a6db627fffda0a0 [file] [log] [blame]
// Copyright 2020 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.
#ifndef SRC_DEVELOPER_SHELL_INTERPRETER_SRC_SERVER_H_
#define SRC_DEVELOPER_SHELL_INTERPRETER_SRC_SERVER_H_
#include <fidl/fuchsia.shell/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/stdcompat/optional.h>
#include <zircon/status.h>
#include <map>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "src/developer/shell/interpreter/src/expressions.h"
#include "src/developer/shell/interpreter/src/interpreter.h"
#include "src/developer/shell/interpreter/src/schema.h"
namespace shell {
namespace interpreter {
namespace server {
class Server;
class Service;
// Holds a context at the server level.
class ServerInterpreterContext {
public:
explicit ServerInterpreterContext(ExecutionContext* execution_context)
: execution_context_(execution_context) {}
ExecutionContext* execution_context() const { return execution_context_; }
// True if there are unused AST nodes.
bool PendingNodes() const { return !expressions_.empty() || !instructions_.empty(); }
// Adds an expression to the context. This expression must be used later by another node.
void AddExpression(std::unique_ptr<Expression> expression) {
expressions_.emplace(expression->id(), std::move(expression));
}
// Adds an instruction to the context. This instruction must be used later by another node.
void AddInstruction(std::unique_ptr<Instruction> instruction) {
instructions_.emplace(instruction->id(), std::move(instruction));
}
// Adds a field schema definition to the context. This definition must be used later by another
// node.
void AddObjectFieldSchema(std::shared_ptr<ObjectFieldSchema> field) {
object_field_schemas_.emplace(field->id(), field);
}
// Adds an object field to the context. This definition must be used later by another node.
void AddObjectField(std::unique_ptr<ObjectDeclarationField> field) {
fields_.emplace(field->id(), std::move(field));
}
// Retrieves the expression for the given node id. If the expression is found, the expression is
// removes from the waiting instruction map.
std::unique_ptr<Expression> GetExpression(const NodeId& node_id);
// Retrieves the field definition for the given node id.
std::unique_ptr<ObjectDeclarationField> GetObjectField(const NodeId& node_id);
// Retrieves the field definition for the given node id.
std::shared_ptr<ObjectFieldSchema> GetObjectFieldSchema(const NodeId& node_id);
private:
// The execution context (interpreter level) associated with this context.
ExecutionContext* const execution_context_;
// All the expressions waiting to be used.
std::map<NodeId, std::unique_ptr<Expression>> expressions_;
// All the instructions waiting to be used.
std::map<NodeId, std::unique_ptr<Instruction>> instructions_;
// All of the fields waiting to be used.
std::map<NodeId, std::unique_ptr<ObjectDeclarationField>> fields_;
// All of the fields waiting to be used.
std::map<NodeId, std::shared_ptr<ObjectFieldSchema>> object_field_schemas_;
};
// Defines an interpreter managed by a server.
class ServerInterpreter : public Interpreter {
public:
explicit ServerInterpreter(Service* service) : service_(service) {}
void EmitError(ExecutionContext* context, std::string error_message) override;
void EmitError(ExecutionContext* context, NodeId node_id, std::string error_message) override;
void DumpDone(ExecutionContext* context) override;
void ContextDone(ExecutionContext* context) override;
void ContextDoneWithAnalysisError(ExecutionContext* context) override;
void ContextDoneWithExecutionError(ExecutionContext* context) override;
void TextResult(ExecutionContext* context, std::string_view text) override;
void Result(ExecutionContext* context, const Value& result) override;
// Gets the server context for the given id.
ServerInterpreterContext* GetServerContext(uint64_t id) {
auto context = contexts_.find(id);
if (context != contexts_.end()) {
return context->second.get();
}
return nullptr;
}
// Creates a server context associated with the interpreter context.
void CreateServerContext(ExecutionContext* context);
// Erases a server context.
void EraseServerContext(uint64_t context_id) { contexts_.erase(context_id); }
// Adds an expression to this context. The expression then waits to be used by another node.
// The argument root_node should always be false.
void AddExpression(ServerInterpreterContext* context, std::unique_ptr<Expression> expression,
bool root_node);
// Adds an instruction to this context. If root_node is true, the instruction is added to the
// interpreter context's pending instruction list.
// If global_node is false, the instruction waits to be used by another node.
void AddInstruction(ServerInterpreterContext* context, std::unique_ptr<Instruction> instruction,
bool global_node);
// Adds a object schema definition to this context. The definition can then be referred to by
// other nodes. The argument root_node should always be false.
void AddObjectSchema(ServerInterpreterContext* context, std::shared_ptr<ObjectSchema> definition,
bool root_node);
void AddObjectFieldSchema(ServerInterpreterContext* context,
std::shared_ptr<ObjectFieldSchema> definitions, bool root_node);
// Adds a field to this context. The definition can then be referred to by
// other nodes. The argument root_node should always be false.
void AddObjectField(ServerInterpreterContext* context,
std::unique_ptr<ObjectDeclarationField> definition, bool root_node);
// Retrives the expression for the given context/node id. If the expression is not found, it emits
// an error.
std::unique_ptr<Expression> GetNullableExpression(ServerInterpreterContext* context,
const NodeId& node_id);
// Retrives the expression for the given context/node id. If the expression is not found, or if
// the expression is null, it emits an error.
std::unique_ptr<Expression> GetExpression(ServerInterpreterContext* context,
const NodeId& container_id, const std::string& member,
const NodeId& node_id);
std::shared_ptr<ObjectFieldSchema> GetObjectFieldSchema(ServerInterpreterContext* context,
const NodeId& node_id);
private:
// The service which currently holds the interpreter.
Service* service_;
// All the server contexts.
std::map<uint64_t, std::unique_ptr<ServerInterpreterContext>> contexts_;
};
// Defines a connection from a client to the interpreter.
class Service final : public fidl::WireServer<fuchsia_shell::Shell> {
public:
explicit Service(Server* server)
: server_(server), interpreter_(std::make_unique<ServerInterpreter>(this)) {}
~Service() final;
Interpreter* interpreter() const { return interpreter_.get(); }
// Sets the binding related to this connection which allows sending events.
// It should be called right after message dispatching is scheduled
// to happen on this Service.
void set_binding(fidl::ServerBindingRef<fuchsia_shell::Shell> binding) {
binding_.emplace(std::move(binding));
}
fidl::ServerBindingRef<fuchsia_shell::Shell>& binding() { return binding_.value(); }
// Called by |Server| to notify that the |Server| object will be destroyed.
// The service should close its connection and schedule its destruction too.
void OnServerShutdown();
void CreateExecutionContext(CreateExecutionContextRequestView request,
CreateExecutionContextCompleter::Sync& completer) override;
void AddNodes(AddNodesRequestView request, AddNodesCompleter::Sync& _completer) override;
void DumpExecutionContext(DumpExecutionContextRequestView request,
DumpExecutionContextCompleter::Sync& completer) override;
void ExecuteExecutionContext(ExecuteExecutionContextRequestView request,
ExecuteExecutionContextCompleter::Sync& completer) override;
void Shutdown(ShutdownRequestView request, ShutdownCompleter::Sync& completer) override;
// Helpers to be able to send events to the client.
fidl::Status OnError(uint64_t context_id,
fidl::VectorView<fuchsia_shell::wire::Location>&& locations,
const std::string& error_message) {
return fidl::WireSendEvent(binding_.value())
->OnError(context_id, std::move(locations), fidl::StringView::FromExternal(error_message));
}
fidl::Status OnError(uint64_t context_id, const std::string& error_message) {
fidl::VectorView<fuchsia_shell::wire::Location> locations;
return OnError(context_id, std::move(locations), error_message);
}
fidl::Status OnDumpDone(uint64_t context_id) {
return fidl::WireSendEvent(binding_.value())->OnDumpDone(context_id);
}
fidl::Status OnExecutionDone(uint64_t context_id, fuchsia_shell::wire::ExecuteResult result) {
return fidl::WireSendEvent(binding_.value())->OnExecutionDone(context_id, result);
}
fidl::Status OnTextResult(uint64_t context_id, const std::string& result, bool partial_result) {
return fidl::WireSendEvent(binding_.value())
->OnTextResult(context_id, fidl::StringView::FromExternal(result), partial_result);
}
fidl::Status OnResult(uint64_t context_id, fidl::VectorView<fuchsia_shell::wire::Node>&& nodes,
bool partial_result) {
return fidl::WireSendEvent(binding_.value())
->OnResult(context_id, std::move(nodes), partial_result);
}
private:
// Helpers to be able to create AST nodes.
void AddIntegerLiteral(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id, const fuchsia_shell::wire::IntegerLiteral& node,
bool root_node);
void AddVariableDefinition(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id,
const fuchsia_shell::wire::VariableDefinition& node, bool root_node);
void AddObjectSchema(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id,
const fuchsia_shell::wire::ObjectSchemaDefinition& node, bool root_node);
void AddObjectSchemaField(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id,
const fuchsia_shell::wire::ObjectFieldSchemaDefinition& field_type,
bool root_node);
void AddObject(ServerInterpreterContext* context, uint64_t node_file_id, uint64_t node_node_id,
const fuchsia_shell::wire::ObjectDefinition& node, bool root_node);
void AddObjectField(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id,
const fuchsia_shell::wire::ObjectFieldDefinition& field_type, bool root_node);
void AddStringLiteral(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id, const ::fidl::StringView& node, bool root_node);
void AddVariable(ServerInterpreterContext* context, uint64_t node_file_id, uint64_t node_node_id,
const fidl::StringView& name, bool root_node);
void AddEmitResult(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id, const fuchsia_shell::wire::NodeId& node,
bool root_node);
void AddAssignment(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id, const fuchsia_shell::wire::Assignment& node,
bool root_node);
void AddAddition(ServerInterpreterContext* context, uint64_t node_file_id, uint64_t node_node_id,
const fuchsia_shell::wire::Addition& node, bool root_node);
// The server which created this service.
Server* server_;
// The binding reference which allows controlling the message dispatching
// of this connection and sending events.
cpp17::optional<fidl::ServerBindingRef<fuchsia_shell::Shell>> binding_;
// The interpreter associated with this service. An interpreter can only be associated to one
// service.
std::unique_ptr<ServerInterpreter> interpreter_;
};
// Class which accepts connections from clients. Each time a new connection is accepted, a Service
// object is created.
class Server {
public:
explicit Server(async::Loop* loop);
Server() = delete;
// Shuts down every service connection on this server, and eventually
// destroying the |Service| instances through unbinding them from the FIDL
// dispatcher.
~Server();
// Unregisters a service previously created with IncomingConnection.
// This should be used when the service is being closed down.
void ForgetService(Service* service) {
for (auto ref = services_.begin(); ref != services_.end(); ++ref) {
if ((*ref) == service) {
services_.erase(ref);
return;
}
}
ZX_PANIC("Invalid service passed to EraseService");
}
bool Listen();
// Listens for connections on the given channel instead of setting up a service.
// Returns whether we were able to bind to the given |channel|. On error, |channel| is closed and
// we do not bind.
zx_status_t IncomingConnection(fidl::ServerEnd<fuchsia_shell::Shell> service_request);
void Run() { loop()->Run(); }
async::Loop* loop() { return loop_; }
private:
async::Loop* loop_;
std::vector<Service*> services_;
};
} // namespace server
} // namespace interpreter
} // namespace shell
#endif // SRC_DEVELOPER_SHELL_INTERPRETER_SRC_SERVER_H_