// export.cc -- Export declarations in Go frontend. // Copyright 2009 The Go Authors. All rights reserved. // 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 "go-c.h" #include "go-diagnostics.h" #include "go-sha1.h" #include "gogo.h" #include "types.h" #include "expressions.h" #include "statements.h" #include "export.h" #include "go-linemap.h" #include "backend.h" // This file handles exporting global declarations. // Class Export. const int Export::magic_len; // Current version magic string. const char Export::cur_magic[Export::magic_len] = { 'v', '3', ';', '\n' }; // Magic strings for previous versions (still supported). const char Export::v1_magic[Export::magic_len] = { 'v', '1', ';', '\n' }; const char Export::v2_magic[Export::magic_len] = { 'v', '2', ';', '\n' }; const int Export::checksum_len; // Type hash table operations, treating aliases as distinct. class Type_hash_alias_identical { public: unsigned int operator()(const Type* type) const { return type->hash_for_method(NULL, (Type::COMPARE_ERRORS | Type::COMPARE_TAGS | Type::COMPARE_EMBEDDED_INTERFACES | Type::COMPARE_ALIASES)); } }; class Type_alias_identical { public: bool operator()(const Type* t1, const Type* t2) const { return Type::are_identical(t1, t2, (Type::COMPARE_ERRORS | Type::COMPARE_TAGS | Type::COMPARE_EMBEDDED_INTERFACES | Type::COMPARE_ALIASES), NULL); } }; // Mapping from Type objects to a constant index. typedef Unordered_map_hash(const Type*, int, Type_hash_alias_identical, Type_alias_identical) Type_refs; // Implementation object for class Export. Hidden implementation avoids // having to #include types.h in export.h, or use a static map. struct Export_impl { Type_refs type_refs; }; // Constructor. Export::Export(Stream* stream) : stream_(stream), type_index_(1), packages_(), impl_(new Export_impl) { go_assert(Export::checksum_len == Go_sha1_helper::checksum_len); } // Destructor. Export::~Export() { delete this->impl_; } // A traversal class to collect functions and global variables // referenced by inlined functions, and also to gather up // referenced types that need to be included in the exports. class Collect_export_references : public Traverse { public: Collect_export_references(Export* exp, const std::map& packages, Unordered_set(Named_object*)* exports, Unordered_set(const Package*)* imports) : Traverse(traverse_expressions | traverse_types), exp_(exp), packages_(packages), exports_(exports), imports_(imports), inline_fcn_worklist_(NULL), exports_finalized_(false) { } // Initial entry point; performs a walk to expand the exports set. void expand_exports(std::vector* inlinable_functions); // Second entry point (called after the method above), to find // all types referenced by exports. void prepare_types(const std::vector& sorted_exports); // Third entry point (called after the method above), to find // all types in expressions referenced by exports. void prepare_expressions(const std::vector& sorted_exports); protected: // Override of parent class method. int expression(Expression**); // Override of parent class method. int type(Type* type); // Traverse the components of a function type. void traverse_function_type(Function_type*); // Traverse the methods of a named type, and register its package. void traverse_named_type(Named_type*); private: // Add a named object to the exports set (during expand_exports()). // Returns TRUE if a new object was added to the exports set, // FALSE otherwise. bool add_to_exports(Named_object*); // The exporter. Export* exp_; // The list of packages known to this compilation. const std::map& packages_; // The set of named objects to export. Unordered_set(Named_object*)* exports_; // Set containing all directly and indirectly imported packages. Unordered_set(const Package*)* imports_; // Functions we've already traversed and don't need to visit again. Unordered_set(Named_object*) checked_functions_; // Worklist of functions we are exporting with inline bodies that need // to be checked. std::vector* inline_fcn_worklist_; // Set to true if expand_exports() has been called and is complete. bool exports_finalized_; }; void Collect_export_references::expand_exports(std::vector* fcns) { this->inline_fcn_worklist_ = fcns; while (!this->inline_fcn_worklist_->empty()) { Named_object* no = this->inline_fcn_worklist_->back(); this->inline_fcn_worklist_->pop_back(); std::pair ins = this->checked_functions_.insert(no); if (ins.second) { // This traversal may add new objects to this->exports_ and new // functions to this->inline_fcn_worklist_. no->func_value()->block()->traverse(this); } } this->inline_fcn_worklist_ = NULL; this->exports_finalized_ = true; } bool Collect_export_references::add_to_exports(Named_object* no) { std::pair ins = this->exports_->insert(no); // If the export list has been finalized, then we should not be // adding anything new to the exports set. go_assert(!this->exports_finalized_ || !ins.second); return ins.second; } int Collect_export_references::expression(Expression** pexpr) { const Expression* expr = *pexpr; const Var_expression* ve = expr->var_expression(); if (ve != NULL) { Named_object* no = ve->named_object(); if (no->is_variable() && no->var_value()->is_global()) { const Package* var_package = no->package(); if (var_package != NULL) this->imports_->insert(var_package); this->add_to_exports(no); no->var_value()->set_is_referenced_by_inline(); } return TRAVERSE_CONTINUE; } const Func_expression* fe = expr->func_expression(); if (fe != NULL) { Named_object* no = fe->named_object(); const Package* func_package = fe->named_object()->package(); if (func_package != NULL) this->imports_->insert(func_package); if (no->is_function_declaration() && no->func_declaration_value()->type()->is_builtin()) return TRAVERSE_CONTINUE; if (this->inline_fcn_worklist_ != NULL) { bool added = this->add_to_exports(no); if (no->is_function()) no->func_value()->set_is_referenced_by_inline(); // If 'added' is false then this object was already in // exports_, in which case it was already added to // check_inline_refs_ the first time we added it to exports_, so // we don't need to add it again. if (added && no->is_function() && no->func_value()->export_for_inlining()) this->inline_fcn_worklist_->push_back(no); } return TRAVERSE_CONTINUE; } const Named_object* nco = expr->named_constant(); if (nco != 0) { const Named_constant *nc = nco->const_value(); Type::traverse(nc->type(), this); return TRAVERSE_CONTINUE; } const Call_expression* call = expr->call_expression(); if (call != NULL) { const Builtin_call_expression* bce = call->builtin_call_expression(); if (bce != NULL && (bce->code() == Builtin_call_expression::BUILTIN_ADD || bce->code() == Builtin_call_expression::BUILTIN_SLICE)) { // This is a reference to unsafe.Add or unsafe.Slice. Make // sure we list the "unsafe" package in the imports and give // it a package index. const std::map::const_iterator p = this->packages_.find("unsafe"); go_assert(p != this->packages_.end()); this->imports_->insert(p->second); } } return TRAVERSE_CONTINUE; } // Collect up the set of types mentioned in expressions of things we're exporting, // and collect all the packages encountered during type traversal, to make sure // we can declare things referered to indirectly (for example, in the body of an // exported inline function from another package). void Collect_export_references::prepare_expressions(const std::vector& sorted_exports) { for (std::vector::const_iterator p = sorted_exports.begin(); p != sorted_exports.end(); ++p) { Named_object* no = *p; if (no->classification() == Named_object::NAMED_OBJECT_CONST) { Expression* e = no->const_value()->expr(); if (e != NULL) Expression::traverse(&e, this); } } } // Collect up the set of types mentioned in things we're exporting, and collect // all the packages encountered during type traversal, to make sure we can // declare things referered to indirectly (for example, in the body of an // exported inline function from another package). void Collect_export_references::prepare_types(const std::vector& sorted_exports) { // Iterate through the exported objects and traverse any types encountered. for (std::vector::const_iterator p = sorted_exports.begin(); p != sorted_exports.end(); ++p) { Named_object* no = *p; switch (no->classification()) { case Named_object::NAMED_OBJECT_CONST: { Type* t = no->const_value()->type(); if (t != NULL && !t->is_abstract()) Type::traverse(t, this); } break; case Named_object::NAMED_OBJECT_TYPE: Type::traverse(no->type_value()->real_type(), this); this->traverse_named_type(no->type_value()); break; case Named_object::NAMED_OBJECT_VAR: Type::traverse(no->var_value()->type(), this); break; case Named_object::NAMED_OBJECT_FUNC: { Function* fn = no->func_value(); this->traverse_function_type(fn->type()); if (fn->export_for_inlining()) fn->block()->traverse(this); } break; case Named_object::NAMED_OBJECT_FUNC_DECLARATION: this->traverse_function_type(no->func_declaration_value()->type()); break; default: // We shouldn't see anything else. If we do we'll give an // error later when we try to actually export it. break; } } } // Record referenced type, record package imports, and make sure we traverse // methods of named types. int Collect_export_references::type(Type* type) { // Skip forwarders; don't try to give them a type index. if (type->forward_declaration_type() != NULL) return TRAVERSE_CONTINUE; // Skip the void type, which we'll see when exporting // unsafe.Pointer. The void type is not itself exported, because // Pointer_type::do_export checks for it. if (type->is_void_type()) return TRAVERSE_SKIP_COMPONENTS; // Skip the nil type, turns up in function bodies. if (type->is_nil_type()) return TRAVERSE_SKIP_COMPONENTS; // Skip abstract types. We should never see these in real code, // only in things like const declarations. if (type->is_abstract()) return TRAVERSE_SKIP_COMPONENTS; if (!this->exp_->record_type(type)) { // We've already seen this type. return TRAVERSE_SKIP_COMPONENTS; } // At this stage of compilation traversing interface types traverses // the final list of methods, but we export the locally defined // methods. If there is an embedded interface type we need to make // sure to export that. Check classification, rather than calling // the interface_type method, because we want to handle named types // below. if (type->classification() == Type::TYPE_INTERFACE) { Interface_type* it = type->interface_type(); const Typed_identifier_list* methods = it->local_methods(); if (methods != NULL) { for (Typed_identifier_list::const_iterator p = methods->begin(); p != methods->end(); ++p) { if (p->name().empty()) Type::traverse(p->type(), this); else this->traverse_function_type(p->type()->function_type()); } } return TRAVERSE_SKIP_COMPONENTS; } Named_type* nt = type->named_type(); if (nt != NULL) this->traverse_named_type(nt); return TRAVERSE_CONTINUE; } void Collect_export_references::traverse_named_type(Named_type* nt) { const Package* package = nt->named_object()->package(); if (package != NULL) this->imports_->insert(package); // We have to traverse the methods of named types, because we are // going to export them. This is not done by ordinary type // traversal. const Bindings* methods = nt->local_methods(); if (methods != NULL) { for (Bindings::const_definitions_iterator pm = methods->begin_definitions(); pm != methods->end_definitions(); ++pm) { Function* fn = (*pm)->func_value(); this->traverse_function_type(fn->type()); if (fn->export_for_inlining()) fn->block()->traverse(this); } for (Bindings::const_declarations_iterator pm = methods->begin_declarations(); pm != methods->end_declarations(); ++pm) { Named_object* mno = pm->second; if (mno->is_function_declaration()) this->traverse_function_type(mno->func_declaration_value()->type()); } } } // Traverse the types in a function type. We don't need the function // type itself, just the receiver, parameter, and result types. void Collect_export_references::traverse_function_type(Function_type* type) { go_assert(type != NULL); if (this->remember_type(type)) return; const Typed_identifier* receiver = type->receiver(); if (receiver != NULL) Type::traverse(receiver->type(), this); const Typed_identifier_list* parameters = type->parameters(); if (parameters != NULL) parameters->traverse(this); const Typed_identifier_list* results = type->results(); if (results != NULL) results->traverse(this); } // Return true if we should export NO. static bool should_export(Named_object* no) { // We only export objects which are locally defined. if (no->package() != NULL) return false; // We don't export packages. if (no->is_package()) return false; // We don't export hidden names. if (Gogo::is_hidden_name(no->name())) return false; // We don't export various special functions. if (Gogo::special_name_pos(no->name()) != std::string::npos) return false; // Methods are exported with the type, not here. if (no->is_function() && no->func_value()->type()->is_method()) return false; if (no->is_function_declaration() && no->func_declaration_value()->type()->is_method()) return false; // Don't export dummy global variables created for initializers when // used with sinks. if (no->is_variable() && no->name()[0] == '_' && no->name()[1] == '.') return false; return true; } // Compare Typed_identifier_list's. static int compare_til(const Typed_identifier_list*, const Typed_identifier_list*); // A functor to sort Named_object pointers by name. struct Sort_bindings { bool operator()(const Named_object* n1, const Named_object* n2) const { if (n1 == n2) return false; if (n1->package() != n2->package()) { if (n1->package() == NULL) return true; if (n2->package() == NULL) return false; // Make sure we don't see the same pkgpath twice. const std::string& p1(n1->package()->pkgpath()); const std::string& p2(n2->package()->pkgpath()); go_assert(p1 != p2); return p1 < p2; } if (n1->name() != n2->name()) return n1->name() < n2->name(); // We shouldn't see the same name twice, but it can happen for // nested type names. go_assert(n1->is_type() && n2->is_type()); unsigned int ind1; const Named_object* g1 = n1->type_value()->in_function(&ind1); unsigned int ind2; const Named_object* g2 = n2->type_value()->in_function(&ind2); if (g1 == NULL) { go_assert(g2 != NULL); return true; } else if (g2 == NULL) return false; else if (g1 == g2) { go_assert(ind1 != ind2); return ind1 < ind2; } else if ((g1->package() != g2->package()) || (g1->name() != g2->name())) return Sort_bindings()(g1, g2); else { // This case can happen if g1 or g2 is a method. if (g1 != NULL && g1->func_value()->is_method()) { const Typed_identifier* r = g1->func_value()->type()->receiver(); g1 = r->type()->named_type()->named_object(); } if (g2 != NULL && g2->func_value()->is_method()) { const Typed_identifier* r = g2->func_value()->type()->receiver(); g2 = r->type()->named_type()->named_object(); } return Sort_bindings()(g1, g2); } } }; // A functor to sort types for export. struct Sort_types { bool operator()(const Type* t1, const Type* t2) const { t1 = t1->forwarded(); t2 = t2->forwarded(); const Named_type* nt1 = t1->named_type(); const Named_type* nt2 = t2->named_type(); if (nt1 != NULL) { if (nt2 != NULL) { Sort_bindings sb; return sb(nt1->named_object(), nt2->named_object()); } else return true; } else if (nt2 != NULL) return false; if (t1->classification() != t2->classification()) return t1->classification() < t2->classification(); Gogo* gogo = go_get_gogo(); Backend_name b1; gogo->type_descriptor_backend_name(t1, NULL, &b1); Backend_name b2; gogo->type_descriptor_backend_name(t2, NULL, &b2); std::string n1 = b1.name(); std::string n2 = b2.name(); if (n1 != n2) return n1 < n2; // We should never see equal types here. If we do, we may not // generate an identical output file for identical input. But the // backend names can be equal because we want to treat aliases // differently while type_descriptor_backend_name does not. In // that case we need to traverse the type elements. // t1 == t2 in case std::sort compares elements to themselves. if (t1 == t2) return false; Sort_types sort; Type_alias_identical identical; go_assert(!identical(t1, t2)); switch (t1->classification()) { case Type::TYPE_ERROR: return false; case Type::TYPE_VOID: case Type::TYPE_BOOLEAN: case Type::TYPE_INTEGER: case Type::TYPE_FLOAT: case Type::TYPE_COMPLEX: case Type::TYPE_STRING: case Type::TYPE_SINK: case Type::TYPE_NIL: case Type::TYPE_CALL_MULTIPLE_RESULT: case Type::TYPE_NAMED: case Type::TYPE_FORWARD: default: go_unreachable(); case Type::TYPE_FUNCTION: { const Function_type* ft1 = t1->function_type(); const Function_type* ft2 = t2->function_type(); const Typed_identifier* r1 = ft1->receiver(); const Typed_identifier* r2 = ft2->receiver(); if (r1 == NULL) go_assert(r2 == NULL); else { go_assert(r2 != NULL); const Type* rt1 = r1->type()->forwarded(); const Type* rt2 = r2->type()->forwarded(); if (!identical(rt1, rt2)) return sort(rt1, rt2); } const Typed_identifier_list* p1 = ft1->parameters(); const Typed_identifier_list* p2 = ft2->parameters(); if (p1 == NULL || p1->empty()) go_assert(p2 == NULL || p2->empty()); else { go_assert(p2 != NULL && !p2->empty()); int i = compare_til(p1, p2); if (i < 0) return false; else if (i > 0) return true; } p1 = ft1->results(); p2 = ft2->results(); if (p1 == NULL || p1->empty()) go_assert(p2 == NULL || p2->empty()); else { go_assert(p2 != NULL && !p2->empty()); int i = compare_til(p1, p2); if (i < 0) return false; else if (i > 0) return true; } go_unreachable(); } case Type::TYPE_POINTER: { const Type* p1 = t1->points_to()->forwarded(); const Type* p2 = t2->points_to()->forwarded(); go_assert(!identical(p1, p2)); return sort(p1, p2); } case Type::TYPE_STRUCT: { const Struct_type* s1 = t1->struct_type(); const Struct_type* s2 = t2->struct_type(); const Struct_field_list* f1 = s1->fields(); const Struct_field_list* f2 = s2->fields(); go_assert(f1 != NULL && f2 != NULL); Struct_field_list::const_iterator p1 = f1->begin(); Struct_field_list::const_iterator p2 = f2->begin(); for (; p2 != f2->end(); ++p1, ++p2) { go_assert(p1 != f1->end()); go_assert(p1->field_name() == p2->field_name()); go_assert(p1->is_anonymous() == p2->is_anonymous()); const Type* ft1 = p1->type()->forwarded(); const Type* ft2 = p2->type()->forwarded(); if (!identical(ft1, ft2)) return sort(ft1, ft2); } go_assert(p1 == f1->end()); go_unreachable(); } case Type::TYPE_ARRAY: { const Type* e1 = t1->array_type()->element_type()->forwarded(); const Type* e2 = t2->array_type()->element_type()->forwarded(); go_assert(!identical(e1, e2)); return sort(e1, e2); } case Type::TYPE_MAP: { const Map_type* m1 = t1->map_type(); const Map_type* m2 = t2->map_type(); const Type* k1 = m1->key_type()->forwarded(); const Type* k2 = m2->key_type()->forwarded(); if (!identical(k1, k2)) return sort(k1, k2); const Type* v1 = m1->val_type()->forwarded(); const Type* v2 = m2->val_type()->forwarded(); go_assert(!identical(v1, v2)); return sort(v1, v2); } case Type::TYPE_CHANNEL: { const Type* e1 = t1->channel_type()->element_type()->forwarded(); const Type* e2 = t2->channel_type()->element_type()->forwarded(); go_assert(!identical(e1, e2)); return sort(e1, e2); } case Type::TYPE_INTERFACE: { const Interface_type* it1 = t1->interface_type(); const Interface_type* it2 = t2->interface_type(); const Typed_identifier_list* m1 = it1->local_methods(); const Typed_identifier_list* m2 = it2->local_methods(); // We know the full method lists are the same, because the // mangled type names were the same, but here we are looking // at the local method lists, which include embedded // interfaces, and we can have an embedded empty interface. if (m1 == NULL || m1->empty()) { go_assert(m2 != NULL && !m2->empty()); return true; } else if (m2 == NULL || m2->empty()) { go_assert(m1 != NULL && !m1->empty()); return false; } int i = compare_til(m1, m2); if (i < 0) return false; else if (i > 0) return true; else go_unreachable(); } } } }; // Compare Typed_identifier_list's with Sort_types, returning -1, 0, +1. static int compare_til( const Typed_identifier_list* til1, const Typed_identifier_list* til2) { Type_alias_identical identical; Sort_types sort; Typed_identifier_list::const_iterator p1 = til1->begin(); Typed_identifier_list::const_iterator p2 = til2->begin(); for (; p2 != til2->end(); ++p1, ++p2) { if (p1 == til1->end()) return -1; const Type* t1 = p1->type()->forwarded(); const Type* t2 = p2->type()->forwarded(); if (!identical(t1, t2)) { if (sort(t1, t2)) return -1; else return +1; } } if (p1 != til1->end()) return +1; return 0; } // Export those identifiers marked for exporting. void Export::export_globals(const std::string& package_name, const std::string& prefix, const std::string& pkgpath, const std::map& packages, const std::map& imports, const std::string& import_init_fn, const Import_init_set& imported_init_fns, const Bindings* bindings, Unordered_set(Named_object*)* functions_marked_inline) { // If there have been any errors so far, don't try to export // anything. That way the export code doesn't have to worry about // mismatched types or other confusions. if (saw_errors()) return; // EXPORTS is the set of objects to export. CHECK_INLINE_REFS is a // list of exported function with inline bodies that need to be // checked for references to other objects. Every function on // CHECK_INLINE_REFS is also on EXPORTS. Unordered_set(Named_object*) exports; std::vector check_inline_refs; check_inline_refs.reserve(functions_marked_inline->size()); // Add all functions/methods from the "marked inlined" set to the // CHECK_INLINE_REFS worklist. for (Unordered_set(Named_object*)::const_iterator p = functions_marked_inline->begin(); p != functions_marked_inline->end(); ++p) check_inline_refs.push_back(*p); for (Bindings::const_definitions_iterator p = bindings->begin_definitions(); p != bindings->end_definitions(); ++p) { if (should_export(*p)) exports.insert(*p); } for (Bindings::const_declarations_iterator p = bindings->begin_declarations(); p != bindings->end_declarations(); ++p) { // We export a function declaration as it may be implemented in // supporting C code. We do not export type declarations. if (p->second->is_function_declaration() && should_export(p->second)) exports.insert(p->second); } // Track all imported packages mentioned in export data. Unordered_set(const Package*) all_imports; Collect_export_references collect(this, packages, &exports, &all_imports); // Walk the set of inlinable routine bodies collected above. This // can potentially expand the exports set. collect.expand_exports(&check_inline_refs); // Export the symbols in sorted order. That will reduce cases where // irrelevant changes to the source code affect the exported // interface. std::vector sorted_exports; sorted_exports.reserve(exports.size()); for (Unordered_set(Named_object*)::const_iterator p = exports.begin(); p != exports.end(); ++p) { sorted_exports.push_back(*p); const Package* pkg = (*p)->package(); if (pkg != NULL) all_imports.insert(pkg); } std::sort(sorted_exports.begin(), sorted_exports.end(), Sort_bindings()); // Collect up the set of types mentioned in things we're exporting, // and any packages that may be referred to indirectly. collect.prepare_types(sorted_exports); collect.prepare_expressions(sorted_exports); // Assign indexes to all exported types and types referenced by // things we're exporting. Return value is index of first non-exported // type. int unexported_type_index = this->assign_type_indices(sorted_exports); // Although the export data is readable, at least this version is, // it is conceptually a binary format. Start with a four byte // version number. this->write_bytes(Export::cur_magic, Export::magic_len); // The package name. this->write_c_string("package "); this->write_string(package_name); this->write_c_string("\n"); // The prefix or package path, used for all global symbols. if (prefix.empty()) { go_assert(!pkgpath.empty()); this->write_c_string("pkgpath "); this->write_string(pkgpath); } else { this->write_c_string("prefix "); this->write_string(prefix); } this->write_c_string("\n"); this->write_packages(packages); this->write_imports(imports, all_imports); this->write_imported_init_fns(package_name, import_init_fn, imported_init_fns); // FIXME: It might be clever to add something about the processor // and ABI being used, although ideally any problems in that area // would be caught by the linker. // Write out all the types, both exported and not. this->write_types(unexported_type_index); // Write out the non-type export data. for (std::vector::const_iterator p = sorted_exports.begin(); p != sorted_exports.end(); ++p) { if (!(*p)->is_type()) (*p)->export_named_object(this); } std::string checksum = this->stream_->checksum(); std::string s = "checksum "; for (std::string::const_iterator p = checksum.begin(); p != checksum.end(); ++p) { unsigned char c = *p; unsigned int dig = c >> 4; s += dig < 10 ? '0' + dig : 'A' + dig - 10; dig = c & 0xf; s += dig < 10 ? '0' + dig : 'A' + dig - 10; } s += "\n"; this->stream_->write_checksum(s); } // Record a type in the "to be indexed" set. Return true if the type // was not already in the set, false otherwise. bool Export::record_type(Type* type) { type = type->forwarded(); std::pair ins = this->impl_->type_refs.insert(std::make_pair(type, 0)); return ins.second; } // Assign the specified type an index. void Export::set_type_index(const Type* type) { type = type->forwarded(); Type_refs::iterator p = this->impl_->type_refs.find(type); go_assert(p != this->impl_->type_refs.end()); int index = this->type_index_; ++this->type_index_; go_assert(p->second == 0); p->second = index; } // This helper assigns type indices to all types mentioned directly or // indirectly in the things we're exporting. Actual exported types are given // indices according to where the appear on the sorted exports list; all other // types appear afterwards. Return value is the total number of exported types // plus 1, e.g. the index of the 1st non-exported type. int Export::assign_type_indices(const std::vector& sorted_exports) { // Assign indexes to all the exported types. for (std::vector::const_iterator p = sorted_exports.begin(); p != sorted_exports.end(); ++p) { if (!(*p)->is_type()) continue; this->record_type((*p)->type_value()); this->set_type_index((*p)->type_value()); } int ret = this->type_index_; // Collect export-referenced, non-builtin types. std::vector types; types.reserve(this->impl_->type_refs.size()); for (Type_refs::const_iterator p = this->impl_->type_refs.begin(); p != this->impl_->type_refs.end(); ++p) { const Type* t = p->first; if (p->second != 0) continue; types.push_back(t); } // Sort the types. std::sort(types.begin(), types.end(), Sort_types()); // Assign numbers to the sorted list. for (std::vector::const_iterator p = types.begin(); p != types.end(); ++p) this->set_type_index((*p)); return ret; } // Sort packages. static bool packages_compare(const Package* a, const Package* b) { if (a->package_name() < b->package_name()) return true; else if (a->package_name() > b->package_name()) return false; if (a->pkgpath() < b->pkgpath()) return true; else if (a->pkgpath() > b->pkgpath()) return false; // In principle if we get here then a == b. Try to do something sensible // even if the import information is inconsistent. if (a->pkgpath_symbol() < b->pkgpath_symbol()) return true; else if (a->pkgpath_symbol() > b->pkgpath_symbol()) return false; return a < b; } // Write out all the known packages whose pkgpath symbol is not a // simple transformation of the pkgpath, so that the importing code // can reliably know it. void Export::write_packages(const std::map& packages) { // Sort for consistent output. std::vector out; for (std::map::const_iterator p = packages.begin(); p != packages.end(); ++p) { if (p->second->pkgpath_symbol() != Gogo::pkgpath_for_symbol(p->second->pkgpath())) out.push_back(p->second); } std::sort(out.begin(), out.end(), packages_compare); for (std::vector::const_iterator p = out.begin(); p != out.end(); ++p) { this->write_c_string("package "); this->write_string((*p)->package_name()); this->write_c_string(" "); this->write_string((*p)->pkgpath()); this->write_c_string(" "); this->write_string((*p)->pkgpath_symbol()); this->write_c_string("\n"); } } // Sort imported packages. static bool import_compare(const std::pair& a, const std::pair& b) { return a.first < b.first; } // Write out the imported packages. void Export::write_imports(const std::map& imports, const Unordered_set(const Package*)& all_imports) { // Sort the imports for more consistent output. Unordered_set(const Package*) seen; std::vector > sorted_imports; for (std::map::const_iterator p = imports.begin(); p != imports.end(); ++p) { sorted_imports.push_back(std::make_pair(p->first, p->second)); seen.insert(p->second); } std::sort(sorted_imports.begin(), sorted_imports.end(), import_compare); int package_index = 1; for (std::vector >::const_iterator p = sorted_imports.begin(); p != sorted_imports.end(); ++p) { this->write_c_string("import "); this->write_string(p->second->package_name()); this->write_c_string(" "); this->write_string(p->second->pkgpath()); this->write_c_string(" \""); this->write_string(p->first); this->write_c_string("\"\n"); this->packages_[p->second] = package_index; package_index++; } // Write out a separate list of indirectly imported packages. std::vector indirect_imports; for (Unordered_set(const Package*)::const_iterator p = all_imports.begin(); p != all_imports.end(); ++p) { if (seen.find(*p) == seen.end()) indirect_imports.push_back(*p); } std::sort(indirect_imports.begin(), indirect_imports.end(), packages_compare); for (std::vector::const_iterator p = indirect_imports.begin(); p != indirect_imports.end(); ++p) { this->write_c_string("indirectimport "); this->write_string((*p)->package_name()); this->write_c_string(" "); this->write_string((*p)->pkgpath()); this->write_c_string("\n"); this->packages_[*p] = package_index; package_index++; } } void Export::add_init_graph_edge(Init_graph* init_graph, unsigned src, unsigned sink) { Init_graph::iterator it = init_graph->find(src); if (it != init_graph->end()) it->second.insert(sink); else { std::set succs; succs.insert(sink); (*init_graph)[src] = succs; } } // Constructs the imported portion of the init graph, e.g. those // edges that we read from imported packages. void Export::populate_init_graph(Init_graph* init_graph, const Import_init_set& imported_init_fns, const std::map& init_idx) { for (Import_init_set::const_iterator p = imported_init_fns.begin(); p != imported_init_fns.end(); ++p) { const Import_init* ii = *p; if (ii->is_dummy()) continue; std::map::const_iterator srcit = init_idx.find(ii->init_name()); go_assert(srcit != init_idx.end()); unsigned src = srcit->second; for (std::set::const_iterator pci = ii->precursors().begin(); pci != ii->precursors().end(); ++pci) { std::map::const_iterator it = init_idx.find(*pci); go_assert(it != init_idx.end()); unsigned sink = it->second; add_init_graph_edge(init_graph, src, sink); } } } // Write out the initialization functions which need to run for this // package. void Export::write_imported_init_fns(const std::string& package_name, const std::string& import_init_fn, const Import_init_set& imported_init_fns) { if (import_init_fn.empty() && imported_init_fns.empty()) return; // Maps a given init function to the its index in the exported "init" clause. std::map init_idx; this->write_c_string("init"); if (!import_init_fn.empty()) { this->write_c_string(" "); this->write_string(package_name); this->write_c_string(" "); this->write_string(import_init_fn); init_idx[import_init_fn] = 0; } if (imported_init_fns.empty()) { this->write_c_string("\n"); return; } typedef std::map > level_map; Init_graph init_graph; level_map inits_at_level; // Walk through the set of import inits (already sorted by // init fcn name) and write them out to the exports. for (Import_init_set::const_iterator p = imported_init_fns.begin(); p != imported_init_fns.end(); ++p) { const Import_init* ii = *p; if (ii->init_name() == import_init_fn) continue; this->write_c_string(" "); this->write_string(ii->package_name()); this->write_c_string(" "); this->write_string(ii->init_name()); // Populate init_idx. go_assert(init_idx.find(ii->init_name()) == init_idx.end()); unsigned idx = init_idx.size(); init_idx[ii->init_name()] = idx; // If the init function has a non-negative priority value, this // is an indication that it was referred to in an older version // export data section (e.g. we read a legacy object // file). Record such init fcns so that we can fix up the graph // for them (handled later in this function). if (ii->priority() > 0) { level_map::iterator it = inits_at_level.find(ii->priority()); if (it == inits_at_level.end()) { std::vector l; l.push_back(ii->init_name()); inits_at_level[ii->priority()] = l; } else it->second.push_back(ii->init_name()); } } this->write_c_string("\n"); // Create the init graph. Start by populating the graph with // all the edges we inherited from imported packages. populate_init_graph(&init_graph, imported_init_fns, init_idx); // Now add edges from the local init function to each of the // imported fcns. if (!import_init_fn.empty() && import_init_fn[0] != '~') { unsigned src = 0; go_assert(init_idx[import_init_fn] == 0); for (Import_init_set::const_iterator p = imported_init_fns.begin(); p != imported_init_fns.end(); ++p) { const Import_init* ii = *p; if (ii->is_dummy()) continue; unsigned sink = init_idx[ii->init_name()]; add_init_graph_edge(&init_graph, src, sink); } } // In the scenario where one or more of the packages we imported // was written with the legacy export data format, add dummy edges // to capture the priority relationships. Here is a package import // graph as an example: // // *A // /| // / | // B *C // /| // / | // *D *E // | /| // |/ | // *F *G // // Let's suppose that the object for package "C" is from an old // gccgo, e.g. it has the old export data format. All other // packages are compiled with the new compiler and have the new // format. Packages with *'s have init functions. The scenario is // that we're compiling a package "A"; during this process we'll // read the export data for "C". It should look something like // // init F F..import 1 G G..import 1 D D..import 2 E E..import 2; // // To capture this information and convey it to the consumers of // "A", the code below adds edges to the graph from each priority K // function to every priority K-1 function for appropriate values // of K. This will potentially add more edges than we need (for // example, an edge from D to G), but given that we don't expect // to see large numbers of old objects, this will hopefully be OK. if (inits_at_level.size() > 0) { for (level_map::reverse_iterator it = inits_at_level.rbegin(); it != inits_at_level.rend(); ++it) { int level = it->first; if (level < 2) break; const std::vector& fcns_at_level = it->second; for (std::vector::const_iterator sit = fcns_at_level.begin(); sit != fcns_at_level.end(); ++sit) { unsigned src = init_idx[*sit]; level_map::iterator it2 = inits_at_level.find(level - 1); if (it2 != inits_at_level.end()) { const std::vector fcns_at_lm1 = it2->second; for (std::vector::const_iterator mit = fcns_at_lm1.begin(); mit != fcns_at_lm1.end(); ++mit) { unsigned sink = init_idx[*mit]; add_init_graph_edge(&init_graph, src, sink); } } } } } // Write out the resulting graph. this->write_c_string("init_graph"); for (Init_graph::const_iterator ki = init_graph.begin(); ki != init_graph.end(); ++ki) { unsigned src = ki->first; const std::set& successors = ki->second; for (std::set::const_iterator vi = successors.begin(); vi != successors.end(); ++vi) { this->write_c_string(" "); this->write_unsigned(src); unsigned sink = (*vi); this->write_c_string(" "); this->write_unsigned(sink); } } this->write_c_string("\n"); } // Write the types to the export stream. void Export::write_types(int unexported_type_index) { // Map from type index to type. std::vector types(static_cast(this->type_index_)); for (Type_refs::const_iterator p = this->impl_->type_refs.begin(); p != this->impl_->type_refs.end(); ++p) { if (p->second >= 0) types.at(p->second) = p->first; } // Write the type information to a buffer. Stream_to_string type_data; Export::Stream* orig_stream = this->stream_; this->stream_ = &type_data; std::vector type_sizes(static_cast(this->type_index_)); type_sizes[0] = 0; // Start at 1 because type index 0 is not used. size_t start_size = 0; for (int i = 1; i < this->type_index_; ++i) { this->write_type_definition(types[i], i); size_t cur_size = type_data.string().size(); type_sizes[i] = cur_size - start_size; start_size = cur_size; } // Back to original stream. this->stream_ = orig_stream; // The line "types MAXP1 EXPORTEDP1 SIZES..." appears before the // types. MAXP1 is one more than the maximum type index used; that // is, it is the size of the array we need to allocate to hold all // the values. Indexes 1 up to but not including EXPORTEDP1 are the // exported types. The other types are not exported. SIZES... is a // list of MAXP1-1 entries listing the size of the type definition // for each type, starting at index 1. char buf[100]; snprintf(buf, sizeof buf, "types %d %d", this->type_index_, unexported_type_index); this->write_c_string(buf); // Start at 1 because type index 0 is not used. for (int i = 1; i < this->type_index_; ++i) { snprintf(buf, sizeof buf, " %lu", static_cast(type_sizes[i])); this->write_c_string(buf); } this->write_c_string("\n"); this->write_string(type_data.string()); } // Write a single type to the export stream. void Export::write_type_definition(const Type* type, int index) { this->write_c_string("type "); char buf[30]; snprintf(buf, sizeof buf, "%d ", index); this->write_c_string(buf); const Named_type* nt = type->named_type(); if (nt != NULL) { const Named_object* no = nt->named_object(); const Package* package = no->package(); this->write_c_string("\""); if (package != NULL && !Gogo::is_hidden_name(no->name())) { this->write_string(package->pkgpath()); this->write_c_string("."); } this->write_string(nt->named_object()->name()); this->write_c_string("\" "); if (!nt->in_heap()) this->write_c_string("notinheap "); if (nt->is_alias()) this->write_c_string("= "); } type->export_type(this); // Type::export_type will print a newline for a named type, but not // otherwise. if (nt == NULL) this->write_c_string("\n"); } // Write a name to the export stream. void Export::write_name(const std::string& name) { if (name.empty()) this->write_c_string("?"); else this->write_string(Gogo::unpack_hidden_name(name)); } // Write an integer value to the export stream. void Export::write_int(int value) { char buf[100]; snprintf(buf, sizeof buf, "%d", value); this->write_c_string(buf); } // Write an integer value to the export stream. void Export::write_unsigned(unsigned value) { char buf[100]; snprintf(buf, sizeof buf, "%u", value); this->write_c_string(buf); } // Return the index of a package. int Export::package_index(const Package* pkg) const { Unordered_map(const Package *, int)::const_iterator p = this->packages_.find(pkg); go_assert(p != this->packages_.end()); int index = p->second; go_assert(index != 0); return index; } // Return the index of the "unsafe" package. int Export::unsafe_package_index() const { for (Unordered_map(const Package*, int)::const_iterator p = this->packages_.begin(); p != this->packages_.end(); ++p) { if (p->first->pkgpath() == "unsafe") { go_assert(p->second != 0); return p->second; } } go_unreachable(); } // Return the index of a type. int Export::type_index(const Type* type) { type = type->forwarded(); Type_refs::const_iterator p = this->impl_->type_refs.find(type); go_assert(p != this->impl_->type_refs.end()); int index = p->second; go_assert(index != 0); return index; } // Export a type. void Export::write_type(const Type* type) { int index = this->type_index(type); char buf[30]; snprintf(buf, sizeof buf, "", index); this->write_c_string(buf); } // Export a type to a function body. void Export::write_type_to(const Type* type, Export_function_body* efb) { int index = this->type_index(type); char buf[30]; snprintf(buf, sizeof buf, "", index); efb->write_c_string(buf); } // Export escape note. void Export::write_escape(std::string* note) { if (note != NULL && *note != "esc:0x0") { this->write_c_string(" "); char buf[50]; go_assert(note->find("esc:") != std::string::npos); snprintf(buf, sizeof buf, "<%s>", note->c_str()); this->write_c_string(buf); } } // Add the builtin types to the export table. void Export::register_builtin_types(Gogo* gogo) { this->register_builtin_type(gogo, "int8", BUILTIN_INT8); this->register_builtin_type(gogo, "int16", BUILTIN_INT16); this->register_builtin_type(gogo, "int32", BUILTIN_INT32); this->register_builtin_type(gogo, "int64", BUILTIN_INT64); this->register_builtin_type(gogo, "uint8", BUILTIN_UINT8); this->register_builtin_type(gogo, "uint16", BUILTIN_UINT16); this->register_builtin_type(gogo, "uint32", BUILTIN_UINT32); this->register_builtin_type(gogo, "uint64", BUILTIN_UINT64); this->register_builtin_type(gogo, "float32", BUILTIN_FLOAT32); this->register_builtin_type(gogo, "float64", BUILTIN_FLOAT64); this->register_builtin_type(gogo, "complex64", BUILTIN_COMPLEX64); this->register_builtin_type(gogo, "complex128", BUILTIN_COMPLEX128); this->register_builtin_type(gogo, "int", BUILTIN_INT); this->register_builtin_type(gogo, "uint", BUILTIN_UINT); this->register_builtin_type(gogo, "uintptr", BUILTIN_UINTPTR); this->register_builtin_type(gogo, "bool", BUILTIN_BOOL); this->register_builtin_type(gogo, "string", BUILTIN_STRING); this->register_builtin_type(gogo, "error", BUILTIN_ERROR); this->register_builtin_type(gogo, "byte", BUILTIN_BYTE); this->register_builtin_type(gogo, "rune", BUILTIN_RUNE); } // Register one builtin type in the export table. void Export::register_builtin_type(Gogo* gogo, const char* name, Builtin_code code) { Named_object* named_object = gogo->lookup_global(name); go_assert(named_object != NULL && named_object->is_type()); std::pair ins = this->impl_->type_refs.insert(std::make_pair(named_object->type_value(), code)); go_assert(ins.second); // We also insert the underlying type. We can see the underlying // type at least for string and bool. It's OK if this insert // fails--we expect duplications here, and it doesn't matter when // they occur. Type* real_type = named_object->type_value()->real_type(); this->impl_->type_refs.insert(std::make_pair(real_type, code)); } // Class Export::Stream. Export::Stream::Stream() { this->sha1_helper_ = go_create_sha1_helper(); go_assert(this->sha1_helper_ != NULL); } Export::Stream::~Stream() { } // Write bytes to the stream. This keeps a checksum of bytes as they // go by. void Export::Stream::write_and_sum_bytes(const char* bytes, size_t length) { this->sha1_helper_->process_bytes(bytes, length); this->do_write(bytes, length); } // Get the checksum. std::string Export::Stream::checksum() { std::string rval = this->sha1_helper_->finish(); delete this->sha1_helper_; return rval; } // Write the checksum string to the export data. void Export::Stream::write_checksum(const std::string& s) { this->do_write(s.data(), s.length()); } // Class Stream_to_section. Stream_to_section::Stream_to_section(Backend* backend) : backend_(backend) { } // Write data to a section. void Stream_to_section::do_write(const char* bytes, size_t length) { this->backend_->write_export_data (bytes, length); } // Class Export_function_body. // Record a temporary statement. unsigned int Export_function_body::record_temporary(const Temporary_statement* temp) { unsigned int ret = this->next_temporary_index_; if (ret > 0x7fffffff) go_error_at(temp->location(), "too many temporary statements in export data"); ++this->next_temporary_index_; std::pair val(temp, ret); std::pair ins = this->temporary_indexes_.insert(val); go_assert(ins.second); return ret; } // Return the index of a temporary statement. unsigned int Export_function_body::temporary_index(const Temporary_statement* temp) { Unordered_map(const Temporary_statement*, unsigned int)::const_iterator p = this->temporary_indexes_.find(temp); go_assert(p != this->temporary_indexes_.end()); return p->second; } // Return the index of an unnamed label. If it doesn't already have // an index, give it one. unsigned int Export_function_body::unnamed_label_index(const Unnamed_label* label) { unsigned int next = this->next_label_index_; std::pair val(label, next); std::pair ins = this->label_indexes_.insert(val); if (!ins.second) return ins.first->second; else { if (next > 0x7fffffff) go_error_at(label->location(), "too many unnamed labels in export data"); ++this->next_label_index_; return next; } }