text: // Copyright 2018 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 "garnet/bin/zxdb/expr/symbol_eval_context.h"
  
  #include "garnet/bin/zxdb/common/err.h"
  #include "garnet/bin/zxdb/expr/expr_value.h"
  #include "garnet/bin/zxdb/expr/resolve_ptr_ref.h"
  #include "garnet/bin/zxdb/symbols/code_block.h"
  #include "garnet/bin/zxdb/symbols/function.h"
  #include "garnet/bin/zxdb/symbols/location.h"
  #include "garnet/bin/zxdb/symbols/modified_type.h"
  #include "garnet/bin/zxdb/symbols/symbol_data_provider.h"
  #include "garnet/bin/zxdb/symbols/variable.h"
  #include "lib/fxl/logging.h"
  #include "lib/fxl/strings/string_printf.h"
  
  namespace zxdb {
  
  SymbolEvalContext::SymbolEvalContext(
      const SymbolContext& symbol_context,
      fxl::RefPtr<SymbolDataProvider> data_provider,
      fxl::RefPtr<CodeBlock> code_block)
      : symbol_context_(symbol_context),
        data_provider_(data_provider),
        resolver_(std::move(data_provider)),
        block_(std::move(code_block)),
        weak_factory_(this) {}
  
  SymbolEvalContext::SymbolEvalContext(
      fxl::RefPtr<SymbolDataProvider> data_provider, const Location& location)
      : symbol_context_(location.symbol_context()),
        data_provider_(data_provider),
        resolver_(std::move(data_provider)),
        weak_factory_(this) {
    if (!location.function())
      return;
    const CodeBlock* function = location.function().Get()->AsCodeBlock();
    if (!function)
      return;
  
    // Const case unfortunately required for RefPtr constructor.
    block_ = fxl::RefPtr<const CodeBlock>(
        const_cast<CodeBlock*>(function->GetMostSpecificChild(
            location.symbol_context(), location.address())));
  }
  
  const Variable* SymbolEvalContext::GetVariableSymbol(const std::string& name) {
    // Search backwards in the nested lexical scopes searching for the first
    // variable or function parameter with the given name.
    const CodeBlock* cur_block = block_.get();
    while (cur_block) {
      if (auto* var = SearchVariableVector(cur_block->variables(), name))
        return var;
  
      if (const Function* function = cur_block->AsFunction()) {
        // Found a function, check for a match in its parameters.
        if (auto* var = SearchVariableVector(function->parameters(), name))
          return var;
        break;  // Don't recurse into higher levels of nesting than a function.
      }
      if (!cur_block->parent())
        break;
      cur_block = cur_block->parent().Get()->AsCodeBlock();
    }
    return nullptr;
  }
  
  void SymbolEvalContext::GetVariableValue(const std::string& name,
                                           ValueCallback cb) {
    if (const Variable* var = GetVariableSymbol(name)) {
      // Resolve the variable value.
      resolver_.ResolveVariable(symbol_context_, var, std::move(cb));
    } else {
      // Not found. In the future, it might be nice to suggest the closest
      // match in the error message.
      cb(Err(fxl::StringPrintf("No variable '%s' in this context.",
                               name.c_str())),
         ExprValue());
    }
  }
  
  SymbolVariableResolver& SymbolEvalContext::GetVariableResolver() {
    return resolver_;
  }
  
  fxl::RefPtr<SymbolDataProvider> SymbolEvalContext::GetDataProvider() {
    return data_provider_;
  }
  
  const Variable* SymbolEvalContext::SearchVariableVector(
      const std::vector<LazySymbol>& vect, const std::string& search_for) {
    for (const auto& cur : vect) {
      const Variable* var = cur.Get()->AsVariable();
      if (!var)
        continue;  // Symbols are corrupt.
      if (search_for == var->GetAssignedName())
        return var;
    }
    return nullptr;
  }
  
  }  // namespace zxdb
  