Create an expression parser for Go.

The Go interpreter doesn't JIT or use LLVM, so this also
moves all the JIT related code from UserExpression to a new class LLVMUserExpression.

Differential Revision: http://reviews.llvm.org/D13073

Fix merge

llvm-svn: 251820
This commit is contained in:
Ryan Brown 2015-11-02 19:30:40 +00:00
parent 7d564544eb
commit 998c8a1c1c
29 changed files with 7215 additions and 397 deletions

View File

@ -4,5 +4,5 @@ ColumnLimit: 120
BreakBeforeBraces: Allman
AlwaysBreakAfterDefinitionReturnType: true
AllowShortFunctionsOnASingleLine: Inline
BreakConstructorInitializersBeforeComma: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true
IndentCaseLabels: true

View File

@ -74,6 +74,7 @@ set( LLDB_USED_LIBS
lldbPluginProcessElfCore
lldbPluginJITLoaderGDB
lldbPluginExpressionParserClang
lldbPluginExpressionParserGo
)
# Windows-only libraries

View File

@ -0,0 +1,108 @@
//===-- LLVMUserExpression.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_LLVMUserExpression_h
#define liblldb_LLVMUserExpression_h
// C Includes
// C++ Includes
#include <string>
#include <map>
#include <vector>
// Project includes
#include "lldb/Expression/UserExpression.h"
namespace lldb_private
{
//----------------------------------------------------------------------
/// @class LLVMUserExpression LLVMUserExpression.h "lldb/Expression/LLVMUserExpression.h"
/// @brief Encapsulates a one-time expression for use in lldb.
///
/// LLDB uses expressions for various purposes, notably to call functions
/// and as a backend for the expr command. LLVMUserExpression is a virtual base
/// class that encapsulates the objects needed to parse and JIT an expression.
/// The actual parsing part will be provided by the specific implementations
/// of LLVMUserExpression - which will be vended through the appropriate TypeSystem.
//----------------------------------------------------------------------
class LLVMUserExpression : public UserExpression
{
public:
LLVMUserExpression(ExecutionContextScope &exe_scope, const char *expr, const char *expr_prefix,
lldb::LanguageType language, ResultType desired_type);
~LLVMUserExpression() override;
lldb::ExpressionResults Execute(Stream &error_stream, ExecutionContext &exe_ctx,
const EvaluateExpressionOptions &options, lldb::UserExpressionSP &shared_ptr_to_me,
lldb::ExpressionVariableSP &result) override;
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;
bool
CanInterpret() override
{
return m_can_interpret;
}
//------------------------------------------------------------------
/// Return the string that the parser should parse. Must be a full
/// translation unit.
//------------------------------------------------------------------
const char *
Text() override
{
return m_transformed_text.c_str();
}
lldb::ModuleSP GetJITModule() override;
protected:
virtual void ScanContext(ExecutionContext &exe_ctx, lldb_private::Error &err) = 0;
bool PrepareToExecuteJITExpression(Stream &error_stream, ExecutionContext &exe_ctx, lldb::addr_t &struct_address);
virtual bool
AddInitialArguments(ExecutionContext &exe_ctx, std::vector<lldb::addr_t> &args, Stream &error_stream)
{
return true;
}
lldb::addr_t m_stack_frame_bottom; ///< The bottom of the allocated stack frame.
lldb::addr_t m_stack_frame_top; ///< The top of the allocated stack frame.
bool m_allow_cxx; ///< True if the language allows C++.
bool m_allow_objc; ///< True if the language allows Objective-C.
std::string m_transformed_text; ///< The text of the expression, as send to the parser
std::shared_ptr<IRExecutionUnit> m_execution_unit_sp; ///< The execution unit the expression is stored in.
std::unique_ptr<Materializer> m_materializer_ap; ///< The materializer to use when running the expression.
lldb::ModuleWP m_jit_module_wp;
bool m_enforce_valid_object; ///< True if the expression parser should enforce the presence of a valid class pointer
///in order to generate the expression as a method.
bool m_in_cplusplus_method; ///< True if the expression is compiled as a C++ member function (true if it was parsed
///when exe_ctx was in a C++ method).
bool m_in_objectivec_method; ///< True if the expression is compiled as an Objective-C method (true if it was parsed
///when exe_ctx was in an Objective-C method).
bool m_in_static_method; ///< True if the expression is compiled as a static (or class) method (currently true if it
///was parsed when exe_ctx was in an Objective-C class method).
bool m_needs_object_ptr; ///< True if "this" or "self" must be looked up and passed in. False if the expression
///doesn't really use them and they can be NULL.
bool m_const_object; ///< True if "this" is const.
Target *m_target; ///< The target for storing persistent data like types and variables.
bool m_can_interpret; ///< True if the expression could be evaluated statically; false otherwise.
lldb::addr_t
m_materialized_address; ///< The address at which the arguments to the expression have been materialized.
Materializer::DematerializerSP m_dematerializer_sp; ///< The dematerializer.
};
} // namespace lldb_private
#endif

View File

