blob: 10e0f88ee6b9bb472d525d499a9261fa4819c87a [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 <map>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "fuchsia/shell/llcpp/fidl.h"
#include "lib/async-loop/cpp/loop.h"
#include "src/developer/shell/interpreter/src/expressions.h"
#include "src/developer/shell/interpreter/src/interpreter.h"
#include "src/developer/shell/interpreter/src/schema.h"
#include "zircon/status.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 schema to the context. This definition must be used later by another node.
void AddObjectSchema(std::shared_ptr<ObjectSchema> definition) {
object_schemas_.emplace(definition->id(), definition);
}
// 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 type definition for the given node id.
std::shared_ptr<ObjectSchema> GetObjectSchema(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 the schema definitions waiting to be used.
std::map<NodeId, std::shared_ptr<ObjectSchema>> object_schemas_;
// 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;
// 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);
// Retrieves the schema definition for the given context/node id. If the definition is not found,
// it emits an error.
std::shared_ptr<ObjectSchema> GetObjectSchema(ServerInterpreterContext* context,
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 llcpp::fuchsia::shell::Shell::Interface {
public:
Service(Server* server, zx_handle_t handle)
: server_(server), handle_(handle), interpreter_(std::make_unique<ServerInterpreter>(this)) {}
Interpreter* interpreter() const { return interpreter_.get(); }
void CreateExecutionContext(uint64_t context_id,
CreateExecutionContextCompleter::Sync completer) override;
void AddNodes(uint64_t context_id,
::fidl::VectorView<::llcpp::fuchsia::shell::NodeDefinition> nodes,
AddNodesCompleter::Sync _completer) override;
void DumpExecutionContext(uint64_t context_id,
DumpExecutionContextCompleter::Sync completer) override;
void ExecuteExecutionContext(uint64_t context_id,
ExecuteExecutionContextCompleter::Sync completer) override;
void LoadGlobal(::fidl::StringView name, LoadGlobalCompleter::Sync completer) override;
void Shutdown(ShutdownCompleter::Sync completer) override;
// Helpers to be able to send events to the client.
zx_status_t OnError(uint64_t context_id, std::vector<llcpp::fuchsia::shell::Location>& locations,
const std::string& error_message) {
return llcpp::fuchsia::shell::Shell::SendOnErrorEvent(::zx::unowned_channel(handle_),
context_id, fidl::unowned_vec(locations),
fidl::unowned_str(error_message));
}
zx_status_t OnError(uint64_t context_id, const std::string& error_message) {
std::vector<llcpp::fuchsia::shell::Location> locations;
return OnError(context_id, locations, error_message);
}
zx_status_t OnDumpDone(uint64_t context_id) {
return llcpp::fuchsia::shell::Shell::SendOnDumpDoneEvent(::zx::unowned_channel(handle_),
context_id);
}
zx_status_t OnExecutionDone(uint64_t context_id, llcpp::fuchsia::shell::ExecuteResult result) {
return llcpp::fuchsia::shell::Shell::SendOnExecutionDoneEvent(::zx::unowned_channel(handle_),
context_id, result);
}
zx_status_t OnTextResult(uint64_t context_id, const std::string& result, bool partial_result) {
return llcpp::fuchsia::shell::Shell::SendOnTextResultEvent(
::zx::unowned_channel(handle_), context_id, fidl::unowned_str(result), 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 llcpp::fuchsia::shell::IntegerLiteral& node,
bool root_node);
void AddVariableDefinition(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id,
const llcpp::fuchsia::shell::VariableDefinition& node, bool root_node);
void AddObjectSchema(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id,
const llcpp::fuchsia::shell::ObjectSchemaDefinition& node, bool root_node);
void AddObjectSchemaField(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id,
const llcpp::fuchsia::shell::ObjectFieldSchemaDefinition& field_type,
bool root_node);
void AddObject(ServerInterpreterContext* context, uint64_t node_file_id, uint64_t node_node_id,
const llcpp::fuchsia::shell::ObjectDefinition& node, bool root_node);
void AddObjectField(ServerInterpreterContext* context, uint64_t node_file_id,
uint64_t node_node_id,
const llcpp::fuchsia::shell::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 llcpp::fuchsia::shell::NodeId& node, bool root_node);
void AddAddition(ServerInterpreterContext* context, uint64_t node_file_id, uint64_t node_node_id,
const llcpp::fuchsia::shell::Addition& node, bool root_node);
// The server which created this service and which owns it.
Server* const server_;
// The handle to communicate with the client.
zx_handle_t handle_;
// 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:
Server();
// Erase a service previously created with AddConnection. This closes the connection.
void EraseService(Service* service) {
for (auto ref = services_.begin(); ref != services_.end(); ++ref) {
if ((*ref).get() == service) {
services_.erase(ref);
return;
}
}
}
// Create a Service for an incoming connection.
Service* AddConnection(zx_handle_t handle) {
auto service = std::make_unique<Service>(this, handle);
auto result = service.get();
services_.emplace_back(std::move(service));
return result;
}
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(zx_handle_t service_request);
void Run() { loop_.Run(); }
async::Loop* loop() { return &loop_; }
private:
async::Loop loop_;
std::vector<std::unique_ptr<Service>> services_;
};
} // namespace server
} // namespace interpreter
} // namespace shell
#endif // SRC_DEVELOPER_SHELL_INTERPRETER_SRC_SERVER_H_