34 changes: 34 additions & 0 deletions lldb/lldb.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -839,10 +839,14 @@
9AC703AF117675410086C050 /* SBInstruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC703AE117675410086C050 /* SBInstruction.cpp */; };
9AC703B1117675490086C050 /* SBInstructionList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC703B0117675490086C050 /* SBInstructionList.cpp */; };
A36FF33C17D8E94600244D40 /* OptionParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A36FF33B17D8E94600244D40 /* OptionParser.cpp */; };
AE44FB301BB07EB20033EB62 /* GoUserExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE44FB2C1BB07DD80033EB62 /* GoUserExpression.cpp */; };
AE44FB311BB07EB80033EB62 /* GoLexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE44FB2A1BB07DD80033EB62 /* GoLexer.cpp */; };
AE44FB321BB07EBC0033EB62 /* GoParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE44FB2B1BB07DD80033EB62 /* GoParser.cpp */; };
AE44FB3E1BB485960033EB62 /* GoLanguageRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE44FB3D1BB485960033EB62 /* GoLanguageRuntime.cpp */; };
AE6897281B94F6DE0018845D /* DWARFASTParserGo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE6897261B94F6DE0018845D /* DWARFASTParserGo.cpp */; };
AE7F56291B8FE418001377A8 /* GoASTContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEFFBA7C1AC4835D0087B932 /* GoASTContext.cpp */; };
AE8F624919EF3E1E00326B21 /* OperatingSystemGo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE8F624719EF3E1E00326B21 /* OperatingSystemGo.cpp */; };
AEB0E4591BD6E9F800B24093 /* LLVMUserExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEB0E4581BD6E9F800B24093 /* LLVMUserExpression.cpp */; };
AEEA34051AC88A7400AB639D /* TypeSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEEA34041AC88A7400AB639D /* TypeSystem.cpp */; };
AF061F87182C97ED00B6A19C /* RegisterContextHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF061F85182C97ED00B6A19C /* RegisterContextHistory.cpp */; };
AF0C112818580CD800C4C45B /* QueueItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF0C112718580CD800C4C45B /* QueueItem.cpp */; };
Expand Down Expand Up @@ -2683,12 +2687,21 @@
9AF16CC7114086A1007A7B3F /* SBBreakpointLocation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBBreakpointLocation.cpp; path = source/API/SBBreakpointLocation.cpp; sourceTree = "<group>"; };
A36FF33B17D8E94600244D40 /* OptionParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptionParser.cpp; sourceTree = "<group>"; };
A36FF33D17D8E98800244D40 /* OptionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionParser.h; path = include/lldb/Host/OptionParser.h; sourceTree = "<group>"; };
AE44FB261BB07DC60033EB62 /* GoAST.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GoAST.h; path = ExpressionParser/Go/GoAST.h; sourceTree = "<group>"; };
AE44FB271BB07DC60033EB62 /* GoLexer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GoLexer.h; path = ExpressionParser/Go/GoLexer.h; sourceTree = "<group>"; };
AE44FB281BB07DC60033EB62 /* GoParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GoParser.h; path = ExpressionParser/Go/GoParser.h; sourceTree = "<group>"; };
AE44FB291BB07DC60033EB62 /* GoUserExpression.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GoUserExpression.h; path = ExpressionParser/Go/GoUserExpression.h; sourceTree = "<group>"; };
AE44FB2A1BB07DD80033EB62 /* GoLexer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoLexer.cpp; path = ExpressionParser/Go/GoLexer.cpp; sourceTree = "<group>"; };
AE44FB2B1BB07DD80033EB62 /* GoParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoParser.cpp; path = ExpressionParser/Go/GoParser.cpp; sourceTree = "<group>"; };
AE44FB2C1BB07DD80033EB62 /* GoUserExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoUserExpression.cpp; path = ExpressionParser/Go/GoUserExpression.cpp; sourceTree = "<group>"; };
AE44FB3C1BB4858A0033EB62 /* GoLanguageRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GoLanguageRuntime.h; path = Go/GoLanguageRuntime.h; sourceTree = "<group>"; };
AE44FB3D1BB485960033EB62 /* GoLanguageRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoLanguageRuntime.cpp; path = Go/GoLanguageRuntime.cpp; sourceTree = "<group>"; };
AE6897261B94F6DE0018845D /* DWARFASTParserGo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFASTParserGo.cpp; sourceTree = "<group>"; };
AE6897271B94F6DE0018845D /* DWARFASTParserGo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFASTParserGo.h; sourceTree = "<group>"; };
AE8F624719EF3E1E00326B21 /* OperatingSystemGo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OperatingSystemGo.cpp; path = Go/OperatingSystemGo.cpp; sourceTree = "<group>"; };
AE8F624819EF3E1E00326B21 /* OperatingSystemGo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OperatingSystemGo.h; path = Go/OperatingSystemGo.h; sourceTree = "<group>"; };
AEB0E4581BD6E9F800B24093 /* LLVMUserExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LLVMUserExpression.cpp; path = source/Expression/LLVMUserExpression.cpp; sourceTree = "<group>"; };
AEB0E45A1BD6EA1400B24093 /* LLVMUserExpression.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LLVMUserExpression.h; path = include/lldb/Expression/LLVMUserExpression.h; sourceTree = "<group>"; };
AEEA33F61AC74FE700AB639D /* TypeSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TypeSystem.h; path = include/lldb/Symbol/TypeSystem.h; sourceTree = "<group>"; };
AEEA34041AC88A7400AB639D /* TypeSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypeSystem.cpp; path = source/Symbol/TypeSystem.cpp; sourceTree = "<group>"; };
AEEA340F1ACA08A000AB639D /* GoASTContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GoASTContext.h; path = include/lldb/Symbol/GoASTContext.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4528,6 +4541,8 @@
4C0083321B9A5DE200D5CF24 /* FunctionCaller.cpp */,
4C00832E1B9A58A700D5CF24 /* UserExpression.h */,
4C0083331B9A5DE200D5CF24 /* UserExpression.cpp */,
AEB0E45A1BD6EA1400B24093 /* LLVMUserExpression.h */,
AEB0E4581BD6E9F800B24093 /* LLVMUserExpression.cpp */,
4C00833D1B9F9B8400D5CF24 /* UtilityFunction.h */,
4C00833F1B9F9BA900D5CF24 /* UtilityFunction.cpp */,
49A1CAC11430E21D00306AC9 /* ExpressionSourceCode.h */,
Expand Down Expand Up @@ -5207,6 +5222,7 @@
isa = PBXGroup;
children = (
4984BA0C1B97620B008658D4 /* Clang */,
AE44FB371BB35A2E0033EB62 /* Go */,
);
name = ExpressionParser;
sourceTree = "<group>";
Expand Down Expand Up @@ -5580,6 +5596,20 @@
name = "SysV-mips";
sourceTree = "<group>";
};
AE44FB371BB35A2E0033EB62 /* Go */ = {
isa = PBXGroup;
children = (
AE44FB261BB07DC60033EB62 /* GoAST.h */,
AE44FB271BB07DC60033EB62 /* GoLexer.h */,
AE44FB2A1BB07DD80033EB62 /* GoLexer.cpp */,
AE44FB281BB07DC60033EB62 /* GoParser.h */,
AE44FB2B1BB07DD80033EB62 /* GoParser.cpp */,
AE44FB291BB07DC60033EB62 /* GoUserExpression.h */,
AE44FB2C1BB07DD80033EB62 /* GoUserExpression.cpp */,
);
name = Go;
sourceTree = "<group>";
};
AE44FB3B1BB485730033EB62 /* Go */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -6281,6 +6311,7 @@
2689FFDA13353D9D00698AC0 /* lldb.cpp in Sources */,
4C0083401B9F9BA900D5CF24 /* UtilityFunction.cpp in Sources */,
26474CCD18D0CB5B0073DEBA /* RegisterContextPOSIX_x86.cpp in Sources */,
AEB0E4591BD6E9F800B24093 /* LLVMUserExpression.cpp in Sources */,
2689FFEF13353DB600698AC0 /* Breakpoint.cpp in Sources */,
267A47FB1B1411C40021A5BC /* NativeRegisterContext.cpp in Sources */,
2689FFF113353DB600698AC0 /* BreakpointID.cpp in Sources */,
Expand Down Expand Up @@ -6410,6 +6441,7 @@
3F81691A1ABA2419001DA9DF /* NameMatches.cpp in Sources */,
94B9E5121BBF20F4000A48DC /* NSString.cpp in Sources */,
AF0E22F018A09FB20009B7D1 /* AppleGetItemInfoHandler.cpp in Sources */,
AE44FB301BB07EB20033EB62 /* GoUserExpression.cpp in Sources */,
2689004E13353E0400698AC0 /* Stream.cpp in Sources */,
2689004F13353E0400698AC0 /* StreamFile.cpp in Sources */,
2689005013353E0400698AC0 /* StreamString.cpp in Sources */,
Expand Down Expand Up @@ -6528,6 +6560,7 @@
949EEDA31BA76577008C63CF /* Cocoa.cpp in Sources */,
3FDFE56C19AF9C44009756A7 /* HostProcessPosix.cpp in Sources */,
268900B413353E5000698AC0 /* RegisterContextMacOSXFrameBackchain.cpp in Sources */,
AE44FB321BB07EBC0033EB62 /* GoParser.cpp in Sources */,
3F8169311ABB7A6D001DA9DF /* SystemInitializer.cpp in Sources */,
949EEDB21BA76731008C63CF /* NSIndexPath.cpp in Sources */,
3FDFED2D19C257A0009756A7 /* HostProcess.cpp in Sources */,
Expand Down Expand Up @@ -6689,6 +6722,7 @@
26D5E163135BB054006EA0A7 /* OptionGroupPlatform.cpp in Sources */,
94CD131A19BA33B400DB7BED /* TypeValidator.cpp in Sources */,
26BD407F135D2AE000237D80 /* FileLineResolver.cpp in Sources */,
AE44FB311BB07EB80033EB62 /* GoLexer.cpp in Sources */,
26A7A035135E6E4200FB369E /* OptionValue.cpp in Sources */,
9A22A161135E30370024DDC3 /* EmulateInstructionARM.cpp in Sources */,
AFDFDFD119E34D3400EAE509 /* ConnectionFileDescriptorPosix.cpp in Sources */,
Expand Down
18 changes: 18 additions & 0 deletions lldb/source/API/SBFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,10 @@ SBFrame::EvaluateExpression (const char *expr)
lldb::DynamicValueType fetch_dynamic_value = frame->CalculateTarget()->GetPreferDynamicValue();
options.SetFetchDynamicValue (fetch_dynamic_value);
options.SetUnwindOnError (true);
if (target->GetLanguage() != eLanguageTypeUnknown)
options.SetLanguage(target->GetLanguage());
else
options.SetLanguage(frame->GetLanguage());
return EvaluateExpression (expr, options);
}
return result;
Expand All @@ -1400,15 +1404,29 @@ SBFrame::EvaluateExpression (const char *expr, lldb::DynamicValueType fetch_dyna
SBExpressionOptions options;
options.SetFetchDynamicValue (fetch_dynamic_value);
options.SetUnwindOnError (true);
ExecutionContext exe_ctx(m_opaque_sp.get());
StackFrame *frame = exe_ctx.GetFramePtr();
Target *target = exe_ctx.GetTargetPtr();
if (target && target->GetLanguage() != eLanguageTypeUnknown)
options.SetLanguage(target->GetLanguage());
else if (frame)
options.SetLanguage(frame->GetLanguage());
return EvaluateExpression (expr, options);
}

