1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package demangle 6 7 import ( 8 "bufio" 9 "flag" 10 "fmt" 11 "os" 12 "strings" 13 "testing" 14 ) 15 16 var verbose = flag.Bool("verbose", false, "print each demangle-expected symbol") 17 18 const filename = "testdata/demangle-expected" 19 20 // A list of exceptions from demangle-expected that we do not handle 21 // the same as the standard demangler. We keep a list of exceptions 22 // so that we can use an exact copy of the file. These exceptions are 23 // all based on different handling of a substitution that refers to a 24 // template parameter. The standard demangler seems to have a bug in 25 // which template it uses when a reference or rvalue-reference refers 26 // to a substitution that resolves to a template parameter. 27 var exceptions = map[string]bool{ 28 "_ZSt7forwardIRN1x14refobjiteratorINS0_3refINS0_4mime30multipart_section_processorObjIZ15get_body_parserIZZN14mime_processor21make_section_iteratorERKNS2_INS3_10sectionObjENS0_10ptrrefBaseEEEbENKUlvE_clEvEUlSB_bE_ZZNS6_21make_section_iteratorESB_bENKSC_clEvEUlSB_E0_ENS1_INS2_INS0_20outputrefiteratorObjIiEES8_EEEERKSsSB_OT_OT0_EUlmE_NS3_32make_multipart_default_discarderISP_EEEES8_EEEEEOT_RNSt16remove_referenceISW_E4typeE": true, 29 "_ZN3mdr16in_cached_threadIRZNK4cudr6GPUSet17parallel_for_eachIZN5tns3d20shape_representation7compute7GPUImpl7executeERKNS_1AINS_7ptr_refIKjEELl3ELl3ENS_8c_strideILl1ELl0EEEEERKNS8_INS9_IjEELl4ELl1ESD_EEEUliRKNS1_7ContextERNS7_5StateEE_JSt6vectorISO_SaISO_EEEEEvOT_DpRT0_EUlSP_E_JSt17reference_wrapperISO_EEEENS_12ScopedFutureIDTclfp_spcl7forwardISW_Efp0_EEEEESV_DpOSW_": true, 30 "_ZNSt9_Any_data9_M_accessIPZN3sel8Selector6SetObjI3FooJPKcMS4_FviEEEEvRT_DpT0_EUlvE_EESA_v": true, 31 "_ZNSt9_Any_data9_M_accessIPZN13ThreadManager7newTaskIRSt5_BindIFSt7_Mem_fnIM5DiaryFivEEPS5_EEIEEESt6futureINSt9result_ofIFT_DpT0_EE4typeEEOSF_DpOSG_EUlvE_EERSF_v": true, 32 "_ZNSt9_Any_data9_M_accessIPZN6cereal18polymorphic_detail15getInputBindingINS1_16JSONInputArchiveEEENS1_6detail15InputBindingMapIT_E11SerializersERS7_jEUlPvRSt10unique_ptrIvNS5_12EmptyDeleterIvEEEE0_EESA_v": true, 33 "_ZNSt9_Any_data9_M_accessIPZ4postISt8functionIFvvEEEvOT_EUlvE_EERS5_v": true, 34 "_ZNSt9_Any_data9_M_accessIPZN13ThreadManager10futureTaskISt5_BindIFSt7_Mem_fnIM6RunnerFvvEEPS5_EEEEvOT_EUlvE_EERSC_v": true, 35 } 36 37 // For simplicity, this test reads an exact copy of 38 // libiberty/testsuite/demangle-expected from GCC. See that file for 39 // the syntax. We ignore all tests that are not --format=gnu-v3 or 40 // --format=auto with a string starting with _Z. 41 func TestExpected(t *testing.T) { 42 f, err := os.Open(filename) 43 if err != nil { 44 t.Fatal(err) 45 } 46 scanner := bufio.NewScanner(f) 47 lineno := 1 48 for { 49 format, got := getOptLine(t, scanner, &lineno) 50 if !got { 51 break 52 } 53 report := lineno 54 input := getLine(t, scanner, &lineno) 55 expect := getLine(t, scanner, &lineno) 56 57 testNoParams := false 58 skip := false 59 if len(format) > 0 && format[0] == '-' { 60 for _, arg := range strings.Fields(format) { 61 switch arg { 62 case "--format=gnu-v3": 63 case "--format=auto": 64 if !strings.HasPrefix(input, "_Z") { 65 skip = true 66 } 67 case "--no-params": 68 testNoParams = true 69 case "--ret-postfix", "--ret-drop": 70 skip = true 71 case "--is-v3-ctor", "--is-v3-dtor": 72 skip = true 73 default: 74 if !strings.HasPrefix(arg, "--format=") { 75 t.Errorf("%s:%d: unrecognized argument %s", filename, report, arg) 76 } 77 skip = true 78 } 79 } 80 } 81 82 // The libiberty testsuite passes DMGL_TYPES to 83 // demangle type names, but that doesn't seem useful 84 // and we don't support it. 85 if !strings.HasPrefix(input, "_Z") && !strings.HasPrefix(input, "_GLOBAL_") { 86 skip = true 87 } 88 89 var expectNoParams string 90 if testNoParams { 91 expectNoParams = getLine(t, scanner, &lineno) 92 } 93 94 if skip { 95 continue 96 } 97 98 oneTest(t, report, input, expect, true) 99 if testNoParams { 100 oneTest(t, report, input, expectNoParams, false) 101 } 102 } 103 if err := scanner.Err(); err != nil { 104 t.Error(err) 105 } 106 } 107 108 // oneTest tests one entry from demangle-expected. 109 func oneTest(t *testing.T, report int, input, expect string, params bool) { 110 if *verbose { 111 fmt.Println(input) 112 } 113 114 exception := exceptions[input] 115 116 var s string 117 var err error 118 if params { 119 s, err = ToString(input) 120 } else { 121 s, err = ToString(input, NoParams) 122 } 123 if err != nil { 124 if exception { 125 t.Logf("%s:%d: ignore expected difference: got %q, expected %q", filename, report, err, expect) 126 return 127 } 128 129 if err != ErrNotMangledName { 130 if input == expect { 131 return 132 } 133 t.Errorf("%s:%d: %v", filename, report, err) 134 return 135 } 136 s = input 137 } 138 139 if s != expect { 140 if exception { 141 t.Logf("%s:%d: ignore expected difference: got %q, expected %q", filename, report, s, expect) 142 } else { 143 var a AST 144 if params { 145 a, err = ToAST(input) 146 } else { 147 a, err = ToAST(input, NoParams) 148 } 149 if err != nil { 150 t.Logf("ToAST error: %v", err) 151 } else { 152 t.Logf("\n%#v", a) 153 } 154 t.Errorf("%s:%d: params: %t: got %q, expected %q", filename, report, params, s, expect) 155 } 156 } else if exception && params { 157 t.Errorf("%s:%d: unexpected success (input listed in exceptions)", filename, report) 158 } 159 } 160 161 // getLine reads a line from demangle-expected. 162 func getLine(t *testing.T, scanner *bufio.Scanner, lineno *int) string { 163 s, got := getOptLine(t, scanner, lineno) 164 if !got { 165 t.Fatalf("%s:%d: unexpected EOF", filename, *lineno) 166 } 167 return s 168 } 169 170 // getOptLine reads an optional line from demangle-expected, returning 171 // false at EOF. It skips comment lines and updates *lineno. 172 func getOptLine(t *testing.T, scanner *bufio.Scanner, lineno *int) (string, bool) { 173 for { 174 if !scanner.Scan() { 175 return "", false 176 } 177 *lineno++ 178 line := scanner.Text() 179 if !strings.HasPrefix(line, "#") { 180 return line, true 181 } 182 } 183 } 184