===================================================================
@@ -1,4 +1,4 @@
-3488a401e50835de5de5c4f153772ac2798d0e71
+0bbc03f81c862fb35be3edee9824698a7892a17e
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
===================================================================
@@ -686,42 +686,33 @@ debug_function_name(Named_object* fn)
if (!fn->is_function())
return Gogo::unpack_hidden_name(fn->name());
- if (fn->func_value()->enclosing() == NULL)
+
+ std::string fnname = Gogo::unpack_hidden_name(fn->name());
+ if (fn->func_value()->is_method())
{
- std::string fnname = Gogo::unpack_hidden_name(fn->name());
- if (fn->func_value()->is_method())
- {
- // Methods in gc compiler are named "T.m" or "(*T).m" where
- // T is the receiver type. Add the receiver here.
- Type* rt = fn->func_value()->type()->receiver()->type();
- switch (rt->classification())
- {
- case Type::TYPE_NAMED:
- fnname = rt->named_type()->name() + "." + fnname;
- break;
-
- case Type::TYPE_POINTER:
- {
- Named_type* nt = rt->points_to()->named_type();
- if (nt != NULL)
- fnname = "(*" + nt->name() + ")." + fnname;
- break;
- }
-
- default:
- break;
- }
- }
- return fnname;
+ // Methods in gc compiler are named "T.m" or "(*T).m" where
+ // T is the receiver type. Add the receiver here.
+ Type* rt = fn->func_value()->type()->receiver()->type();
+ switch (rt->classification())
+ {
+ case Type::TYPE_NAMED:
+ fnname = rt->named_type()->name() + "." + fnname;
+ break;
+
+ case Type::TYPE_POINTER:
+ {
+ Named_type* nt = rt->points_to()->named_type();
+ if (nt != NULL)
+ fnname = "(*" + nt->name() + ")." + fnname;
+ break;
+ }
+
+ default:
+ break;
+ }
}
- // Closures are named ".$nested#" where # is a global counter. Add outer
- // function name for better distinguishing. This is also closer to what
- // gc compiler prints, "outer.func#".
- Named_object* enclosing = fn->func_value()->enclosing();
- std::string name = Gogo::unpack_hidden_name(fn->name());
- std::string outer_name = Gogo::unpack_hidden_name(enclosing->name());
- return outer_name + "." + name;
+ return fnname;
}
// Return the name of the current function.
===================================================================
@@ -1310,6 +1310,16 @@ Func_descriptor_expression::do_get_backe
&& Linemap::is_predeclared_location(no->location()))
is_descriptor = true;
+ // The runtime package implements some functions defined in the
+ // syscall package. Let the syscall package define the descriptor
+ // in this case.
+ if (gogo->compiling_runtime()
+ && gogo->package_name() == "runtime"
+ && no->is_function()
+ && !no->func_value()->asm_name().empty()
+ && no->func_value()->asm_name().compare(0, 8, "syscall.") == 0)
+ is_descriptor = true;
+
Btype* btype = this->type()->get_backend(gogo);
Bvariable* bvar;
@@ -6845,7 +6855,8 @@ Bound_method_expression::create_thunk(Go
if (orig_fntype == NULL || !orig_fntype->is_method())
{
- ins.first->second = Named_object::make_erroneous_name(Gogo::thunk_name());
+ ins.first->second =
+ Named_object::make_erroneous_name(gogo->thunk_name());
return ins.first->second;
}
@@ -6853,8 +6864,8 @@ Bound_method_expression::create_thunk(Go
// The type here is wrong--it should be the C function type. But it
// doesn't really matter.
Type* vt = Type::make_pointer_type(Type::make_void_type());
- sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc)));
- sfl->push_back(Struct_field(Typed_identifier("val.1",
+ sfl->push_back(Struct_field(Typed_identifier("fn", vt, loc)));
+ sfl->push_back(Struct_field(Typed_identifier("val",
orig_fntype->receiver()->type(),
loc)));
Struct_type* st = Type::make_struct_type(sfl, loc);
@@ -6863,7 +6874,7 @@ Bound_method_expression::create_thunk(Go
Function_type* new_fntype = orig_fntype->copy_with_names();
- std::string thunk_name = Gogo::thunk_name();
+ std::string thunk_name = gogo->thunk_name();
Named_object* new_no = gogo->start_function(thunk_name, new_fntype,
false, loc);
@@ -7009,10 +7020,10 @@ Bound_method_expression::do_flatten(Gogo
// away with this.
Struct_field_list* fields = new Struct_field_list();
- fields->push_back(Struct_field(Typed_identifier("fn.0",
+ fields->push_back(Struct_field(Typed_identifier("fn",
thunk->func_value()->type(),
loc)));
- fields->push_back(Struct_field(Typed_identifier("val.1", val->type(), loc)));
+ fields->push_back(Struct_field(Typed_identifier("val", val->type(), loc)));
Struct_type* st = Type::make_struct_type(fields, loc);
st->set_is_struct_incomparable();
@@ -11889,25 +11900,25 @@ Interface_field_reference_expression::cr
const Typed_identifier* method_id = type->find_method(name);
if (method_id == NULL)
- return Named_object::make_erroneous_name(Gogo::thunk_name());
+ return Named_object::make_erroneous_name(gogo->thunk_name());
Function_type* orig_fntype = method_id->type()->function_type();
if (orig_fntype == NULL)
- return Named_object::make_erroneous_name(Gogo::thunk_name());
+ return Named_object::make_erroneous_name(gogo->thunk_name());
Struct_field_list* sfl = new Struct_field_list();
// The type here is wrong--it should be the C function type. But it
// doesn't really matter.
Type* vt = Type::make_pointer_type(Type::make_void_type());
- sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc)));
- sfl->push_back(Struct_field(Typed_identifier("val.1", type, loc)));
+ sfl->push_back(Struct_field(Typed_identifier("fn", vt, loc)));
+ sfl->push_back(Struct_field(Typed_identifier("val", type, loc)));
Struct_type* st = Type::make_struct_type(sfl, loc);
st->set_is_struct_incomparable();
Type* closure_type = Type::make_pointer_type(st);
Function_type* new_fntype = orig_fntype->copy_with_names();
- std::string thunk_name = Gogo::thunk_name();
+ std::string thunk_name = gogo->thunk_name();
Named_object* new_no = gogo->start_function(thunk_name, new_fntype,
false, loc);
@@ -11995,10 +12006,10 @@ Interface_field_reference_expression::do
Location loc = this->location();
Struct_field_list* fields = new Struct_field_list();
- fields->push_back(Struct_field(Typed_identifier("fn.0",
+ fields->push_back(Struct_field(Typed_identifier("fn",
thunk->func_value()->type(),
loc)));
- fields->push_back(Struct_field(Typed_identifier("val.1",
+ fields->push_back(Struct_field(Typed_identifier("val",
this->expr_->type(),
loc)));
Struct_type* st = Type::make_struct_type(fields, loc);
@@ -12247,7 +12258,7 @@ Selector_expression::lower_method_expres
return f;
}
- Named_object* no = gogo->start_function(Gogo::thunk_name(), fntype, false,
+ Named_object* no = gogo->start_function(gogo->thunk_name(), fntype, false,
location);
Named_object* vno = gogo->lookup(receiver_name, NULL);
===================================================================
@@ -4,11 +4,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include "go-system.h"
+
+#include "gogo.h"
#include "go-location.h"
#include "go-linemap.h"
#include "go-encode-id.h"
+#include "lex.h"
-// Return whether the character c is OK to use in the assembler.
+// Return whether the character c is OK to use in the assembler. We
+// only permit ASCII alphanumeric characters, underscore, and dot.
static bool
char_needs_encoding(char c)
@@ -27,7 +32,7 @@ char_needs_encoding(char c)
case 'y': case 'z':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
- case '_': case '.': case '$': case '/':
+ case '_': case '.':
return false;
default:
return true;
@@ -77,11 +82,25 @@ fetch_utf8_char(const char* p, unsigned
return len;
}
-// Encode an identifier using ASCII characters.
+// Encode an identifier using ASCII characters. The encoding is
+// described in detail near the end of the long comment at the start
+// of names.cc. Short version: translate all non-ASCII-alphanumeric
+// characters into ..uXXXX or ..UXXXXXXXX.
std::string
go_encode_id(const std::string &id)
{
+ if (Lex::is_invalid_identifier(id))
+ {
+ go_assert(saw_errors());
+ return id;
+ }
+
+ // The encoding is only unambiguous if the input string does not
+ // contain ..u or ..U.
+ go_assert(id.find("..u") == std::string::npos);
+ go_assert(id.find("..U") == std::string::npos);
+
std::string ret;
const char* p = id.c_str();
const char* pend = p + id.length();
@@ -89,16 +108,25 @@ go_encode_id(const std::string &id)
{
unsigned int c;
size_t len = fetch_utf8_char(p, &c);
- if (len == 1 && !char_needs_encoding(c))
- ret += c;
+ if (len == 1)
+ {
+ // At this point we should only be seeing alphanumerics or
+ // underscore or dot.
+ go_assert(!char_needs_encoding(c));
+ ret += c;
+ }
+ else if (c < 0x10000)
+ {
+ char buf[16];
+ snprintf(buf, sizeof buf, "..u%04x", c);
+ ret += buf;
+ }
else
- {
- ret += "$U";
- char buf[30];
- snprintf(buf, sizeof buf, "%x", c);
- ret += buf;
- ret += "$";
- }
+ {
+ char buf[16];
+ snprintf(buf, sizeof buf, "..U%08x", c);
+ ret += buf;
+ }
p += len;
}
return ret;
@@ -111,3 +139,35 @@ go_selectively_encode_id(const std::stri
return go_encode_id(id);
return std::string();
}
+
+// Encode a struct field tag. This is only used when we need to
+// create a type descriptor for an anonymous struct type with field
+// tags. This mangling is applied before go_encode_id. We skip
+// alphanumerics and underscore, replace every other single byte
+// character with .xNN, and leave larger UTF-8 characters for
+// go_encode_id.
+
+std::string
+go_mangle_struct_tag(const std::string& tag)
+{
+ std::string ret;
+ const char* p = tag.c_str();
+ const char* pend = p + tag.length();
+ while (p < pend)
+ {
+ unsigned int c;
+ size_t len = fetch_utf8_char(p, &c);
+ if (len > 1)
+ ret.append(p, len);
+ else if (!char_needs_encoding(c) && c != '.')
+ ret += c;
+ else
+ {
+ char buf[16];
+ snprintf(buf, sizeof buf, ".x%02x", c);
+ ret += buf;
+ }
+ p += len;
+ }
+ return ret;
+}
===================================================================
@@ -9,10 +9,9 @@
#include "backend.h"
-// Given an identifier corresponding to a function or variable,
-// this helper returns TRUE if the identifier needs special
-// encoding to be used as an ASM name (symbol), FALSE if the name
-// is OK as is.
+// Given an identifier that will appear in assembly code, this helper
+// returns TRUE if the identifier needs special encoding to be used as
+// an ASM name, FALSE if the name is OK as is.
extern bool
go_id_needs_encoding(const std::string& str);
@@ -22,9 +21,12 @@ extern std::string
go_encode_id(const std::string &id);
// Returns the empty string if the specified name needs encoding,
-// otherwise invokes go_encode_id() on the name and returns the
-// result.
+// otherwise invokes go_encode_id on the name and returns the result.
extern std::string
go_selectively_encode_id(const std::string &id);
+// Encodes a struct tag that appears in a type literal encoding.
+extern std::string
+go_mangle_struct_tag(const std::string& tag);
+
#endif // !defined(GO_ENCODE_ID_H)
===================================================================
@@ -1758,7 +1758,7 @@ Gogo::start_function(const std::string&
else
{
// Invent a name for a nested function.
- nested_name = this->nested_function_name();
+ nested_name = this->nested_function_name(enclosing);
pname = &nested_name;
}
@@ -4821,9 +4821,9 @@ Function::Function(Function_type* type,
: type_(type), enclosing_(enclosing), results_(NULL),
closure_var_(NULL), block_(block), location_(location), labels_(),
local_type_count_(0), descriptor_(NULL), fndecl_(NULL), defer_stack_(NULL),
- pragmas_(0), is_sink_(false), results_are_named_(false),
- is_unnamed_type_stub_method_(false), calls_recover_(false),
- is_recover_thunk_(false), has_recover_thunk_(false),
+ pragmas_(0), nested_functions_(0), is_sink_(false),
+ results_are_named_(false), is_unnamed_type_stub_method_(false),
+ calls_recover_(false), is_recover_thunk_(false), has_recover_thunk_(false),
calls_defer_retaddr_(false), is_type_specific_function_(false),
in_unique_section_(false)
{
@@ -4948,7 +4948,7 @@ Function::set_closure_type()
// The first field of a closure is always a pointer to the function
// code.
Type* voidptr_type = Type::make_pointer_type(Type::make_void_type());
- st->push_field(Struct_field(Typed_identifier(".$f", voidptr_type,
+ st->push_field(Struct_field(Typed_identifier(".f", voidptr_type,
this->location_)));
unsigned int index = 1;
===================================================================
@@ -801,7 +801,7 @@ class Gogo
// Return the name to use for a generated stub method.
std::string
- stub_method_name(const std::string& method_name);
+ stub_method_name(const Package*, const std::string& method_name);
// Return the names of the hash and equality functions for TYPE.
void
@@ -826,7 +826,7 @@ class Gogo
// Return a name to use for a thunk function. A thunk function is
// one we create during the compilation, for a go statement or a
// defer statement or a method expression.
- static std::string
+ std::string
thunk_name();
// Return whether an object is a thunk.
@@ -838,8 +838,8 @@ class Gogo
init_function_name();
// Return the name to use for a nested function.
- static std::string
- nested_function_name();
+ std::string
+ nested_function_name(Named_object* enclosing);
// Return the name to use for a sink funciton.
std::string
@@ -887,6 +887,12 @@ class Gogo
std::string
interface_method_table_name(Interface_type*, Type*, bool is_pointer);
+ // Return whether NAME is a special name that can not be passed to
+ // unpack_hidden_name. This is needed because various special names
+ // use "..SUFFIX", but unpack_hidden_name just looks for '.'.
+ static bool
+ is_special_name(const std::string& name);
+
private:
// During parsing, we keep a stack of functions. Each function on
// the stack is one that we are currently parsing. For each
@@ -1233,6 +1239,11 @@ class Function
results_are_named() const
{ return this->results_are_named_; }
+ // Return the assembler name.
+ const std::string&
+ asm_name() const
+ { return this->asm_name_; }
+
// Set the assembler name.
void
set_asm_name(const std::string& asm_name)
@@ -1250,6 +1261,14 @@ class Function
this->pragmas_ = pragmas;
}
+ // Return the index to use for a nested function.
+ unsigned int
+ next_nested_function_index()
+ {
+ ++this->nested_functions_;
+ return this->nested_functions_;
+ }
+
// Whether this method should not be included in the type
// descriptor.
bool
@@ -1510,6 +1529,8 @@ class Function
Temporary_statement* defer_stack_;
// Pragmas for this function. This is a set of GOPRAGMA bits.
unsigned int pragmas_;
+ // Number of nested functions defined within this function.
+ unsigned int nested_functions_;
// True if this function is sink-named. No code is generated.
bool is_sink_ : 1;
// True if the result variables are named.
===================================================================
@@ -2761,16 +2761,19 @@ bool
Lex::is_exported_name(const std::string& name)
{
unsigned char c = name[0];
- if (c != '$')
+ if (c != '.')
return c >= 'A' && c <= 'Z';
else
{
const char* p = name.data();
size_t len = name.length();
- if (len < 2 || p[1] != 'U')
+ if (len < 4 || p[1] != '.' || (p[2] != 'u' && p[2] != 'U'))
return false;
unsigned int ci = 0;
- for (size_t i = 2; i < len && p[i] != '$'; ++i)
+ size_t want = (p[2] == 'u' ? 4 : 8);
+ if (len < want + 3)
+ return false;
+ for (size_t i = 3; i < want; ++i)
{
c = p[i];
if (!Lex::is_hex_digit(c))
===================================================================
@@ -15,6 +15,190 @@
// assembly code. This is not used for names that appear only in the
// debug info.
+// Our external names contain only ASCII alphanumeric characters,
+// underscore, and dot. (According to the GCC sources, dot is not
+// permitted in assembler symbols on VxWorks and MMIX. We will not
+// support those systems.) Go names can not contain dot, so we rely
+// on using dot to encode Unicode characters, and to separate Go
+// symbols by package, and so forth. We assume that none of the
+// non-Go symbols in the final link will contain a dot, so we don't
+// worry about conflicts.
+//
+// We first describe the basic symbol names, used to represent Go
+// functions and variables. These never start with a dot, never end
+// with a dot, never contain two consecutive dots, and never contain a
+// dot followed by a digit.
+//
+// The external name for a normal Go symbol NAME, a function or
+// variable, is simply "PKGPATH.NAME". Note that NAME is not the
+// packed form used for the "hidden" name internally in the compiler;
+// it is the name that appears in the source code. PKGPATH is the
+// -fgo-pkgpath option as adjusted by Gogo::pkgpath_for_symbol. Note
+// that PKGPATH can not contain a dot and neither can NAME. Also,
+// NAME may not begin with a digit. NAME may require further encoding
+// for non-ASCII characters as described below, but until that
+// encoding these symbols contain exactly one dot, and they do not
+// start with a dot.
+//
+// The external name for a method NAME for a named type TYPE is
+// "PKGPATH.TYPE.NAME". Unlike the gc compiler, the external name
+// does not indicate whether this is a pointer method or a value
+// method; a named type can not have both a pointer and value method
+// with the same name, so there is no ambiguity. PKGPATH is the
+// package path of the package in which TYPE is defined. Here none of
+// PKGPATH, TYPE, or NAME can be empty or contain a dot, and neither
+// TYPE nor NAME may begin with a digit. Before encoding these names
+// contain exactly two dots, not consecutive, and they do not start
+// with a dot.
+//
+// It's uncommon, but the use of type literals with embedded fields
+// can cause us to have methods on unnamed types. The external names
+// for these are also PKGPATH.TYPE.NAME, where TYPE is an
+// approximately readable version of the type literal, described
+// below. As the type literal encoding always contains multiple dots,
+// these names always contain more than two dots. Although the type
+// literal encoding contains dots, neither PKGPATH nor NAME can
+// contain a dot, and neither TYPE nor NAME can begin with a digit.
+// The effect is that PKGPATH is always the portion of the name before
+// the first dot and NAME is always the portion after the last dot.
+// There is no ambiguity as long as encoded type literals are
+// unambiguous.
+//
+// Also uncommon is an external name that must refer to a named type
+// defined within a function. While such a type can not have methods
+// itself, it can pick up embedded methods, and those methods need
+// names. These are treated as a kind of type literal written as,
+// before type literal encoding, FNNAME.TYPENAME(INDEX) or, for a
+// method, TYPE.MNAME.TYPENAME(INDEX). INDEX is the index of that
+// named type within the function, as a single function can have
+// multiple types with the same name. This is unambiguous as
+// parentheses can not appear in a type literal in this form (they can
+// only appear in interface method declarations).
+//
+// That is the end of the list of basic names. The remaining names
+// exist for special purposes, and are differentiated from the basic
+// names by containing two consecutive dots.
+//
+// The hash function for a type is treated as a method whose name is
+// ".hash". That is, the method name begins with a dot. The effect
+// is that there will be two consecutive dots in the name; the name
+// will always end with "..hash".
+//
+// Similarly the equality function for a type is treated as a method
+// whose name is ".eq".
+//
+// The function descriptor for a function is the same as the name of
+// the function with an added suffix "..f".
+//
+// A thunk for a go or defer statement is treated as a function whose
+// name is ".thunkNN" where NN is a sequence of digits (these
+// functions are never globally visible). Thus the final name of a
+// thunk will be PKGPATH..thunkNN.
+//
+// An init function is treated as a function whose name is ".initNN"
+// where NN is a sequence of digits (these functions are never
+// globally visible). Thus the final name of an init function will be
+// PKGPATH..initNN.
+//
+// A nested function is given the name of outermost enclosing function
+// or method with an added suffix "..funcNN" where NN is a sequence of
+// digits. Note that the function descriptor of a nested function, if
+// needed, will end with "..funcNN..f".
+//
+// A recover thunk is the same as the name of the function with an
+// added suffix "..r".
+//
+// The name of a type descriptor for a named type is PKGPATH.TYPE..d.
+//
+// The name of a type descriptor for an unnamed type is type..TYPE.
+// That is, the string "type.." followed by the type literal encoding.
+// These names are common symbols, in the linker's sense of the word
+// common: in the final executable there is only one instance of the
+// type descriptor for a given unnamed type. The type literal
+// encoding can never start with a digit or with 'u' or 'U'.
+//
+// The name of the GC symbol for a named type is PKGPATH.TYPE..g.
+//
+// The name of the GC symbol for an unnamed type is typeg..TYPE.
+// These are common symbols.
+//
+// The name of a ptrmask symbol is gcbits..B32 where B32 is an
+// encoding of the ptrmask bits using only ASCII letters without 'u'
+// or 'U'. These are common symbols.
+//
+// An interface method table for assigning the non-interface type TYPE
+// to the interface type ITYPE is named imt..ITYPE..TYPE. If ITYPE or
+// TYPE is a named type, they are written as PKGPATH.TYPE. Otherwise
+// they are written as a type literal. An interface method table for
+// a pointer method set uses pimt instead of imt.
+//
+// The names of composite literal initializers, including the GC root
+// variable, are not referenced. They must not conflict with any C
+// language names, but the names are otherwise unimportant. They are
+// named "go..CNN" where NN is a sequence of digits. The names do not
+// include the PKGPATH.
+//
+// The map zero value, a common symbol that represents the zero value
+// of a map, is named simply "go..zerovalue". The name does not
+// include the PKGPATH.
+//
+// The import function for the main package is referenced by C code,
+// and is named __go_init_main. For other packages it is
+// PKGPATH..import.
+//
+// The type literal encoding is essentially a single line version of
+// the type literal, such as "struct { pkgpath.i int; J int }". In
+// this representation unexported names use their pkgpath, exported
+// names omit it.
+//
+// The type literal encoding is not quite valid Go, as some aspects of
+// compiler generated types can not be represented. For example,
+// incomparable struct types have an extra field "{x}". Struct tags
+// are quoted inside curly braces, rather than introduce an encoding
+// for quotes. Struct tags can contain any character, so any single
+// byte Unicode character that is not alphanumeric or underscore is
+// replaced with .xNN where NN is the hex encoding.
+//
+// There is a simple encoding for glue characters in type literals:
+// .0 - ' '
+// .1 - '*'
+// .2 - ';'
+// .3 - ','
+// .4 - '{'
+// .5 - '}'
+// .6 - '['
+// .7 - ']'
+// .8 - '('
+// .9 - ')'
+// This is unambiguous as, although the type literal can contain a dot
+// as shown above, those dots are always followed by a name and names
+// can not begin with a digit. A dot is always followed by a name or
+// a digit, and a type literal can neither start nor end with a dot,
+// so this never introduces consecutive dots.
+//
+// Struct tags can contain any character, so they need special
+// treatment. Alphanumerics, underscores, and Unicode characters that
+// require more than a single byte are left alone (Unicode characters
+// will be encoded later, as described below). Other single bytes
+// characters are replace with .xNN where NN is the hex encoding.
+//
+// Since Go identifiers can contain Unicode characters, we must encode
+// them into ASCII. We do this last, after the name is generated as
+// described above and after type literals are encoded. To make the
+// encoding unambiguous, we introduce it with two consecutive dots.
+// This is followed by the letter u and four hex digits or the letter
+// U and eight digits, just as in the language only using ..u and ..U
+// instead of \u and \U. Since before this encoding names can never
+// contain consecutive dots followed by 'u' or 'U', and after this
+// encoding "..u" and "..U" are followed by a known number of
+// characters, this is unambiguous.
+//
+// Demangling these names is straightforward:
+// - replace ..uXXXX with a unicode character
+// - replace ..UXXXXXXXX with a unicode character
+// - replace .D, where D is a digit, with the character from the above
+// That will get you as close as possible to a readable name.
+
// Return the assembler name to use for an exported function, a
// method, or a function/method declaration. This is not called if
// the function has been given an explicit name via a magic //extern
@@ -27,30 +211,20 @@ std::string
Gogo::function_asm_name(const std::string& go_name, const Package* package,
const Type* rtype)
{
- std::string ret = (package == NULL
- ? this->pkgpath_symbol()
- : package->pkgpath_symbol());
-
- if (rtype != NULL
- && Gogo::is_hidden_name(go_name)
- && Gogo::hidden_name_pkgpath(go_name) != this->pkgpath())
- {
- // This is a method created for an unexported method of an
- // imported embedded type. Use the pkgpath of the imported
- // package.
- std::string p = Gogo::hidden_name_pkgpath(go_name);
- ret = this->pkgpath_symbol_for_package(p);
- }
-
- ret.append(1, '.');
- ret.append(Gogo::unpack_hidden_name(go_name));
-
+ std::string ret;
if (rtype != NULL)
- {
- ret.append(1, '.');
- ret.append(rtype->mangled_name(this));
- }
-
+ ret = rtype->mangled_name(this);
+ else if (package == NULL)
+ ret = this->pkgpath_symbol();
+ else
+ ret = package->pkgpath_symbol();
+ ret.push_back('.');
+ // Check for special names that will break if we use
+ // Gogo::unpack_hidden_name.
+ if (Gogo::is_special_name(go_name))
+ ret.append(go_name);
+ else
+ ret.append(Gogo::unpack_hidden_name(go_name));
return go_encode_id(ret);
}
@@ -60,41 +234,45 @@ Gogo::function_asm_name(const std::strin
std::string
Gogo::function_descriptor_name(Named_object* no)
{
- std::string var_name;
- if (no->is_function_declaration()
- && !no->func_declaration_value()->asm_name().empty()
- && Linemap::is_predeclared_location(no->location()))
- {
- if (no->func_declaration_value()->asm_name().substr(0, 8) != "runtime.")
- var_name = no->func_declaration_value()->asm_name() + "_descriptor";
- else
- var_name = no->func_declaration_value()->asm_name() + "$descriptor";
- }
- else
- {
- if (no->package() == NULL)
- var_name = this->pkgpath_symbol();
- else
- var_name = no->package()->pkgpath_symbol();
- var_name.push_back('.');
- var_name.append(Gogo::unpack_hidden_name(no->name()));
- var_name.append("$descriptor");
- }
- return var_name;
+ if (no->is_function() && !no->func_value()->asm_name().empty())
+ return no->func_value()->asm_name() + "..f";
+ else if (no->is_function_declaration()
+ && !no->func_declaration_value()->asm_name().empty())
+ return no->func_declaration_value()->asm_name() + "..f";
+ std::string ret = this->function_asm_name(no->name(), no->package(), NULL);
+ ret.append("..f");
+ return ret;
}
// Return the name to use for a generated stub method. MNAME is the
-// method name. These functions are globally visible. Note that this
-// is the function name that corresponds to the name used for the
-// method in Go source code, if this stub method were written in Go.
-// The assembler name will be generated by Gogo::function_asm_name,
-// and because this is a method that name will include the receiver
-// type.
+// method name. PACKAGE is the package where the type that needs this
+// stub method is defined. These functions are globally visible.
+// Note that this is the function name that corresponds to the name
+// used for the method in Go source code, if this stub method were
+// written in Go. The assembler name will be generated by
+// Gogo::function_asm_name, and because this is a method that name
+// will include the receiver type.
std::string
-Gogo::stub_method_name(const std::string& mname)
+Gogo::stub_method_name(const Package* package, const std::string& mname)
{
- return mname + "$stub";
+ if (!Gogo::is_hidden_name(mname))
+ return mname + "..stub";
+
+ const std::string& ppkgpath(package == NULL
+ ? this->pkgpath()
+ : package->pkgpath());
+ std::string mpkgpath = Gogo::hidden_name_pkgpath(mname);
+ if (mpkgpath == ppkgpath)
+ return Gogo::unpack_hidden_name(mname) + "..stub";
+
+ // We are creating a stub method for an unexported method of an
+ // imported embedded type. We need to disambiguate the method name.
+ std::string ret = this->pkgpath_symbol_for_package(mpkgpath);
+ ret.push_back('.');
+ ret.append(Gogo::unpack_hidden_name(mname));
+ ret.append("..stub");
+ return ret;
}
// Return the names of the hash and equality functions for TYPE. If
@@ -106,48 +284,12 @@ Gogo::specific_type_function_names(const
std::string *hash_name,
std::string *equal_name)
{
- std::string base_name;
- if (name == NULL)
- {
- // Mangled names can have '.' if they happen to refer to named
- // types in some way. That's fine if this is simply a named
- // type, but otherwise it will confuse the code that builds
- // function identifiers. Remove '.' when necessary.
- base_name = type->mangled_name(this);
- size_t i;
- while ((i = base_name.find('.')) != std::string::npos)
- base_name[i] = '$';
- base_name = this->pack_hidden_name(base_name, false);
- }
- else
- {
- // This name is already hidden or not as appropriate.
- base_name = name->name();
- unsigned int index;
- const Named_object* in_function = name->in_function(&index);
- if (in_function != NULL)
- {
- base_name.append(1, '$');
- const Typed_identifier* rcvr =
- in_function->func_value()->type()->receiver();
- if (rcvr != NULL)
- {
- Named_type* rcvr_type = rcvr->type()->deref()->named_type();
- base_name.append(Gogo::unpack_hidden_name(rcvr_type->name()));
- base_name.append(1, '$');
- }
- base_name.append(Gogo::unpack_hidden_name(in_function->name()));
- if (index > 0)
- {
- char buf[30];
- snprintf(buf, sizeof buf, "%u", index);
- base_name += '$';
- base_name += buf;
- }
- }
- }
- *hash_name = base_name + "$hash";
- *equal_name = base_name + "$equal";
+ const Type* rtype = type;
+ if (name != NULL)
+ rtype = name;
+ std::string tname = rtype->mangled_name(this);
+ *hash_name = tname + "..hash";
+ *equal_name = tname + "..eq";
}
// Return the assembler name to use for a global variable. GO_NAME is
@@ -158,10 +300,12 @@ Gogo::specific_type_function_names(const
std::string
Gogo::global_var_asm_name(const std::string& go_name, const Package* package)
{
- std::string ret = (package != NULL
- ? package->pkgpath_symbol()
- : this->pkgpath_symbol());
- ret.push_back('.');
+ std::string ret;
+ if (package == NULL)
+ ret = this->pkgpath_symbol();
+ else
+ ret = package->pkgpath_symbol();
+ ret.append(1, '.');
ret.append(Gogo::unpack_hidden_name(go_name));
return go_encode_id(ret);
}
@@ -172,9 +316,10 @@ Gogo::global_var_asm_name(const std::str
std::string
Gogo::erroneous_name()
{
+ go_assert(saw_errors());
static int erroneous_count;
char name[50];
- snprintf(name, sizeof name, "$erroneous%d", erroneous_count);
+ snprintf(name, sizeof name, ".erroneous%d", erroneous_count);
++erroneous_count;
return name;
}
@@ -184,7 +329,7 @@ Gogo::erroneous_name()
bool
Gogo::is_erroneous_name(const std::string& name)
{
- return name.compare(0, 10, "$erroneous") == 0;
+ return name.compare(0, 10, ".erroneous") == 0;
}
// Return a name for a thunk object.
@@ -194,9 +339,10 @@ Gogo::thunk_name()
{
static int thunk_count;
char thunk_name[50];
- snprintf(thunk_name, sizeof thunk_name, "$thunk%d", thunk_count);
+ snprintf(thunk_name, sizeof thunk_name, "..thunk%d", thunk_count);
++thunk_count;
- return thunk_name;
+ std::string ret = this->pkgpath_symbol();
+ return ret + thunk_name;
}
// Return whether a function is a thunk.
@@ -204,7 +350,14 @@ Gogo::thunk_name()
bool
Gogo::is_thunk(const Named_object* no)
{
- return no->name().compare(0, 6, "$thunk") == 0;
+ const std::string& name(no->name());
+ size_t i = name.find("..thunk");
+ if (i == std::string::npos)
+ return false;
+ for (i += 7; i < name.size(); ++i)
+ if (name[i] < '0' || name[i] > '9')
+ return false;
+ return true;
}
// Return the name to use for an init function. There can be multiple
@@ -215,21 +368,50 @@ Gogo::init_function_name()
{
static int init_count;
char buf[30];
- snprintf(buf, sizeof buf, ".$init%d", init_count);
+ snprintf(buf, sizeof buf, "..init%d", init_count);
++init_count;
- return buf;
+ std::string ret = this->pkgpath_symbol();
+ return ret + buf;
}
// Return the name to use for a nested function.
std::string
-Gogo::nested_function_name()
+Gogo::nested_function_name(Named_object* enclosing)
{
- static int nested_count;
+ std::string prefix;
+ unsigned int index;
+ if (enclosing == NULL)
+ {
+ // A function literal at top level, as in
+ // var f = func() {}
+ static unsigned int toplevel_index;
+ ++toplevel_index;
+ index = toplevel_index;
+ prefix = ".go";
+ }
+ else
+ {
+ while (true)
+ {
+ Named_object* parent = enclosing->func_value()->enclosing();
+ if (parent == NULL)
+ break;
+ enclosing = parent;
+ }
+ const Typed_identifier* rcvr =
+ enclosing->func_value()->type()->receiver();
+ if (rcvr != NULL)
+ {
+ prefix = rcvr->type()->mangled_name(this);
+ prefix.push_back('.');
+ }
+ prefix.append(Gogo::unpack_hidden_name(enclosing->name()));
+ index = enclosing->func_value()->next_nested_function_index();
+ }
char buf[30];
- snprintf(buf, sizeof buf, ".$nested%d", nested_count);
- ++nested_count;
- return buf;
+ snprintf(buf, sizeof buf, "..func%u", index);
+ return prefix + buf;
}
// Return the name to use for a sink function, a function whose name
@@ -241,7 +423,7 @@ Gogo::sink_function_name()
{
static int sink_count;
char buf[30];
- snprintf(buf, sizeof buf, ".$sink%d", sink_count);
+ snprintf(buf, sizeof buf, ".sink%d", sink_count);
++sink_count;
return buf;
}
@@ -255,7 +437,7 @@ Gogo::redefined_function_name()
{
static int redefinition_count;
char buf[30];
- snprintf(buf, sizeof buf, ".$redefined%d", redefinition_count);
+ snprintf(buf, sizeof buf, ".redefined%d", redefinition_count);
++redefinition_count;
return buf;
}
@@ -266,13 +448,17 @@ Gogo::redefined_function_name()
std::string
Gogo::recover_thunk_name(const std::string& name, const Type* rtype)
{
- std::string ret(name);
+ std::string ret;
if (rtype != NULL)
{
- ret.push_back('$');
- ret.append(rtype->mangled_name(this));
+ ret = rtype->mangled_name(this);
+ ret.append(1, '.');
}
- ret.append("$recover");
+ if (Gogo::is_special_name(name))
+ ret.append(name);
+ else
+ ret.append(Gogo::unpack_hidden_name(name));
+ ret.append("..r");
return ret;
}
@@ -284,7 +470,7 @@ Gogo::recover_thunk_name(const std::stri
std::string
Gogo::gc_root_name()
{
- return "gc0";
+ return "go..C0";
}
// Return the name to use for a composite literal or string
@@ -296,8 +482,8 @@ Gogo::initializer_name()
{
static unsigned int counter;
char buf[30];
- snprintf(buf, sizeof buf, "C%u", counter);
++counter;
+ snprintf(buf, sizeof buf, "go..C%u", counter);
return buf;
}
@@ -307,7 +493,7 @@ Gogo::initializer_name()
std::string
Gogo::map_zero_value_name()
{
- return "go$zerovalue";
+ return "go..zerovalue";
}
// Return the name to use for the import control function.
@@ -343,11 +529,50 @@ Type::mangled_name(Gogo* gogo) const
{
std::string ret;
- // The do_mangled_name virtual function should set RET to the
- // mangled name. For a composite type it should append a code for
- // the composition and then call do_mangled_name on the components.
+ // The do_mangled_name virtual function will set RET to the mangled
+ // name before glue character mapping.
this->do_mangled_name(gogo, &ret);
+ // Type descriptor names and interface method table names use a ".."
+ // before the mangled name of a type, so to avoid ambiguity the
+ // mangled name must not start with 'u' or 'U' or a digit.
+ go_assert((ret[0] < '0' || ret[0] > '9') && ret[0] != ' ');
+ if (ret[0] == 'u' || ret[0] == 'U')
+ ret = " " + ret;
+
+ // Map glue characters as described above.
+
+ // The mapping is only unambiguous if there is no .DIGIT in the
+ // string, so check that.
+ for (size_t i = ret.find('.');
+ i != std::string::npos;
+ i = ret.find('.', i + 1))
+ {
+ if (i + 1 < ret.size())
+ {
+ char c = ret[i + 1];
+ go_assert(c < '0' || c > '9');
+ }
+ }
+
+ // The order of these characters is the replacement code.
+ const char * const replace = " *;,{}[]()";
+
+ const size_t rlen = strlen(replace);
+ char buf[2];
+ buf[0] = '.';
+ for (size_t ri = 0; ri < rlen; ++ri)
+ {
+ buf[1] = '0' + ri;
+ while (true)
+ {
+ size_t i = ret.find(replace[ri]);
+ if (i == std::string::npos)
+ break;
+ ret.replace(i, 1, buf, 2);
+ }
+ }
+
return ret;
}
@@ -357,27 +582,27 @@ Type::mangled_name(Gogo* gogo) const
void
Error_type::do_mangled_name(Gogo*, std::string* ret) const
{
- ret->push_back('E');
+ ret->append("{error}");
}
void
Void_type::do_mangled_name(Gogo*, std::string* ret) const
{
- ret->push_back('v');
+ ret->append("{void}");
}
void
Boolean_type::do_mangled_name(Gogo*, std::string* ret) const
{
- ret->push_back('b');
+ ret->append("bool");
}
void
Integer_type::do_mangled_name(Gogo*, std::string* ret) const
{
char buf[100];
- snprintf(buf, sizeof buf, "i%s%s%de",
- this->is_abstract_ ? "a" : "",
+ snprintf(buf, sizeof buf, "%s%si%d",
+ this->is_abstract_ ? "{abstract}" : "",
this->is_unsigned_ ? "u" : "",
this->bits_);
ret->append(buf);
@@ -387,8 +612,8 @@ void
Float_type::do_mangled_name(Gogo*, std::string* ret) const
{
char buf[100];
- snprintf(buf, sizeof buf, "f%s%de",
- this->is_abstract_ ? "a" : "",
+ snprintf(buf, sizeof buf, "%sfloat%d",
+ this->is_abstract_ ? "{abstract}" : "",
this->bits_);
ret->append(buf);
}
@@ -397,8 +622,8 @@ void
Complex_type::do_mangled_name(Gogo*, std::string* ret) const
{
char buf[100];
- snprintf(buf, sizeof buf, "c%s%de",
- this->is_abstract_ ? "a" : "",
+ snprintf(buf, sizeof buf, "%sc%d",
+ this->is_abstract_ ? "{abstract}" : "",
this->bits_);
ret->append(buf);
}
@@ -406,83 +631,103 @@ Complex_type::do_mangled_name(Gogo*, std
void
String_type::do_mangled_name(Gogo*, std::string* ret) const
{
- ret->push_back('z');
+ ret->append("string");
}
void
Function_type::do_mangled_name(Gogo* gogo, std::string* ret) const
{
- ret->push_back('F');
+ ret->append("func");
if (this->receiver_ != NULL)
{
- ret->push_back('m');
+ ret->push_back('(');
this->append_mangled_name(this->receiver_->type(), gogo, ret);
+ ret->append(")");
}
+ ret->push_back('(');
const Typed_identifier_list* params = this->parameters();
if (params != NULL)
{
- ret->push_back('p');
+ bool first = true;
for (Typed_identifier_list::const_iterator p = params->begin();
p != params->end();
++p)
- this->append_mangled_name(p->type(), gogo, ret);
- if (this->is_varargs_)
- ret->push_back('V');
- ret->push_back('e');
+ {
+ if (first)
+ first = false;
+ else
+ ret->push_back(',');
+ if (this->is_varargs_ && p + 1 == params->end())
+ {
+ // We can't use "..." here because the mangled name
+ // might start with 'u' or 'U', which would be ambiguous
+ // with the encoding of Unicode characters.
+ ret->append(",,,");
+ }
+ this->append_mangled_name(p->type(), gogo, ret);
+ }
}
+ ret->push_back(')');
+ ret->push_back('(');
const Typed_identifier_list* results = this->results();
if (results != NULL)
{
- ret->push_back('r');
+ bool first = true;
for (Typed_identifier_list::const_iterator p = results->begin();
p != results->end();
++p)
- this->append_mangled_name(p->type(), gogo, ret);
- ret->push_back('e');
+ {
+ if (first)
+ first = false;
+ else
+ ret->append(",");
+ this->append_mangled_name(p->type(), gogo, ret);
+ }
}
-
- ret->push_back('e');
+ ret->push_back(')');
}
void
Pointer_type::do_mangled_name(Gogo* gogo, std::string* ret) const
{
- ret->push_back('p');
+ ret->push_back('*');
this->append_mangled_name(this->to_type_, gogo, ret);
}
void
Nil_type::do_mangled_name(Gogo*, std::string* ret) const
{
- ret->push_back('n');
+ ret->append("{nil}");
}
void
Struct_type::do_mangled_name(Gogo* gogo, std::string* ret) const
{
- ret->push_back('S');
+ ret->append("struct{");
+
+ if (this->is_struct_incomparable_)
+ ret->append("{x}");
const Struct_field_list* fields = this->fields_;
if (fields != NULL)
{
+ bool first = true;
for (Struct_field_list::const_iterator p = fields->begin();
p != fields->end();
++p)
{
- if (p->is_anonymous())
- ret->append("0_");
+ if (first)
+ first = false;
else
- {
+ ret->push_back(';');
- std::string n(Gogo::mangle_possibly_hidden_name(p->field_name()));
- char buf[20];
- snprintf(buf, sizeof buf, "%u_",
- static_cast<unsigned int>(n.length()));
- ret->append(buf);
- ret->append(n);
+ if (!p->is_anonymous())
+ {
+ ret->append(Gogo::mangle_possibly_hidden_name(p->field_name()));
+ ret->push_back(' ');
}
// For an anonymous field with an alias type, the field name
@@ -493,44 +738,26 @@ Struct_type::do_mangled_name(Gogo* gogo,
p->type()->named_type()->append_mangled_type_name(gogo, true, ret);
else
this->append_mangled_name(p->type(), gogo, ret);
+
if (p->has_tag())
{
- const std::string& tag(p->tag());
- std::string out;
- for (std::string::const_iterator p = tag.begin();
- p != tag.end();
- ++p)
- {
- if (ISALNUM(*p) || *p == '_')
- out.push_back(*p);
- else
- {
- char buf[20];
- snprintf(buf, sizeof buf, ".%x.",
- static_cast<unsigned int>(*p));
- out.append(buf);
- }
- }
- char buf[20];
- snprintf(buf, sizeof buf, "T%u_",
- static_cast<unsigned int>(out.length()));
- ret->append(buf);
- ret->append(out);
+ // Use curly braces around a struct tag, since they are
+ // unambiguous here and we have no encoding for
+ // quotation marks.
+ ret->push_back('{');
+ ret->append(go_mangle_struct_tag(p->tag()));
+ ret->push_back('}');
}
}
}
- if (this->is_struct_incomparable_)
- ret->push_back('x');
-
- ret->push_back('e');
+ ret->push_back('}');
}
void
Array_type::do_mangled_name(Gogo* gogo, std::string* ret) const
{
- ret->push_back('A');
- this->append_mangled_name(this->element_type_, gogo, ret);
+ ret->push_back('[');
if (this->length_ != NULL)
{
Numeric_constant nc;
@@ -550,30 +777,31 @@ Array_type::do_mangled_name(Gogo* gogo,
free(s);
mpz_clear(val);
if (this->is_array_incomparable_)
- ret->push_back('x');
+ ret->append("x");
}
- ret->push_back('e');
+ ret->push_back(']');
+ this->append_mangled_name(this->element_type_, gogo, ret);
}
void
Map_type::do_mangled_name(Gogo* gogo, std::string* ret) const
{
- ret->push_back('M');
+ ret->append("map[");
this->append_mangled_name(this->key_type_, gogo, ret);
- ret->append("__");
+ ret->push_back(']');
this->append_mangled_name(this->val_type_, gogo, ret);
}
void
Channel_type::do_mangled_name(Gogo* gogo, std::string* ret) const
{
- ret->push_back('C');
+ if (!this->may_send_)
+ ret->append("{}");
+ ret->append("chan");
+ if (!this->may_receive_)
+ ret->append("{}");
+ ret->push_back(' ');
this->append_mangled_name(this->element_type_, gogo, ret);
- if (this->may_send_)
- ret->push_back('s');
- if (this->may_receive_)
- ret->push_back('r');
- ret->push_back('e');
}
void
@@ -581,31 +809,34 @@ Interface_type::do_mangled_name(Gogo* go
{
go_assert(this->methods_are_finalized_);
- ret->push_back('I');
+ ret->append("interface{");
const Typed_identifier_list* methods = this->all_methods_;
if (methods != NULL && !this->seen_)
{
this->seen_ = true;
+ bool first = true;
for (Typed_identifier_list::const_iterator p = methods->begin();
p != methods->end();
++p)
{
+ if (first)
+ first = false;
+ else
+ ret->push_back(';');
+
if (!p->name().empty())
{
- std::string n(Gogo::mangle_possibly_hidden_name(p->name()));
- char buf[20];
- snprintf(buf, sizeof buf, "%u_",
- static_cast<unsigned int>(n.length()));
- ret->append(buf);
- ret->append(n);
+ ret->append(Gogo::mangle_possibly_hidden_name(p->name()));
+ ret->push_back(' ');
}
+
this->append_mangled_name(p->type(), gogo, ret);
}
this->seen_ = false;
}
- ret->push_back('e');
+ ret->push_back('}');
}
void
@@ -622,18 +853,12 @@ Forward_declaration_type::do_mangled_nam
else
{
const Named_object* no = this->named_object();
- std::string name;
if (no->package() == NULL)
- name = gogo->pkgpath_symbol();
+ ret->append(gogo->pkgpath_symbol());
else
- name = no->package()->pkgpath_symbol();
- name += '.';
- name += Gogo::unpack_hidden_name(no->name());
- char buf[20];
- snprintf(buf, sizeof buf, "N%u_",
- static_cast<unsigned int>(name.length()));
- ret->append(buf);
- ret->append(name);
+ ret->append(no->package()->pkgpath_symbol());
+ ret->push_back('.');
+ ret->append(Gogo::unpack_hidden_name(no->name()));
}
}
@@ -662,37 +887,44 @@ Named_type::append_mangled_type_name(Gog
go_assert(this->in_function_ == NULL);
else
{
- const std::string& pkgpath(no->package() == NULL
- ? gogo->pkgpath_symbol()
- : no->package()->pkgpath_symbol());
- name = pkgpath;
- name.append(1, '.');
if (this->in_function_ != NULL)
{
const Typed_identifier* rcvr =
this->in_function_->func_value()->type()->receiver();
if (rcvr != NULL)
{
- Named_type* rcvr_type = rcvr->type()->deref()->named_type();
- name.append(Gogo::unpack_hidden_name(rcvr_type->name()));
- name.append(1, '.');
- }
- name.append(Gogo::unpack_hidden_name(this->in_function_->name()));
- name.append(1, '$');
- if (this->in_function_index_ > 0)
- {
- char buf[30];
- snprintf(buf, sizeof buf, "%u", this->in_function_index_);
- name.append(buf);
- name.append(1, '$');
+ std::string m = rcvr->type()->mangled_name(gogo);
+ // Turn a leading ".1" back into "*" since we are going
+ // to type-mangle this name again.
+ if (m.compare(0, 2, ".1") == 0)
+ m = "*" + m.substr(2);
+ ret->append(m);
}
+ else if (this->in_function_->package() == NULL)
+ ret->append(gogo->pkgpath_symbol());
+ else
+ ret->append(this->in_function_->package()->pkgpath_symbol());
+ ret->push_back('.');
+ ret->append(Gogo::unpack_hidden_name(this->in_function_->name()));
+ }
+ else
+ {
+ if (no->package() == NULL)
+ ret->append(gogo->pkgpath_symbol());
+ else
+ ret->append(no->package()->pkgpath_symbol());
}
+ ret->push_back('.');
+ }
+
+ ret->append(Gogo::unpack_hidden_name(no->name()));
+
+ if (this->in_function_ != NULL && this->in_function_index_ > 0)
+ {
+ char buf[30];
+ snprintf(buf, sizeof buf, "..i%u", this->in_function_index_);
+ ret->append(buf);
}
- name.append(Gogo::unpack_hidden_name(no->name()));
- char buf[20];
- snprintf(buf, sizeof buf, "N%u_", static_cast<unsigned int>(name.length()));
- ret->append(buf);
- ret->append(name);
}
// Return the name for the type descriptor symbol for TYPE. This can
@@ -704,50 +936,53 @@ Gogo::type_descriptor_name(Type* type, N
{
// The type descriptor symbol for the unsafe.Pointer type is defined
// in libgo/runtime/go-unsafe-pointer.c, so just use a reference to
- // that symbol.
+ // that symbol for all unsafe pointer types.
if (type->is_unsafe_pointer_type())
- return "__go_tdn_unsafe.Pointer";
+ return "unsafe.Pointer..d";
if (nt == NULL)
- return "__go_td_" + type->mangled_name(this);
+ return "type.." + type->mangled_name(this);
+ std::string ret;
Named_object* no = nt->named_object();
unsigned int index;
const Named_object* in_function = nt->in_function(&index);
- std::string ret = "__go_tdn_";
if (nt->is_builtin())
go_assert(in_function == NULL);
else
{
- const std::string& pkgpath(no->package() == NULL
- ? this->pkgpath_symbol()
- : no->package()->pkgpath_symbol());
- ret.append(pkgpath);
- ret.append(1, '.');
if (in_function != NULL)
{
const Typed_identifier* rcvr =
in_function->func_value()->type()->receiver();
if (rcvr != NULL)
- {
- Named_type* rcvr_type = rcvr->type()->deref()->named_type();
- ret.append(Gogo::unpack_hidden_name(rcvr_type->name()));
- ret.append(1, '.');
- }
+ ret.append(rcvr->type()->mangled_name(this));
+ else if (in_function->package() == NULL)
+ ret.append(this->pkgpath_symbol());
+ else
+ ret.append(in_function->package()->pkgpath_symbol());
+ ret.push_back('.');
ret.append(Gogo::unpack_hidden_name(in_function->name()));
- ret.append(1, '.');
- if (index > 0)
- {
- char buf[30];
- snprintf(buf, sizeof buf, "%u", index);
- ret.append(buf);
- ret.append(1, '.');
- }
+ ret.push_back('.');
}
+
+ if (no->package() == NULL)
+ ret.append(this->pkgpath_symbol());
+ else
+ ret.append(no->package()->pkgpath_symbol());
+ ret.push_back('.');
}
- std::string mname(Gogo::mangle_possibly_hidden_name(no->name()));
- ret.append(mname);
+ ret.append(Gogo::mangle_possibly_hidden_name(no->name()));
+
+ if (in_function != NULL && index > 0)
+ {
+ char buf[30];
+ snprintf(buf, sizeof buf, "..i%u", index);
+ ret.append(buf);
+ }
+
+ ret.append("..d");
return ret;
}
@@ -761,11 +996,11 @@ Gogo::type_descriptor_name(Type* type, N
std::string
Gogo::gc_symbol_name(Type* type)
{
- return this->type_descriptor_name(type, type->named_type()) + "$gc";
+ return this->type_descriptor_name(type, type->named_type()) + "..g";
}
// Return the name for a ptrmask variable. PTRMASK_SYM_NAME is a
-// base64 string encoding the ptrmask (as returned by Ptrmask::symname
+// base32 string encoding the ptrmask (as returned by Ptrmask::symname
// in types.cc). This name is used to intialize the gcdata field of a
// type descriptor. These names are globally visible. (Note that
// some type descriptors will initialize the gcdata field with a name
@@ -774,7 +1009,7 @@ Gogo::gc_symbol_name(Type* type)
std::string
Gogo::ptrmask_symbol_name(const std::string& ptrmask_sym_name)
{
- return "runtime.gcbits." + ptrmask_sym_name;
+ return "gcbits.." + ptrmask_sym_name;
}
// Return the name to use for an interface method table used for the
@@ -786,8 +1021,25 @@ std::string
Gogo::interface_method_table_name(Interface_type* itype, Type* type,
bool is_pointer)
{
- return ((is_pointer ? "__go_pimt__" : "__go_imt_")
+ return ((is_pointer ? "pimt.." : "imt..")
+ itype->mangled_name(this)
- + "__"
+ + ".."
+ type->mangled_name(this));
}
+
+// Return whether NAME is a special name that can not be passed to
+// unpack_hidden_name. This is needed because various special names
+// use "..SUFFIX", but unpack_hidden_name just looks for '.'.
+
+bool
+Gogo::is_special_name(const std::string& name)
+{
+ return (name.find("..hash") != std::string::npos
+ || name.find("..eq") != std::string::npos
+ || name.find("..stub") != std::string::npos
+ || name.find("..func") != std::string::npos
+ || name.find("..r") != std::string::npos
+ || name.find("..init") != std::string::npos
+ || name.find("..thunk") != std::string::npos
+ || name.find("..import") != std::string::npos);
+}
===================================================================
@@ -2120,7 +2120,7 @@ Thunk_statement::simplify_statement(Gogo
fn = Expression::make_temporary_reference(fn_temp, location);
}
- std::string thunk_name = Gogo::thunk_name();
+ std::string thunk_name = gogo->thunk_name();
// Build the thunk.
this->build_thunk(gogo, thunk_name);
===================================================================
@@ -2654,14 +2654,14 @@ Ptrmask::set_from(Gogo* gogo, Type* type
// Return a symbol name for this ptrmask. This is used to coalesce
// identical ptrmasks, which are common. The symbol name must use
// only characters that are valid in symbols. It's nice if it's
-// short. We convert it to a base64 string.
+// short. We convert it to a string that uses only 32 characters,
+// avoiding digits and u and U.
std::string
Ptrmask::symname() const
{
- const char chars[65] =
- "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.";
- go_assert(chars[64] == '\0');
+ const char chars[33] = "abcdefghijklmnopqrstvwxyzABCDEFG";
+ go_assert(chars[32] == '\0');
std::string ret;
unsigned int b = 0;
int remaining = 0;
@@ -2671,18 +2671,18 @@ Ptrmask::symname() const
{
b |= *p << remaining;
remaining += 8;
- while (remaining >= 6)
+ while (remaining >= 5)
{
- ret += chars[b & 0x3f];
- b >>= 6;
- remaining -= 6;
+ ret += chars[b & 0x1f];
+ b >>= 5;
+ remaining -= 5;
}
}
while (remaining > 0)
{
- ret += chars[b & 0x3f];
- b >>= 6;
- remaining -= 6;
+ ret += chars[b & 0x1f];
+ b >>= 5;
+ remaining -= 5;
}
return ret;
}
@@ -4447,7 +4447,11 @@ Function_type::is_identical(const Functi
}
const Typed_identifier_list* parms1 = this->parameters();
+ if (parms1 != NULL && parms1->empty())
+ parms1 = NULL;
const Typed_identifier_list* parms2 = t->parameters();
+ if (parms2 != NULL && parms2->empty())
+ parms2 = NULL;
if ((parms1 != NULL) != (parms2 != NULL))
{
if (reason != NULL)
@@ -4492,7 +4496,11 @@ Function_type::is_identical(const Functi
}
const Typed_identifier_list* results1 = this->results();
+ if (results1 != NULL && results1->empty())
+ results1 = NULL;
const Typed_identifier_list* results2 = t->results();
+ if (results2 != NULL && results2->empty())
+ results2 = NULL;
if ((results1 != NULL) != (results2 != NULL))
{
if (reason != NULL)
@@ -11144,7 +11152,7 @@ Type::build_stub_methods(Gogo* gogo, con
package = NULL;
else
package = type->named_type()->named_object()->package();
- std::string stub_name = gogo->stub_method_name(name);
+ std::string stub_name = gogo->stub_method_name(package, name);
Named_object* stub;
if (package != NULL)
stub = Named_object::make_function_declaration(stub_name, package,
===================================================================
@@ -425,7 +425,7 @@ func TestPanicTraceback(t *testing.T) {
// Check functions in the traceback.
fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
if runtime.Compiler == "gccgo" {
- fns = []string{"main.$nested", "panic", "main.$nested", "panic", "main.pt2", "main.pt1"}
+ fns = []string{"main.pt1..func1", "panic", "main.pt2..func1", "panic", "main.pt2", "main.pt1"}
}
for _, fn := range fns {
var re *regexp.Regexp
@@ -570,7 +570,7 @@ func TestPanicInlined(t *testing.T) {
buf = buf[:n]
want := []byte("(*point).negate(")
if runtime.Compiler == "gccgo" {
- want = []byte("negate.pN18_runtime_test.point")
+ want = []byte("point.negate")
}
if !bytes.Contains(buf, want) {
t.Logf("%s", buf)
===================================================================
@@ -656,7 +656,7 @@ func canrecover(retaddr uintptr) bool {
}
// Ignore other functions in the reflect package.
- if hasprefix(name, "reflect.") {
+ if hasprefix(name, "reflect.") || hasprefix(name, ".1reflect.") {
continue
}
===================================================================
@@ -730,7 +730,7 @@ func TestMutexProfile(t *testing.T) {
stks := stacks(p)
for _, want := range [][]string{
// {"sync.(*Mutex).Unlock", "pprof.blockMutex.func1"},
- {"sync.Unlock.pN10_sync.Mutex", "pprof.$nested17"},
+ {".1sync.Mutex.Unlock", "pprof.blockMutex..func1"},
} {
if !containsStack(stks, want) {
t.Errorf("No matching stack entry for %+v", want)
===================================================================
@@ -17,7 +17,7 @@
#endif
extern _Bool Exited (uint32_t *w)
- __asm__ (GOSYM_PREFIX "syscall.Exited.N18_syscall.WaitStatus");
+ __asm__ (GOSYM_PREFIX "syscall.WaitStatus.Exited");
_Bool
Exited (uint32_t *w)
@@ -26,7 +26,7 @@ Exited (uint32_t *w)
}
extern _Bool Signaled (uint32_t *w)
- __asm__ (GOSYM_PREFIX "syscall.Signaled.N18_syscall.WaitStatus");
+ __asm__ (GOSYM_PREFIX "syscall.WaitStatus.Signaled");
_Bool
Signaled (uint32_t *w)
@@ -35,7 +35,7 @@ Signaled (uint32_t *w)
}
extern _Bool Stopped (uint32_t *w)
- __asm__ (GOSYM_PREFIX "syscall.Stopped.N18_syscall.WaitStatus");
+ __asm__ (GOSYM_PREFIX "syscall.WaitStatus.Stopped");
_Bool
Stopped (uint32_t *w)
@@ -44,7 +44,7 @@ Stopped (uint32_t *w)
}
extern _Bool Continued (uint32_t *w)
- __asm__ (GOSYM_PREFIX "syscall.Continued.N18_syscall.WaitStatus");
+ __asm__ (GOSYM_PREFIX "syscall.WaitStatus.Continued");
_Bool
Continued (uint32_t *w)
@@ -53,7 +53,7 @@ Continued (uint32_t *w)
}
extern _Bool CoreDump (uint32_t *w)
- __asm__ (GOSYM_PREFIX "syscall.CoreDump.N18_syscall.WaitStatus");
+ __asm__ (GOSYM_PREFIX "syscall.WaitStatus.CoreDump");
_Bool
CoreDump (uint32_t *w)
@@ -62,7 +62,7 @@ CoreDump (uint32_t *w)
}
extern int ExitStatus (uint32_t *w)
- __asm__ (GOSYM_PREFIX "syscall.ExitStatus.N18_syscall.WaitStatus");
+ __asm__ (GOSYM_PREFIX "syscall.WaitStatus.ExitStatus");
int
ExitStatus (uint32_t *w)
@@ -73,7 +73,7 @@ ExitStatus (uint32_t *w)
}
extern int Signal (uint32_t *w)
- __asm__ (GOSYM_PREFIX "syscall.Signal.N18_syscall.WaitStatus");
+ __asm__ (GOSYM_PREFIX "syscall.WaitStatus.Signal");
int
Signal (uint32_t *w)
@@ -84,7 +84,7 @@ Signal (uint32_t *w)
}
extern int StopSignal (uint32_t *w)
- __asm__ (GOSYM_PREFIX "syscall.StopSignal.N18_syscall.WaitStatus");
+ __asm__ (GOSYM_PREFIX "syscall.WaitStatus.StopSignal");
int
StopSignal (uint32_t *w)
@@ -95,7 +95,7 @@ StopSignal (uint32_t *w)
}
extern int TrapCause (uint32_t *w)
- __asm__ (GOSYM_PREFIX "syscall.TrapCause.N18_syscall.WaitStatus");
+ __asm__ (GOSYM_PREFIX "syscall.WaitStatus.TrapCause");
int
TrapCause (uint32_t *w __attribute__ ((unused)))
===================================================================
@@ -68,13 +68,14 @@ callback (void *data, uintptr_t pc, cons
{
const char *p;
- p = __builtin_strchr (function, '.');
- if (p != NULL && __builtin_strncmp (p + 1, "$thunk", 6) == 0)
+ p = function + __builtin_strlen (function);
+ while (p > function && p[-1] >= '0' && p[-1] <= '9')
+ --p;
+ if (p - function > 7 && __builtin_strncmp (p - 7, "..thunk", 7) == 0)
return 0;
- p = __builtin_strrchr (function, '$');
- if (p != NULL && __builtin_strcmp(p, "$recover") == 0)
+ if (p - function > 3 && __builtin_strcmp (p - 3, "..r") == 0)
return 0;
- if (p != NULL && __builtin_strncmp(p, "$stub", 5) == 0)
+ if (p - function > 6 && __builtin_strcmp (p - 6, "..stub") == 0)
return 0;
}
===================================================================
@@ -15,10 +15,10 @@
descriptor. */
extern const struct __go_type_descriptor unsafe_Pointer
- __asm__ (GOSYM_PREFIX "__go_tdn_unsafe.Pointer");
+ __asm__ (GOSYM_PREFIX "unsafe.Pointer..d");
extern const byte unsafe_Pointer_gc[]
- __asm__ (GOSYM_PREFIX "__go_tdn_unsafe.Pointer$gc");
+ __asm__ (GOSYM_PREFIX "unsafe.Pointer..g");
/* Used to determine the field alignment. */
struct field_align
@@ -38,9 +38,9 @@ static const String reflection_string =
const byte unsafe_Pointer_gc[] = { 1 };
extern const FuncVal runtime_pointerhash_descriptor
- __asm__ (GOSYM_PREFIX "runtime.pointerhash$descriptor");
+ __asm__ (GOSYM_PREFIX "runtime.pointerhash..f");
extern const FuncVal runtime_pointerequal_descriptor
- __asm__ (GOSYM_PREFIX "runtime.pointerequal$descriptor");
+ __asm__ (GOSYM_PREFIX "runtime.pointerequal..f");
const struct __go_type_descriptor unsafe_Pointer =
{
@@ -75,7 +75,7 @@ const struct __go_type_descriptor unsafe
it to be defined elsewhere. */
extern const struct __go_ptr_type pointer_unsafe_Pointer
- __asm__ (GOSYM_PREFIX "__go_td_pN14_unsafe.Pointer");
+ __asm__ (GOSYM_PREFIX "type...1unsafe.Pointer");
/* The reflection string. */
#define PREFLECTION "*unsafe.Pointer"
@@ -86,7 +86,7 @@ static const String preflection_string =
};
extern const byte pointer_unsafe_Pointer_gc[]
- __asm__ (GOSYM_PREFIX "__go_td_pN14_unsafe.Pointer$gc");
+ __asm__ (GOSYM_PREFIX "type...1unsafe.Pointer..g");
const byte pointer_unsafe_Pointer_gc[] = { 1 };
===================================================================
@@ -0,0 +1,195 @@
+// names-1 is a change detector for Go symbol names. We don't want
+// the name mangling to change silently.
+package main
+
+import (
+ "bytes"
+ "debug/elf"
+ "debug/macho"
+ "debug/pe"
+ "debug/xcoff"
+ "fmt"
+ "os"
+ "strings"
+)
+
+type Type int
+type Alias = int
+
+//go:noinline
+func Function1(out *bytes.Buffer) int {
+ var f2 func(int) int
+ f1 := func(i int) int {
+ if i == 0 {
+ return 0
+ }
+ type NestedType struct { a int }
+ t := NestedType{f2(i-1)}
+ fmt.Fprint(out, t)
+ return t.a
+ }
+ f2 = func(i int) int {
+ if i == 0 {
+ return 0
+ }
+ type NestedType struct { a int }
+ t := NestedType{f1(i-1)}
+ fmt.Fprint(out, t)
+ return t.a
+ }
+ return f1(10) + f2(10)
+}
+
+//go:noinline
+func Function2(out *bytes.Buffer) {
+ {
+ type T struct { b int }
+ fmt.Fprint(out, T{1})
+ }
+ {
+ type T struct { b int }
+ fmt.Fprint(out, T{2})
+ }
+}
+
+func (t Type) M(bool, int8, float32, complex64, string, func(), func(int16) (float64, complex128), *byte, struct { f int "tag #$%^&{}: 世界" }, []int32, [24]int64, map[uint8]uint16, chan uint32, <-chan uint64, chan <- uintptr, Type, Alias) {
+}
+
+//go:noinline
+func Function3(out *bytes.Buffer) {
+ fmt.Fprintf(out, "%T", Type(0))
+}
+
+func main() {
+ var b bytes.Buffer
+ Function1(&b)
+ Function2(&b)
+ Function3(&b)
+ _ = len(b.String())
+
+ for _, n := range []string{"/proc/self/exe", os.Args[0]} {
+ if f, err := os.Open(n); err == nil {
+ checkFile(f)
+ return
+ }
+ }
+ fmt.Println("checksyms: could not find executable")
+ fmt.Println("UNSUPPORTED: checksyms")
+}
+
+func checkFile(f *os.File) {
+ var syms []string
+ if ef, err := elf.NewFile(f); err == nil {
+ esyms, err := ef.Symbols()
+ if err != nil {
+ panic(err)
+ }
+ for _, esym := range esyms {
+ syms = append(syms, esym.Name)
+ }
+ } else if mf, err := macho.NewFile(f); err == nil {
+ for _, msym := range mf.Symtab.Syms {
+ syms = append(syms, msym.Name)
+ }
+ } else if pf, err := pe.NewFile(f); err == nil {
+ for _, psym := range pf.Symbols {
+ syms = append(syms, psym.Name)
+ }
+ } else if xf, err := xcoff.NewFile(f); err == nil {
+ for _, xsym := range xf.Symbols {
+ syms = append(syms, xsym.Name)
+ }
+ } else {
+ fmt.Println("checksyms: could not parse executable")
+ fmt.Println("UNSUPPORTED: checksyms")
+ return
+ }
+ checkSyms(syms)
+}
+
+var want = []string{
+ "main.Function1",
+ "main.Function1..f",
+ "main.Function1..func1",
+ "main.Function1..func1.main.NestedType..d",
+ "main.Function1..func2",
+ "main.Function1..func2.main.NestedType..d",
+ "main.Function2",
+ "main.Function2..f",
+ "main.Function2.main.T..d",
+ "main.Function2.main.T..i1..d",
+ "main.Function3",
+ "main.Function3..f",
+ "main.Type..d",
+ "main.Type.M",
+ "main.main",
+ "main.want",
+ "type...1.1main.Type", // Why is this here?
+ "type...1main.Function1..func1.NestedType",
+ "type...1main.Function1..func2.NestedType",
+ "type...1main.Function2.T",
+ "type...1main.Function2.T..i1",
+ "type...1main.Type",
+ "type..func.8.1main.Type.3bool.3int8.3float32.3complex64.3string.3func.8.9.8.9.3func.8int16.9.8float64.3complex128.9.3.1uint8.3struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5.3.6.7int32.3.624.7int64.3map.6uint8.7uint16.3chan.0uint32.3.4.5chan.0uint64.3chan.4.5.0uintptr.3main.Type.3int.9.8.9",
+ "type..func.8bool.3int8.3float32.3complex64.3string.3func.8.9.8.9.3func.8int16.9.8float64.3complex128.9.3.1uint8.3struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5.3.6.7int32.3.624.7int64.3map.6uint8.7uint16.3chan.0uint32.3.4.5chan.0uint64.3chan.4.5.0uintptr.3main.Type.3int.9.8.9",
+ "type..func.8main.Type.3bool.3int8.3float32.3complex64.3string.3func.8.9.8.9.3func.8int16.9.8float64.3complex128.9.3.1uint8.3struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5.3.6.7int32.3.624.7int64.3map.6uint8.7uint16.3chan.0uint32.3.4.5chan.0uint64.3chan.4.5.0uintptr.3main.Type.3int.9.8.9",
+ "type..struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5",
+}
+
+func checkSyms(syms []string) {
+ m := make(map[string]bool)
+ for _, sym := range syms {
+ if strings.Contains(sym, ".") {
+ m[sym] = true
+ }
+ }
+
+ ok := true
+ for _, w := range want {
+ if m[w] {
+ delete(m, w)
+ } else {
+ fmt.Printf("checksyms: missing expected symbol %q\n", w)
+ ok = false
+ }
+ }
+
+ for sym := range m {
+ if !strings.Contains(sym, "main") {
+ continue
+ }
+
+ // Skip some symbols we may see but know are unimportant.
+ if sym == "go-main.c" {
+ continue
+ }
+ if strings.HasPrefix(sym, "runtime.") {
+ continue
+ }
+
+ // We can see a lot of spurious .eq and .hash
+ // functions for types defined in other packages.
+ // This is a bug but skip them for now.
+ if strings.Contains(sym, "..eq") || strings.Contains(sym, "..hash") {
+ continue
+ }
+
+ // Skip closure types by skipping incomparable structs.
+ // This may be a bug, not sure.
+ if strings.Contains(sym, ".4x.5") {
+ continue
+ }
+
+ // These functions may be inlined.
+ if sym == "main.checkFile" || sym == "main.checkSyms" {
+ continue
+ }
+
+ fmt.Printf("checksyms: found unexpected symbol %q\n", sym)
+ ok = false
+ }
+
+ if !ok {
+ fmt.Println("FAIL: checksyms")
+ }
+}