SBValue
SBFrame::EvaluateExpression (const char *expr, lldb::DynamicValueType fetch_dynamic_value, bool unwind_on_error)
{
SBExpressionOptions options;
ExecutionContext exe_ctx(m_opaque_sp.get());
options.SetFetchDynamicValue (fetch_dynamic_value);
options.SetUnwindOnError (unwind_on_error);
StackFrame *frame = exe_ctx.GetFramePtr();
Target *target = exe_ctx.GetTargetPtr();
if (target && target->GetLanguage() != eLanguageTypeUnknown)
options.SetLanguage(target->GetLanguage());
else if (frame)
options.SetLanguage(frame->GetLanguage());
return EvaluateExpression (expr, options);
}

Expand Down
1 change: 1 addition & 0 deletions lldb/source/Expression/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ add_lldb_library(lldbExpression
IRExecutionUnit.cpp
IRInterpreter.cpp
IRMemoryMap.cpp
LLVMUserExpression.cpp
Materializer.cpp
REPL.cpp
UserExpression.cpp
Expand Down
353 changes: 353 additions & 0 deletions lldb/source/Expression/LLVMUserExpression.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,353 @@
//===-- LLVMUserExpression.cpp ----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// C Includes
// C++ Includes

// Project includes
#include "lldb/Expression/LLVMUserExpression.h"
#include "lldb/Core/ConstString.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Core/ValueObjectConstResult.h"
#include "lldb/Expression/ExpressionSourceCode.h"
#include "lldb/Expression/IRExecutionUnit.h"
#include "lldb/Expression/IRInterpreter.h"
#include "lldb/Expression/Materializer.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolVendor.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanCallUserExpression.h"

using namespace lldb_private;

LLVMUserExpression::LLVMUserExpression(ExecutionContextScope &exe_scope, const char *expr, const char *expr_prefix,
lldb::LanguageType language, ResultType desired_type)
: UserExpression(exe_scope, expr, expr_prefix, language, desired_type),
m_stack_frame_bottom(LLDB_INVALID_ADDRESS),
m_stack_frame_top(LLDB_INVALID_ADDRESS),
m_transformed_text(),
m_execution_unit_sp(),
m_materializer_ap(),
m_jit_module_wp(),
m_enforce_valid_object(true),
m_in_cplusplus_method(false),
m_in_objectivec_method(false),
m_in_static_method(false),
m_needs_object_ptr(false),
m_const_object(false),
m_target(NULL),
m_can_interpret(false),
m_materialized_address(LLDB_INVALID_ADDRESS)
{
}

LLVMUserExpression::~LLVMUserExpression()
{
if (m_target)
{
lldb::ModuleSP jit_module_sp(m_jit_module_wp.lock());
if (jit_module_sp)
m_target->GetImages().Remove(jit_module_sp);
}
}

lldb::ExpressionResults
LLVMUserExpression::Execute(Stream &error_stream, ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options,
lldb::UserExpressionSP &shared_ptr_to_me, lldb::ExpressionVariableSP &result)
{
// The expression log is quite verbose, and if you're just tracking the execution of the
// expression, it's quite convenient to have these logs come out with the STEP log as well.
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP));

if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret)
{
lldb::addr_t struct_address = LLDB_INVALID_ADDRESS;

if (!PrepareToExecuteJITExpression(error_stream, exe_ctx, struct_address))
{
error_stream.Printf("Errored out in %s, couldn't PrepareToExecuteJITExpression", __FUNCTION__);
return lldb::eExpressionSetupError;
}

lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS;
lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS;

if (m_can_interpret)
{
llvm::Module *module = m_execution_unit_sp->GetModule();
llvm::Function *function = m_execution_unit_sp->GetFunction();

if (!module || !function)
{
error_stream.Printf("Supposed to interpret, but nothing is there");
return lldb::eExpressionSetupError;
}

Error interpreter_error;

std::vector<lldb::addr_t> args;

if (!AddInitialArguments(exe_ctx, args, error_stream))
{
error_stream.Printf("Errored out in %s, couldn't AddInitialArguments", __FUNCTION__);
return lldb::eExpressionSetupError;
}

args.push_back(struct_address);

function_stack_bottom = m_stack_frame_bottom;
function_stack_top = m_stack_frame_top;

IRInterpreter::Interpret(*module, *function, args, *m_execution_unit_sp.get(), interpreter_error,
function_stack_bottom, function_stack_top, exe_ctx);

if (!interpreter_error.Success())
{
error_stream.Printf("Supposed to interpret, but failed: %s", interpreter_error.AsCString());
return lldb::eExpressionDiscarded;
}
}
else
{
if (!exe_ctx.HasThreadScope())
{
error_stream.Printf("UserExpression::Execute called with no thread selected.");
return lldb::eExpressionSetupError;
}

Address wrapper_address(m_jit_start_addr);

std::vector<lldb::addr_t> args;

if (!AddInitialArguments(exe_ctx, args, error_stream))
{
error_stream.Printf("Errored out in %s, couldn't AddInitialArguments", __FUNCTION__);
return lldb::eExpressionSetupError;
}

args.push_back(struct_address);

lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression(exe_ctx.GetThreadRef(), wrapper_address,
args, options, shared_ptr_to_me));

if (!call_plan_sp || !call_plan_sp->ValidatePlan(&error_stream))
return lldb::eExpressionSetupError;

ThreadPlanCallUserExpression *user_expression_plan =
static_cast<ThreadPlanCallUserExpression *>(call_plan_sp.get());

lldb::addr_t function_stack_pointer = user_expression_plan->GetFunctionStackPointer();

function_stack_bottom = function_stack_pointer - HostInfo::GetPageSize();
function_stack_top = function_stack_pointer;

if (log)
log->Printf("-- [UserExpression::Execute] Execution of expression begins --");

if (exe_ctx.GetProcessPtr())
exe_ctx.GetProcessPtr()->SetRunningUserExpression(true);

lldb::ExpressionResults execution_result =
exe_ctx.GetProcessRef().RunThreadPlan(exe_ctx, call_plan_sp, options, error_stream);

if (exe_ctx.GetProcessPtr())
exe_ctx.GetProcessPtr()->SetRunningUserExpression(false);

if (log)
log->Printf("-- [UserExpression::Execute] Execution of expression completed --");

if (execution_result == lldb::eExpressionInterrupted || execution_result == lldb::eExpressionHitBreakpoint)
{
const char *error_desc = NULL;

if (call_plan_sp)
{
lldb::StopInfoSP real_stop_info_sp = call_plan_sp->GetRealStopInfo();
if (real_stop_info_sp)
error_desc = real_stop_info_sp->GetDescription();
}
if (error_desc)
error_stream.Printf("Execution was interrupted, reason: %s.", error_desc);
else
error_stream.PutCString("Execution was interrupted.");

if ((execution_result == lldb::eExpressionInterrupted && options.DoesUnwindOnError()) ||
(execution_result == lldb::eExpressionHitBreakpoint && options.DoesIgnoreBreakpoints()))
error_stream.PutCString(
"\nThe process has been returned to the state before expression evaluation.");
else
{
if (execution_result == lldb::eExpressionHitBreakpoint)
user_expression_plan->TransferExpressionOwnership();
error_stream.PutCString(
"\nThe process has been left at the point where it was interrupted, "
"use \"thread return -x\" to return to the state before expression evaluation.");
}

return execution_result;
}
else if (execution_result == lldb::eExpressionStoppedForDebug)
{
error_stream.PutCString(
"Execution was halted at the first instruction of the expression "
"function because \"debug\" was requested.\n"
"Use \"thread return -x\" to return to the state before expression evaluation.");
return execution_result;
}
else if (execution_result != lldb::eExpressionCompleted)
{
error_stream.Printf("Couldn't execute function; result was %s\n",
Process::ExecutionResultAsCString(execution_result));
return execution_result;
}
}

if (FinalizeJITExecution(error_stream, exe_ctx, result, function_stack_bottom, function_stack_top))
{
return lldb::eExpressionCompleted;
}
else
{
return lldb::eExpressionResultUnavailable;
}
}
else
{
error_stream.Printf("Expression can't be run, because there is no JIT compiled function");
return lldb::eExpressionSetupError;
}
}

bool
LLVMUserExpression::FinalizeJITExecution(Stream &error_stream, ExecutionContext &exe_ctx,
lldb::ExpressionVariableSP &result, lldb::addr_t function_stack_bottom,
lldb::addr_t function_stack_top)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));

