| // 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<std::string, Package*>& 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<Named_object*>* inlinable_functions); |
| |
| // Second entry point (called after the method above), to find |
| // all types referenced by exports. |
| void |
| prepare_types(const std::vector<Named_object*>& 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<Named_object*>& 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<std::string, Package*>& 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<Named_object*>* 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<Named_object*>* 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<Unordered_set(Named_object*)::iterator, bool> 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<Unordered_set(Named_object*)::iterator, bool> 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<std::string, Package*>::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<Named_object*>& sorted_exports) |
| { |
| for (std::vector<Named_object*>::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<Named_object*>& sorted_exports) |
| { |
| // Iterate through the exported objects and traverse any types encountered. |
| for (std::vector<Named_object*>::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<std::string, Package*>& packages, |
| const std::map<std::string, Package*>& 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<Named_object*> 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<Named_object*> 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<Named_object*>::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<Type_refs::iterator, bool> 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<Named_object*>& sorted_exports) |
| { |
| // Assign indexes to all the exported types. |
| for (std::vector<Named_object*>::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<const Type*> 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 Type *>::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<std::string, Package*>& packages) |
| { |
| // Sort for consistent output. |
| std::vector<Package*> out; |
| for (std::map<std::string, Package*>::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<Package*>::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<std::string, Package*>& a, |
| const std::pair<std::string, Package*>& b) |
| { |
| return a.first < b.first; |
| } |
| |
| // Write out the imported packages. |
| |
| void |
| Export::write_imports(const std::map<std::string, Package*>& imports, |
| const Unordered_set(const Package*)& all_imports) |
| { |
| // Sort the imports for more consistent output. |
| Unordered_set(const Package*) seen; |
| std::vector<std::pair<std::string, Package*> > sorted_imports; |
| for (std::map<std::string, Package*>::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<std::pair<std::string, Package*> >::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<const Package*> 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 Package*>::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<unsigned> 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<std::string, unsigned>& 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<std::string, unsigned>::const_iterator srcit = |
| init_idx.find(ii->init_name()); |
| go_assert(srcit != init_idx.end()); |
| unsigned src = srcit->second; |
| for (std::set<std::string>::const_iterator pci = ii->precursors().begin(); |
| pci != ii->precursors().end(); |
| ++pci) |
| { |
| std::map<std::string, unsigned>::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<std::string, unsigned> 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<int, std::vector<std::string> > 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<std::string> 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<std::string>& fcns_at_level = it->second; |
| for (std::vector<std::string>::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<std::string> fcns_at_lm1 = it2->second; |
| for (std::vector<std::string>::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<unsigned>& successors = ki->second; |
| for (std::set<unsigned>::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<const Type*> types(static_cast<size_t>(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<size_t> type_sizes(static_cast<size_t>(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<unsigned long>(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, "<type %d>", 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, "<type %d>", 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<Type_refs::iterator, bool> 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<const Temporary_statement*, unsigned int> val(temp, ret); |
| std::pair<Unordered_map(const Temporary_statement*, unsigned int)::iterator, |
| bool> 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<const Unnamed_label*, unsigned int> val(label, next); |
| std::pair<Unordered_map(const Unnamed_label*, unsigned int)::iterator, |
| bool> 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; |
| } |
| } |