@ -102,11 +102,7 @@ public:
bool keep_result_in_memory,
bool generate_debug_info) = 0;
bool
CanInterpret ()
{
return m_can_interpret;
}
virtual bool CanInterpret() = 0;
bool
MatchesContext (ExecutionContext &exe_ctx);
@ -138,12 +134,10 @@ public:
/// @return
/// A Process::Execution results value.
//------------------------------------------------------------------
lldb::ExpressionResults
Execute (Stream &error_stream,
ExecutionContext &exe_ctx,
const EvaluateExpressionOptions& options,
lldb::UserExpressionSP &shared_ptr_to_me,
lldb::ExpressionVariableSP &result);
virtual lldb::ExpressionResults Execute(Stream &error_stream, ExecutionContext &exe_ctx,
const EvaluateExpressionOptions &options,
lldb::UserExpressionSP &shared_ptr_to_me,
lldb::ExpressionVariableSP &result) = 0;
//------------------------------------------------------------------
/// Apply the side effects of the function to program state.
@ -168,21 +162,18 @@ public:
/// @return
/// A Process::Execution results value.
//------------------------------------------------------------------
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);
virtual 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) = 0;
//------------------------------------------------------------------
/// Return the string that the parser should parse. Must be a full
/// translation unit.
/// Return the string that the parser should parse.
//------------------------------------------------------------------
const char *
Text() override
{
return m_transformed_text.c_str();
return m_expr_text.c_str();
}
//------------------------------------------------------------------
@ -251,6 +242,12 @@ public:
return lldb::ExpressionVariableSP();
}
virtual lldb::ModuleSP
GetJITModule()
{
return lldb::ModuleSP();
}
//------------------------------------------------------------------
/// Evaluate one expression in the scratch context of the
/// target passed in the exe_ctx and return its result.
@ -308,23 +305,6 @@ protected:
/// Populate m_in_cplusplus_method and m_in_objectivec_method based on the environment.
//------------------------------------------------------------------
virtual void
ScanContext (ExecutionContext &exe_ctx,
lldb_private::Error &err) = 0;
bool
PrepareToExecuteJITExpression (Stream &error_stream,
ExecutionContext &exe_ctx,
lldb::addr_t &struct_address);
virtual bool
AddInitialArguments (ExecutionContext &exe_ctx,
std::vector<lldb::addr_t> &args,
Stream &error_stream)
{
return true;
}
void
InstallContext (ExecutionContext &exe_ctx);
@ -335,31 +315,11 @@ protected:
lldb::StackFrameSP &frame_sp);
Address m_address; ///< The address the process is stopped in.
lldb::addr_t m_stack_frame_bottom; ///< The bottom of the allocated stack frame.
lldb::addr_t m_stack_frame_top; ///< The top of the allocated stack frame.
std::string m_expr_text; ///< The text of the expression, as typed by the user
std::string m_expr_prefix; ///< The text of the translation-level definitions, as provided by the user
lldb::LanguageType m_language; ///< The language to use when parsing (eLanguageTypeUnknown means use defaults)
bool m_allow_cxx; ///< True if the language allows C++.
bool m_allow_objc; ///< True if the language allows Objective-C.
std::string m_transformed_text; ///< The text of the expression, as send to the parser
ResultType m_desired_type; ///< The type to coerce the expression's result to. If eResultTypeAny, inferred from the expression.
std::shared_ptr<IRExecutionUnit> m_execution_unit_sp; ///< The execution unit the expression is stored in.
std::unique_ptr<Materializer> m_materializer_ap; ///< The materializer to use when running the expression.
lldb::ModuleWP m_jit_module_wp;
bool m_enforce_valid_object; ///< True if the expression parser should enforce the presence of a valid class pointer in order to generate the expression as a method.
bool m_in_cplusplus_method; ///< True if the expression is compiled as a C++ member function (true if it was parsed when exe_ctx was in a C++ method).
bool m_in_objectivec_method; ///< True if the expression is compiled as an Objective-C method (true if it was parsed when exe_ctx was in an Objective-C method).
bool m_in_static_method; ///< True if the expression is compiled as a static (or class) method (currently true if it was parsed when exe_ctx was in an Objective-C class method).
bool m_needs_object_ptr; ///< True if "this" or "self" must be looked up and passed in. False if the expression doesn't really use them and they can be nullptr.
bool m_const_object; ///< True if "this" is const.
Target *m_target; ///< The target for storing persistent data like types and variables.
bool m_can_interpret; ///< True if the expression could be evaluated statically; false otherwise.
lldb::addr_t m_materialized_address; ///< The address at which the arguments to the expression have been materialized.
Materializer::DematerializerSP m_dematerializer_sp; ///< The dematerializer.
};
} // namespace lldb_private

View File

@ -254,8 +254,7 @@ class GoASTContext : public TypeSystem
lldb::BasicType GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) override;
CompilerType GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding,
size_t bit_size) override;
CompilerType GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding, size_t bit_size) override;
uint32_t GetNumFields(lldb::opaque_compiler_type_t type) override;
@ -394,6 +393,15 @@ class GoASTContext : public TypeSystem
const GoASTContext &operator=(const GoASTContext &) = delete;
};
} // namespace lldb_private
class GoASTContextForExpr : public GoASTContext
{
public:
GoASTContextForExpr(lldb::TargetSP target) : m_target_wp(target) {}
UserExpression *GetUserExpression(const char *expr, const char *expr_prefix, lldb::LanguageType language,
Expression::ResultType desired_type) override;
private:
lldb::TargetWP m_target_wp;
};
}
#endif // liblldb_GoASTContext_h_

View File

