1 /**
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #ifndef PANDA_RUNTIME_VTABLE_BUILDER_STANDARD_INL_H
16 #define PANDA_RUNTIME_VTABLE_BUILDER_STANDARD_INL_H
17
18 #include "runtime/include/vtable_builder_standard.h"
19 #include "runtime/include/vtable_builder_base-inl.h"
20
21 namespace ark {
22
23 template <class OverridePred>
IsOverriddenBy(Method::ProtoId const & base,Method::ProtoId const & derv)24 bool StandardVTableBuilder<OverridePred>::IsOverriddenBy(Method::ProtoId const &base, Method::ProtoId const &derv)
25 {
26 if (&base.GetPandaFile() == &derv.GetPandaFile() && base.GetEntityId() == derv.GetEntityId()) {
27 return true;
28 }
29 return VTableProtoIdentical()(base, derv);
30 }
31
32 template <class OverridePred>
ProcessClassMethod(const MethodInfo * info)33 bool StandardVTableBuilder<OverridePred>::ProcessClassMethod(const MethodInfo *info)
34 {
35 bool compatibleFound = false;
36
37 for (auto it = SameNameMethodInfoIterator(vtable_.Methods(), info); !it.IsEmpty(); it.Next()) {
38 auto &[itInfo, itEntry] = it.Value();
39
40 if (!itInfo->IsBase()) {
41 continue;
42 }
43 if (IsOverriddenBy(itInfo->GetProtoId(), info->GetProtoId()) && OverridePred()(itInfo, info)) {
44 if (UNLIKELY(itInfo->IsFinal())) {
45 OnVTableConflict(errorHandler_, ClassLinker::Error::OVERRIDES_FINAL, "Method overrides final method",
46 info, itInfo);
47 return false;
48 }
49 itEntry.SetCandidate(info);
50 compatibleFound = true;
51 }
52 }
53
54 if (!compatibleFound) {
55 vtable_.AddEntry(info);
56 }
57 return true;
58 }
59
60 template <class OverridePred>
ScanConflictingDefaultMethods(const MethodInfo * info)61 std::optional<MethodInfo const *> StandardVTableBuilder<OverridePred>::ScanConflictingDefaultMethods(
62 const MethodInfo *info)
63 {
64 // NOTE(vpukhov): test public flag
65 for (auto it = SameNameMethodInfoIterator(vtable_.Methods(), info); !it.IsEmpty(); it.Next()) {
66 MethodInfo const *itinfo = it.Value().second.CandidateOr(it.Value().first);
67 VTableInfo::MethodEntry *itentry = &it.Value().second;
68
69 if (!IsOverriddenBy(info->GetProtoId(), itinfo->GetProtoId())) {
70 continue;
71 }
72 if (!itinfo->IsInterfaceMethod()) {
73 // there is at least one implementing method, so skip default method
74 return std::nullopt;
75 }
76 if (!itinfo->IsBase()) {
77 break;
78 }
79 // NOTE(vpukhov): that method is possibly a conflict, but we traverse the whole itable to handle such cases
80 itentry->SetCandidate(info);
81 }
82
83 for (auto it = SameNameMethodInfoIterator(vtable_.CopiedMethods(), info); !it.IsEmpty(); it.Next()) {
84 MethodInfo const *itinfo = it.Value().first;
85 if (!IsOverriddenBy(info->GetProtoId(), itinfo->GetProtoId())) {
86 continue;
87 }
88 if (itinfo->GetMethod()->GetClass()->Implements(info->GetMethod()->GetClass())) {
89 // more specific compatible method is defined, so skip default method
90 return std::nullopt;
91 }
92 return itinfo;
93 }
94
95 return nullptr;
96 }
97
98 // we have to guarantee that while we are iterating itable, the child interface has to be accessed before father
99 // interface. interface without inheritance has no limit.
100 template <class OverridePred>
IsMaxSpecificInterfaceMethod(const Class * iface,const Method & method,size_t startindex,const ITable & itable)101 bool StandardVTableBuilder<OverridePred>::IsMaxSpecificInterfaceMethod(const Class *iface, const Method &method,
102 size_t startindex, const ITable &itable)
103 {
104 for (size_t j = startindex; j < itable.Size(); j++) {
105 auto currentIface = itable[j].GetInterface();
106 if (!currentIface->Implements(iface)) {
107 continue;
108 }
109 for (auto &curmethod : currentIface->GetVirtualMethods()) {
110 if (method.GetName() != curmethod.GetName()) {
111 continue;
112 }
113 if (IsOverriddenBy(method.GetProtoId(), curmethod.GetProtoId())) {
114 return false;
115 }
116 }
117 }
118 return true;
119 }
120
121 template <class OverridePred>
ProcessDefaultMethod(ITable itable,size_t itableIdx,MethodInfo * methodInfo)122 bool StandardVTableBuilder<OverridePred>::ProcessDefaultMethod(ITable itable, size_t itableIdx, MethodInfo *methodInfo)
123 {
124 auto skipOrConflict = ScanConflictingDefaultMethods(methodInfo);
125 if (!skipOrConflict.has_value()) {
126 return true;
127 }
128 MethodInfo const *conflict = skipOrConflict.value();
129 Class *iface = itable[itableIdx].GetInterface();
130 Method *method = methodInfo->GetMethod();
131
132 // if the default method is added for the first time, just add it.
133 if (LIKELY(conflict == nullptr)) {
134 VTableInfo::CopiedMethodEntry *entry = &vtable_.AddCopiedEntry(methodInfo);
135 if (!IsMaxSpecificInterfaceMethod(iface, *method, itableIdx + 1, itable)) {
136 entry->SetStatus(CopiedMethod::Status::ABSTRACT);
137 }
138 return true;
139 }
140
141 VTableInfo::CopiedMethodEntry *entry = &vtable_.CopiedMethods().find(conflict)->second;
142
143 // Use the following algorithm to judge whether we have to replace existing DEFAULT METHOD.
144 // 1. if existing default method is ICCE, just skip.
145 // 2. if new method is not max-specific method, just skip.
146 // existing default method can be AME or not, has no effect on final result. its okay.
147 // 3. if new method is max-specific method, check whether existing default method is AME
148 // 3.1 if no, set ICCE flag for existing method
149 // 3.2 if yes, replace existing method with new method(new method becomes a candidate)
150 if (entry->GetStatus() == CopiedMethod::Status::CONFLICT) {
151 return true;
152 }
153 if (!IsMaxSpecificInterfaceMethod(iface, *method, itableIdx + 1, itable)) {
154 return true;
155 }
156 if (entry->GetStatus() != CopiedMethod::Status::ABSTRACT) {
157 entry->SetStatus(CopiedMethod::Status::CONFLICT);
158 return true;
159 }
160 entry->SetStatus(CopiedMethod::Status::ORDINARY);
161 vtable_.UpdateCopiedEntry(conflict, methodInfo);
162 return true;
163 }
164
165 } // namespace ark
166
167 #endif // PANDA_RUNTIME_VTABLE_BUILDER_STANDARD_INL_H
168