| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,353 @@ | ||
| //===-- LLVMUserExpression.cpp ----------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // C Includes | ||
| // C++ Includes | ||
|
|
||
| // Project includes | ||
| #include "lldb/Expression/LLVMUserExpression.h" | ||
| #include "lldb/Core/ConstString.h" | ||
| #include "lldb/Core/Log.h" | ||
| #include "lldb/Core/Module.h" | ||
| #include "lldb/Core/StreamFile.h" | ||
| #include "lldb/Core/StreamString.h" | ||
| #include "lldb/Core/ValueObjectConstResult.h" | ||
| #include "lldb/Expression/ExpressionSourceCode.h" | ||
| #include "lldb/Expression/IRExecutionUnit.h" | ||
| #include "lldb/Expression/IRInterpreter.h" | ||
| #include "lldb/Expression/Materializer.h" | ||
| #include "lldb/Host/HostInfo.h" | ||
| #include "lldb/Symbol/Block.h" | ||
| #include "lldb/Symbol/ClangASTContext.h" | ||
| #include "lldb/Symbol/Function.h" | ||
| #include "lldb/Symbol/ObjectFile.h" | ||
| #include "lldb/Symbol/SymbolVendor.h" | ||
| #include "lldb/Symbol/Type.h" | ||
| #include "lldb/Symbol/ClangExternalASTSourceCommon.h" | ||
| #include "lldb/Symbol/VariableList.h" | ||
| #include "lldb/Target/ExecutionContext.h" | ||
| #include "lldb/Target/Process.h" | ||
| #include "lldb/Target/StackFrame.h" | ||
| #include "lldb/Target/Target.h" | ||
| #include "lldb/Target/ThreadPlan.h" | ||
| #include "lldb/Target/ThreadPlanCallUserExpression.h" | ||
|
|
||
| using namespace lldb_private; | ||
|
|
||
| LLVMUserExpression::LLVMUserExpression(ExecutionContextScope &exe_scope, const char *expr, const char *expr_prefix, | ||
| lldb::LanguageType language, ResultType desired_type) | ||
| : UserExpression(exe_scope, expr, expr_prefix, language, desired_type), | ||
| m_stack_frame_bottom(LLDB_INVALID_ADDRESS), | ||
| m_stack_frame_top(LLDB_INVALID_ADDRESS), | ||
| m_transformed_text(), | ||
| m_execution_unit_sp(), | ||
| m_materializer_ap(), | ||
| m_jit_module_wp(), | ||
| m_enforce_valid_object(true), | ||
| m_in_cplusplus_method(false), | ||
| m_in_objectivec_method(false), | ||
| m_in_static_method(false), | ||
| m_needs_object_ptr(false), | ||
| m_const_object(false), | ||
| m_target(NULL), | ||
| m_can_interpret(false), | ||
| m_materialized_address(LLDB_INVALID_ADDRESS) | ||
| { | ||
| } | ||
|
|
||
| LLVMUserExpression::~LLVMUserExpression() | ||
| { | ||
| if (m_target) | ||
| { | ||
| lldb::ModuleSP jit_module_sp(m_jit_module_wp.lock()); | ||
| if (jit_module_sp) | ||
| m_target->GetImages().Remove(jit_module_sp); | ||
| } | ||
| } | ||
|
|
||
| lldb::ExpressionResults | ||
| LLVMUserExpression::Execute(Stream &error_stream, ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options, | ||
| lldb::UserExpressionSP &shared_ptr_to_me, lldb::ExpressionVariableSP &result) | ||
| { | ||
| // The expression log is quite verbose, and if you're just tracking the execution of the | ||
| // expression, it's quite convenient to have these logs come out with the STEP log as well. | ||
| Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); | ||
|
|
||
| if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) | ||
| { | ||
| lldb::addr_t struct_address = LLDB_INVALID_ADDRESS; | ||
|
|
||
| if (!PrepareToExecuteJITExpression(error_stream, exe_ctx, struct_address)) | ||
| { | ||
| error_stream.Printf("Errored out in %s, couldn't PrepareToExecuteJITExpression", __FUNCTION__); | ||
| return lldb::eExpressionSetupError; | ||
| } | ||
|
|
||
| lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS; | ||
| lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS; | ||
|
|
||
| if (m_can_interpret) | ||
| { | ||
| llvm::Module *module = m_execution_unit_sp->GetModule(); | ||
| llvm::Function *function = m_execution_unit_sp->GetFunction(); | ||
|
|
||
| if (!module || !function) | ||
| { | ||
| error_stream.Printf("Supposed to interpret, but nothing is there"); | ||
| return lldb::eExpressionSetupError; | ||
| } | ||
|
|
||
| Error interpreter_error; | ||
|
|
||
| std::vector<lldb::addr_t> args; | ||
|
|
||
| if (!AddInitialArguments(exe_ctx, args, error_stream)) | ||
| { | ||
| error_stream.Printf("Errored out in %s, couldn't AddInitialArguments", __FUNCTION__); | ||
| return lldb::eExpressionSetupError; | ||
| } | ||
|
|
||
| args.push_back(struct_address); | ||
|
|
||
| function_stack_bottom = m_stack_frame_bottom; | ||
| function_stack_top = m_stack_frame_top; | ||
|
|
||
| IRInterpreter::Interpret(*module, *function, args, *m_execution_unit_sp.get(), interpreter_error, | ||
| function_stack_bottom, function_stack_top, exe_ctx); | ||
|
|
||
| if (!interpreter_error.Success()) | ||
| { | ||
| error_stream.Printf("Supposed to interpret, but failed: %s", interpreter_error.AsCString()); | ||
| return lldb::eExpressionDiscarded; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| if (!exe_ctx.HasThreadScope()) | ||
| { | ||
| error_stream.Printf("UserExpression::Execute called with no thread selected."); | ||
| return lldb::eExpressionSetupError; | ||
| } | ||
|
|
||
| Address wrapper_address(m_jit_start_addr); | ||
|
|
||
| std::vector<lldb::addr_t> args; | ||
|
|
||
| if (!AddInitialArguments(exe_ctx, args, error_stream)) | ||
| { | ||
| error_stream.Printf("Errored out in %s, couldn't AddInitialArguments", __FUNCTION__); | ||
| return lldb::eExpressionSetupError; | ||
| } | ||
|
|
||
| args.push_back(struct_address); | ||
|
|
||
| lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression(exe_ctx.GetThreadRef(), wrapper_address, | ||
| args, options, shared_ptr_to_me)); | ||
|
|
||
| if (!call_plan_sp || !call_plan_sp->ValidatePlan(&error_stream)) | ||
| return lldb::eExpressionSetupError; | ||
|
|
||
| ThreadPlanCallUserExpression *user_expression_plan = | ||
| static_cast<ThreadPlanCallUserExpression *>(call_plan_sp.get()); | ||
|
|
||
| lldb::addr_t function_stack_pointer = user_expression_plan->GetFunctionStackPointer(); | ||
|
|
||
| function_stack_bottom = function_stack_pointer - HostInfo::GetPageSize(); | ||
| function_stack_top = function_stack_pointer; | ||
|
|
||
| if (log) | ||
| log->Printf("-- [UserExpression::Execute] Execution of expression begins --"); | ||
|
|
||
| if (exe_ctx.GetProcessPtr()) | ||
| exe_ctx.GetProcessPtr()->SetRunningUserExpression(true); | ||
|
|
||
| lldb::ExpressionResults execution_result = | ||
| exe_ctx.GetProcessRef().RunThreadPlan(exe_ctx, call_plan_sp, options, error_stream); | ||
|
|
||
| if (exe_ctx.GetProcessPtr()) | ||
| exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); | ||
|
|
||
| if (log) | ||
| log->Printf("-- [UserExpression::Execute] Execution of expression completed --"); | ||
|
|
||
| if (execution_result == lldb::eExpressionInterrupted || execution_result == lldb::eExpressionHitBreakpoint) | ||
| { | ||
| const char *error_desc = NULL; | ||
|
|
||
| if (call_plan_sp) | ||
| { | ||
| lldb::StopInfoSP real_stop_info_sp = call_plan_sp->GetRealStopInfo(); | ||
| if (real_stop_info_sp) | ||
| error_desc = real_stop_info_sp->GetDescription(); | ||
| } | ||
| if (error_desc) | ||
| error_stream.Printf("Execution was interrupted, reason: %s.", error_desc); | ||
| else | ||
| error_stream.PutCString("Execution was interrupted."); | ||
|
|
||
| if ((execution_result == lldb::eExpressionInterrupted && options.DoesUnwindOnError()) || | ||
| (execution_result == lldb::eExpressionHitBreakpoint && options.DoesIgnoreBreakpoints())) | ||
| error_stream.PutCString( | ||
| "\nThe process has been returned to the state before expression evaluation."); | ||
| else | ||
| { | ||
| if (execution_result == lldb::eExpressionHitBreakpoint) | ||
| user_expression_plan->TransferExpressionOwnership(); | ||
| error_stream.PutCString( | ||
| "\nThe process has been left at the point where it was interrupted, " | ||
| "use \"thread return -x\" to return to the state before expression evaluation."); | ||
| } | ||
|
|
||
| return execution_result; | ||
| } | ||
| else if (execution_result == lldb::eExpressionStoppedForDebug) | ||
| { | ||
| error_stream.PutCString( | ||
| "Execution was halted at the first instruction of the expression " | ||
| "function because \"debug\" was requested.\n" | ||
| "Use \"thread return -x\" to return to the state before expression evaluation."); | ||
| return execution_result; | ||
| } | ||
| else if (execution_result != lldb::eExpressionCompleted) | ||
| { | ||
| error_stream.Printf("Couldn't execute function; result was %s\n", | ||
| Process::ExecutionResultAsCString(execution_result)); | ||
| return execution_result; | ||
| } | ||
| } | ||
|
|
||
| if (FinalizeJITExecution(error_stream, exe_ctx, result, function_stack_bottom, function_stack_top)) | ||
| { | ||
| return lldb::eExpressionCompleted; | ||
| } | ||
| else | ||
| { | ||
| return lldb::eExpressionResultUnavailable; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| error_stream.Printf("Expression can't be run, because there is no JIT compiled function"); | ||
| return lldb::eExpressionSetupError; | ||
| } | ||
| } | ||
|
|
||
| bool | ||
| LLVMUserExpression::FinalizeJITExecution(Stream &error_stream, ExecutionContext &exe_ctx, | ||
| lldb::ExpressionVariableSP &result, lldb::addr_t function_stack_bottom, | ||
| lldb::addr_t function_stack_top) | ||
| { | ||
| Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); | ||
|
|
||
| if (log) | ||
| log->Printf("-- [UserExpression::FinalizeJITExecution] Dematerializing after execution --"); | ||
|
|
||
| if (!m_dematerializer_sp) | ||
| { | ||
| error_stream.Printf("Couldn't apply expression side effects : no dematerializer is present"); | ||
| return false; | ||
| } | ||
|
|
||
| Error dematerialize_error; | ||
|
|
||
| m_dematerializer_sp->Dematerialize(dematerialize_error, function_stack_bottom, function_stack_top); | ||
|
|
||
| if (!dematerialize_error.Success()) | ||
| { | ||
| error_stream.Printf("Couldn't apply expression side effects : %s\n", | ||
| dematerialize_error.AsCString("unknown error")); | ||
| return false; | ||
| } | ||
|
|
||
| result = GetResultAfterDematerialization(exe_ctx.GetBestExecutionContextScope()); | ||
|
|
||
| if (result) | ||
| result->TransferAddress(); | ||
|
|
||
| m_dematerializer_sp.reset(); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool | ||
| LLVMUserExpression::PrepareToExecuteJITExpression(Stream &error_stream, ExecutionContext &exe_ctx, | ||
| lldb::addr_t &struct_address) | ||
| { | ||
| lldb::TargetSP target; | ||
| lldb::ProcessSP process; | ||
| lldb::StackFrameSP frame; | ||
|
|
||
| if (!LockAndCheckContext(exe_ctx, target, process, frame)) | ||
| { | ||
| error_stream.Printf("The context has changed before we could JIT the expression!\n"); | ||
| return false; | ||
| } | ||
|
|
||
| if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) | ||
| { | ||
| if (m_materialized_address == LLDB_INVALID_ADDRESS) | ||
| { | ||
| Error alloc_error; | ||
|
|
||
| IRMemoryMap::AllocationPolicy policy = | ||
| m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly : IRMemoryMap::eAllocationPolicyMirror; | ||
|
|
||
| m_materialized_address = m_execution_unit_sp->Malloc( | ||
| m_materializer_ap->GetStructByteSize(), m_materializer_ap->GetStructAlignment(), | ||
| lldb::ePermissionsReadable | lldb::ePermissionsWritable, policy, alloc_error); | ||
|
|
||
| if (!alloc_error.Success()) | ||
| { | ||
| error_stream.Printf("Couldn't allocate space for materialized struct: %s\n", alloc_error.AsCString()); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| struct_address = m_materialized_address; | ||
|
|
||
| if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS) | ||
| { | ||
| Error alloc_error; | ||
|
|
||
| const size_t stack_frame_size = 512 * 1024; | ||
|
|
||
| m_stack_frame_bottom = m_execution_unit_sp->Malloc(stack_frame_size, 8, | ||
| lldb::ePermissionsReadable | lldb::ePermissionsWritable, | ||
| IRMemoryMap::eAllocationPolicyHostOnly, alloc_error); | ||
|
|
||
| m_stack_frame_top = m_stack_frame_bottom + stack_frame_size; | ||
|
|
||
| if (!alloc_error.Success()) | ||
| { | ||
| error_stream.Printf("Couldn't allocate space for the stack frame: %s\n", alloc_error.AsCString()); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| Error materialize_error; | ||
|
|
||
| m_dematerializer_sp = | ||
| m_materializer_ap->Materialize(frame, *m_execution_unit_sp, struct_address, materialize_error); | ||
|
|
||
| if (!materialize_error.Success()) | ||
| { | ||
| error_stream.Printf("Couldn't materialize: %s\n", materialize_error.AsCString()); | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| lldb::ModuleSP | ||
| LLVMUserExpression::GetJITModule() | ||
| { | ||
| if (m_execution_unit_sp) | ||
| return m_execution_unit_sp->GetJITModule(); | ||
| return lldb::ModuleSP(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,2 @@ | ||
| add_subdirectory(Clang) | ||
| add_subdirectory(Go) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| add_lldb_library(lldbPluginExpressionParserGo | ||
| GoLexer.cpp | ||
| GoParser.cpp | ||
| GoUserExpression.cpp | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| //===-- GoLexer.h -----------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef liblldb_GoLexer_h | ||
| #define liblldb_GoLexer_h | ||
|
|
||
| #include "llvm/ADT/StringRef.h" | ||
| #include "llvm/ADT/StringMap.h" | ||
|
|
||
| namespace lldb_private | ||
| { | ||
|
|
||
| class GoLexer | ||
| { | ||
| public: | ||
| explicit GoLexer(const char *src); | ||
|
|
||
| enum TokenType | ||
| { | ||
| TOK_EOF, | ||
| TOK_INVALID, | ||
| TOK_IDENTIFIER, | ||
| LIT_INTEGER, | ||
| LIT_FLOAT, | ||
| LIT_IMAGINARY, | ||
| LIT_RUNE, | ||
| LIT_STRING, | ||
| KEYWORD_BREAK, | ||
| KEYWORD_DEFAULT, | ||
| KEYWORD_FUNC, | ||
| KEYWORD_INTERFACE, | ||
| KEYWORD_SELECT, | ||
| KEYWORD_CASE, | ||
| KEYWORD_DEFER, | ||
| KEYWORD_GO, | ||
| KEYWORD_MAP, | ||
| KEYWORD_STRUCT, | ||
| KEYWORD_CHAN, | ||
| KEYWORD_ELSE, | ||
| KEYWORD_GOTO, | ||
| KEYWORD_PACKAGE, | ||
| KEYWORD_SWITCH, | ||
| KEYWORD_CONST, | ||
| KEYWORD_FALLTHROUGH, | ||
| KEYWORD_IF, | ||
| KEYWORD_RANGE, | ||
| KEYWORD_TYPE, | ||
| KEYWORD_CONTINUE, | ||
| KEYWORD_FOR, | ||
| KEYWORD_IMPORT, | ||
| KEYWORD_RETURN, | ||
| KEYWORD_VAR, | ||
| OP_PLUS, | ||
| OP_MINUS, | ||
| OP_STAR, | ||
| OP_SLASH, | ||
| OP_PERCENT, | ||
| OP_AMP, | ||
| OP_PIPE, | ||
| OP_CARET, | ||
| OP_LSHIFT, | ||
| OP_RSHIFT, | ||
| OP_AMP_CARET, | ||
| OP_PLUS_EQ, | ||
| OP_MINUS_EQ, | ||
| OP_STAR_EQ, | ||
| OP_SLASH_EQ, | ||
| OP_PERCENT_EQ, | ||
| OP_AMP_EQ, | ||
| OP_PIPE_EQ, | ||
| OP_CARET_EQ, | ||
| OP_LSHIFT_EQ, | ||
| OP_RSHIFT_EQ, | ||
| OP_AMP_CARET_EQ, | ||
| OP_AMP_AMP, | ||
| OP_PIPE_PIPE, | ||
| OP_LT_MINUS, | ||
| OP_PLUS_PLUS, | ||
| OP_MINUS_MINUS, | ||
| OP_EQ_EQ, | ||
| OP_LT, | ||
| OP_GT, | ||
| OP_EQ, | ||
| OP_BANG, | ||
| OP_BANG_EQ, | ||
| OP_LT_EQ, | ||
| OP_GT_EQ, | ||
| OP_COLON_EQ, | ||
| OP_DOTS, | ||
| OP_LPAREN, | ||
| OP_LBRACK, | ||
| OP_LBRACE, | ||
| OP_COMMA, | ||
| OP_DOT, | ||
| OP_RPAREN, | ||
| OP_RBRACK, | ||
| OP_RBRACE, | ||
| OP_SEMICOLON, | ||
| OP_COLON, | ||
| }; | ||
|
|
||
| struct Token | ||
| { | ||
| explicit Token(TokenType t, llvm::StringRef text) : m_type(t), m_value(text) {} | ||
| TokenType m_type; | ||
| llvm::StringRef m_value; | ||
| }; | ||
|
|
||
| const Token &Lex(); | ||
|
|
||
| size_t | ||
| BytesRemaining() const | ||
| { | ||
| return m_end - m_src; | ||
| } | ||
| llvm::StringRef | ||
| GetString(int len) const | ||
| { | ||
| return llvm::StringRef(m_src, len); | ||
| } | ||
|
|
||
| static TokenType LookupKeyword(llvm::StringRef id); | ||
| static llvm::StringRef LookupToken(TokenType t); | ||
|
|
||
| private: | ||
| bool | ||
| IsDecimal(char c) | ||
| { | ||
| return c >= '0' && c <= '9'; | ||
| } | ||
| bool | ||
| IsHexChar(char c) | ||
| { | ||
| if (c >= '0' && c <= '9') | ||
| return true; | ||
| if (c >= 'A' && c <= 'F') | ||
| return true; | ||
| if (c >= 'a' && c <= 'f') | ||
| return true; | ||
| return false; | ||
| } | ||
| bool | ||
| IsLetterOrDigit(char c) | ||
| { | ||
| if (c >= 'a' && c <= 'z') | ||
| return true; | ||
| if (c >= 'A' && c <= 'Z') | ||
| return true; | ||
| if (c == '_') | ||
| return true; | ||
| if (c >= '0' && c <= '9') | ||
| return true; | ||
| // Treat all non-ascii chars as letters for simplicity. | ||
| return 0 != (c & 0x80); | ||
| } | ||
| bool | ||
| IsWhitespace(char c) | ||
| { | ||
| switch (c) | ||
| { | ||
| case ' ': | ||
| case '\t': | ||
| case '\r': | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| bool SkipWhitespace(); | ||
| bool SkipComment(); | ||
|
|
||
| TokenType InternalLex(bool newline); | ||
|
|
||
| TokenType DoOperator(); | ||
|
|
||
| TokenType DoIdent(); | ||
|
|
||
| TokenType DoNumber(); | ||
|
|
||
| TokenType DoRune(); | ||
|
|
||
| TokenType DoString(); | ||
|
|
||
| static llvm::StringMap<TokenType> *InitKeywords(); | ||
|
|
||
| static llvm::StringMap<TokenType> *m_keywords; | ||
|
|
||
| const char *m_src; | ||
| const char *m_end; | ||
| Token m_last_token; | ||
| }; | ||
|
|
||
| } // namespace lldb_private | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| //===-- GoParser.h -----------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef liblldb_GoParser_h | ||
| #define liblldb_GoParser_h | ||
|
|
||
| #include "lldb/lldb-private.h" | ||
| #include "Plugins/ExpressionParser/Go/GoAST.h" | ||
| #include "Plugins/ExpressionParser/Go/GoLexer.h" | ||
|
|
||
| namespace lldb_private | ||
| { | ||
| class GoParser | ||
| { | ||
| public: | ||
| explicit GoParser(const char *src); | ||
|
|
||
| GoASTStmt *Statement(); | ||
|
|
||
| GoASTStmt *GoStmt(); | ||
| GoASTStmt *ReturnStmt(); | ||
| GoASTStmt *BranchStmt(); | ||
| GoASTStmt *EmptyStmt(); | ||
| GoASTStmt *ExpressionStmt(GoASTExpr *e); | ||
| GoASTStmt *IncDecStmt(GoASTExpr *e); | ||
| GoASTStmt *Assignment(GoASTExpr *e); | ||
| GoASTBlockStmt *Block(); | ||
|
|
||
| GoASTExpr *MoreExpressionList(); // ["," Expression] | ||
| GoASTIdent *MoreIdentifierList(); // ["," Identifier] | ||
|
|
||
| GoASTExpr *Expression(); | ||
| GoASTExpr *UnaryExpr(); | ||
| GoASTExpr *OrExpr(); | ||
| GoASTExpr *AndExpr(); | ||
| GoASTExpr *RelExpr(); | ||
| GoASTExpr *AddExpr(); | ||
| GoASTExpr *MulExpr(); | ||
| GoASTExpr *PrimaryExpr(); | ||
| GoASTExpr *Operand(); | ||
| GoASTExpr *Conversion(); | ||
|
|
||
| GoASTExpr *Selector(GoASTExpr *e); | ||
| GoASTExpr *IndexOrSlice(GoASTExpr *e); | ||
| GoASTExpr *TypeAssertion(GoASTExpr *e); | ||
| GoASTExpr *Arguments(GoASTExpr *e); | ||
|
|
||
| GoASTExpr *Type(); | ||
| GoASTExpr *Type2(); | ||
| GoASTExpr *ArrayOrSliceType(bool allowEllipsis); | ||
| GoASTExpr *StructType(); | ||
| GoASTExpr *FunctionType(); | ||
| GoASTExpr *InterfaceType(); | ||
| GoASTExpr *MapType(); | ||
| GoASTExpr *ChanType(); | ||
| GoASTExpr *ChanType2(); | ||
|
|
||
| GoASTExpr *Name(); | ||
| GoASTExpr *QualifiedIdent(GoASTIdent *p); | ||
| GoASTIdent *Identifier(); | ||
|
|
||
| GoASTField *FieldDecl(); | ||
| GoASTExpr *AnonymousFieldType(); | ||
| GoASTExpr *FieldNamesAndType(GoASTField *f); | ||
|
|
||
| GoASTFieldList *Params(); | ||
| GoASTField *ParamDecl(); | ||
| GoASTExpr *ParamType(); | ||
| GoASTFuncType *Signature(); | ||
| GoASTExpr *CompositeLit(); | ||
| GoASTExpr *FunctionLit(); | ||
| GoASTExpr *Element(); | ||
| GoASTCompositeLit *LiteralValue(); | ||
|
|
||
| bool | ||
| Failed() const | ||
| { | ||
| return m_failed; | ||
| } | ||
| bool | ||
| AtEOF() const | ||
| { | ||
| return m_lexer.BytesRemaining() == 0 && m_pos == m_tokens.size(); | ||
| } | ||
|
|
||
| void GetError(Error &error); | ||
|
|
||
| private: | ||
| class Rule; | ||
| friend class Rule; | ||
|
|
||
| std::nullptr_t | ||
| syntaxerror() | ||
| { | ||
| m_failed = true; | ||
| return nullptr; | ||
| } | ||
| GoLexer::Token & | ||
| next() | ||
| { | ||
| if (m_pos >= m_tokens.size()) | ||
| { | ||
| if (m_pos != 0 && | ||
| (m_tokens.back().m_type == GoLexer::TOK_EOF || m_tokens.back().m_type == GoLexer::TOK_INVALID)) | ||
| return m_tokens.back(); | ||
| m_pos = m_tokens.size(); | ||
| m_tokens.push_back(m_lexer.Lex()); | ||
| } | ||
| return m_tokens[m_pos++]; | ||
| } | ||
| GoLexer::TokenType | ||
| peek() | ||
| { | ||
| GoLexer::Token &tok = next(); | ||
| --m_pos; | ||
| return tok.m_type; | ||
| } | ||
| GoLexer::Token * | ||
| match(GoLexer::TokenType t) | ||
| { | ||
| GoLexer::Token &tok = next(); | ||
| if (tok.m_type == t) | ||
| return &tok; | ||
| --m_pos; | ||
| m_last_tok = t; | ||
| return nullptr; | ||
| } | ||
| GoLexer::Token * | ||
| mustMatch(GoLexer::TokenType t) | ||
| { | ||
| GoLexer::Token *tok = match(t); | ||
| if (tok) | ||
| return tok; | ||
| return syntaxerror(); | ||
| } | ||
| bool Semicolon(); | ||
|
|
||
| GoASTStmt * | ||
| FinishStmt(GoASTStmt *s) | ||
| { | ||
| if (!Semicolon()) | ||
| m_failed = true; | ||
| return s; | ||
| } | ||
|
|
||
| llvm::StringRef CopyString(llvm::StringRef s); | ||
|
|
||
| GoLexer m_lexer; | ||
| std::vector<GoLexer::Token> m_tokens; | ||
| size_t m_pos; | ||
| llvm::StringRef m_error; | ||
| llvm::StringRef m_last; | ||
| GoLexer::TokenType m_last_tok; | ||
| llvm::StringMap<uint8_t> m_strings; | ||
| bool m_failed; | ||
| }; | ||
| } | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| //===-- GoUserExpression.h -----------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef liblldb_GoUserExpression_h_ | ||
| #define liblldb_GoUserExpression_h_ | ||
|
|
||
| // C Includes | ||
| // C++ Includes | ||
| #include <string> | ||
| #include <map> | ||
| #include <vector> | ||
|
|
||
| // Other libraries and framework includes | ||
| // Project includes | ||
|
|
||
| #include "lldb/lldb-forward.h" | ||
| #include "lldb/lldb-private.h" | ||
| #include "lldb/Expression/UserExpression.h" | ||
| #include "lldb/Expression/ExpressionVariable.h" | ||
| #include "lldb/Target/ExecutionContext.h" | ||
|
|
||
| namespace lldb_private | ||
| { | ||
| class GoParser; | ||
|
|
||
| class GoPersistentExpressionState : public PersistentExpressionState | ||
| { | ||
| public: | ||
| GoPersistentExpressionState(); | ||
|
|
||
| ConstString GetNextPersistentVariableName() override; | ||
|
|
||
| void RemovePersistentVariable(lldb::ExpressionVariableSP variable) override; | ||
|
|
||
| lldb::addr_t | ||
| LookupSymbol(const ConstString &name) override | ||
| { | ||
| return LLDB_INVALID_ADDRESS; | ||
| } | ||
|
|
||
| static bool | ||
| classof(const PersistentExpressionState *pv) | ||
| { | ||
| return pv->getKind() == PersistentExpressionState::eKindGo; | ||
| } | ||
|
|
||
| private: | ||
| uint32_t m_next_persistent_variable_id; ///< The counter used by GetNextResultName(). | ||
| }; | ||
|
|
||
| //---------------------------------------------------------------------- | ||
| /// @class GoUserExpression GoUserExpression.h "lldb/Expression/GoUserExpression.h" | ||
| /// @brief Encapsulates a single expression for use with Go | ||
| /// | ||
| /// LLDB uses expressions for various purposes, notably to call functions | ||
| /// and as a backend for the expr command. GoUserExpression encapsulates | ||
| /// the objects needed to parse and interpret an expression. | ||
| //---------------------------------------------------------------------- | ||
| class GoUserExpression : public UserExpression | ||
| { | ||
| public: | ||
| GoUserExpression(ExecutionContextScope &exe_scope, const char *expr, const char *expr_prefix, | ||
| lldb::LanguageType language, ResultType desired_type); | ||
|
|
||
| virtual bool Parse(Stream &error_stream, ExecutionContext &exe_ctx, lldb_private::ExecutionPolicy execution_policy, | ||
| bool keep_result_in_memory, bool generate_debug_info) override; | ||
|
|
||
| virtual lldb::ExpressionResults Execute(Stream &error_stream, ExecutionContext &exe_ctx, | ||
| const EvaluateExpressionOptions &options, | ||
| lldb::UserExpressionSP &shared_ptr_to_me, | ||
| lldb::ExpressionVariableSP &result) override; | ||
|
|
||
| bool | ||
| CanInterpret() override | ||
| { | ||
| return true; | ||
| } | ||
| bool | ||
| FinalizeJITExecution(Stream &error_stream, ExecutionContext &exe_ctx, lldb::ExpressionVariableSP &result, | ||
| lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS, | ||
| lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS) override | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| private: | ||
| class GoInterpreter; | ||
| std::unique_ptr<GoInterpreter> m_interpreter; | ||
| }; | ||
|
|
||
| } // namespace lldb_private | ||
|
|
||
| #endif // liblldb_GoUserExpression_h_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| ##===- source/Plugins/ExpressionParser/Clang ---------------*- Makefile -*-===## | ||
| # | ||
| # The LLVM Compiler Infrastructure | ||
| # | ||
| # This file is distributed under the University of Illinois Open Source | ||
| # License. See LICENSE.TXT for details. | ||
| # | ||
| ##===----------------------------------------------------------------------===## | ||
|
|
||
| LLDB_LEVEL := ../../../.. | ||
| LIBRARYNAME := lldbPluginExpressionParserGo | ||
| BUILD_ARCHIVE = 1 | ||
|
|
||
| include $(LLDB_LEVEL)/Makefile |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,356 @@ | ||
| import StringIO | ||
|
|
||
| def addNodes(): | ||
| addNode("ArrayType", "Expr", "len", "Expr", "elt", "Expr") | ||
| addNode("AssignStmt", "Stmt", "lhs", "[]Expr", "rhs", "[]Expr", "define", "bool") | ||
| addNode("BadDecl", "Decl") | ||
| addNode("BadExpr", "Expr") | ||
| addNode("BadStmt", "Stmt") | ||
| addNode("BasicLit", "Expr", "value", "Token") | ||
| addNode("BinaryExpr", "Expr", "x", "Expr", "y", "Expr", "op", "TokenType") | ||
| addNode("BlockStmt", "Stmt", "list", "[]Stmt") | ||
| addNode("Ident", "Expr", "name", "Token") | ||
| addNode("BranchStmt", "Stmt", "label", "Ident", "tok", "TokenType") | ||
| addNode("CallExpr", "Expr", "fun", "Expr", "args", "[]Expr", "ellipsis", "bool") | ||
| addNode("CaseClause", "Stmt", "list", "[]Expr", "body", "[]Stmt") | ||
| addNode("ChanType", "Expr", "dir", "ChanDir", "value", "Expr") | ||
| addNode("CommClause", "Stmt", "comm", "Stmt", "body", "[]Stmt") | ||
| addNode("CompositeLit", "Expr", "type", "Expr", "elts", "[]Expr") | ||
| addNode("DeclStmt", "Stmt", "decl", "Decl") | ||
| addNode("DeferStmt", "Stmt", "call", "CallExpr") | ||
| addNode("Ellipsis", "Expr", "elt", "Expr") | ||
| addNode("EmptyStmt", "Stmt") | ||
| addNode("ExprStmt", "Stmt", "x", "Expr") | ||
| addNode("Field", "Node", "names", "[]Ident", "type", "Expr", "tag", "BasicLit") | ||
| addNode("FieldList", "Node", "list", "[]Field") | ||
| addNode("ForStmt", "Stmt", "init", "Stmt", "cond", "Expr", "post", "Stmt", "body", "BlockStmt") | ||
| addNode("FuncType", "Expr", "params", "FieldList", "results", "FieldList") | ||
| addNode("FuncDecl", "Decl", "recv", "FieldList", "name", "Ident", "type", "FuncType", "body", "BlockStmt") | ||
| addNode("FuncLit", "Expr", "type", "FuncType", "body", "BlockStmt") | ||
| addNode("GenDecl", "Decl", "tok", "TokenType", "specs", "[]Spec") | ||
| addNode("GoStmt", "Stmt", "call", "CallExpr") | ||
| addNode("IfStmt", "Stmt", "init", "Stmt", "cond", "Expr", "body", "BlockStmt", "els", "Stmt") | ||
| addNode("ImportSpec", "Spec", "name", "Ident", "path", "BasicLit") | ||
| addNode("IncDecStmt", "Stmt", "x", "Expr", "tok", "TokenType") | ||
| addNode("IndexExpr", "Expr", "x", "Expr", "index", "Expr") | ||
| addNode("InterfaceType", "Expr", "methods", "FieldList") | ||
| addNode("KeyValueExpr", "Expr", "key", "Expr", "value", "Expr") | ||
| addNode("LabeledStmt", "Stmt", "label", "Ident", "stmt", "Stmt") | ||
| addNode("MapType", "Expr", "key", "Expr", "value", "Expr") | ||
| addNode("ParenExpr", "Expr", "x", "Expr") | ||
| addNode("RangeStmt", "Stmt", "key", "Expr", "value", "Expr", "define", "bool", "x", "Expr", "body", "BlockStmt") | ||
| addNode("ReturnStmt", "Stmt", "results", "[]Expr") | ||
| addNode("SelectStmt", "Stmt", "body", "BlockStmt") | ||
| addNode("SelectorExpr", "Expr", "x", "Expr", "sel", "Ident") | ||
| addNode("SendStmt", "Stmt", "chan", "Expr", "value", "Expr") | ||
| addNode("SliceExpr", "Expr", "x", "Expr", "low", "Expr", "high", "Expr", "max", "Expr", "slice3", "bool") | ||
| addNode("StarExpr", "Expr", "x", "Expr") | ||
| addNode("StructType", "Expr", "fields", "FieldList") | ||
| addNode("SwitchStmt", "Stmt", "init", "Stmt", "tag", "Expr", "body", "BlockStmt") | ||
| addNode("TypeAssertExpr", "Expr", "x", "Expr", "type", "Expr") | ||
| addNode("TypeSpec", "Spec", "name", "Ident", "type", "Expr") | ||
| addNode("TypeSwitchStmt", "Stmt", "init", "Stmt", "assign", "Stmt", "body", "BlockStmt") | ||
| addNode("UnaryExpr", "Expr", "op", "TokenType", "x", "Expr") | ||
| addNode("ValueSpec", "Spec", "names", "[]Ident", "type", "Expr", "values", "[]Expr") | ||
| addParent("Decl", "Node") | ||
| addParent("Expr", "Node") | ||
| addParent("Spec", "Node") | ||
| addParent("Stmt", "Node") | ||
|
|
||
|
|
||
| class Member(object): | ||
| def __init__(self, name, typename): | ||
| self.title = name.title() | ||
| self.sname = name | ||
| self.mname = 'm_' + name | ||
| self.is_list = typename.startswith("[]") | ||
| self.is_value = isValueType(typename) | ||
| if self.is_value: | ||
| self.argtype = typename | ||
| self.mtype = typename | ||
| elif self.is_list: | ||
| self.argtype = 'GoAST' + typename[2:] | ||
| self.mtype = 'std::vector<std::unique_ptr<%s> >' % self.argtype | ||
| else: | ||
| self.argtype = 'GoAST' + typename | ||
| self.mtype = 'std::unique_ptr<%s>' % self.argtype | ||
| self.mname = self.mname + '_up' | ||
|
|
||
|
|
||
| kinds = {} | ||
| parentClasses = StringIO.StringIO() | ||
| childClasses = StringIO.StringIO() | ||
| walker = StringIO.StringIO() | ||
|
|
||
| def startClass(name, parent, out): | ||
| out.write(""" | ||
| class GoAST%s : public GoAST%s | ||
| { | ||
| public: | ||
| """ % (name, parent)) | ||
|
|
||
| def endClass(name, out): | ||
| out.write(""" | ||
| %(name)s(const %(name)s &) = delete; | ||
| const %(name)s &operator=(const %(name)s &) = delete; | ||
| }; | ||
| """ % {'name': 'GoAST' + name}) | ||
|
|
||
| def addNode(name, parent, *children): | ||
| startClass(name, parent, childClasses) | ||
| l = kinds.setdefault(parent, []) | ||
| l.append(name) | ||
| children = createMembers(name, children) | ||
| addConstructor(name, parent, children) | ||
| childClasses.write(""" | ||
| const char * | ||
| GetKindName() const override | ||
| { | ||
| return "%(name)s"; | ||
| } | ||
| static bool | ||
| classof(const GoASTNode *n) | ||
| { | ||
| return n->GetKind() == e%(name)s; | ||
| } | ||
| """ % {'name':name}) | ||
| addChildren(name, children) | ||
| endClass(name, childClasses) | ||
|
|
||
| def isValueType(typename): | ||
| if typename[0].islower(): | ||
| return True | ||
| if typename[0].isupper(): | ||
| return typename.startswith('Token') or typename == 'ChanDir' | ||
| return False | ||
|
|
||
|
|
||
| def createMembers(name, children): | ||
| l = len(children) | ||
| if (l % 2) != 0: | ||
| raise Exception("Invalid children for %s: %s" % (name, children)) | ||
| return [Member(children[i], children[i + 1]) for i in xrange(0, l, 2)] | ||
|
|
||
|
|
||
| def addConstructor(name, parent, children): | ||
| for c in children: | ||
| if c.is_list: | ||
| children = [x for x in children if x.is_value] | ||
| break | ||
| childClasses.write(' ') | ||
| if len(children) == 1: | ||
| childClasses.write('explicit ') | ||
| childClasses.write('GoAST%s(' % name) | ||
| for i in xrange(len(children)): | ||
| if i > 0: | ||
| childClasses.write(', ') | ||
|
|
||
| c = children[i] | ||
| if c.is_value: | ||
| childClasses.write(c.argtype) | ||
| childClasses.write(' ') | ||
| else: | ||
| childClasses.write('%s *' % c.argtype) | ||
| childClasses.write(c.sname) | ||
| childClasses.write(') : GoAST%s(e%s)' % (parent, name)) | ||
| for c in children: | ||
| childClasses.write(', ') | ||
| childClasses.write('%(mname)s(%(sname)s)' % c.__dict__) | ||
| childClasses.write(""" {} | ||
| ~GoAST%s() override = default; | ||
| """ % name) | ||
|
|
||
|
|
||
| def addChildren(name, children): | ||
| if len(children) == 0: | ||
| return | ||
| walker.write(""" | ||
| case e%(n)s: | ||
| { | ||
| GoAST%(n)s *n = llvm::cast<GoAST%(n)s>(this); | ||
| (void)n;""" % {'n':name}) | ||
| for c in children: | ||
| if c.is_list: | ||
| childClasses.write(""" | ||
| size_t | ||
| Num%(title)s() const | ||
| { | ||
| return %(mname)s.size(); | ||
| } | ||
| const %(argtype)s * | ||
| Get%(title)s(int i) const | ||
| { | ||
| return %(mname)s[i].get(); | ||
| } | ||
| void | ||
| Add%(title)s(%(argtype)s *%(sname)s) | ||
| { | ||
| %(mname)s.push_back(std::unique_ptr<%(argtype)s>(%(sname)s)); | ||
| } | ||
| """ % c.__dict__) | ||
| walker.write(""" | ||
| for (auto& e : n->%s) { v(e.get()); }""" % c.mname) | ||
| else: | ||
| const = '' | ||
| get = '' | ||
| set = '' | ||
| t = c.argtype | ||
| if isValueType(t): | ||
| set = '%(mname)s = %(sname)s' % c.__dict__ | ||
| t = t + ' ' | ||
| else: | ||
| t = t + ' *' | ||
| const = 'const ' | ||
| get = '.get()' | ||
| set = '%(mname)s.reset(%(sname)s)' % c.__dict__ | ||
| walker.write(""" | ||
| v(n->%s.get());""" % c.mname) | ||
| childClasses.write(""" | ||
| %(const)s%(type)s | ||
| Get%(title)s() const | ||
| { | ||
| return %(mname)s%(get)s; | ||
| } | ||
| void | ||
| Set%(title)s(%(type)s%(sname)s) | ||
| { | ||
| %(set)s; | ||
| } | ||
| """ % {'const':const, 'title': c.title, 'sname': c.sname, 'get': get, 'set': set, 'type': t, 'mname': c.mname}) | ||
| childClasses.write('\n private:\n friend class GoASTNode;\n') | ||
| walker.write(""" | ||
| return; | ||
| }""") | ||
| for c in children: | ||
| childClasses.write(' %s %s;\n' %(c.mtype, c.mname)) | ||
|
|
||
|
|
||
| def addParent(name, parent): | ||
| startClass(name, parent, parentClasses) | ||
| l = kinds[name] | ||
| minName = l[0] | ||
| maxName = l[-1] | ||
| parentClasses.write(""" template <typename R, typename V> R Visit(V *v) const; | ||
| static bool | ||
| classof(const GoASTNode *n) | ||
| { | ||
| return n->GetKind() >= e%s && n->GetKind() <= e%s; | ||
| } | ||
| protected: | ||
| explicit GoAST%s(NodeKind kind) : GoASTNode(kind) { } | ||
| private: | ||
| """ % (minName, maxName, name)) | ||
| endClass(name, parentClasses) | ||
|
|
||
| addNodes() | ||
|
|
||
| print """//===-- GoAST.h -------------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // DO NOT EDIT. | ||
| // Generated by gen_go_ast.py | ||
| #ifndef liblldb_GoAST_h | ||
| #define liblldb_GoAST_h | ||
| #include "lldb/lldb-forward.h" | ||
| #include "lldb/lldb-private.h" | ||
| #include "llvm/Support/Casting.h" | ||
| #include "Plugins/ExpressionParser/Go/GoLexer.h" | ||
| namespace lldb_private | ||
| { | ||
| class GoASTNode | ||
| { | ||
| public: | ||
| typedef GoLexer::TokenType TokenType; | ||
| typedef GoLexer::Token Token; | ||
| enum ChanDir | ||
| { | ||
| eChanBidir, | ||
| eChanSend, | ||
| eChanRecv, | ||
| }; | ||
| enum NodeKind | ||
| {""" | ||
| for l in kinds.itervalues(): | ||
| for x in l: | ||
| print " e%s," % x | ||
| print """ }; | ||
| virtual ~GoASTNode() = default; | ||
| NodeKind | ||
| GetKind() const | ||
| { | ||
| return m_kind; | ||
| } | ||
| virtual const char *GetKindName() const = 0; | ||
| template <typename V> void WalkChildren(V &v); | ||
| protected: | ||
| explicit GoASTNode(NodeKind kind) : m_kind(kind) { } | ||
| private: | ||
| const NodeKind m_kind; | ||
| GoASTNode(const GoASTNode &) = delete; | ||
| const GoASTNode &operator=(const GoASTNode &) = delete; | ||
| }; | ||
| """ | ||
|
|
||
|
|
||
| print parentClasses.getvalue() | ||
| print childClasses.getvalue() | ||
|
|
||
| for k, l in kinds.iteritems(): | ||
| if k == 'Node': | ||
| continue | ||
| print """ | ||
| template <typename R, typename V> | ||
| R GoAST%s::Visit(V* v) const | ||
| { | ||
| switch(GetKind()) | ||
| {""" % k | ||
| for subtype in l: | ||
| print """ case e%(n)s: | ||
| return v->Visit%(n)s(llvm::cast<const GoAST%(n)s>(this));""" % {'n':subtype} | ||
|
|
||
| print """ default: | ||
| assert(false && "Invalid kind"); | ||
| } | ||
| }""" | ||
|
|
||
| print """ | ||
| template <typename V> | ||
| void GoASTNode::WalkChildren(V &v) | ||
| { | ||
| switch (m_kind) | ||
| { | ||
| """ | ||
| print walker.getvalue() | ||
| print""" | ||
| case eEmptyStmt: | ||
| case eBadDecl: | ||
| case eBadExpr: | ||
| case eBadStmt: | ||
| break; | ||
| } | ||
| } | ||
| } // namespace lldb_private | ||
| #endif | ||
| """ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| """Test the go expression parser/interpreter.""" | ||
|
|
||
| import os, time | ||
| import unittest2 | ||
| import lldb | ||
| import lldbutil | ||
| from lldbtest import * | ||
|
|
||
| class TestGoUserExpression(TestBase): | ||
|
|
||
| mydir = TestBase.compute_mydir(__file__) | ||
|
|
||
| @python_api_test | ||
| @skipIfRemote # Not remote test suit ready | ||
| @skipUnlessGoInstalled | ||
| def test_with_dsym_and_python_api(self): | ||
| """Test GoASTUserExpress.""" | ||
| self.buildGo() | ||
| self.launchProcess() | ||
| self.go_expressions() | ||
|
|
||
| def setUp(self): | ||
| # Call super's setUp(). | ||
| TestBase.setUp(self) | ||
| # Find the line numbers to break inside main(). | ||
| self.main_source = "main.go" | ||
| self.break_line = line_number(self.main_source, '// Set breakpoint here.') | ||
|
|
||
| def check_builtin(self, name, size=0, typeclass=lldb.eTypeClassBuiltin): | ||
| tl = self.target().FindTypes(name) | ||
| self.assertEqual(1, len(tl)) | ||
| t = list(tl)[0] | ||
| self.assertEqual(name, t.name) | ||
| self.assertEqual(typeclass, t.type) | ||
| if size > 0: | ||
| self.assertEqual(size, t.size) | ||
|
|
||
| def launchProcess(self): | ||
| exe = os.path.join(os.getcwd(), "a.out") | ||
|
|
||
| target = self.dbg.CreateTarget(exe) | ||
| self.assertTrue(target, VALID_TARGET) | ||
|
|
||
| bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) | ||
| self.assertTrue(bpt, VALID_BREAKPOINT) | ||
|
|
||
| # Now launch the process, and do not stop at entry point. | ||
| process = target.LaunchSimple (None, None, self.get_process_working_directory()) | ||
|
|
||
| self.assertTrue(process, PROCESS_IS_VALID) | ||
|
|
||
| # The stop reason of the thread should be breakpoint. | ||
| thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt) | ||
|
|
||
| # Make sure we stopped at the first breakpoint. | ||
| self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") | ||
| self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") | ||
|
|
||
| frame = thread_list[0].GetFrameAtIndex(0) | ||
| self.assertTrue (frame, "Got a valid frame 0 frame.") | ||
|
|
||
| def go_expressions(self): | ||
| frame = self.frame() | ||
| v = frame.EvaluateExpression("1") | ||
| self.assertEqual(1, v.GetValueAsSigned()) | ||
| x = frame.EvaluateExpression("x") | ||
| self.assertEqual(22, x.GetValueAsSigned()) | ||
|
|
||
| a = frame.EvaluateExpression("a") | ||
| self.assertEqual(3, a.GetNumChildren()) | ||
| a0 = a.GetChildAtIndex(0) | ||
| self.assertEqual(8, a0.GetValueAsSigned()) | ||
|
|
||
| # Array indexing | ||
| a0 = frame.EvaluateExpression("a[0]") | ||
| self.assertEqual(8, a0.GetValueAsSigned()) | ||
|
|
||
| # Slice indexing | ||
| b1 = frame.EvaluateExpression("b[1]") | ||
| self.assertEqual(9, b1.GetValueAsSigned()) | ||
|
|
||
| # Test global in this package | ||
| g = frame.EvaluateExpression("myGlobal") | ||
| self.assertEqual(17, g.GetValueAsSigned(), str(g)) | ||
|
|
||
| # Global with package name | ||
| g = frame.EvaluateExpression("main.myGlobal") | ||
| self.assertEqual(17, g.GetValueAsSigned(), str(g)) | ||
|
|
||
| # Global with quoted package name | ||
| g = frame.EvaluateExpression('"main".myGlobal') | ||
| self.assertEqual(17, g.GetValueAsSigned(), str(g)) | ||
|
|
||
| # Casting with package local type | ||
| s = frame.EvaluateExpression("*(*myStruct)(i.data)") | ||
| sb = s.GetChildMemberWithName("a") | ||
| self.assertEqual(2, sb.GetValueAsSigned()) | ||
|
|
||
| # casting with explicit package | ||
| s = frame.EvaluateExpression("*(*main.myStruct)(i.data)") | ||
| sb = s.GetChildMemberWithName("a") | ||
| self.assertEqual(2, sb.GetValueAsSigned()) | ||
|
|
||
| # Casting quoted package | ||
| s = frame.EvaluateExpression('*(*"main".myStruct)(i.data)') | ||
| sb = s.GetChildMemberWithName("b") | ||
| self.assertEqual(-1, sb.GetValueAsSigned()) | ||
|
|
||
| if __name__ == '__main__': | ||
| import atexit | ||
| lldb.SBDebugger.Initialize() | ||
| atexit.register(lambda: lldb.SBDebugger.Terminate()) | ||
| unittest2.main() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package main | ||
|
|
||
| import "fmt" | ||
|
|
||
| type myStruct struct { | ||
| a, b int | ||
| } | ||
|
|
||
| var myGlobal = 17 | ||
|
|
||
| func myFunc(i interface{}) { | ||
| a := [...]int{8, 9, 10} | ||
| b := a[:] | ||
| x := 22 | ||
| fmt.Println(a, b, x, i, myGlobal) // Set breakpoint here. | ||
| } | ||
|
|
||
| func main() { | ||
| s := myStruct {2, -1} | ||
| myFunc(s) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| add_lldb_unittest(ExpressionTests | ||
| GoParserTest.cpp | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,250 @@ | ||
| //===-- GoParserTest.cpp ------------------------------------------*- C++ -*-===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) | ||
| // Workaround for MSVC standard library bug, which fails to include <thread> when | ||
| // exceptions are disabled. | ||
| #include <eh.h> | ||
| #endif | ||
|
|
||
| #include <sstream> | ||
|
|
||
| #include "gtest/gtest.h" | ||
|
|
||
| #include "lldb/Core/Error.h" | ||
| #include "lldb/Expression/GoParser.h" | ||
|
|
||
| using namespace lldb_private; | ||
|
|
||
| namespace | ||
| { | ||
| struct ASTPrinter | ||
| { | ||
| ASTPrinter(GoASTNode *n) { (*this)(n); } | ||
|
|
||
| void | ||
| operator()(GoASTNode *n) | ||
| { | ||
| if (n == nullptr) | ||
| { | ||
| m_stream << "nil "; | ||
| return; | ||
| } | ||
| m_stream << "(" << n->GetKindName() << " "; | ||
| n->WalkChildren(*this); | ||
| if (auto *nn = llvm::dyn_cast<GoASTAssignStmt>(n)) | ||
| m_stream << nn->GetDefine() << " "; | ||
| if (auto *nn = llvm::dyn_cast<GoASTBasicLit>(n)) | ||
| m_stream << nn->GetValue().m_value.str() << " "; | ||
| if (auto *nn = llvm::dyn_cast<GoASTBinaryExpr>(n)) | ||
| m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " "; | ||
| if (auto *nn = llvm::dyn_cast<GoASTIdent>(n)) | ||
| m_stream << nn->GetName().m_value.str() << " "; | ||
| if (auto *nn = llvm::dyn_cast<GoASTBranchStmt>(n)) | ||
| m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; | ||
| if (auto *nn = llvm::dyn_cast<GoASTCallExpr>(n)) | ||
| m_stream << (nn->GetEllipsis() ? "..." : "") << " "; | ||
| if (auto *nn = llvm::dyn_cast<GoASTChanType>(n)) | ||
| m_stream << nn->GetDir() << " "; | ||
| if (auto *nn = llvm::dyn_cast<GoASTGenDecl>(n)) | ||
| m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; | ||
| if (auto *nn = llvm::dyn_cast<GoASTIncDecStmt>(n)) | ||
| m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " "; | ||
| if (auto *nn = llvm::dyn_cast<GoASTRangeStmt>(n)) | ||
| m_stream << nn->GetDefine() << " "; | ||
| if (auto *nn = llvm::dyn_cast<GoASTSliceExpr>(n)) | ||
| m_stream << nn->GetSlice3() << " "; | ||
| if (auto *nn = llvm::dyn_cast<GoASTUnaryExpr>(n)) | ||
| m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " "; | ||
| m_stream << ") "; | ||
| } | ||
|
|
||
| const std::string | ||
| str() const | ||
| { | ||
| return m_stream.str(); | ||
| } | ||
| std::stringstream m_stream; | ||
| }; | ||
|
|
||
| testing::AssertionResult | ||
| CheckStatement(const char *_s, const char *c_expr, const char *sexpr, const char *code) | ||
| { | ||
| GoParser parser(code); | ||
| std::unique_ptr<GoASTStmt> stmt(parser.Statement()); | ||
| if (parser.Failed() || !stmt) | ||
| { | ||
| Error err; | ||
| parser.GetError(err); | ||
| return testing::AssertionFailure() << "Error parsing " << c_expr << "\n\t" << err.AsCString(); | ||
| } | ||
| std::string actual_sexpr = ASTPrinter(stmt.get()).str(); | ||
| if (actual_sexpr == sexpr) | ||
| return testing::AssertionSuccess(); | ||
| return testing::AssertionFailure() << "Parsing: " << c_expr << "\nExpected: " << sexpr | ||
| << "\nGot: " << actual_sexpr; | ||
| } | ||
| } // namespace | ||
|
|
||
| #define EXPECT_PARSE(s, c) EXPECT_PRED_FORMAT2(CheckStatement, s, c) | ||
|
|
||
| TEST(GoParserTest, ParseBasicLiterals) | ||
| { | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 0 ) ) ", "0"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 42 ) ) ", "42"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 0600 ) ) ", "0600"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 0xBadFace ) ) ", "0xBadFace"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 170141183460469231731687303715884105727 ) ) ", | ||
| "170141183460469231731687303715884105727"); | ||
|
|
||
| EXPECT_PARSE("(ExprStmt (BasicLit 0. ) ) ", "0."); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 72.40 ) ) ", "72.40"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 072.40 ) ) ", "072.40"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 2.71828 ) ) ", "2.71828"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 1.e+0 ) ) ", "1.e+0"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11 ) ) ", "6.67428e-11"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 1E6 ) ) ", "1E6"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6 ) ) ", ".12345E+6"); | ||
|
|
||
| EXPECT_PARSE("(ExprStmt (BasicLit 0i ) ) ", "0i"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 011i ) ) ", "011i"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 0.i ) ) ", "0.i"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 2.71828i ) ) ", "2.71828i"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11i ) ) ", "6.67428e-11i"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit 1E6i ) ) ", "1E6i"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6i ) ) ", ".12345E+6i"); | ||
|
|
||
| EXPECT_PARSE("(ExprStmt (BasicLit 'a' ) ) ", "'a'"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit '本' ) ) ", "'本'"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit \"abc\" ) ) ", "\"abc\""); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit `abc` ) ) ", "`abc`"); | ||
| EXPECT_PARSE("(ExprStmt (BasicLit `ab\nc` ) ) ", "`ab\nc`"); | ||
| } | ||
|
|
||
| TEST(GoParserTest, ParseOperand) | ||
| { | ||
| EXPECT_PARSE("(ExprStmt (Ident a ) ) ", "a"); | ||
| EXPECT_PARSE("(ExprStmt (Ident _x9 ) ) ", "_x9"); | ||
| EXPECT_PARSE("(ExprStmt (Ident ThisVariableIsExported ) ) ", "ThisVariableIsExported"); | ||
| EXPECT_PARSE("(ExprStmt (Ident αβ ) ) ", "αβ"); | ||
|
|
||
| EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident math ) (Ident Sin ) ) ) ", "math.Sin"); | ||
| } | ||
|
|
||
| TEST(GoParserTest, ParseCompositeLiterals) | ||
| { | ||
| EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Point3D ) ) ) ", "Point3D{}"); | ||
| EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Line ) (Ident origin ) (CompositeLit (Ident Point3D ) (KeyValueExpr " | ||
| "(Ident y ) (UnaryExpr (BasicLit 4 ) - ) ) (KeyValueExpr (Ident z ) (BasicLit 12.3 ) ) ) ) ) ", | ||
| "Line{origin, Point3D{y: -4, z: 12.3}}"); | ||
| EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident string ) ) ) ) ", "[10]string{}"); | ||
| EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 6 ) (Ident int ) ) (BasicLit 1 ) (BasicLit 2 ) " | ||
| "(BasicLit 3 ) (BasicLit 5 ) ) ) ", | ||
| "[6]int {1, 2, 3, 5}"); | ||
| EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType nil (Ident int ) ) (BasicLit 2 ) (BasicLit 3 ) (BasicLit 5 ) " | ||
| "(BasicLit 7 ) (BasicLit 9 ) (BasicLit 2147483647 ) ) ) ", | ||
| "[]int{2, 3, 5, 7, 9, 2147483647}"); | ||
| EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 128 ) (Ident bool ) ) (KeyValueExpr (BasicLit 'a' ) " | ||
| "(Ident true ) ) (KeyValueExpr (BasicLit 'e' ) (Ident true ) ) (KeyValueExpr (BasicLit 'i' ) (Ident " | ||
| "true ) ) (KeyValueExpr (BasicLit 'o' ) (Ident true ) ) (KeyValueExpr (BasicLit 'u' ) (Ident true ) ) " | ||
| "(KeyValueExpr (BasicLit 'y' ) (Ident true ) ) ) ) ", | ||
| "[128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}"); | ||
| EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident float32 ) ) (UnaryExpr (BasicLit 1 ) - ) " | ||
| "(KeyValueExpr (BasicLit 4 ) (UnaryExpr (BasicLit 0.1 ) - ) ) (UnaryExpr (BasicLit 0.1 ) - ) " | ||
| "(KeyValueExpr (BasicLit 9 ) (UnaryExpr (BasicLit 1 ) - ) ) ) ) ", | ||
| "[10]float32{-1, 4: -0.1, -0.1, 9: -1}"); | ||
| } | ||
|
|
||
| TEST(GoParserTest, ParseEllipsisArray) | ||
| { | ||
| EXPECT_PARSE( | ||
| "(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident string ) ) (BasicLit `Sat` ) (BasicLit `Sun` ) ) ) ", | ||
| "[...]string {`Sat`, `Sun`}"); | ||
| EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident Point ) ) (CompositeLit nil (BasicLit 1.5 " | ||
| ") (UnaryExpr (BasicLit 3.5 ) - ) ) (CompositeLit nil (BasicLit 0 ) (BasicLit 0 ) ) ) ) ", | ||
| "[...]Point{{1.5, -3.5}, {0, 0}}"); | ||
| } | ||
|
|
||
| TEST(GoParserTest, ParseMap) | ||
| { | ||
| EXPECT_PARSE("(ExprStmt (CompositeLit (MapType (Ident string ) (Ident float32 ) ) (KeyValueExpr (BasicLit `C0` ) " | ||
| "(BasicLit 16.35 ) ) (KeyValueExpr (BasicLit `D0` ) (BasicLit 18.35 ) ) ) ) ", | ||
| "map[string]float32{`C0`: 16.35, `D0`: 18.35, }"); | ||
| } | ||
|
|
||
| TEST(GoParserTest, UnaryExpr) | ||
| { | ||
| EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) + ) ) ", "+x"); | ||
| EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) - ) ) ", "-x"); | ||
| EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ! ) ) ", "!x"); | ||
| EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ^ ) ) ", "^x"); | ||
| EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) & ) ) ", "&x"); | ||
| EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) <- ) ) ", "<-x"); | ||
| EXPECT_PARSE("(ExprStmt (StarExpr (Ident x ) ) ) ", "*x"); | ||
| } | ||
|
|
||
| TEST(GoParserTest, BinaryExpr) | ||
| { | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) || ) ) ", "a || b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) && ) ) ", "a && b"); | ||
|
|
||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) == ) ) ", "a == b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) != ) ) ", "a != b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) < ) ) ", "a < b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) <= ) ) ", "a <= b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) > ) ) ", "a > b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >= ) ) ", "a >= b"); | ||
|
|
||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) + ) ) ", "a + b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) - ) ) ", "a - b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) | ) ) ", "a | b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) ^ ) ) ", "a ^ b"); | ||
|
|
||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) * ) ) ", "a * b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) / ) ) ", "a / b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) % ) ) ", "a % b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) << ) ) ", "a << b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >> ) ) ", "a >> b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) & ) ) ", "a & b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) &^ ) ) ", "a &^ b"); | ||
|
|
||
| EXPECT_PARSE( | ||
| "(ExprStmt (BinaryExpr (BasicLit 23 ) (BinaryExpr (BasicLit 3 ) (IndexExpr (Ident x ) (Ident i ) ) * ) + ) ) ", | ||
| "23 + 3*x[i]"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (UnaryExpr (UnaryExpr (Ident a ) + ) + ) + ) ) ", "a + + + a"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (UnaryExpr (Ident a ) ^ ) (Ident b ) >> ) ) ", "^a >> b"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (CallExpr (Ident f ) ) (CallExpr (Ident g ) ) || ) ) ", "f() || g()"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (BinaryExpr (Ident x ) (BinaryExpr (Ident y ) (BasicLit 1 ) + ) == ) " | ||
| "(BinaryExpr (UnaryExpr (Ident chanPtr ) <- ) (BasicLit 0 ) > ) && ) ) ", | ||
| "x == y+1 && <-chanPtr > 0"); | ||
| } | ||
|
|
||
| TEST(GoParserTest, PrimaryExpr) | ||
| { | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident x ) (CallExpr (Ident f ) ) <= ) ) ", "x <= f()"); | ||
| EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident s ) (BasicLit `.txt` ) + ) ) ", "(s + `.txt`)"); | ||
| EXPECT_PARSE("(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident true ) ) ) ", "f(3.1415, true)"); | ||
| EXPECT_PARSE("(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident a ) ... ) ) ", "f(3.1415, a...)"); | ||
| EXPECT_PARSE("(ExprStmt (IndexExpr (Ident m ) (BasicLit '1' ) ) ) ", "m['1']"); | ||
| EXPECT_PARSE("(ExprStmt (SliceExpr (Ident s ) (Ident i ) (BinaryExpr (Ident j ) (BasicLit 1 ) + ) nil 0 ) ) ", | ||
| "s[i : j + 1]"); | ||
| EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident obj ) (Ident color ) ) ) ", "obj.color"); | ||
| EXPECT_PARSE("(ExprStmt (CallExpr (SelectorExpr (IndexExpr (SelectorExpr (Ident f ) (Ident p ) ) (Ident i ) ) " | ||
| "(Ident x ) ) ) ) ", | ||
| "f.p[i].x()"); | ||
| } | ||
|
|
||
| TEST(GoParserTest, Conversions) | ||
| { | ||
| EXPECT_PARSE("(ExprStmt (StarExpr (CallExpr (Ident Point ) (Ident p ) ) ) ) ", "*Point(p)"); | ||
| EXPECT_PARSE("(ExprStmt (CallExpr (StarExpr (Ident Point ) ) (Ident p ) ) ) ", "(*Point)(p)"); | ||
| EXPECT_PARSE("(ExprStmt (UnaryExpr (CallExpr (ChanType (Ident int ) 0 ) (Ident c ) ) <- ) ) ", "<-chan int(c)"); | ||
| EXPECT_PARSE("(ExprStmt (TypeAssertExpr (Ident y ) (SelectorExpr (Ident io ) (Ident Reader ) ) ) ) ", | ||
| "y.(io.Reader)"); | ||
| } |