@ -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 */; };
@ -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>"; };
@ -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 */,
@ -5207,6 +5222,7 @@
isa = PBXGroup;
children = (
4984BA0C1B97620B008658D4 /* Clang */,
AE44FB371BB35A2E0033EB62 /* Go */,
);
name = ExpressionParser;
sourceTree = "<group>";
@ -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 = (
@ -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 */,
@ -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 */,
@ -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 */,
@ -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 */,

View File

@ -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;
@ -1400,6 +1404,13 @@ 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);
}
@ -1407,8 +1418,15 @@ 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);
}

View File

@ -8,6 +8,7 @@ add_lldb_library(lldbExpression
IRExecutionUnit.cpp
IRInterpreter.cpp
IRMemoryMap.cpp
LLVMUserExpression.cpp
Materializer.cpp
REPL.cpp
UserExpression.cpp

View File

@ -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();
}

View File

@ -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
@ -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,
@ -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;

View File

@ -1 +1,2 @@
add_subdirectory(Clang)
add_subdirectory(Go)

View File

@ -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)
{

View File

@ -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"
@ -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 };

View File

@ -0,0 +1,5 @@
add_lldb_library(lldbPluginExpressionParserGo
GoLexer.cpp
GoParser.cpp
GoUserExpression.cpp
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,402 @@
//===-- GoLexer.cpp ---------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <string.h>
#include "GoLexer.h"
using namespace lldb_private;
llvm::StringMap<GoLexer::TokenType> *GoLexer::m_keywords;
GoLexer::GoLexer(const char *src) : m_src(src), m_end(src + strlen(src)), m_last_token(TOK_INVALID, "")
{
}
bool
GoLexer::SkipWhitespace()
{
bool saw_newline = false;
for (; m_src < m_end; ++m_src)
{
if (*m_src == '\n')
saw_newline = true;
if (*m_src == '/' && !SkipComment())
return saw_newline;
else if (!IsWhitespace(*m_src))
return saw_newline;
}
return saw_newline;
}
bool
GoLexer::SkipComment()
{
if (m_src[0] == '/' && m_src[1] == '/')
{
for (const char *c = m_src + 2; c < m_end; ++c)
{
if (*c == '\n')
{
m_src = c - 1;
return true;
}
}
return true;
}
else if (m_src[0] == '/' && m_src[1] == '*')
{
for (const char *c = m_src + 2; c < m_end; ++c)
{
if (c[0] == '*' && c[1] == '/')
{
m_src = c + 1;
return true;
}
}
}
return false;
}
const GoLexer::Token &
GoLexer::Lex()
{
bool newline = SkipWhitespace();
const char *start = m_src;
m_last_token.m_type = InternalLex(newline);
m_last_token.m_value = llvm::StringRef(start, m_src - start);
return m_last_token;
}
GoLexer::TokenType
GoLexer::InternalLex(bool newline)
{
if (m_src >= m_end)
{
return TOK_EOF;
}
if (newline)
{
switch (m_last_token.m_type)
{
case TOK_IDENTIFIER:
case LIT_FLOAT:
case LIT_IMAGINARY:
case LIT_INTEGER:
case LIT_RUNE:
case LIT_STRING:
case KEYWORD_BREAK:
case KEYWORD_CONTINUE:
case KEYWORD_FALLTHROUGH:
case KEYWORD_RETURN:
case OP_PLUS_PLUS:
case OP_MINUS_MINUS:
case OP_RPAREN:
case OP_RBRACK:
case OP_RBRACE:
return OP_SEMICOLON;
default:
break;
}
}
char c = *m_src;
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return DoNumber();
case '+':
case '-':
case '*':
case '/':
case '%':
case '&':
case '|':
case '^':
case '<':
case '>':
case '!':
case ':':
case ';':
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case ',':
case '=':
return DoOperator();
case '.':
if (IsDecimal(m_src[1]))
return DoNumber();
return DoOperator();
case '$':
// For lldb persistent vars.
return DoIdent();
case '"':
case '`':
return DoString();
case '\'':
return DoRune();
default:
break;
}
if (IsLetterOrDigit(c))
return DoIdent();
++m_src;
return TOK_INVALID;
}
GoLexer::TokenType
GoLexer::DoOperator()
{
TokenType t = TOK_INVALID;
if (m_end - m_src > 2)
{
t = LookupKeyword(llvm::StringRef(m_src, 3));
if (t != TOK_INVALID)
m_src += 3;
}
if (t == TOK_INVALID && m_end - m_src > 1)
{
t = LookupKeyword(llvm::StringRef(m_src, 2));
if (t != TOK_INVALID)
m_src += 2;
}
if (t == TOK_INVALID)
{
t = LookupKeyword(llvm::StringRef(m_src, 1));
++m_src;
}
return t;
}
GoLexer::TokenType
GoLexer::DoIdent()
{
const char *start = m_src++;
while (m_src < m_end && IsLetterOrDigit(*m_src))
{
++m_src;
}
TokenType kw = LookupKeyword(llvm::StringRef(start, m_src - start));
if (kw != TOK_INVALID)
return kw;
return TOK_IDENTIFIER;
}
GoLexer::TokenType
GoLexer::DoNumber()
{
if (m_src[0] == '0' && (m_src[1] == 'x' || m_src[1] == 'X'))
{
m_src += 2;
while (IsHexChar(*m_src))
++m_src;
return LIT_INTEGER;
}
bool dot_ok = true;
bool e_ok = true;
while (true)
{
while (IsDecimal(*m_src))
++m_src;
switch (*m_src)
{
case 'i':
++m_src;
return LIT_IMAGINARY;
case '.':
if (!dot_ok)
return LIT_FLOAT;
++m_src;
dot_ok = false;
break;
case 'e':
case 'E':
if (!e_ok)
return LIT_FLOAT;
dot_ok = e_ok = false;
++m_src;
if (*m_src == '+' || *m_src == '-')
++m_src;
break;
default:
if (dot_ok)
return LIT_INTEGER;
return LIT_FLOAT;
}
}
}
GoLexer::TokenType
GoLexer::DoRune()
{
while (++m_src < m_end)
{
switch (*m_src)
{
case '\'':
++m_src;
return LIT_RUNE;
case '\n':
return TOK_INVALID;
case '\\':
if (m_src[1] == '\n')
return TOK_INVALID;
++m_src;
}
}
return TOK_INVALID;
}
GoLexer::TokenType
GoLexer::DoString()
{
if (*m_src == '`')
{
while (++m_src < m_end)
{
if (*m_src == '`')
{
++m_src;
return LIT_STRING;
}
}
return TOK_INVALID;
}
while (++m_src < m_end)
{
switch (*m_src)
{
case '"':
++m_src;
return LIT_STRING;
case '\n':
return TOK_INVALID;
case '\\':
if (m_src[1] == '\n')
return TOK_INVALID;
++m_src;
}
}
return TOK_INVALID;
}
GoLexer::TokenType
GoLexer::LookupKeyword(llvm::StringRef id)
{
if (m_keywords == nullptr)
m_keywords = InitKeywords();
const auto &it = m_keywords->find(id);
if (it == m_keywords->end())
return TOK_INVALID;
return it->second;
}
llvm::StringRef
GoLexer::LookupToken(TokenType t)
{
if (m_keywords == nullptr)
m_keywords = InitKeywords();
for (const auto &e : *m_keywords)
{
if (e.getValue() == t)
return e.getKey();
}
return "";
}
llvm::StringMap<GoLexer::TokenType> *
GoLexer::InitKeywords()
{
auto &result = *new llvm::StringMap<TokenType>(128);
result["break"] = KEYWORD_BREAK;
result["default"] = KEYWORD_DEFAULT;
result["func"] = KEYWORD_FUNC;
result["interface"] = KEYWORD_INTERFACE;
result["select"] = KEYWORD_SELECT;
result["case"] = KEYWORD_CASE;
result["defer"] = KEYWORD_DEFER;
result["go"] = KEYWORD_GO;
result["map"] = KEYWORD_MAP;
result["struct"] = KEYWORD_STRUCT;
result["chan"] = KEYWORD_CHAN;
result["else"] = KEYWORD_ELSE;
result["goto"] = KEYWORD_GOTO;
result["package"] = KEYWORD_PACKAGE;
result["switch"] = KEYWORD_SWITCH;
result["const"] = KEYWORD_CONST;
result["fallthrough"] = KEYWORD_FALLTHROUGH;
result["if"] = KEYWORD_IF;
result["range"] = KEYWORD_RANGE;
result["type"] = KEYWORD_TYPE;
result["continue"] = KEYWORD_CONTINUE;
result["for"] = KEYWORD_FOR;
result["import"] = KEYWORD_IMPORT;
result["return"] = KEYWORD_RETURN;
result["var"] = KEYWORD_VAR;
result["+"] = OP_PLUS;
result["-"] = OP_MINUS;
result["*"] = OP_STAR;
result["/"] = OP_SLASH;
result["%"] = OP_PERCENT;
result["&"] = OP_AMP;
result["|"] = OP_PIPE;
result["^"] = OP_CARET;
result["<<"] = OP_LSHIFT;
result[">>"] = OP_RSHIFT;
result["&^"] = OP_AMP_CARET;
result["+="] = OP_PLUS_EQ;
result["-="] = OP_MINUS_EQ;
result["*="] = OP_STAR_EQ;
result["/="] = OP_SLASH_EQ;
result["%="] = OP_PERCENT_EQ;
result["&="] = OP_AMP_EQ;
result["|="] = OP_PIPE_EQ;
result["^="] = OP_CARET_EQ;
result["<<="] = OP_LSHIFT_EQ;
result[">>="] = OP_RSHIFT_EQ;
result["&^="] = OP_AMP_CARET_EQ;
result["&&"] = OP_AMP_AMP;
result["||"] = OP_PIPE_PIPE;
result["<-"] = OP_LT_MINUS;
result["++"] = OP_PLUS_PLUS;
result["--"] = OP_MINUS_MINUS;
result["=="] = OP_EQ_EQ;
result["<"] = OP_LT;
result[">"] = OP_GT;
result["="] = OP_EQ;
result["!"] = OP_BANG;
result["!="] = OP_BANG_EQ;
result["<="] = OP_LT_EQ;
result[">="] = OP_GT_EQ;
result[":="] = OP_COLON_EQ;
result["..."] = OP_DOTS;
result["("] = OP_LPAREN;
result["["] = OP_LBRACK;
result["{"] = OP_LBRACE;
result[","] = OP_COMMA;
result["."] = OP_DOT;
result[")"] = OP_RPAREN;
result["]"] = OP_RBRACK;
result["}"] = OP_RBRACE;
result[";"] = OP_SEMICOLON;
result[":"] = OP_COLON;
return &result;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -0,0 +1,735 @@
//===-- GoUserExpression.cpp ---------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <stdio.h>
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include <cstdlib>
#include <string>
#include <map>
#include <vector>
#include "GoUserExpression.h"
#include "lldb/lldb-private.h"
#include "lldb/Core/ConstString.h"
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/DataEncoder.h"
#include "lldb/Core/DataExtractor.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/Core/ValueObjectRegister.h"
#include "lldb/Expression/ExpressionVariable.h"
#include "lldb/Symbol/TypeList.h"
#include "lldb/Symbol/GoASTContext.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanCallUserExpression.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringMap.h"
#include "Plugins/ExpressionParser/Go/GoAST.h"
#include "Plugins/ExpressionParser/Go/GoParser.h"
using namespace lldb_private;
using namespace lldb;
class GoUserExpression::GoInterpreter
{
public:
GoInterpreter(ExecutionContext &exe_ctx, const char *expr)
: m_exe_ctx(exe_ctx), m_frame(exe_ctx.GetFrameSP()), m_parser(expr)
{
if (m_frame)
{
const SymbolContext &ctx = m_frame->GetSymbolContext(eSymbolContextFunction);
ConstString fname = ctx.GetFunctionName();
if (fname.GetLength() > 0)
{
size_t dot = fname.GetStringRef().find('.');
if (dot != llvm::StringRef::npos)
m_package = llvm::StringRef(fname.AsCString(), dot);
}
}
}
void
set_use_dynamic(DynamicValueType use_dynamic)
{
m_use_dynamic = use_dynamic;
}
bool Parse();
lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx);
lldb::ValueObjectSP EvaluateStatement(const GoASTStmt *s);
lldb::ValueObjectSP EvaluateExpr(const GoASTExpr *e);
ValueObjectSP
VisitBadExpr(const GoASTBadExpr *e)
{
m_parser.GetError(m_error);
return nullptr;
}
ValueObjectSP VisitParenExpr(const GoASTParenExpr *e);
ValueObjectSP VisitIdent(const GoASTIdent *e);
ValueObjectSP VisitStarExpr(const GoASTStarExpr *e);
ValueObjectSP VisitSelectorExpr(const GoASTSelectorExpr *e);
ValueObjectSP VisitBasicLit(const GoASTBasicLit *e);
ValueObjectSP VisitIndexExpr(const GoASTIndexExpr *e);
ValueObjectSP VisitUnaryExpr(const GoASTUnaryExpr *e);
ValueObjectSP VisitCallExpr(const GoASTCallExpr *e);
ValueObjectSP
VisitTypeAssertExpr(const GoASTTypeAssertExpr *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitBinaryExpr(const GoASTBinaryExpr *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitArrayType(const GoASTArrayType *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitChanType(const GoASTChanType *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitCompositeLit(const GoASTCompositeLit *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitEllipsis(const GoASTEllipsis *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitFuncType(const GoASTFuncType *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitFuncLit(const GoASTFuncLit *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitInterfaceType(const GoASTInterfaceType *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitKeyValueExpr(const GoASTKeyValueExpr *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitMapType(const GoASTMapType *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitSliceExpr(const GoASTSliceExpr *e)
{
return NotImplemented(e);
}
ValueObjectSP
VisitStructType(const GoASTStructType *e)
{
return NotImplemented(e);
}
CompilerType EvaluateType(const GoASTExpr *e);
Error &
error()
{
return m_error;
}
private:
std::nullptr_t
NotImplemented(const GoASTExpr *e)
{
m_error.SetErrorStringWithFormat("%s node not implemented", e->GetKindName());
return nullptr;
}
ExecutionContext m_exe_ctx;
lldb::StackFrameSP m_frame;
GoParser m_parser;
DynamicValueType m_use_dynamic;
Error m_error;
llvm::StringRef m_package;
std::vector<std::unique_ptr<GoASTStmt>> m_statements;
};
VariableSP
FindGlobalVariable(TargetSP target, llvm::Twine name)
{
ConstString fullname(name.str());
VariableList variable_list;
const bool append = true;
if (!target)
{
return nullptr;
}
const uint32_t match_count = target->GetImages().FindGlobalVariables(fullname, append, 1, variable_list);
if (match_count == 1)
{
return variable_list.GetVariableAtIndex(0);
}
return nullptr;
}
CompilerType
LookupType(TargetSP target, ConstString name)
{
if (!target)
return CompilerType();
SymbolContext sc;
TypeList type_list;
uint32_t num_matches = target->GetImages().FindTypes(sc, name, false, 2, type_list);
if (num_matches > 0)
{
return type_list.GetTypeAtIndex(0)->GetFullCompilerType();
}
return CompilerType();
}
GoUserExpression::GoUserExpression(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)
{
}
bool
GoUserExpression::Parse(Stream &error_stream, ExecutionContext &exe_ctx, lldb_private::ExecutionPolicy execution_policy,
bool keep_result_in_memory, bool generate_debug_info)
{
InstallContext(exe_ctx);
m_interpreter.reset(new GoInterpreter(exe_ctx, GetUserText()));
if (m_interpreter->Parse())
return true;
const char *error_cstr = m_interpreter->error().AsCString();
if (error_cstr && error_cstr[0])
error_stream.Printf("error: %s\n", error_cstr);
else
error_stream.Printf("error: expression can't be interpreted or run\n");
return false;
}
lldb::ExpressionResults
GoUserExpression::Execute(Stream &error_stream, ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options,
lldb::UserExpressionSP &shared_ptr_to_me, lldb::ExpressionVariableSP &result)
{
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP));
lldb_private::ExecutionPolicy execution_policy = options.GetExecutionPolicy();
lldb::ExpressionResults execution_results = lldb::eExpressionSetupError;
Process *process = exe_ctx.GetProcessPtr();
Target *target = exe_ctx.GetTargetPtr();
if (target == nullptr || process == nullptr || process->GetState() != lldb::eStateStopped)
{
if (execution_policy == eExecutionPolicyAlways)
{
if (log)
log->Printf("== [GoUserExpression::Evaluate] Expression may not run, but is not constant ==");
error_stream.Printf("expression needed to run but couldn't");
return execution_results;
}
}
m_interpreter->set_use_dynamic(options.GetUseDynamic());
ValueObjectSP result_val_sp = m_interpreter->Evaluate(exe_ctx);
Error err = m_interpreter->error();
m_interpreter.reset();
if (!result_val_sp)
{
const char *error_cstr = err.AsCString();
if (error_cstr && error_cstr[0])
error_stream.Printf("error: %s\n", error_cstr);
else
error_stream.Printf("error: expression can't be interpreted or run\n");
return lldb::eExpressionDiscarded;
}
result.reset(new ExpressionVariable(ExpressionVariable::eKindGo));
result->m_live_sp = result->m_frozen_sp = result_val_sp;
result->m_flags |= ExpressionVariable::EVIsProgramReference;
PersistentExpressionState *pv = target->GetPersistentExpressionStateForLanguage(eLanguageTypeGo);
if (pv != nullptr)
{
result->SetName(pv->GetNextPersistentVariableName());
pv->AddVariable(result);
}
return lldb::eExpressionCompleted;
}
bool
GoUserExpression::GoInterpreter::Parse()
{
for (std::unique_ptr<GoASTStmt> stmt(m_parser.Statement()); stmt; stmt.reset(m_parser.Statement()))
{
if (m_parser.Failed())
break;
m_statements.emplace_back(std::move(stmt));
}
if (m_parser.Failed() || !m_parser.AtEOF())
m_parser.GetError(m_error);
return m_error.Success();
}
ValueObjectSP
GoUserExpression::GoInterpreter::Evaluate(ExecutionContext &exe_ctx)
{
m_exe_ctx = exe_ctx;
ValueObjectSP result;
for (const std::unique_ptr<GoASTStmt> &stmt : m_statements)
{
result = EvaluateStatement(stmt.get());
if (m_error.Fail())
return nullptr;
}
return result;
}
ValueObjectSP
GoUserExpression::GoInterpreter::EvaluateStatement(const lldb_private::GoASTStmt *stmt)
{
ValueObjectSP result;
switch (stmt->GetKind())
{
case GoASTNode::eBlockStmt:
{
const GoASTBlockStmt *block = llvm::cast<GoASTBlockStmt>(stmt);
for (size_t i = 0; i < block->NumList(); ++i)
result = EvaluateStatement(block->GetList(i));
break;
}
case GoASTNode::eBadStmt:
m_parser.GetError(m_error);
break;
case GoASTNode::eExprStmt:
{
const GoASTExprStmt *expr = llvm::cast<GoASTExprStmt>(stmt);
return EvaluateExpr(expr->GetX());
}
default:
m_error.SetErrorStringWithFormat("%s node not supported", stmt->GetKindName());
}
return result;
}
ValueObjectSP
GoUserExpression::GoInterpreter::EvaluateExpr(const lldb_private::GoASTExpr *e)
{
if (e)
return e->Visit<ValueObjectSP>(this);
return ValueObjectSP();
}
ValueObjectSP
GoUserExpression::GoInterpreter::VisitParenExpr(const lldb_private::GoASTParenExpr *e)
{
return EvaluateExpr(e->GetX());
}
ValueObjectSP
GoUserExpression::GoInterpreter::VisitIdent(const GoASTIdent *e)
{
ValueObjectSP val;
if (m_frame)
{
VariableSP var_sp;
std::string varname = e->GetName().m_value.str();
if (varname.size() > 1 && varname[0] == '$')
{
RegisterContextSP reg_ctx_sp = m_frame->GetRegisterContext();
const RegisterInfo *reg = reg_ctx_sp->GetRegisterInfoByName(varname.c_str() + 1);
if (reg)
{
std::string type;
switch (reg->encoding)
{
case lldb::eEncodingSint:
type.append("int");
break;
case lldb::eEncodingUint:
type.append("uint");
break;
case lldb::eEncodingIEEE754:
type.append("float");
break;
default:
m_error.SetErrorString("Invaild register encoding");
return nullptr;
}
switch (reg->byte_size)
{
case 8:
type.append("64");
break;
case 4:
type.append("32");
break;
case 2:
type.append("16");
break;
case 1:
type.append("8");
break;
default:
m_error.SetErrorString("Invaild register size");
return nullptr;
}
ValueObjectSP regVal =
ValueObjectRegister::Create(m_frame.get(), reg_ctx_sp, reg->kinds[eRegisterKindLLDB]);
CompilerType goType = LookupType(m_frame->CalculateTarget(), ConstString(type));
if (regVal)
{
regVal = regVal->Cast(goType);
return regVal;
}
}
m_error.SetErrorString("Invaild register name");
return nullptr;
}
VariableListSP var_list_sp(m_frame->GetInScopeVariableList(false));
if (var_list_sp)
{
var_sp = var_list_sp->FindVariable(ConstString(varname));
if (var_sp)
val = m_frame->GetValueObjectForFrameVariable(var_sp, m_use_dynamic);
else
{
// When a variable is on the heap instead of the stack, go records a variable
// '&x' instead of 'x'.
var_sp = var_list_sp->FindVariable(ConstString("&" + varname));
if (var_sp)
{
val = m_frame->GetValueObjectForFrameVariable(var_sp, m_use_dynamic);
if (val)
val = val->Dereference(m_error);
if (m_error.Fail())
return nullptr;
}
}
}
if (!val)
{
m_error.Clear();
TargetSP target = m_frame->CalculateTarget();
if (!target)
{
m_error.SetErrorString("No target");
return nullptr;
}
var_sp = FindGlobalVariable(target, m_package + "." + e->GetName().m_value);
if (var_sp)
return m_frame->TrackGlobalVariable(var_sp, m_use_dynamic);
}
}
if (!val)
m_error.SetErrorStringWithFormat("Unknown variable %s", e->GetName().m_value.str().c_str());
return val;
}
ValueObjectSP
GoUserExpression::GoInterpreter::VisitStarExpr(const GoASTStarExpr *e)
{
ValueObjectSP target = EvaluateExpr(e->GetX());
if (!target)
return nullptr;
return target->Dereference(m_error);
}
ValueObjectSP
GoUserExpression::GoInterpreter::VisitSelectorExpr(const lldb_private::GoASTSelectorExpr *e)
{
ValueObjectSP target = EvaluateExpr(e->GetX());
if (target)
{
if (target->GetCompilerType().IsPointerType())
{
target = target->Dereference(m_error);
if (m_error.Fail())
return nullptr;
}
ConstString field(e->GetSel()->GetName().m_value);
ValueObjectSP result = target->GetChildMemberWithName(field, true);
if (!result)
m_error.SetErrorStringWithFormat("Unknown child %s", field.AsCString());
return result;
}
if (const GoASTIdent *package = llvm::dyn_cast<GoASTIdent>(e->GetX()))
{
if (VariableSP global = FindGlobalVariable(m_exe_ctx.GetTargetSP(),
package->GetName().m_value + "." + e->GetSel()->GetName().m_value))
{
if (m_frame)
{
m_error.Clear();
return m_frame->GetValueObjectForFrameVariable(global, m_use_dynamic);
}
}
}
if (const GoASTBasicLit *packageLit = llvm::dyn_cast<GoASTBasicLit>(e->GetX()))
{
if (packageLit->GetValue().m_type == GoLexer::LIT_STRING)
{
std::string value = packageLit->GetValue().m_value.str();
value = value.substr(1, value.size() - 2);
if (VariableSP global =
FindGlobalVariable(m_exe_ctx.GetTargetSP(), value + "." + e->GetSel()->GetName().m_value))
{
if (m_frame)
{
m_error.Clear();
return m_frame->TrackGlobalVariable(global, m_use_dynamic);
}
}
}
}
// EvaluateExpr should have already set m_error.
return target;
}
ValueObjectSP
GoUserExpression::GoInterpreter::VisitBasicLit(const lldb_private::GoASTBasicLit *e)
{
std::string value = e->GetValue().m_value.str();
if (e->GetValue().m_type != GoLexer::LIT_INTEGER)
{
m_error.SetErrorStringWithFormat("Unsupported literal %s", value.c_str());
return nullptr;
}
errno = 0;
int64_t intvalue = strtol(value.c_str(), NULL, 0);
if (errno != 0)
{
m_error.SetErrorToErrno();
return nullptr;
}
DataBufferSP buf(new DataBufferHeap(sizeof(intvalue), 0));
TargetSP target = m_exe_ctx.GetTargetSP();
if (!target)
{
m_error.SetErrorString("No target");
return nullptr;
}
ByteOrder order = target->GetArchitecture().GetByteOrder();
uint8_t addr_size = target->GetArchitecture().GetAddressByteSize();
DataEncoder enc(buf, order, addr_size);
enc.PutU64(0, static_cast<uint64_t>(intvalue));
DataExtractor data(buf, order, addr_size);
CompilerType type = LookupType(target, ConstString("int64"));
return ValueObject::CreateValueObjectFromData(nullptr, data, m_exe_ctx, type);
}
ValueObjectSP
GoUserExpression::GoInterpreter::VisitIndexExpr(const lldb_private::GoASTIndexExpr *e)
{
ValueObjectSP target = EvaluateExpr(e->GetX());
if (!target)
return nullptr;
ValueObjectSP index = EvaluateExpr(e->GetIndex());
if (!index)
return nullptr;
bool is_signed;
if (!index->GetCompilerType().IsIntegerType(is_signed))
{
m_error.SetErrorString("Unsupported index");
return nullptr;
}
size_t idx;
if (is_signed)
idx = index->GetValueAsSigned(0);
else
idx = index->GetValueAsUnsigned(0);
if (GoASTContext::IsGoSlice(target->GetCompilerType()))
{
target = target->GetStaticValue();
ValueObjectSP cap = target->GetChildMemberWithName(ConstString("cap"), true);
if (cap)
{
uint64_t capval = cap->GetValueAsUnsigned(0);
if (idx >= capval)
{
m_error.SetErrorStringWithFormat("Invalid index %" PRIu64 " , cap = %" PRIu64, uint64_t(idx), capval);
return nullptr;
}
}
target = target->GetChildMemberWithName(ConstString("array"), true);
if (target && m_use_dynamic != eNoDynamicValues)
{
ValueObjectSP dynamic = target->GetDynamicValue(m_use_dynamic);
if (dynamic)
target = dynamic;
}
if (!target)
return nullptr;
return target->GetSyntheticArrayMember(idx, true);
}
return target->GetChildAtIndex(idx, true);
}
ValueObjectSP
GoUserExpression::GoInterpreter::VisitUnaryExpr(const GoASTUnaryExpr *e)
{
ValueObjectSP x = EvaluateExpr(e->GetX());
if (!x)
return nullptr;
switch (e->GetOp())
{
case GoLexer::OP_AMP:
{
CompilerType type = x->GetCompilerType().GetPointerType();
uint64_t address = x->GetAddressOf();
return ValueObject::CreateValueObjectFromAddress(nullptr, address, m_exe_ctx, type);
}
case GoLexer::OP_PLUS:
return x;
default:
m_error.SetErrorStringWithFormat("Operator %s not supported",
GoLexer::LookupToken(e->GetOp()).str().c_str());
return nullptr;
}
}
CompilerType
GoUserExpression::GoInterpreter::EvaluateType(const GoASTExpr *e)
{
TargetSP target = m_exe_ctx.GetTargetSP();
if (auto *id = llvm::dyn_cast<GoASTIdent>(e))
{
CompilerType result = LookupType(target, ConstString(id->GetName().m_value));
if (result.IsValid())
return result;
std::string fullname = (m_package + "." + id->GetName().m_value).str();
result = LookupType(target, ConstString(fullname));
if (!result)
m_error.SetErrorStringWithFormat("Unknown type %s", fullname.c_str());
return result;
}
if (auto *sel = llvm::dyn_cast<GoASTSelectorExpr>(e))
{
std::string package;
if (auto *pkg_node = llvm::dyn_cast<GoASTIdent>(sel->GetX()))
{
package = pkg_node->GetName().m_value.str();
}
else if (auto *str_node = llvm::dyn_cast<GoASTBasicLit>(sel->GetX()))
{
if (str_node->GetValue().m_type == GoLexer::LIT_STRING)
{
package = str_node->GetValue().m_value.substr(1).str();
package.resize(package.length() - 1);
}
}
if (package.empty())
{
m_error.SetErrorStringWithFormat("Invalid %s in type expression", sel->GetX()->GetKindName());
return CompilerType();
}
std::string fullname = (package + "." + sel->GetSel()->GetName().m_value).str();
CompilerType result = LookupType(target, ConstString(fullname));
if (!result)
m_error.SetErrorStringWithFormat("Unknown type %s", fullname.c_str());
return result;
}
if (auto *star = llvm::dyn_cast<GoASTStarExpr>(e))
{
CompilerType elem = EvaluateType(star->GetX());
return elem.GetPointerType();
}
if (auto *paren = llvm::dyn_cast<GoASTParenExpr>(e))
return EvaluateType(paren->GetX());
if (auto *array = llvm::dyn_cast<GoASTArrayType>(e))
{
CompilerType elem = EvaluateType(array->GetElt());
}
m_error.SetErrorStringWithFormat("Invalid %s in type expression", e->GetKindName());
return CompilerType();
}
ValueObjectSP
GoUserExpression::GoInterpreter::VisitCallExpr(const lldb_private::GoASTCallExpr *e)
{
ValueObjectSP x = EvaluateExpr(e->GetFun());
if (x || e->NumArgs() != 1)
{
m_error.SetErrorStringWithFormat("Code execution not supported");
return nullptr;
}
m_error.Clear();
CompilerType type = EvaluateType(e->GetFun());
if (!type)
{
return nullptr;
}
ValueObjectSP value = EvaluateExpr(e->GetArgs(0));
if (!value)
return nullptr;
// TODO: Handle special conversions
return value->Cast(type);
}
GoPersistentExpressionState::GoPersistentExpressionState() : PersistentExpressionState(eKindGo)
{
}
ConstString
GoPersistentExpressionState::GetNextPersistentVariableName()
{
char name_cstr[256];
// We can't use the same variable format as clang.
::snprintf(name_cstr, sizeof(name_cstr), "$go%u", m_next_persistent_variable_id++);
ConstString name(name_cstr);
return name;
}
void
GoPersistentExpressionState::RemovePersistentVariable(lldb::ExpressionVariableSP variable)
{
RemoveVariable(variable);
const char *name = variable->GetName().AsCString();
if (*(name++) != '$')
return;
if (*(name++) != 'g')
return;
if (*(name++) != 'o')
return;
if (strtoul(name, NULL, 0) == m_next_persistent_variable_id - 1)
m_next_persistent_variable_id--;
}

View File

@ -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_

View File

@ -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

View File

@ -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
"""

View File

@ -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;
@ -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)
{
}
@ -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;
}
@ -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)
@ -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;
@ -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);
}
@ -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;
}

View File

@ -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()

View File

@ -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)
}

View File

@ -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)

View File

@ -0,0 +1,3 @@
add_lldb_unittest(ExpressionTests
GoParserTest.cpp
)

View File

@ -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)");
}