if (log)
log->Printf("-- [UserExpression::FinalizeJITExecution] Dematerializing after execution --");

if (!m_dematerializer_sp)
{
error_stream.Printf("Couldn't apply expression side effects : no dematerializer is present");
return false;
}

Error dematerialize_error;

m_dematerializer_sp->Dematerialize(dematerialize_error, function_stack_bottom, function_stack_top);

if (!dematerialize_error.Success())
{
error_stream.Printf("Couldn't apply expression side effects : %s\n",
dematerialize_error.AsCString("unknown error"));
return false;
}

result = GetResultAfterDematerialization(exe_ctx.GetBestExecutionContextScope());

if (result)
result->TransferAddress();

m_dematerializer_sp.reset();

return true;
}

bool
LLVMUserExpression::PrepareToExecuteJITExpression(Stream &error_stream, ExecutionContext &exe_ctx,
lldb::addr_t &struct_address)
{
lldb::TargetSP target;
lldb::ProcessSP process;
lldb::StackFrameSP frame;

if (!LockAndCheckContext(exe_ctx, target, process, frame))
{
error_stream.Printf("The context has changed before we could JIT the expression!\n");
return false;
}

if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret)
{
if (m_materialized_address == LLDB_INVALID_ADDRESS)
{
Error alloc_error;

IRMemoryMap::AllocationPolicy policy =
m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly : IRMemoryMap::eAllocationPolicyMirror;

m_materialized_address = m_execution_unit_sp->Malloc(
m_materializer_ap->GetStructByteSize(), m_materializer_ap->GetStructAlignment(),
lldb::ePermissionsReadable | lldb::ePermissionsWritable, policy, alloc_error);

if (!alloc_error.Success())
{
error_stream.Printf("Couldn't allocate space for materialized struct: %s\n", alloc_error.AsCString());
return false;
}
}

struct_address = m_materialized_address;

if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS)
{
Error alloc_error;

const size_t stack_frame_size = 512 * 1024;

m_stack_frame_bottom = m_execution_unit_sp->Malloc(stack_frame_size, 8,
lldb::ePermissionsReadable | lldb::ePermissionsWritable,
IRMemoryMap::eAllocationPolicyHostOnly, alloc_error);

m_stack_frame_top = m_stack_frame_bottom + stack_frame_size;

if (!alloc_error.Success())
{
error_stream.Printf("Couldn't allocate space for the stack frame: %s\n", alloc_error.AsCString());
return false;
}
}

Error materialize_error;

m_dematerializer_sp =
m_materializer_ap->Materialize(frame, *m_execution_unit_sp, struct_address, materialize_error);

if (!materialize_error.Success())
{
error_stream.Printf("Couldn't materialize: %s\n", materialize_error.AsCString());
return false;
}
}
return true;
}

lldb::ModuleSP
LLVMUserExpression::GetJITModule()
{
if (m_execution_unit_sp)
return m_execution_unit_sp->GetJITModule();
return lldb::ModuleSP();
}
331 changes: 9 additions & 322 deletions lldb/source/Expression/UserExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,42 +45,18 @@

using namespace lldb_private;

UserExpression::UserExpression (ExecutionContextScope &exe_scope,
const char *expr,
const char *expr_prefix,
lldb::LanguageType language,
ResultType desired_type) :
Expression (exe_scope),
m_stack_frame_bottom (LLDB_INVALID_ADDRESS),
m_stack_frame_top (LLDB_INVALID_ADDRESS),
m_expr_text (expr),
m_expr_prefix (expr_prefix ? expr_prefix : ""),
m_language (language),
m_transformed_text (),
m_desired_type (desired_type),
m_execution_unit_sp(),
m_materializer_ap(),
m_jit_module_wp(),
m_enforce_valid_object (true),
m_in_cplusplus_method (false),
m_in_objectivec_method (false),
m_in_static_method(false),
m_needs_object_ptr (false),
m_const_object (false),
m_target (NULL),
m_can_interpret (false),
m_materialized_address (LLDB_INVALID_ADDRESS)
UserExpression::UserExpression(ExecutionContextScope &exe_scope, const char *expr, const char *expr_prefix,
lldb::LanguageType language, ResultType desired_type)
: Expression(exe_scope),
m_expr_text(expr),
m_expr_prefix(expr_prefix ? expr_prefix : ""),
m_language(language),
m_desired_type(desired_type)
{
}

UserExpression::~UserExpression ()
{
if (m_target)
{
lldb::ModuleSP jit_module_sp (m_jit_module_wp.lock());
if (jit_module_sp)
m_target->GetImages().Remove(jit_module_sp);
}
}

void
Expand Down Expand Up @@ -170,295 +146,6 @@ UserExpression::GetObjectPointer (lldb::StackFrameSP frame_sp,
return ret;
}

bool
UserExpression::PrepareToExecuteJITExpression (Stream &error_stream,
ExecutionContext &exe_ctx,
lldb::addr_t &struct_address)
{
lldb::TargetSP target;
lldb::ProcessSP process;
lldb::StackFrameSP frame;

if (!LockAndCheckContext(exe_ctx,
target,
process,
frame))
{
error_stream.Printf("The context has changed before we could JIT the expression!\n");
return false;
}

if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret)
{
if (m_materialized_address == LLDB_INVALID_ADDRESS)
{
Error alloc_error;

IRMemoryMap::AllocationPolicy policy = m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly : IRMemoryMap::eAllocationPolicyMirror;

m_materialized_address = m_execution_unit_sp->Malloc(m_materializer_ap->GetStructByteSize(),
m_materializer_ap->GetStructAlignment(),
lldb::ePermissionsReadable | lldb::ePermissionsWritable,
policy,
alloc_error);

if (!alloc_error.Success())
{
error_stream.Printf("Couldn't allocate space for materialized struct: %s\n", alloc_error.AsCString());
return false;
}
}

struct_address = m_materialized_address;

if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS)
{
Error alloc_error;

const size_t stack_frame_size = 512 * 1024;

m_stack_frame_bottom = m_execution_unit_sp->Malloc(stack_frame_size,
8,
lldb::ePermissionsReadable | lldb::ePermissionsWritable,
IRMemoryMap::eAllocationPolicyHostOnly,
alloc_error);

m_stack_frame_top = m_stack_frame_bottom + stack_frame_size;

if (!alloc_error.Success())
{
error_stream.Printf("Couldn't allocate space for the stack frame: %s\n", alloc_error.AsCString());
return false;
}
}

Error materialize_error;

m_dematerializer_sp = m_materializer_ap->Materialize(frame, *m_execution_unit_sp, struct_address, materialize_error);

if (!materialize_error.Success())
{
error_stream.Printf("Couldn't materialize: %s\n", materialize_error.AsCString());
return false;
}
}
return true;
}

bool
UserExpression::FinalizeJITExecution (Stream &error_stream,
ExecutionContext &exe_ctx,
lldb::ExpressionVariableSP &result,
lldb::addr_t function_stack_bottom,
lldb::addr_t function_stack_top)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));

if (log)
log->Printf("-- [UserExpression::FinalizeJITExecution] Dematerializing after execution --");

if (!m_dematerializer_sp)
{
error_stream.Printf ("Couldn't apply expression side effects : no dematerializer is present");
return false;
}

Error dematerialize_error;

m_dematerializer_sp->Dematerialize(dematerialize_error, function_stack_bottom, function_stack_top);

if (!dematerialize_error.Success())
{
error_stream.Printf ("Couldn't apply expression side effects : %s\n", dematerialize_error.AsCString("unknown error"));
return false;
}

result = GetResultAfterDematerialization(exe_ctx.GetBestExecutionContextScope());

if (result)
result->TransferAddress();

m_dematerializer_sp.reset();

return true;
}

lldb::ExpressionResults
UserExpression::Execute (Stream &error_stream,
ExecutionContext &exe_ctx,
const EvaluateExpressionOptions& options,
lldb::UserExpressionSP &shared_ptr_to_me,
lldb::ExpressionVariableSP &result)
{
// The expression log is quite verbose, and if you're just tracking the execution of the
// expression, it's quite convenient to have these logs come out with the STEP log as well.
Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP));

