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:
parent
7d564544eb
commit
998c8a1c1c
@ -4,5 +4,5 @@ ColumnLimit: 120
|
||||
BreakBeforeBraces: Allman
|
||||
AlwaysBreakAfterDefinitionReturnType: true
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
IndentCaseLabels: true
|
||||
|
||||
@ -74,6 +74,7 @@ set( LLDB_USED_LIBS
|
||||
lldbPluginProcessElfCore
|
||||
lldbPluginJITLoaderGDB
|
||||
lldbPluginExpressionParserClang
|
||||
lldbPluginExpressionParserGo
|
||||
)
|
||||
|
||||
# Windows-only libraries
|
||||
|
||||
108
lldb/include/lldb/Expression/LLVMUserExpression.h
Normal file
108
lldb/include/lldb/Expression/LLVMUserExpression.h
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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 */,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ add_lldb_library(lldbExpression
|
||||
IRExecutionUnit.cpp
|
||||
IRInterpreter.cpp
|
||||
IRMemoryMap.cpp
|
||||
LLVMUserExpression.cpp
|
||||
Materializer.cpp
|
||||
REPL.cpp
|
||||
UserExpression.cpp
|
||||
|
||||
353
lldb/source/Expression/LLVMUserExpression.cpp
Normal file
353
lldb/source/Expression/LLVMUserExpression.cpp
Normal 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();
|
||||
}
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -1 +1,2 @@
|
||||
add_subdirectory(Clang)
|
||||
add_subdirectory(Go)
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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 };
|
||||
|
||||
5
lldb/source/Plugins/ExpressionParser/Go/CMakeLists.txt
Normal file
5
lldb/source/Plugins/ExpressionParser/Go/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
add_lldb_library(lldbPluginExpressionParserGo
|
||||
GoLexer.cpp
|
||||
GoParser.cpp
|
||||
GoUserExpression.cpp
|
||||
)
|
||||
3225
lldb/source/Plugins/ExpressionParser/Go/GoAST.h
Normal file
3225
lldb/source/Plugins/ExpressionParser/Go/GoAST.h
Normal file
File diff suppressed because it is too large
Load Diff
402
lldb/source/Plugins/ExpressionParser/Go/GoLexer.cpp
Normal file
402
lldb/source/Plugins/ExpressionParser/Go/GoLexer.cpp
Normal 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;
|
||||
}
|
||||
201
lldb/source/Plugins/ExpressionParser/Go/GoLexer.h
Normal file
201
lldb/source/Plugins/ExpressionParser/Go/GoLexer.h
Normal 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
|
||||
1035
lldb/source/Plugins/ExpressionParser/Go/GoParser.cpp
Normal file
1035
lldb/source/Plugins/ExpressionParser/Go/GoParser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
165
lldb/source/Plugins/ExpressionParser/Go/GoParser.h
Normal file
165
lldb/source/Plugins/ExpressionParser/Go/GoParser.h
Normal 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
|
||||
735
lldb/source/Plugins/ExpressionParser/Go/GoUserExpression.cpp
Normal file
735
lldb/source/Plugins/ExpressionParser/Go/GoUserExpression.cpp
Normal 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--;
|
||||
}
|
||||
99
lldb/source/Plugins/ExpressionParser/Go/GoUserExpression.h
Normal file
99
lldb/source/Plugins/ExpressionParser/Go/GoUserExpression.h
Normal 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_
|
||||
14
lldb/source/Plugins/ExpressionParser/Go/Makefile
Normal file
14
lldb/source/Plugins/ExpressionParser/Go/Makefile
Normal 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
|
||||
356
lldb/source/Plugins/ExpressionParser/Go/gen_go_ast.py
Normal file
356
lldb/source/Plugins/ExpressionParser/Go/gen_go_ast.py
Normal 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
|
||||
"""
|
||||
@ -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;
|
||||
}
|
||||
|
||||
113
lldb/test/lang/go/expressions/TestExpressions.py
Normal file
113
lldb/test/lang/go/expressions/TestExpressions.py
Normal 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()
|
||||
21
lldb/test/lang/go/expressions/main.go
Normal file
21
lldb/test/lang/go/expressions/main.go
Normal 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)
|
||||
}
|
||||
@ -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)
|
||||
|
||||
3
lldb/unittests/Expression/CMakeLists.txt
Normal file
3
lldb/unittests/Expression/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
add_lldb_unittest(ExpressionTests
|
||||
GoParserTest.cpp
|
||||
)
|
||||
250
lldb/unittests/Expression/GoParserTest.cpp
Normal file
250
lldb/unittests/Expression/GoParserTest.cpp
Normal 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)");
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user