blob: d1b700f68e902fd48389e097327097be2a9f9bfb [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_SCOPE_H_
#define SRC_DEVELOPER_SHELL_INTERPRETER_SRC_SCOPE_H_
#include <lib/syslog/cpp/macros.h>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "src/developer/shell/interpreter/src/code.h"
#include "src/developer/shell/interpreter/src/nodes.h"
#include "src/developer/shell/interpreter/src/value.h"
namespace shell {
namespace interpreter {
class Thread;
// Base class for all scope variables.
class Variable {
public:
Variable(NodeId id, const std::string& name, size_t index, std::unique_ptr<Type> type,
bool is_mutable)
: id_(id), name_(name), index_(index), type_(std::move(type)), is_mutable_(is_mutable) {}
NodeId id() const { return id_; }
const std::string& name() const { return name_; }
size_t index() const { return index_; }
const Type* type() const { return type_.get(); }
bool is_mutable() const { return is_mutable_; }
// Clear the variable for this execution scope. For reference counted objects, that also releases
// the object.
void Clear(ExecutionScope* scope) { type_->ClearVariable(scope, index_); }
private:
// Id of the node which defines the variable.
const NodeId id_;
// Name of the variable.
const std::string name_;
// Index in the execution scope's data_ vector.
const size_t index_;
// The type of the variable.
std::unique_ptr<Type> type_;
// True if we can modify the variable initial value.
const bool is_mutable_;
};
// Defines a scope. This can be a global scope (at the isolate level) or a scope associated to
// a thread, a function, a block, ...
// This scope is used during compilation and, eventually, during execution for generic code.
class Scope {
public:
Scope() = default;
size_t size() const { return current_index_; }
// Returns the variable with the given name.
const Variable* GetVariable(const std::string& name) const {
auto result = variables_.find(name);
if (result == variables_.end()) {
return nullptr;
}
return result->second.get();
}
const Variable* SearchVariable(const NodeId& node_id) const {
for (const auto& variable : variables_) {
if (variable.second->id() == node_id) {
return variable.second.get();
}
}
return nullptr;
}
// Creates a variable.
Variable* CreateVariable(NodeId id, const std::string& name, std::unique_ptr<Type> type,
bool is_mutable) {
size_t size = type->Size();
FX_DCHECK(size > 0);
AlignIndex(size);
auto variable =
std::make_unique<Variable>(id, name, current_index_, std::move(type), is_mutable);
auto returned_value = variable.get();
variables_.emplace(std::make_pair(name, std::move(variable)));
current_index_ += size;
return returned_value;
}
// Shutdown the scope. That releases all the allocated objects.
void Shutdown(ExecutionScope* execution_scope);
private:
void AlignIndex(size_t size) { current_index_ = (current_index_ + (size - 1)) & ~(size - 1); }
// All the variables for this scope.
std::map<std::string, std::unique_ptr<Variable>> variables_;
// Current index in the data_ field of an execution scope.
size_t current_index_ = 0;
};
// Defines the storage for one scope. It can be the global storage for a global scope or a local
// storage for a function.
class ExecutionScope {
public:
ExecutionScope() = default;
// Returns the size of the allocated global data.
size_t size() const { return data_.size(); }
// Resizes the storage to be able to store the newly created variables.
void Resize(size_t new_size) {
// We can only add variables.
FX_DCHECK(new_size >= data_.size());
data_.resize(new_size);
}
// Retrieves a pointer to the storage.
uint8_t* Data(size_t index, size_t size) {
FX_DCHECK(index + size <= data_.size());
return data_.data() + index;
}
template <typename T>
T* Data(size_t index) {
return reinterpret_cast<T*>(Data(index, sizeof(T)));
}
// Retrieves a pointer to the storage.
const uint8_t* Data(size_t index, size_t size) const {
FX_DCHECK(index + size <= data_.size());
return data_.data() + index;
}
template <typename T>
const T* Data(size_t index) const {
return reinterpret_cast<const T*>(Data(index, sizeof(T)));
}
// Loads the current content of |variable| for this storage into |value|.
void Load(const Variable* variable, Value* value) const {
variable->type()->LoadVariable(this, variable->index(), value);
}
// Executes |code| for |context| |thread| for this storage.
void Execute(ExecutionContext* context, Thread* thread, std::unique_ptr<code::Code> code);
private:
// The stored data.
std::vector<uint8_t> data_;
};
} // namespace interpreter
} // namespace shell
#endif // SRC_DEVELOPER_SHELL_INTERPRETER_SRC_SCOPE_H_