if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret)
{
lldb::addr_t struct_address = LLDB_INVALID_ADDRESS;

if (!PrepareToExecuteJITExpression (error_stream, exe_ctx, struct_address))
{
error_stream.Printf("Errored out in %s, couldn't PrepareToExecuteJITExpression", __FUNCTION__);
return lldb::eExpressionSetupError;
}

lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS;
lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS;

if (m_can_interpret)
{
llvm::Module *module = m_execution_unit_sp->GetModule();
llvm::Function *function = m_execution_unit_sp->GetFunction();

if (!module || !function)
{
error_stream.Printf("Supposed to interpret, but nothing is there");
return lldb::eExpressionSetupError;
}

Error interpreter_error;

std::vector<lldb::addr_t> args;

if (!AddInitialArguments(exe_ctx, args, error_stream))
{
error_stream.Printf ("Errored out in %s, couldn't AddInitialArguments", __FUNCTION__);
return lldb::eExpressionSetupError;
}

args.push_back(struct_address);

function_stack_bottom = m_stack_frame_bottom;
function_stack_top = m_stack_frame_top;

IRInterpreter::Interpret (*module,
*function,
args,
*m_execution_unit_sp.get(),
interpreter_error,
function_stack_bottom,
function_stack_top,
exe_ctx);

if (!interpreter_error.Success())
{
error_stream.Printf("Supposed to interpret, but failed: %s", interpreter_error.AsCString());
return lldb::eExpressionDiscarded;
}
}
else
{
if (!exe_ctx.HasThreadScope())
{
error_stream.Printf("UserExpression::Execute called with no thread selected.");
return lldb::eExpressionSetupError;
}

Address wrapper_address (m_jit_start_addr);

std::vector<lldb::addr_t> args;

if (!AddInitialArguments(exe_ctx, args, error_stream))
{
error_stream.Printf ("Errored out in %s, couldn't AddInitialArguments", __FUNCTION__);
return lldb::eExpressionSetupError;
}

args.push_back(struct_address);

lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression (exe_ctx.GetThreadRef(),
wrapper_address,
args,
options,
shared_ptr_to_me));

if (!call_plan_sp || !call_plan_sp->ValidatePlan (&error_stream))
return lldb::eExpressionSetupError;

ThreadPlanCallUserExpression *user_expression_plan = static_cast<ThreadPlanCallUserExpression *>(call_plan_sp.get());

lldb::addr_t function_stack_pointer = user_expression_plan->GetFunctionStackPointer();

function_stack_bottom = function_stack_pointer - HostInfo::GetPageSize();
function_stack_top = function_stack_pointer;

if (log)
log->Printf("-- [UserExpression::Execute] Execution of expression begins --");

if (exe_ctx.GetProcessPtr())
exe_ctx.GetProcessPtr()->SetRunningUserExpression(true);

lldb::ExpressionResults execution_result = exe_ctx.GetProcessRef().RunThreadPlan (exe_ctx,
call_plan_sp,
options,
error_stream);

if (exe_ctx.GetProcessPtr())
exe_ctx.GetProcessPtr()->SetRunningUserExpression(false);

if (log)
log->Printf("-- [UserExpression::Execute] Execution of expression completed --");

if (execution_result == lldb::eExpressionInterrupted || execution_result == lldb::eExpressionHitBreakpoint)
{
const char *error_desc = NULL;

if (call_plan_sp)
{
lldb::StopInfoSP real_stop_info_sp = call_plan_sp->GetRealStopInfo();
if (real_stop_info_sp)
error_desc = real_stop_info_sp->GetDescription();
}
if (error_desc)
error_stream.Printf ("Execution was interrupted, reason: %s.", error_desc);
else
error_stream.PutCString ("Execution was interrupted.");

if ((execution_result == lldb::eExpressionInterrupted && options.DoesUnwindOnError())
|| (execution_result == lldb::eExpressionHitBreakpoint && options.DoesIgnoreBreakpoints()))
error_stream.PutCString ("\nThe process has been returned to the state before expression evaluation.");
else
{
if (execution_result == lldb::eExpressionHitBreakpoint)
user_expression_plan->TransferExpressionOwnership();
error_stream.PutCString ("\nThe process has been left at the point where it was interrupted, "
"use \"thread return -x\" to return to the state before expression evaluation.");
}

return execution_result;
}
else if (execution_result == lldb::eExpressionStoppedForDebug)
{
error_stream.PutCString ("Execution was halted at the first instruction of the expression "
"function because \"debug\" was requested.\n"
"Use \"thread return -x\" to return to the state before expression evaluation.");
return execution_result;
}
else if (execution_result != lldb::eExpressionCompleted)
{
error_stream.Printf ("Couldn't execute function; result was %s\n", Process::ExecutionResultAsCString (execution_result));
return execution_result;
}
}

if (FinalizeJITExecution (error_stream, exe_ctx, result, function_stack_bottom, function_stack_top))
{
return lldb::eExpressionCompleted;
}
else
{
return lldb::eExpressionResultUnavailable;
}
}
else
{
error_stream.Printf("Expression can't be run, because there is no JIT compiled function");
return lldb::eExpressionSetupError;
}
}

