blob: b9b265edd68c391b94da44ed0af00949357723d5 [file] [log] [blame]
//===-- RustFunctionCaller.cpp ---------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Plugins/ExpressionParser/Clang/ASTStructExtractor.h"
#include "RustFunctionCaller.h"
#include "Plugins/ExpressionParser/Clang/ClangExpressionParser.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/IR/Module.h"
#include "lldb/Core/Module.h"
#include "lldb/Utility/State.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Core/ValueObjectList.h"
#include "lldb/Expression/DiagnosticManager.h"
#include "lldb/Expression/IRExecutionUnit.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Symbol/RustASTContext.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanCallFunction.h"
#include "lldb/Utility/DataExtractor.h"
#include "lldb/Utility/Log.h"
using namespace lldb_private;
//----------------------------------------------------------------------
// RustFunctionCaller constructor
//----------------------------------------------------------------------
RustFunctionCaller::RustFunctionCaller(ExecutionContextScope &exe_scope,
const CompilerType &function_type,
const CompilerType &return_type,
const Address &functionAddress,
const ValueList &arg_value_list,
const char *name)
: ClangFunctionCaller(exe_scope, return_type, functionAddress, arg_value_list, name),
m_function_type(function_type)
{
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
RustFunctionCaller::~RustFunctionCaller() {}
static bool
AppendType(std::string *output, RustASTContext *ast, RustASTContext::TypeNameMap *name_map,
const std::string &varname, CompilerType type) {
std::string value;
if (!ast->GetCABITypeDeclaration(type, varname, name_map, &value)) {
return false;
}
output->append(" ");
output->append(value);
output->append(";\n");
return true;
}
unsigned RustFunctionCaller::CompileFunction(lldb::ThreadSP thread_to_use_sp,
DiagnosticManager &diagnostic_manager) {
if (m_compiled)
return 0;
// Compilation might call code, make sure to keep on the thread the caller
// indicated.
ThreadList::ExpressionExecutionThreadPusher execution_thread_pusher(
thread_to_use_sp);
RustASTContext *ast =
llvm::dyn_cast_or_null<RustASTContext>(m_function_return_type.GetTypeSystem());
if (!ast) {
diagnostic_manager.PutString(eDiagnosticSeverityError, "not in a Rust context!?");
return 1;
}
// Cons up the function we're going to wrap our call in, then compile it...
// We declare the function "extern "C"" because the compiler might be in C++
// mode which would mangle the name and then we couldn't find it again...
m_wrapper_function_text.clear();
m_wrapper_function_text.append("extern \"C\" void ");
m_wrapper_function_text.append(m_wrapper_function_name);
m_wrapper_function_text.append(" (void *input)\n{\n");
// For the Itanium ABI we want to generate a non-standard-layout
// type, so that ASTStructExtractor can see the length of the type
// without padding, so that the size of the final element is
// correct. FIXME this seems horrible, maybe a fix in
// ASTStructExtractor is more appropriate.
m_wrapper_function_text.append(" struct empty { };\n");
m_wrapper_function_text.append(" struct a : empty { };\n");
m_wrapper_function_text.append(" struct b : empty { };\n");
RustASTContext::TypeNameMap name_map;
std::string code;
code.append(" struct ");
code.append(m_wrapper_struct_name);
code.append(" : a, b {\n");
// ASTStructExtractor requires the first argument to be the
// function.
if (!AppendType(&code, ast, &name_map, "fn_ptr", m_function_type)) {
diagnostic_manager.PutString(eDiagnosticSeverityError,
"could not compute Rust type declaration");
return 1;
}
// This was ensured by the caller.
assert(unsigned(m_function_type.GetNumberOfFunctionArguments()) == m_arg_values.GetSize());
std::string arguments;
for (int i = 0; i < m_function_type.GetFunctionArgumentCount(); ++i) {
// We use the actual argument types in this structure so that
// copy-in always preserves values, and then we let the C++
// compiler do any scalar conversions. Rust doesn't have
// coercions like this, but it has type inferencing, which we
// don't, so this is handy for users who want to write "32"
// instead of "32f32".
CompilerType arg_type = m_arg_values.GetValueAtIndex(i)->GetCompilerType();
// FIXME work around a FunctionCaller problem. Note that the
// actual argument is already a pointer at this point, see
// RustParse.
bool is_aggregate = m_function_type.GetFunctionArgumentTypeAtIndex(i).IsAggregateType();
std::string argname = "__arg_" + std::to_string(i);
if (!AppendType(&code, ast, &name_map, argname, arg_type)) {
diagnostic_manager.PutString(eDiagnosticSeverityError,
"could not compute Rust type declaration");
return 1;
}
if (i > 0) {
arguments.append(", ");
}
if (is_aggregate) {
arguments.append("*");
}
arguments.append("__lldb_fn_data->");
arguments.append(argname);
}
// ASTStructExtractor requires that the last field hold the result.
if (!AppendType(&code, ast, &name_map, "result",
m_function_type.GetFunctionReturnType())) {
diagnostic_manager.PutString(eDiagnosticSeverityError,
"could not compute Rust type declaration");
return 1;
}
m_wrapper_function_text.append(name_map.typedefs);
m_wrapper_function_text.append(code);
m_wrapper_function_text.append(" };\n");
m_wrapper_function_text.append(" ");
m_wrapper_function_text.append(m_wrapper_struct_name);
m_wrapper_function_text.append(" *__lldb_fn_data = (");
m_wrapper_function_text.append(m_wrapper_struct_name);
m_wrapper_function_text.append(" *) input;\n");
m_wrapper_function_text.append(" __lldb_fn_data->result = __lldb_fn_data->fn_ptr(");
m_wrapper_function_text.append(arguments);
m_wrapper_function_text.append(");\n}\n");
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
if (log)
log->Printf("Expression: \n\n%s\n\n", m_wrapper_function_text.c_str());
// Okay, now compile this expression
lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock());
unsigned num_errors;
if (jit_process_sp) {
m_parser.reset(new ClangExpressionParser(jit_process_sp.get(), *this, true));
num_errors = m_parser->Parse(diagnostic_manager);
} else {
diagnostic_manager.PutString(eDiagnosticSeverityError,
"no process - unable to inject function");
num_errors = 1;
}
m_compiled = (num_errors == 0);
return num_errors;
}