Home | History | Annotate | Download | only in demangle
      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