lldb::ExpressionResults
UserExpression::Evaluate (ExecutionContext &exe_ctx,
const EvaluateExpressionOptions& options,
Expand Down Expand Up @@ -570,8 +257,8 @@ UserExpression::Evaluate (ExecutionContext &exe_ctx,
else
{
// If a pointer to a lldb::ModuleSP was passed in, return the JIT'ed module if one was created
if (jit_module_sp_ptr && user_expression_sp->m_execution_unit_sp)
*jit_module_sp_ptr = user_expression_sp->m_execution_unit_sp->GetJITModule();
if (jit_module_sp_ptr)
*jit_module_sp_ptr = user_expression_sp->GetJITModule();

lldb::ExpressionVariableSP expr_result;

Expand Down
1 change: 1 addition & 0 deletions lldb/source/Plugins/ExpressionParser/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
add_subdirectory(Clang)
add_subdirectory(Go)
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,10 @@

using namespace lldb_private;

ClangUserExpression::ClangUserExpression (ExecutionContextScope &exe_scope,
const char *expr,
const char *expr_prefix,
lldb::LanguageType language,
ResultType desired_type) :
UserExpression (exe_scope, expr, expr_prefix, language, desired_type),
m_type_system_helper(*m_target_wp.lock().get())
ClangUserExpression::ClangUserExpression(ExecutionContextScope &exe_scope, const char *expr, const char *expr_prefix,
lldb::LanguageType language, ResultType desired_type)
: LLVMUserExpression(exe_scope, expr, expr_prefix, language, desired_type),
m_type_system_helper(*m_target_wp.lock().get())
{
switch (m_language)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#include "lldb/lldb-private.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/ClangForward.h"
#include "lldb/Expression/UserExpression.h"
#include "lldb/Expression/LLVMUserExpression.h"
#include "lldb/Expression/Materializer.h"
#include "lldb/Target/ExecutionContext.h"

Expand All @@ -43,7 +43,7 @@ namespace lldb_private
/// the objects needed to parse and interpret or JIT an expression. It
/// uses the Clang parser to produce LLVM IR from the expression.
//----------------------------------------------------------------------
class ClangUserExpression : public UserExpression
class ClangUserExpression : public LLVMUserExpression
{
public:
enum { kDefaultTimeout = 500000u };
Expand Down
5 changes: 5 additions & 0 deletions lldb/source/Plugins/ExpressionParser/Go/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
add_lldb_library(lldbPluginExpressionParserGo
GoLexer.cpp
GoParser.cpp
GoUserExpression.cpp
)
3,225 changes: 3,225 additions & 0 deletions lldb/source/Plugins/ExpressionParser/Go/GoAST.h

Large diffs are not rendered by default.

402 changes: 402 additions & 0 deletions lldb/source/Plugins/ExpressionParser/Go/GoLexer.cpp

Large diffs are not rendered by default.

201 changes: 201 additions & 0 deletions lldb/source/Plugins/ExpressionParser/Go/GoLexer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
//===-- GoLexer.h -----------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_GoLexer_h
#define liblldb_GoLexer_h

#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringMap.h"

namespace lldb_private
{

class GoLexer
{
public:
explicit GoLexer(const char *src);

enum TokenType
{
TOK_EOF,
TOK_INVALID,
TOK_IDENTIFIER,
LIT_INTEGER,
LIT_FLOAT,
LIT_IMAGINARY,
LIT_RUNE,
LIT_STRING,
KEYWORD_BREAK,
KEYWORD_DEFAULT,
KEYWORD_FUNC,
KEYWORD_INTERFACE,
KEYWORD_SELECT,
KEYWORD_CASE,
KEYWORD_DEFER,
KEYWORD_GO,
KEYWORD_MAP,
KEYWORD_STRUCT,
KEYWORD_CHAN,
KEYWORD_ELSE,
KEYWORD_GOTO,
KEYWORD_PACKAGE,
KEYWORD_SWITCH,
KEYWORD_CONST,
KEYWORD_FALLTHROUGH,
KEYWORD_IF,
KEYWORD_RANGE,
KEYWORD_TYPE,
KEYWORD_CONTINUE,
KEYWORD_FOR,
KEYWORD_IMPORT,
KEYWORD_RETURN,
KEYWORD_VAR,
OP_PLUS,
OP_MINUS,
OP_STAR,
OP_SLASH,
OP_PERCENT,
OP_AMP,
OP_PIPE,
OP_CARET,
OP_LSHIFT,
OP_RSHIFT,
OP_AMP_CARET,
OP_PLUS_EQ,
OP_MINUS_EQ,
OP_STAR_EQ,
OP_SLASH_EQ,
OP_PERCENT_EQ,
OP_AMP_EQ,
OP_PIPE_EQ,
OP_CARET_EQ,
OP_LSHIFT_EQ,
OP_RSHIFT_EQ,
OP_AMP_CARET_EQ,
OP_AMP_AMP,
OP_PIPE_PIPE,
OP_LT_MINUS,
OP_PLUS_PLUS,
OP_MINUS_MINUS,
OP_EQ_EQ,
OP_LT,
OP_GT,
OP_EQ,
OP_BANG,
OP_BANG_EQ,
OP_LT_EQ,
OP_GT_EQ,
OP_COLON_EQ,
OP_DOTS,
OP_LPAREN,
OP_LBRACK,
OP_LBRACE,
OP_COMMA,
OP_DOT,
OP_RPAREN,
OP_RBRACK,
OP_RBRACE,
OP_SEMICOLON,
OP_COLON,
};

struct Token
{
explicit Token(TokenType t, llvm::StringRef text) : m_type(t), m_value(text) {}
TokenType m_type;
llvm::StringRef m_value;
};

const Token &Lex();

size_t
BytesRemaining() const
{
return m_end - m_src;
}
llvm::StringRef
GetString(int len) const
{
return llvm::StringRef(m_src, len);
}

static TokenType LookupKeyword(llvm::StringRef id);
static llvm::StringRef LookupToken(TokenType t);

private:
bool
IsDecimal(char c)
{
return c >= '0' && c <= '9';
}
bool
IsHexChar(char c)
{
if (c >= '0' && c <= '9')
return true;
if (c >= 'A' && c <= 'F')
return true;
if (c >= 'a' && c <= 'f')
return true;
return false;
}
bool
IsLetterOrDigit(char c)
{
if (c >= 'a' && c <= 'z')
return true;
if (c >= 'A' && c <= 'Z')
return true;
if (c == '_')
return true;
if (c >= '0' && c <= '9')
return true;
// Treat all non-ascii chars as letters for simplicity.
return 0 != (c & 0x80);
}
bool
IsWhitespace(char c)
{
switch (c)
{
case ' ':
case '\t':
case '\r':
return true;
}
return false;
}

bool SkipWhitespace();
bool SkipComment();

TokenType InternalLex(bool newline);

TokenType DoOperator();

TokenType DoIdent();

TokenType DoNumber();

TokenType DoRune();

TokenType DoString();

static llvm::StringMap<TokenType> *InitKeywords();

static llvm::StringMap<TokenType> *m_keywords;

const char *m_src;
const char *m_end;
Token m_last_token;
};

} // namespace lldb_private

#endif
1,035 changes: 1,035 additions & 0 deletions lldb/source/Plugins/ExpressionParser/Go/GoParser.cpp

Large diffs are not rendered by default.

165 changes: 165 additions & 0 deletions lldb/source/Plugins/ExpressionParser/Go/GoParser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
//===-- GoParser.h -----------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_GoParser_h
#define liblldb_GoParser_h

#include "lldb/lldb-private.h"
#include "Plugins/ExpressionParser/Go/GoAST.h"
#include "Plugins/ExpressionParser/Go/GoLexer.h"

namespace lldb_private
{
class GoParser
{
public:
explicit GoParser(const char *src);

GoASTStmt *Statement();

GoASTStmt *GoStmt();
GoASTStmt *ReturnStmt();
GoASTStmt *BranchStmt();
GoASTStmt *EmptyStmt();
GoASTStmt *ExpressionStmt(GoASTExpr *e);
GoASTStmt *IncDecStmt(GoASTExpr *e);
GoASTStmt *Assignment(GoASTExpr *e);
GoASTBlockStmt *Block();

GoASTExpr *MoreExpressionList(); // ["," Expression]
GoASTIdent *MoreIdentifierList(); // ["," Identifier]

GoASTExpr *Expression();
GoASTExpr *UnaryExpr();
GoASTExpr *OrExpr();
GoASTExpr *AndExpr();
GoASTExpr *RelExpr();
GoASTExpr *AddExpr();
GoASTExpr *MulExpr();
GoASTExpr *PrimaryExpr();
GoASTExpr *Operand();
GoASTExpr *Conversion();

GoASTExpr *Selector(GoASTExpr *e);
GoASTExpr *IndexOrSlice(GoASTExpr *e);
GoASTExpr *TypeAssertion(GoASTExpr *e);
GoASTExpr *Arguments(GoASTExpr *e);

GoASTExpr *Type();
GoASTExpr *Type2();
GoASTExpr *ArrayOrSliceType(bool allowEllipsis);
GoASTExpr *StructType();
GoASTExpr *FunctionType();
GoASTExpr *InterfaceType();
GoASTExpr *MapType();
GoASTExpr *ChanType();
GoASTExpr *ChanType2();

GoASTExpr *Name();
GoASTExpr *QualifiedIdent(GoASTIdent *p);
GoASTIdent *Identifier();

GoASTField *FieldDecl();
GoASTExpr *AnonymousFieldType();
GoASTExpr *FieldNamesAndType(GoASTField *f);

GoASTFieldList *Params();
GoASTField *ParamDecl();
GoASTExpr *ParamType();
GoASTFuncType *Signature();
GoASTExpr *CompositeLit();
GoASTExpr *FunctionLit();
GoASTExpr *Element();
GoASTCompositeLit *LiteralValue();

bool
Failed() const
{
return m_failed;
}
bool
AtEOF() const
{
return m_lexer.BytesRemaining() == 0 && m_pos == m_tokens.size();
}

void GetError(Error &error);

private:
class Rule;
friend class Rule;

std::nullptr_t
syntaxerror()
{
m_failed = true;
return nullptr;
}
GoLexer::Token &
next()
{
if (m_pos >= m_tokens.size())
{
if (m_pos != 0 &&
(m_tokens.back().m_type == GoLexer::TOK_EOF || m_tokens.back().m_type == GoLexer::TOK_INVALID))
return m_tokens.back();
m_pos = m_tokens.size();
m_tokens.push_back(m_lexer.Lex());
}
return m_tokens[m_pos++];
}
GoLexer::TokenType
peek()
{
GoLexer::Token &tok = next();
--m_pos;
return tok.m_type;
}
GoLexer::Token *
match(GoLexer::TokenType t)
{
GoLexer::Token &tok = next();
if (tok.m_type == t)
return &tok;
--m_pos;
m_last_tok = t;
return nullptr;
}
GoLexer::Token *
mustMatch(GoLexer::TokenType t)
{
GoLexer::Token *tok = match(t);
if (tok)
return tok;
return syntaxerror();
}
bool Semicolon();

GoASTStmt *
FinishStmt(GoASTStmt *s)
{
if (!Semicolon())
m_failed = true;
return s;
}

llvm::StringRef CopyString(llvm::StringRef s);

GoLexer m_lexer;
std::vector<GoLexer::Token> m_tokens;
size_t m_pos;
llvm::StringRef m_error;
llvm::StringRef m_last;
GoLexer::TokenType m_last_tok;
llvm::StringMap<uint8_t> m_strings;
bool m_failed;
};
}

#endif
735 changes: 735 additions & 0 deletions lldb/source/Plugins/ExpressionParser/Go/GoUserExpression.cpp

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions lldb/source/Plugins/ExpressionParser/Go/GoUserExpression.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//===-- GoUserExpression.h -----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_GoUserExpression_h_
#define liblldb_GoUserExpression_h_

// C Includes
// C++ Includes
#include <string>
#include <map>
#include <vector>

// Other libraries and framework includes
// Project includes

#include "lldb/lldb-forward.h"
#include "lldb/lldb-private.h"
#include "lldb/Expression/UserExpression.h"
#include "lldb/Expression/ExpressionVariable.h"
#include "lldb/Target/ExecutionContext.h"

namespace lldb_private
{
class GoParser;

class GoPersistentExpressionState : public PersistentExpressionState
{
public:
GoPersistentExpressionState();

ConstString GetNextPersistentVariableName() override;

void RemovePersistentVariable(lldb::ExpressionVariableSP variable) override;

lldb::addr_t
LookupSymbol(const ConstString &name) override
{
return LLDB_INVALID_ADDRESS;
}

static bool
classof(const PersistentExpressionState *pv)
{
return pv->getKind() == PersistentExpressionState::eKindGo;
}

private:
uint32_t m_next_persistent_variable_id; ///< The counter used by GetNextResultName().
};

//----------------------------------------------------------------------
/// @class GoUserExpression GoUserExpression.h "lldb/Expression/GoUserExpression.h"
/// @brief Encapsulates a single expression for use with Go
///
/// LLDB uses expressions for various purposes, notably to call functions
/// and as a backend for the expr command. GoUserExpression encapsulates
/// the objects needed to parse and interpret an expression.
//----------------------------------------------------------------------
class GoUserExpression : public UserExpression
{
public:
GoUserExpression(ExecutionContextScope &exe_scope, const char *expr, const char *expr_prefix,
lldb::LanguageType language, ResultType desired_type);

virtual bool Parse(Stream &error_stream, ExecutionContext &exe_ctx, lldb_private::ExecutionPolicy execution_policy,
bool keep_result_in_memory, bool generate_debug_info) override;

virtual lldb::ExpressionResults Execute(Stream &error_stream, ExecutionContext &exe_ctx,
const EvaluateExpressionOptions &options,
lldb::UserExpressionSP &shared_ptr_to_me,
lldb::ExpressionVariableSP &result) override;

bool
CanInterpret() override
{
return true;
}
bool
FinalizeJITExecution(Stream &error_stream, ExecutionContext &exe_ctx, lldb::ExpressionVariableSP &result,
lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS,
lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS) override
{
return true;
}

private:
class GoInterpreter;
std::unique_ptr<GoInterpreter> m_interpreter;
};

} // namespace lldb_private

#endif // liblldb_GoUserExpression_h_
14 changes: 14 additions & 0 deletions lldb/source/Plugins/ExpressionParser/Go/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
##===- source/Plugins/ExpressionParser/Clang ---------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##

LLDB_LEVEL := ../../../..
LIBRARYNAME := lldbPluginExpressionParserGo
BUILD_ARCHIVE = 1

include $(LLDB_LEVEL)/Makefile
356 changes: 356 additions & 0 deletions lldb/source/Plugins/ExpressionParser/Go/gen_go_ast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
import StringIO

def addNodes():
addNode("ArrayType", "Expr", "len", "Expr", "elt", "Expr")
addNode("AssignStmt", "Stmt", "lhs", "[]Expr", "rhs", "[]Expr", "define", "bool")
addNode("BadDecl", "Decl")
addNode("BadExpr", "Expr")
addNode("BadStmt", "Stmt")
addNode("BasicLit", "Expr", "value", "Token")
addNode("BinaryExpr", "Expr", "x", "Expr", "y", "Expr", "op", "TokenType")
addNode("BlockStmt", "Stmt", "list", "[]Stmt")
addNode("Ident", "Expr", "name", "Token")
addNode("BranchStmt", "Stmt", "label", "Ident", "tok", "TokenType")
addNode("CallExpr", "Expr", "fun", "Expr", "args", "[]Expr", "ellipsis", "bool")
addNode("CaseClause", "Stmt", "list", "[]Expr", "body", "[]Stmt")
addNode("ChanType", "Expr", "dir", "ChanDir", "value", "Expr")
addNode("CommClause", "Stmt", "comm", "Stmt", "body", "[]Stmt")
addNode("CompositeLit", "Expr", "type", "Expr", "elts", "[]Expr")
addNode("DeclStmt", "Stmt", "decl", "Decl")
addNode("DeferStmt", "Stmt", "call", "CallExpr")
addNode("Ellipsis", "Expr", "elt", "Expr")
addNode("EmptyStmt", "Stmt")
addNode("ExprStmt", "Stmt", "x", "Expr")
addNode("Field", "Node", "names", "[]Ident", "type", "Expr", "tag", "BasicLit")
addNode("FieldList", "Node", "list", "[]Field")
addNode("ForStmt", "Stmt", "init", "Stmt", "cond", "Expr", "post", "Stmt", "body", "BlockStmt")
addNode("FuncType", "Expr", "params", "FieldList", "results", "FieldList")
addNode("FuncDecl", "Decl", "recv", "FieldList", "name", "Ident", "type", "FuncType", "body", "BlockStmt")
addNode("FuncLit", "Expr", "type", "FuncType", "body", "BlockStmt")
addNode("GenDecl", "Decl", "tok", "TokenType", "specs", "[]Spec")
addNode("GoStmt", "Stmt", "call", "CallExpr")
addNode("IfStmt", "Stmt", "init", "Stmt", "cond", "Expr", "body", "BlockStmt", "els", "Stmt")
addNode("ImportSpec", "Spec", "name", "Ident", "path", "BasicLit")
addNode("IncDecStmt", "Stmt", "x", "Expr", "tok", "TokenType")
addNode("IndexExpr", "Expr", "x", "Expr", "index", "Expr")
addNode("InterfaceType", "Expr", "methods", "FieldList")
addNode("KeyValueExpr", "Expr", "key", "Expr", "value", "Expr")
addNode("LabeledStmt", "Stmt", "label", "Ident", "stmt", "Stmt")
addNode("MapType", "Expr", "key", "Expr", "value", "Expr")
addNode("ParenExpr", "Expr", "x", "Expr")
addNode("RangeStmt", "Stmt", "key", "Expr", "value", "Expr", "define", "bool", "x", "Expr", "body", "BlockStmt")
addNode("ReturnStmt", "Stmt", "results", "[]Expr")
addNode("SelectStmt", "Stmt", "body", "BlockStmt")
addNode("SelectorExpr", "Expr", "x", "Expr", "sel", "Ident")
addNode("SendStmt", "Stmt", "chan", "Expr", "value", "Expr")
addNode("SliceExpr", "Expr", "x", "Expr", "low", "Expr", "high", "Expr", "max", "Expr", "slice3", "bool")
addNode("StarExpr", "Expr", "x", "Expr")
addNode("StructType", "Expr", "fields", "FieldList")
addNode("SwitchStmt", "Stmt", "init", "Stmt", "tag", "Expr", "body", "BlockStmt")
addNode("TypeAssertExpr", "Expr", "x", "Expr", "type", "Expr")
addNode("TypeSpec", "Spec", "name", "Ident", "type", "Expr")
addNode("TypeSwitchStmt", "Stmt", "init", "Stmt", "assign", "Stmt", "body", "BlockStmt")
addNode("UnaryExpr", "Expr", "op", "TokenType", "x", "Expr")
addNode("ValueSpec", "Spec", "names", "[]Ident", "type", "Expr", "values", "[]Expr")
addParent("Decl", "Node")
addParent("Expr", "Node")
addParent("Spec", "Node")
addParent("Stmt", "Node")


class Member(object):
def __init__(self, name, typename):
self.title = name.title()
self.sname = name
self.mname = 'm_' + name
self.is_list = typename.startswith("[]")
self.is_value = isValueType(typename)
if self.is_value:
self.argtype = typename
self.mtype = typename
elif self.is_list:
self.argtype = 'GoAST' + typename[2:]
self.mtype = 'std::vector<std::unique_ptr<%s> >' % self.argtype
else:
self.argtype = 'GoAST' + typename
self.mtype = 'std::unique_ptr<%s>' % self.argtype
self.mname = self.mname + '_up'


kinds = {}
parentClasses = StringIO.StringIO()
childClasses = StringIO.StringIO()
walker = StringIO.StringIO()

def startClass(name, parent, out):
out.write("""
class GoAST%s : public GoAST%s
{
public:
""" % (name, parent))

def endClass(name, out):
out.write("""
%(name)s(const %(name)s &) = delete;
const %(name)s &operator=(const %(name)s &) = delete;
};
""" % {'name': 'GoAST' + name})

def addNode(name, parent, *children):
startClass(name, parent, childClasses)
l = kinds.setdefault(parent, [])
l.append(name)
children = createMembers(name, children)
addConstructor(name, parent, children)
childClasses.write("""
const char *
GetKindName() const override
{
return "%(name)s";
}
static bool
classof(const GoASTNode *n)
{
return n->GetKind() == e%(name)s;
}
""" % {'name':name})
addChildren(name, children)
endClass(name, childClasses)

def isValueType(typename):
if typename[0].islower():
return True
if typename[0].isupper():
return typename.startswith('Token') or typename == 'ChanDir'
return False


def createMembers(name, children):
l = len(children)
if (l % 2) != 0:
raise Exception("Invalid children for %s: %s" % (name, children))
return [Member(children[i], children[i + 1]) for i in xrange(0, l, 2)]


def addConstructor(name, parent, children):
for c in children:
if c.is_list:
children = [x for x in children if x.is_value]
break
childClasses.write(' ')
if len(children) == 1:
childClasses.write('explicit ')
childClasses.write('GoAST%s(' % name)
for i in xrange(len(children)):
if i > 0:
childClasses.write(', ')

c = children[i]
if c.is_value:
childClasses.write(c.argtype)
childClasses.write(' ')
else:
childClasses.write('%s *' % c.argtype)
childClasses.write(c.sname)
childClasses.write(') : GoAST%s(e%s)' % (parent, name))
for c in children:
childClasses.write(', ')
childClasses.write('%(mname)s(%(sname)s)' % c.__dict__)
childClasses.write(""" {}
~GoAST%s() override = default;
""" % name)


def addChildren(name, children):
if len(children) == 0:
return
walker.write("""
case e%(n)s:
{
GoAST%(n)s *n = llvm::cast<GoAST%(n)s>(this);
(void)n;""" % {'n':name})
for c in children:
if c.is_list:
childClasses.write("""
size_t
Num%(title)s() const
{
return %(mname)s.size();
}
const %(argtype)s *
Get%(title)s(int i) const
{
return %(mname)s[i].get();
}
void
Add%(title)s(%(argtype)s *%(sname)s)
{
%(mname)s.push_back(std::unique_ptr<%(argtype)s>(%(sname)s));
}
""" % c.__dict__)
walker.write("""
for (auto& e : n->%s) { v(e.get()); }""" % c.mname)
else:
const = ''
get = ''
set = ''
t = c.argtype
if isValueType(t):
set = '%(mname)s = %(sname)s' % c.__dict__
t = t + ' '
else:
t = t + ' *'
const = 'const '
get = '.get()'
set = '%(mname)s.reset(%(sname)s)' % c.__dict__
walker.write("""
v(n->%s.get());""" % c.mname)
childClasses.write("""
%(const)s%(type)s
Get%(title)s() const
{
return %(mname)s%(get)s;
}
void
Set%(title)s(%(type)s%(sname)s)
{
%(set)s;
}
""" % {'const':const, 'title': c.title, 'sname': c.sname, 'get': get, 'set': set, 'type': t, 'mname': c.mname})
childClasses.write('\n private:\n friend class GoASTNode;\n')
walker.write("""
return;
}""")
for c in children:
childClasses.write(' %s %s;\n' %(c.mtype, c.mname))


def addParent(name, parent):
startClass(name, parent, parentClasses)
l = kinds[name]
minName = l[0]
maxName = l[-1]
parentClasses.write(""" template <typename R, typename V> R Visit(V *v) const;
static bool
classof(const GoASTNode *n)
{
return n->GetKind() >= e%s && n->GetKind() <= e%s;
}
protected:
explicit GoAST%s(NodeKind kind) : GoASTNode(kind) { }
private:
""" % (minName, maxName, name))
endClass(name, parentClasses)

addNodes()

print """//===-- GoAST.h -------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// DO NOT EDIT.
// Generated by gen_go_ast.py
#ifndef liblldb_GoAST_h
#define liblldb_GoAST_h
#include "lldb/lldb-forward.h"
#include "lldb/lldb-private.h"
#include "llvm/Support/Casting.h"
#include "Plugins/ExpressionParser/Go/GoLexer.h"
namespace lldb_private
{
class GoASTNode
{
public:
typedef GoLexer::TokenType TokenType;
typedef GoLexer::Token Token;
enum ChanDir
{
eChanBidir,
eChanSend,
eChanRecv,
};
enum NodeKind
{"""
for l in kinds.itervalues():
for x in l:
print " e%s," % x
print """ };
virtual ~GoASTNode() = default;
NodeKind
GetKind() const
{
return m_kind;
}
virtual const char *GetKindName() const = 0;
template <typename V> void WalkChildren(V &v);
protected:
explicit GoASTNode(NodeKind kind) : m_kind(kind) { }
private:
const NodeKind m_kind;
GoASTNode(const GoASTNode &) = delete;
const GoASTNode &operator=(const GoASTNode &) = delete;
};
"""


print parentClasses.getvalue()
print childClasses.getvalue()

for k, l in kinds.iteritems():
if k == 'Node':
continue
print """
template <typename R, typename V>
R GoAST%s::Visit(V* v) const
{
switch(GetKind())
{""" % k
for subtype in l:
print """ case e%(n)s:
return v->Visit%(n)s(llvm::cast<const GoAST%(n)s>(this));""" % {'n':subtype}

print """ default:
assert(false && "Invalid kind");
}
}"""

print """
template <typename V>
void GoASTNode::WalkChildren(V &v)
{
switch (m_kind)
{
"""
print walker.getvalue()
print"""
case eEmptyStmt:
case eBadDecl:
case eBadExpr:
case eBadStmt:
break;
}
}
} // namespace lldb_private
#endif
"""
35 changes: 30 additions & 5 deletions lldb/source/Symbol/GoASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Target.h"

#include "Plugins/ExpressionParser/Go/GoUserExpression.h"
#include "Plugins/SymbolFile/DWARF/DWARFASTParserGo.h"

using namespace lldb;
Expand Down Expand Up @@ -200,9 +201,7 @@ class GoStruct : public GoType
};

GoStruct(int kind, const ConstString &name, int64_t byte_size)
: GoType(kind, name)
, m_is_complete(false)
, m_byte_size(byte_size)
: GoType(kind == 0 ? KIND_STRUCT : kind, name), m_is_complete(false), m_byte_size(byte_size)
{
}

Expand Down Expand Up @@ -327,14 +326,20 @@ GoASTContext::CreateInstance (lldb::LanguageType language, Module *module, Targe
if (language == eLanguageTypeGo)
{
ArchSpec arch;
std::shared_ptr<GoASTContext> go_ast_sp;
if (module)
{
arch = module->GetArchitecture();
go_ast_sp = std::shared_ptr<GoASTContext>(new GoASTContext);
}
else if (target)
{
arch = target->GetArchitecture();
go_ast_sp = std::shared_ptr<GoASTContextForExpr>(new GoASTContextForExpr(target->shared_from_this()));
}

if (arch.IsValid())
{
std::shared_ptr<GoASTContext> go_ast_sp(new GoASTContext);
go_ast_sp->SetAddressByteSize(arch.GetAddressByteSize());
return go_ast_sp;
}
Expand Down Expand Up @@ -414,6 +419,10 @@ GoASTContext::IsAggregateType(lldb::opaque_compiler_type_t type)
return false;
if (kind == GoType::KIND_PTR)
return false;
if (kind == GoType::KIND_CHAN)
return false;
if (kind == GoType::KIND_MAP)
return false;
if (kind == GoType::KIND_STRING)
return false;
if (kind == GoType::KIND_UNSAFEPOINTER)
Expand Down Expand Up @@ -583,7 +592,8 @@ GoASTContext::IsPointerType(lldb::opaque_compiler_type_t type, CompilerType *poi
case GoType::KIND_PTR:
case GoType::KIND_UNSAFEPOINTER:
case GoType::KIND_CHAN:
// TODO: is map a pointer? string? function?
case GoType::KIND_MAP:
// TODO: is function a pointer?
return true;
default:
return false;
Expand Down Expand Up @@ -1064,6 +1074,11 @@ GoASTContext::GetNumChildren(lldb::opaque_compiler_type_t type, bool omit_empty_
{
return array->GetLength();
}
else if (t->IsTypedef())
{
return t->GetElementType().GetNumChildren(omit_empty_base_classes);
}

return GetNumFields(type);
}

Expand Down Expand Up @@ -1491,3 +1506,13 @@ GoASTContext::GetDWARFParser()
m_dwarf_ast_parser_ap.reset(new DWARFASTParserGo(*this));
return m_dwarf_ast_parser_ap.get();
}

UserExpression *
GoASTContextForExpr::GetUserExpression(const char *expr, const char *expr_prefix, lldb::LanguageType language,
Expression::ResultType desired_type)
{
TargetSP target = m_target_wp.lock();
if (target)
return new GoUserExpression(*target, expr, expr_prefix, language, desired_type);
return nullptr;
}
113 changes: 113 additions & 0 deletions lldb/test/lang/go/expressions/TestExpressions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"""Test the go expression parser/interpreter."""

import os, time
import unittest2
import lldb
import lldbutil
from lldbtest import *

class TestGoUserExpression(TestBase):

mydir = TestBase.compute_mydir(__file__)

@python_api_test
@skipIfRemote # Not remote test suit ready
@skipUnlessGoInstalled
def test_with_dsym_and_python_api(self):
"""Test GoASTUserExpress."""
self.buildGo()
self.launchProcess()
self.go_expressions()

def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line numbers to break inside main().
self.main_source = "main.go"
self.break_line = line_number(self.main_source, '// Set breakpoint here.')

def check_builtin(self, name, size=0, typeclass=lldb.eTypeClassBuiltin):
tl = self.target().FindTypes(name)
self.assertEqual(1, len(tl))
t = list(tl)[0]
self.assertEqual(name, t.name)
self.assertEqual(typeclass, t.type)
if size > 0:
self.assertEqual(size, t.size)

def launchProcess(self):
exe = os.path.join(os.getcwd(), "a.out")

target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)

bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line)
self.assertTrue(bpt, VALID_BREAKPOINT)

# Now launch the process, and do not stop at entry point.
process = target.LaunchSimple (None, None, self.get_process_working_directory())

self.assertTrue(process, PROCESS_IS_VALID)

# The stop reason of the thread should be breakpoint.
thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt)

# Make sure we stopped at the first breakpoint.
self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.")
self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.")

frame = thread_list[0].GetFrameAtIndex(0)
self.assertTrue (frame, "Got a valid frame 0 frame.")

def go_expressions(self):
frame = self.frame()
v = frame.EvaluateExpression("1")
self.assertEqual(1, v.GetValueAsSigned())
x = frame.EvaluateExpression("x")
self.assertEqual(22, x.GetValueAsSigned())

a = frame.EvaluateExpression("a")
self.assertEqual(3, a.GetNumChildren())
a0 = a.GetChildAtIndex(0)
self.assertEqual(8, a0.GetValueAsSigned())

# Array indexing
a0 = frame.EvaluateExpression("a[0]")
self.assertEqual(8, a0.GetValueAsSigned())

# Slice indexing
b1 = frame.EvaluateExpression("b[1]")
self.assertEqual(9, b1.GetValueAsSigned())

# Test global in this package
g = frame.EvaluateExpression("myGlobal")
self.assertEqual(17, g.GetValueAsSigned(), str(g))

# Global with package name
g = frame.EvaluateExpression("main.myGlobal")
self.assertEqual(17, g.GetValueAsSigned(), str(g))

# Global with quoted package name
g = frame.EvaluateExpression('"main".myGlobal')
self.assertEqual(17, g.GetValueAsSigned(), str(g))

# Casting with package local type
s = frame.EvaluateExpression("*(*myStruct)(i.data)")
sb = s.GetChildMemberWithName("a")
self.assertEqual(2, sb.GetValueAsSigned())

# casting with explicit package
s = frame.EvaluateExpression("*(*main.myStruct)(i.data)")
sb = s.GetChildMemberWithName("a")
self.assertEqual(2, sb.GetValueAsSigned())

# Casting quoted package
s = frame.EvaluateExpression('*(*"main".myStruct)(i.data)')
sb = s.GetChildMemberWithName("b")
self.assertEqual(-1, sb.GetValueAsSigned())

if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()
21 changes: 21 additions & 0 deletions lldb/test/lang/go/expressions/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import "fmt"

type myStruct struct {
a, b int
}

var myGlobal = 17

func myFunc(i interface{}) {
a := [...]int{8, 9, 10}
b := a[:]
x := 22
fmt.Println(a, b, x, i, myGlobal) // Set breakpoint here.
}

func main() {
s := myStruct {2, -1}
myFunc(s)
}
1 change: 1 addition & 0 deletions lldb/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ function(add_lldb_unittest test_name)
endfunction()

add_subdirectory(Editline)
add_subdirectory(Expression)
add_subdirectory(Host)
add_subdirectory(Interpreter)
add_subdirectory(ScriptInterpreter)
Expand Down
3 changes: 3 additions & 0 deletions lldb/unittests/Expression/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
add_lldb_unittest(ExpressionTests
GoParserTest.cpp
)
250 changes: 250 additions & 0 deletions lldb/unittests/Expression/GoParserTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
//===-- GoParserTest.cpp ------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0)
// Workaround for MSVC standard library bug, which fails to include <thread> when
// exceptions are disabled.
#include <eh.h>
#endif

#include <sstream>

#include "gtest/gtest.h"

#include "lldb/Core/Error.h"
#include "lldb/Expression/GoParser.h"

using namespace lldb_private;

namespace
{
struct ASTPrinter
{
ASTPrinter(GoASTNode *n) { (*this)(n); }

void
operator()(GoASTNode *n)
{
if (n == nullptr)
{
m_stream << "nil ";
return;
}
m_stream << "(" << n->GetKindName() << " ";
n->WalkChildren(*this);
if (auto *nn = llvm::dyn_cast<GoASTAssignStmt>(n))
m_stream << nn->GetDefine() << " ";
if (auto *nn = llvm::dyn_cast<GoASTBasicLit>(n))
m_stream << nn->GetValue().m_value.str() << " ";
if (auto *nn = llvm::dyn_cast<GoASTBinaryExpr>(n))
m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " ";
if (auto *nn = llvm::dyn_cast<GoASTIdent>(n))
m_stream << nn->GetName().m_value.str() << " ";
if (auto *nn = llvm::dyn_cast<GoASTBranchStmt>(n))
m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " ";
if (auto *nn = llvm::dyn_cast<GoASTCallExpr>(n))
m_stream << (nn->GetEllipsis() ? "..." : "") << " ";
if (auto *nn = llvm::dyn_cast<GoASTChanType>(n))
m_stream << nn->GetDir() << " ";
if (auto *nn = llvm::dyn_cast<GoASTGenDecl>(n))
m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " ";
if (auto *nn = llvm::dyn_cast<GoASTIncDecStmt>(n))
m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " ";
if (auto *nn = llvm::dyn_cast<GoASTRangeStmt>(n))
m_stream << nn->GetDefine() << " ";
if (auto *nn = llvm::dyn_cast<GoASTSliceExpr>(n))
m_stream << nn->GetSlice3() << " ";
if (auto *nn = llvm::dyn_cast<GoASTUnaryExpr>(n))
m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " ";
m_stream << ") ";
}

const std::string
str() const
{
return m_stream.str();
}
std::stringstream m_stream;
};

testing::AssertionResult
CheckStatement(const char *_s, const char *c_expr, const char *sexpr, const char *code)
{
GoParser parser(code);
std::unique_ptr<GoASTStmt> stmt(parser.Statement());
if (parser.Failed() || !stmt)
{
Error err;
parser.GetError(err);
return testing::AssertionFailure() << "Error parsing " << c_expr << "\n\t" << err.AsCString();
}
std::string actual_sexpr = ASTPrinter(stmt.get()).str();
if (actual_sexpr == sexpr)
return testing::AssertionSuccess();
return testing::AssertionFailure() << "Parsing: " << c_expr << "\nExpected: " << sexpr
<< "\nGot: " << actual_sexpr;
}
} // namespace

#define EXPECT_PARSE(s, c) EXPECT_PRED_FORMAT2(CheckStatement, s, c)

TEST(GoParserTest, ParseBasicLiterals)
{
EXPECT_PARSE("(ExprStmt (BasicLit 0 ) ) ", "0");
EXPECT_PARSE("(ExprStmt (BasicLit 42 ) ) ", "42");
EXPECT_PARSE("(ExprStmt (BasicLit 0600 ) ) ", "0600");
EXPECT_PARSE("(ExprStmt (BasicLit 0xBadFace ) ) ", "0xBadFace");
EXPECT_PARSE("(ExprStmt (BasicLit 170141183460469231731687303715884105727 ) ) ",
"170141183460469231731687303715884105727");

EXPECT_PARSE("(ExprStmt (BasicLit 0. ) ) ", "0.");
EXPECT_PARSE("(ExprStmt (BasicLit 72.40 ) ) ", "72.40");
EXPECT_PARSE("(ExprStmt (BasicLit 072.40 ) ) ", "072.40");
EXPECT_PARSE("(ExprStmt (BasicLit 2.71828 ) ) ", "2.71828");
EXPECT_PARSE("(ExprStmt (BasicLit 1.e+0 ) ) ", "1.e+0");
EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11 ) ) ", "6.67428e-11");
EXPECT_PARSE("(ExprStmt (BasicLit 1E6 ) ) ", "1E6");
EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6 ) ) ", ".12345E+6");

