this repo has no description
at trunk 154 lines 5.8 kB view raw
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 2#include "mro.h" 3 4#include "runtime.h" 5 6namespace py { 7 8// Fills merge_lists with the MROs of the parents, followed by the parents list. 9static word populateMergeLists(Thread* thread, const MutableTuple& merge_lists, 10 const Tuple& parents, 11 Vector<word>* merge_list_indices /* out */) { 12 word num_parents = parents.length(); 13 DCHECK(merge_lists.length() == num_parents + 1, 14 "merge the linearizations of each parent with the parent list"); 15 HandleScope scope(thread); 16 word new_mro_length = 2; // C + ... + object 17 for (word i = 0; i < num_parents; i++) { 18 Type parent_class(&scope, parents.at(i)); // B_i 19 Tuple parent_mro(&scope, parent_class.mro()); // L[B_i] 20 21 new_mro_length += parent_mro.length(); 22 merge_lists.atPut(i, *parent_mro); 23 merge_list_indices->push_back(0); 24 } 25 merge_lists.atPut(num_parents, *parents); // B_1 B_2 ... B_n 26 merge_list_indices->push_back(0); 27 return new_mro_length - num_parents; // all parent MROs end with object 28} 29 30// Returns true if there is an i such that mro->at(i) == cls, i > head_idx. 31static bool tailContains(const Tuple& mro, const Object& cls, word head_idx) { 32 for (word i = head_idx + 1, length = mro.length(); i < length; i++) { 33 if (mro.at(i) == *cls) { 34 return true; 35 } 36 } 37 return false; 38} 39 40// Looks for a head class in merge_lists (i.e. the class indicated by the 41// corresponding index in merge_list_indices) which does not appear in any of 42// the merge_lists at a position *after* the head class of that list. 43static RawObject findNext(Thread* thread, const Tuple& merge_lists, 44 const Vector<word>& merge_list_indices) { 45 HandleScope scope(thread); 46 Object candidate_head(&scope, Unbound::object()); 47 for (word i = 0, length = merge_lists.length(); i < length; i++) { 48 word cur_idx = merge_list_indices[i]; 49 Tuple cur_mro(&scope, merge_lists.at(i)); 50 51 if (cur_idx >= cur_mro.length()) { 52 continue; 53 } 54 55 candidate_head = cur_mro.at(cur_idx); 56 for (word j = 0; j < length; j++) { 57 if (j == i) { 58 continue; 59 } 60 Tuple other_mro(&scope, merge_lists.at(j)); 61 if (tailContains(other_mro, candidate_head, merge_list_indices[j])) { 62 candidate_head = Error::notFound(); 63 break; 64 } 65 } 66 if (!candidate_head.isError()) { 67 return *candidate_head; 68 } 69 } 70 return Error::notFound(); 71} 72 73RawObject computeMro(Thread* thread, const Type& type) { 74 HandleScope scope(thread); 75 Runtime* runtime = thread->runtime(); 76 77 Tuple parents(&scope, type.bases()); 78 word num_parents = parents.length(); 79 DCHECK(num_parents > 0, "must have at least 1 parent"); 80 81 Vector<word> merge_list_indices; 82 word merge_list_length = num_parents + 1; 83 merge_list_indices.reserve(merge_list_length); 84 MutableTuple merge_lists(&scope, runtime->newMutableTuple(merge_list_length)); 85 word new_mro_length = 86 populateMergeLists(thread, merge_lists, parents, &merge_list_indices); 87 DCHECK(merge_list_indices.size() == merge_list_length, 88 "expected %ld indices, got %ld", merge_list_length, 89 merge_list_indices.size()); 90 91 // The length of new_mro will be longer than necessary when there is overlap 92 // between the MROs of the parents. 93 MutableTuple new_mro(&scope, runtime->newMutableTuple(new_mro_length)); 94 95 word next_idx = 0; 96 new_mro.atPut(next_idx, *type); 97 next_idx++; 98 99 // To compute the MRO, repeatedly look for a head of one or more 100 // MROs, which is not in the tail of any other MROs, and consume 101 // all matching heads. This is O(n^2) as implemented, but so is 102 // CPython's implementation, so we can rest assured no real program 103 // is going to cause a major problem here. 104 RawObject next_head = Error::notFound(); 105 while (!(next_head = findNext(thread, merge_lists, merge_list_indices)) 106 .isError()) { 107 Type next_head_cls(&scope, next_head); 108 for (word i = 0; i < merge_list_length; i++) { 109 auto& cur_idx = merge_list_indices[i]; 110 Tuple cur_mro(&scope, merge_lists.at(i)); 111 if (cur_idx < cur_mro.length() && cur_mro.at(cur_idx) == *next_head_cls) { 112 cur_idx++; 113 } 114 } 115 new_mro.atPut(next_idx, *next_head_cls); 116 next_idx++; 117 } 118 119 for (word i = 0; i < merge_list_length; i++) { 120 if (merge_list_indices[i] != Tuple::cast(merge_lists.at(i)).length()) { 121 MutableTuple names(&scope, runtime->newMutableTuple(num_parents)); 122 Type parent(&scope, runtime->typeAt(LayoutId::kNoneType)); 123 for (word j = 0; j < num_parents; j++) { 124 parent = parents.at(j); 125 names.atPut(j, parent.name()); 126 } 127 Str sep(&scope, SmallStr::fromCStr(", ")); 128 Tuple names_immutable(&scope, names.becomeImmutable()); 129 Object bases(&scope, 130 runtime->strJoin(thread, sep, names_immutable, num_parents)); 131 return thread->raiseWithFmt(LayoutId::kTypeError, 132 "Cannot create a consistent method " 133 "resolution order (MRO) for bases %S", 134 &bases); 135 } 136 } 137 138 for (word i = 0, length = parents.length(); i < length; i++) { 139 for (word j = i + 1; j < length; j++) { 140 if (parents.at(i) == parents.at(j)) { 141 Type superclass_type(&scope, parents.at(i)); 142 Str superclass_type_name(&scope, superclass_type.name()); 143 return thread->raiseWithFmt(LayoutId::kTypeError, 144 "duplicate base class %S", 145 &superclass_type_name); 146 } 147 } 148 } 149 150 // Copy the mro to an array of exact size. (new_mro_length is an upper bound). 151 return runtime->tupleSubseq(thread, new_mro, 0, next_idx); 152} 153 154} // namespace py