EXPECT_PARSE("(ExprStmt (BasicLit 0i ) ) ", "0i");
EXPECT_PARSE("(ExprStmt (BasicLit 011i ) ) ", "011i");
EXPECT_PARSE("(ExprStmt (BasicLit 0.i ) ) ", "0.i");
EXPECT_PARSE("(ExprStmt (BasicLit 2.71828i ) ) ", "2.71828i");
EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11i ) ) ", "6.67428e-11i");
EXPECT_PARSE("(ExprStmt (BasicLit 1E6i ) ) ", "1E6i");
EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6i ) ) ", ".12345E+6i");

EXPECT_PARSE("(ExprStmt (BasicLit 'a' ) ) ", "'a'");
EXPECT_PARSE("(ExprStmt (BasicLit '本' ) ) ", "'本'");
EXPECT_PARSE("(ExprStmt (BasicLit \"abc\" ) ) ", "\"abc\"");
EXPECT_PARSE("(ExprStmt (BasicLit `abc` ) ) ", "`abc`");
EXPECT_PARSE("(ExprStmt (BasicLit `ab\nc` ) ) ", "`ab\nc`");
}

TEST(GoParserTest, ParseOperand)
{
EXPECT_PARSE("(ExprStmt (Ident a ) ) ", "a");
EXPECT_PARSE("(ExprStmt (Ident _x9 ) ) ", "_x9");
EXPECT_PARSE("(ExprStmt (Ident ThisVariableIsExported ) ) ", "ThisVariableIsExported");
EXPECT_PARSE("(ExprStmt (Ident αβ ) ) ", "αβ");

EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident math ) (Ident Sin ) ) ) ", "math.Sin");
}

TEST(GoParserTest, ParseCompositeLiterals)
{
EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Point3D ) ) ) ", "Point3D{}");
EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Line ) (Ident origin ) (CompositeLit (Ident Point3D ) (KeyValueExpr "
"(Ident y ) (UnaryExpr (BasicLit 4 ) - ) ) (KeyValueExpr (Ident z ) (BasicLit 12.3 ) ) ) ) ) ",
"Line{origin, Point3D{y: -4, z: 12.3}}");
EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident string ) ) ) ) ", "[10]string{}");
EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 6 ) (Ident int ) ) (BasicLit 1 ) (BasicLit 2 ) "
"(BasicLit 3 ) (BasicLit 5 ) ) ) ",
"[6]int {1, 2, 3, 5}");
EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType nil (Ident int ) ) (BasicLit 2 ) (BasicLit 3 ) (BasicLit 5 ) "
"(BasicLit 7 ) (BasicLit 9 ) (BasicLit 2147483647 ) ) ) ",
"[]int{2, 3, 5, 7, 9, 2147483647}");
EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 128 ) (Ident bool ) ) (KeyValueExpr (BasicLit 'a' ) "
"(Ident true ) ) (KeyValueExpr (BasicLit 'e' ) (Ident true ) ) (KeyValueExpr (BasicLit 'i' ) (Ident "
"true ) ) (KeyValueExpr (BasicLit 'o' ) (Ident true ) ) (KeyValueExpr (BasicLit 'u' ) (Ident true ) ) "
"(KeyValueExpr (BasicLit 'y' ) (Ident true ) ) ) ) ",
"[128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}");
EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident float32 ) ) (UnaryExpr (BasicLit 1 ) - ) "
"(KeyValueExpr (BasicLit 4 ) (UnaryExpr (BasicLit 0.1 ) - ) ) (UnaryExpr (BasicLit 0.1 ) - ) "
"(KeyValueExpr (BasicLit 9 ) (UnaryExpr (BasicLit 1 ) - ) ) ) ) ",
"[10]float32{-1, 4: -0.1, -0.1, 9: -1}");
}

TEST(GoParserTest, ParseEllipsisArray)
{
EXPECT_PARSE(
"(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident string ) ) (BasicLit `Sat` ) (BasicLit `Sun` ) ) ) ",
"[...]string {`Sat`, `Sun`}");
EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident Point ) ) (CompositeLit nil (BasicLit 1.5 "
") (UnaryExpr (BasicLit 3.5 ) - ) ) (CompositeLit nil (BasicLit 0 ) (BasicLit 0 ) ) ) ) ",
"[...]Point{{1.5, -3.5}, {0, 0}}");
}

TEST(GoParserTest, ParseMap)
{
EXPECT_PARSE("(ExprStmt (CompositeLit (MapType (Ident string ) (Ident float32 ) ) (KeyValueExpr (BasicLit `C0` ) "
"(BasicLit 16.35 ) ) (KeyValueExpr (BasicLit `D0` ) (BasicLit 18.35 ) ) ) ) ",
"map[string]float32{`C0`: 16.35, `D0`: 18.35, }");
}

TEST(GoParserTest, UnaryExpr)
{
EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) + ) ) ", "+x");
EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) - ) ) ", "-x");
EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ! ) ) ", "!x");
EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ^ ) ) ", "^x");
EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) & ) ) ", "&x");
EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) <- ) ) ", "<-x");
EXPECT_PARSE("(ExprStmt (StarExpr (Ident x ) ) ) ", "*x");
}

TEST(GoParserTest, BinaryExpr)
{
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) || ) ) ", "a || b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) && ) ) ", "a && b");

EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) == ) ) ", "a == b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) != ) ) ", "a != b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) < ) ) ", "a < b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) <= ) ) ", "a <= b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) > ) ) ", "a > b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >= ) ) ", "a >= b");

EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) + ) ) ", "a + b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) - ) ) ", "a - b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) | ) ) ", "a | b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) ^ ) ) ", "a ^ b");

EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) * ) ) ", "a * b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) / ) ) ", "a / b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) % ) ) ", "a % b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) << ) ) ", "a << b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >> ) ) ", "a >> b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) & ) ) ", "a & b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) &^ ) ) ", "a &^ b");

EXPECT_PARSE(
"(ExprStmt (BinaryExpr (BasicLit 23 ) (BinaryExpr (BasicLit 3 ) (IndexExpr (Ident x ) (Ident i ) ) * ) + ) ) ",
"23 + 3*x[i]");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (UnaryExpr (UnaryExpr (Ident a ) + ) + ) + ) ) ", "a + + + a");
EXPECT_PARSE("(ExprStmt (BinaryExpr (UnaryExpr (Ident a ) ^ ) (Ident b ) >> ) ) ", "^a >> b");
EXPECT_PARSE("(ExprStmt (BinaryExpr (CallExpr (Ident f ) ) (CallExpr (Ident g ) ) || ) ) ", "f() || g()");
EXPECT_PARSE("(ExprStmt (BinaryExpr (BinaryExpr (Ident x ) (BinaryExpr (Ident y ) (BasicLit 1 ) + ) == ) "
"(BinaryExpr (UnaryExpr (Ident chanPtr ) <- ) (BasicLit 0 ) > ) && ) ) ",
"x == y+1 && <-chanPtr > 0");
}

TEST(GoParserTest, PrimaryExpr)
{
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident x ) (CallExpr (Ident f ) ) <= ) ) ", "x <= f()");
EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident s ) (BasicLit `.txt` ) + ) ) ", "(s + `.txt`)");
EXPECT_PARSE("(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident true ) ) ) ", "f(3.1415, true)");
EXPECT_PARSE("(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident a ) ... ) ) ", "f(3.1415, a...)");
EXPECT_PARSE("(ExprStmt (IndexExpr (Ident m ) (BasicLit '1' ) ) ) ", "m['1']");
EXPECT_PARSE("(ExprStmt (SliceExpr (Ident s ) (Ident i ) (BinaryExpr (Ident j ) (BasicLit 1 ) + ) nil 0 ) ) ",
"s[i : j + 1]");
EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident obj ) (Ident color ) ) ) ", "obj.color");
EXPECT_PARSE("(ExprStmt (CallExpr (SelectorExpr (IndexExpr (SelectorExpr (Ident f ) (Ident p ) ) (Ident i ) ) "
"(Ident x ) ) ) ) ",
"f.p[i].x()");
}

TEST(GoParserTest, Conversions)
{
EXPECT_PARSE("(ExprStmt (StarExpr (CallExpr (Ident Point ) (Ident p ) ) ) ) ", "*Point(p)");
EXPECT_PARSE("(ExprStmt (CallExpr (StarExpr (Ident Point ) ) (Ident p ) ) ) ", "(*Point)(p)");
EXPECT_PARSE("(ExprStmt (UnaryExpr (CallExpr (ChanType (Ident int ) 0 ) (Ident c ) ) <- ) ) ", "<-chan int(c)");
EXPECT_PARSE("(ExprStmt (TypeAssertExpr (Ident y ) (SelectorExpr (Ident io ) (Ident Reader ) ) ) ) ",
"y.(io.Reader)");
}