xref: /llvm-project/clang/lib/Basic/Module.cpp (revision f4754ea0ed7ddc35042bacbc47d661bfe660f132)
1 //===- Module.cpp - Describe a module -------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines the Module class, which describes a module in the source
10 // code.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/Basic/Module.h"
15 #include "clang/Basic/CharInfo.h"
16 #include "clang/Basic/FileManager.h"
17 #include "clang/Basic/LangOptions.h"
18 #include "clang/Basic/SourceLocation.h"
19 #include "clang/Basic/TargetInfo.h"
20 #include "llvm/ADT/ArrayRef.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/ADT/StringMap.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/ADT/StringSwitch.h"
25 #include "llvm/Support/Compiler.h"
26 #include "llvm/Support/ErrorHandling.h"
27 #include "llvm/Support/raw_ostream.h"
28 #include <algorithm>
29 #include <cassert>
30 #include <functional>
31 #include <string>
32 #include <utility>
33 #include <vector>
34 
35 using namespace clang;
36 
37 Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent,
38                bool IsFramework, bool IsExplicit, unsigned VisibilityID)
39     : Name(Name), DefinitionLoc(DefinitionLoc), Parent(Parent),
40       VisibilityID(VisibilityID), IsMissingRequirement(false),
41       HasIncompatibleModuleFile(false), IsAvailable(true),
42       IsFromModuleFile(false), IsFramework(IsFramework), IsExplicit(IsExplicit),
43       IsSystem(false), IsExternC(false), IsInferred(false),
44       InferSubmodules(false), InferExplicitSubmodules(false),
45       InferExportWildcard(false), ConfigMacrosExhaustive(false),
46       NoUndeclaredIncludes(false), ModuleMapIsPrivate(false),
47       HasUmbrellaDir(false), NameVisibility(Hidden) {
48   if (Parent) {
49     if (!Parent->isAvailable())
50       IsAvailable = false;
51     if (Parent->IsSystem)
52       IsSystem = true;
53     if (Parent->IsExternC)
54       IsExternC = true;
55     if (Parent->NoUndeclaredIncludes)
56       NoUndeclaredIncludes = true;
57     if (Parent->ModuleMapIsPrivate)
58       ModuleMapIsPrivate = true;
59     IsMissingRequirement = Parent->IsMissingRequirement;
60 
61     Parent->SubModuleIndex[Name] = Parent->SubModules.size();
62     Parent->SubModules.push_back(this);
63   }
64 }
65 
66 Module::~Module() {
67   for (submodule_iterator I = submodule_begin(), IEnd = submodule_end();
68        I != IEnd; ++I) {
69     delete *I;
70   }
71 }
72 
73 static bool isPlatformEnvironment(const TargetInfo &Target, StringRef Feature) {
74   StringRef Platform = Target.getPlatformName();
75   StringRef Env = Target.getTriple().getEnvironmentName();
76 
77   // Attempt to match platform and environment.
78   if (Platform == Feature || Target.getTriple().getOSName() == Feature ||
79       Env == Feature)
80     return true;
81 
82   auto CmpPlatformEnv = [](StringRef LHS, StringRef RHS) {
83     auto Pos = LHS.find("-");
84     if (Pos == StringRef::npos)
85       return false;
86     SmallString<128> NewLHS = LHS.slice(0, Pos);
87     NewLHS += LHS.slice(Pos+1, LHS.size());
88     return NewLHS == RHS;
89   };
90 
91   SmallString<128> PlatformEnv = Target.getTriple().getOSAndEnvironmentName();
92   // Darwin has different but equivalent variants for simulators, example:
93   //   1. x86_64-apple-ios-simulator
94   //   2. x86_64-apple-iossimulator
95   // where both are valid examples of the same platform+environment but in the
96   // variant (2) the simulator is hardcoded as part of the platform name. Both
97   // forms above should match for "iossimulator" requirement.
98   if (Target.getTriple().isOSDarwin() && PlatformEnv.endswith("simulator"))
99     return PlatformEnv == Feature || CmpPlatformEnv(PlatformEnv, Feature);
100 
101   return PlatformEnv == Feature;
102 }
103 
104 /// Determine whether a translation unit built using the current
105 /// language options has the given feature.
106 static bool hasFeature(StringRef Feature, const LangOptions &LangOpts,
107                        const TargetInfo &Target) {
108   bool HasFeature = llvm::StringSwitch<bool>(Feature)
109                         .Case("altivec", LangOpts.AltiVec)
110                         .Case("blocks", LangOpts.Blocks)
111                         .Case("coroutines", LangOpts.Coroutines)
112                         .Case("cplusplus", LangOpts.CPlusPlus)
113                         .Case("cplusplus11", LangOpts.CPlusPlus11)
114                         .Case("cplusplus14", LangOpts.CPlusPlus14)
115                         .Case("cplusplus17", LangOpts.CPlusPlus17)
116                         .Case("c99", LangOpts.C99)
117                         .Case("c11", LangOpts.C11)
118                         .Case("c17", LangOpts.C17)
119                         .Case("freestanding", LangOpts.Freestanding)
120                         .Case("gnuinlineasm", LangOpts.GNUAsm)
121                         .Case("objc", LangOpts.ObjC)
122                         .Case("objc_arc", LangOpts.ObjCAutoRefCount)
123                         .Case("opencl", LangOpts.OpenCL)
124                         .Case("tls", Target.isTLSSupported())
125                         .Case("zvector", LangOpts.ZVector)
126                         .Default(Target.hasFeature(Feature) ||
127                                  isPlatformEnvironment(Target, Feature));
128   if (!HasFeature)
129     HasFeature = std::find(LangOpts.ModuleFeatures.begin(),
130                            LangOpts.ModuleFeatures.end(),
131                            Feature) != LangOpts.ModuleFeatures.end();
132   return HasFeature;
133 }
134 
135 bool Module::isAvailable(const LangOptions &LangOpts, const TargetInfo &Target,
136                          Requirement &Req,
137                          UnresolvedHeaderDirective &MissingHeader,
138                          Module *&ShadowingModule) const {
139   if (IsAvailable)
140     return true;
141 
142   for (const Module *Current = this; Current; Current = Current->Parent) {
143     if (Current->ShadowingModule) {
144       ShadowingModule = Current->ShadowingModule;
145       return false;
146     }
147     for (unsigned I = 0, N = Current->Requirements.size(); I != N; ++I) {
148       if (hasFeature(Current->Requirements[I].first, LangOpts, Target) !=
149               Current->Requirements[I].second) {
150         Req = Current->Requirements[I];
151         return false;
152       }
153     }
154     if (!Current->MissingHeaders.empty()) {
155       MissingHeader = Current->MissingHeaders.front();
156       return false;
157     }
158   }
159 
160   llvm_unreachable("could not find a reason why module is unavailable");
161 }
162 
163 bool Module::isSubModuleOf(const Module *Other) const {
164   const Module *This = this;
165   do {
166     if (This == Other)
167       return true;
168 
169     This = This->Parent;
170   } while (This);
171 
172   return false;
173 }
174 
175 const Module *Module::getTopLevelModule() const {
176   const Module *Result = this;
177   while (Result->Parent)
178     Result = Result->Parent;
179 
180   return Result;
181 }
182 
183 static StringRef getModuleNameFromComponent(
184     const std::pair<std::string, SourceLocation> &IdComponent) {
185   return IdComponent.first;
186 }
187 
188 static StringRef getModuleNameFromComponent(StringRef R) { return R; }
189 
190 template<typename InputIter>
191 static void printModuleId(raw_ostream &OS, InputIter Begin, InputIter End,
192                           bool AllowStringLiterals = true) {
193   for (InputIter It = Begin; It != End; ++It) {
194     if (It != Begin)
195       OS << ".";
196 
197     StringRef Name = getModuleNameFromComponent(*It);
198     if (!AllowStringLiterals || isValidIdentifier(Name))
199       OS << Name;
200     else {
201       OS << '"';
202       OS.write_escaped(Name);
203       OS << '"';
204     }
205   }
206 }
207 
208 template<typename Container>
209 static void printModuleId(raw_ostream &OS, const Container &C) {
210   return printModuleId(OS, C.begin(), C.end());
211 }
212 
213 std::string Module::getFullModuleName(bool AllowStringLiterals) const {
214   SmallVector<StringRef, 2> Names;
215 
216   // Build up the set of module names (from innermost to outermost).
217   for (const Module *M = this; M; M = M->Parent)
218     Names.push_back(M->Name);
219 
220   std::string Result;
221 
222   llvm::raw_string_ostream Out(Result);
223   printModuleId(Out, Names.rbegin(), Names.rend(), AllowStringLiterals);
224   Out.flush();
225 
226   return Result;
227 }
228 
229 bool Module::fullModuleNameIs(ArrayRef<StringRef> nameParts) const {
230   for (const Module *M = this; M; M = M->Parent) {
231     if (nameParts.empty() || M->Name != nameParts.back())
232       return false;
233     nameParts = nameParts.drop_back();
234   }
235   return nameParts.empty();
236 }
237 
238 Module::DirectoryName Module::getUmbrellaDir() const {
239   if (Header U = getUmbrellaHeader())
240     return {"", U.Entry->getDir()};
241 
242   return {UmbrellaAsWritten, static_cast<const DirectoryEntry *>(Umbrella)};
243 }
244 
245 void Module::addTopHeader(const FileEntry *File) {
246   assert(File);
247   TopHeaders.insert(File);
248 }
249 
250 ArrayRef<const FileEntry *> Module::getTopHeaders(FileManager &FileMgr) {
251   if (!TopHeaderNames.empty()) {
252     for (std::vector<std::string>::iterator
253            I = TopHeaderNames.begin(), E = TopHeaderNames.end(); I != E; ++I) {
254       if (auto FE = FileMgr.getFile(*I))
255         TopHeaders.insert(*FE);
256     }
257     TopHeaderNames.clear();
258   }
259 
260   return llvm::makeArrayRef(TopHeaders.begin(), TopHeaders.end());
261 }
262 
263 bool Module::directlyUses(const Module *Requested) const {
264   auto *Top = getTopLevelModule();
265 
266   // A top-level module implicitly uses itself.
267   if (Requested->isSubModuleOf(Top))
268     return true;
269 
270   for (auto *Use : Top->DirectUses)
271     if (Requested->isSubModuleOf(Use))
272       return true;
273 
274   // Anyone is allowed to use our builtin stddef.h and its accompanying module.
275   if (!Requested->Parent && Requested->Name == "_Builtin_stddef_max_align_t")
276     return true;
277 
278   return false;
279 }
280 
281 void Module::addRequirement(StringRef Feature, bool RequiredState,
282                             const LangOptions &LangOpts,
283                             const TargetInfo &Target) {
284   Requirements.push_back(Requirement(std::string(Feature), RequiredState));
285 
286   // If this feature is currently available, we're done.
287   if (hasFeature(Feature, LangOpts, Target) == RequiredState)
288     return;
289 
290   markUnavailable(/*MissingRequirement*/true);
291 }
292 
293 void Module::markUnavailable(bool MissingRequirement) {
294   auto needUpdate = [MissingRequirement](Module *M) {
295     return M->IsAvailable || (!M->IsMissingRequirement && MissingRequirement);
296   };
297 
298   if (!needUpdate(this))
299     return;
300 
301   SmallVector<Module *, 2> Stack;
302   Stack.push_back(this);
303   while (!Stack.empty()) {
304     Module *Current = Stack.back();
305     Stack.pop_back();
306 
307     if (!needUpdate(Current))
308       continue;
309 
310     Current->IsAvailable = false;
311     Current->IsMissingRequirement |= MissingRequirement;
312     for (submodule_iterator Sub = Current->submodule_begin(),
313                          SubEnd = Current->submodule_end();
314          Sub != SubEnd; ++Sub) {
315       if (needUpdate(*Sub))
316         Stack.push_back(*Sub);
317     }
318   }
319 }
320 
321 Module *Module::findSubmodule(StringRef Name) const {
322   llvm::StringMap<unsigned>::const_iterator Pos = SubModuleIndex.find(Name);
323   if (Pos == SubModuleIndex.end())
324     return nullptr;
325 
326   return SubModules[Pos->getValue()];
327 }
328 
329 Module *Module::findOrInferSubmodule(StringRef Name) {
330   llvm::StringMap<unsigned>::const_iterator Pos = SubModuleIndex.find(Name);
331   if (Pos != SubModuleIndex.end())
332     return SubModules[Pos->getValue()];
333   if (!InferSubmodules)
334     return nullptr;
335   Module *Result = new Module(Name, SourceLocation(), this, false, InferExplicitSubmodules, 0);
336   Result->InferExplicitSubmodules = InferExplicitSubmodules;
337   Result->InferSubmodules = InferSubmodules;
338   Result->InferExportWildcard = InferExportWildcard;
339   if (Result->InferExportWildcard)
340     Result->Exports.push_back(Module::ExportDecl(nullptr, true));
341   return Result;
342 }
343 
344 void Module::getExportedModules(SmallVectorImpl<Module *> &Exported) const {
345   // All non-explicit submodules are exported.
346   for (std::vector<Module *>::const_iterator I = SubModules.begin(),
347                                              E = SubModules.end();
348        I != E; ++I) {
349     Module *Mod = *I;
350     if (!Mod->IsExplicit)
351       Exported.push_back(Mod);
352   }
353 
354   // Find re-exported modules by filtering the list of imported modules.
355   bool AnyWildcard = false;
356   bool UnrestrictedWildcard = false;
357   SmallVector<Module *, 4> WildcardRestrictions;
358   for (unsigned I = 0, N = Exports.size(); I != N; ++I) {
359     Module *Mod = Exports[I].getPointer();
360     if (!Exports[I].getInt()) {
361       // Export a named module directly; no wildcards involved.
362       Exported.push_back(Mod);
363 
364       continue;
365     }
366 
367     // Wildcard export: export all of the imported modules that match
368     // the given pattern.
369     AnyWildcard = true;
370     if (UnrestrictedWildcard)
371       continue;
372 
373     if (Module *Restriction = Exports[I].getPointer())
374       WildcardRestrictions.push_back(Restriction);
375     else {
376       WildcardRestrictions.clear();
377       UnrestrictedWildcard = true;
378     }
379   }
380 
381   // If there were any wildcards, push any imported modules that were
382   // re-exported by the wildcard restriction.
383   if (!AnyWildcard)
384     return;
385 
386   for (unsigned I = 0, N = Imports.size(); I != N; ++I) {
387     Module *Mod = Imports[I];
388     bool Acceptable = UnrestrictedWildcard;
389     if (!Acceptable) {
390       // Check whether this module meets one of the restrictions.
391       for (unsigned R = 0, NR = WildcardRestrictions.size(); R != NR; ++R) {
392         Module *Restriction = WildcardRestrictions[R];
393         if (Mod == Restriction || Mod->isSubModuleOf(Restriction)) {
394           Acceptable = true;
395           break;
396         }
397       }
398     }
399 
400     if (!Acceptable)
401       continue;
402 
403     Exported.push_back(Mod);
404   }
405 }
406 
407 void Module::buildVisibleModulesCache() const {
408   assert(VisibleModulesCache.empty() && "cache does not need building");
409 
410   // This module is visible to itself.
411   VisibleModulesCache.insert(this);
412 
413   // Every imported module is visible.
414   SmallVector<Module *, 16> Stack(Imports.begin(), Imports.end());
415   while (!Stack.empty()) {
416     Module *CurrModule = Stack.pop_back_val();
417 
418     // Every module transitively exported by an imported module is visible.
419     if (VisibleModulesCache.insert(CurrModule).second)
420       CurrModule->getExportedModules(Stack);
421   }
422 }
423 
424 void Module::print(raw_ostream &OS, unsigned Indent) const {
425   OS.indent(Indent);
426   if (IsFramework)
427     OS << "framework ";
428   if (IsExplicit)
429     OS << "explicit ";
430   OS << "module ";
431   printModuleId(OS, &Name, &Name + 1);
432 
433   if (IsSystem || IsExternC) {
434     OS.indent(Indent + 2);
435     if (IsSystem)
436       OS << " [system]";
437     if (IsExternC)
438       OS << " [extern_c]";
439   }
440 
441   OS << " {\n";
442 
443   if (!Requirements.empty()) {
444     OS.indent(Indent + 2);
445     OS << "requires ";
446     for (unsigned I = 0, N = Requirements.size(); I != N; ++I) {
447       if (I)
448         OS << ", ";
449       if (!Requirements[I].second)
450         OS << "!";
451       OS << Requirements[I].first;
452     }
453     OS << "\n";
454   }
455 
456   if (Header H = getUmbrellaHeader()) {
457     OS.indent(Indent + 2);
458     OS << "umbrella header \"";
459     OS.write_escaped(H.NameAsWritten);
460     OS << "\"\n";
461   } else if (DirectoryName D = getUmbrellaDir()) {
462     OS.indent(Indent + 2);
463     OS << "umbrella \"";
464     OS.write_escaped(D.NameAsWritten);
465     OS << "\"\n";
466   }
467 
468   if (!ConfigMacros.empty() || ConfigMacrosExhaustive) {
469     OS.indent(Indent + 2);
470     OS << "config_macros ";
471     if (ConfigMacrosExhaustive)
472       OS << "[exhaustive]";
473     for (unsigned I = 0, N = ConfigMacros.size(); I != N; ++I) {
474       if (I)
475         OS << ", ";
476       OS << ConfigMacros[I];
477     }
478     OS << "\n";
479   }
480 
481   struct {
482     StringRef Prefix;
483     HeaderKind Kind;
484   } Kinds[] = {{"", HK_Normal},
485                {"textual ", HK_Textual},
486                {"private ", HK_Private},
487                {"private textual ", HK_PrivateTextual},
488                {"exclude ", HK_Excluded}};
489 
490   for (auto &K : Kinds) {
491     assert(&K == &Kinds[K.Kind] && "kinds in wrong order");
492     for (auto &H : Headers[K.Kind]) {
493       OS.indent(Indent + 2);
494       OS << K.Prefix << "header \"";
495       OS.write_escaped(H.NameAsWritten);
496       OS << "\" { size " << H.Entry->getSize()
497          << " mtime " << H.Entry->getModificationTime() << " }\n";
498     }
499   }
500   for (auto *Unresolved : {&UnresolvedHeaders, &MissingHeaders}) {
501     for (auto &U : *Unresolved) {
502       OS.indent(Indent + 2);
503       OS << Kinds[U.Kind].Prefix << "header \"";
504       OS.write_escaped(U.FileName);
505       OS << "\"";
506       if (U.Size || U.ModTime) {
507         OS << " {";
508         if (U.Size)
509           OS << " size " << *U.Size;
510         if (U.ModTime)
511           OS << " mtime " << *U.ModTime;
512         OS << " }";
513       }
514       OS << "\n";
515     }
516   }
517 
518   if (!ExportAsModule.empty()) {
519     OS.indent(Indent + 2);
520     OS << "export_as" << ExportAsModule << "\n";
521   }
522 
523   for (submodule_const_iterator MI = submodule_begin(), MIEnd = submodule_end();
524        MI != MIEnd; ++MI)
525     // Print inferred subframework modules so that we don't need to re-infer
526     // them (requires expensive directory iteration + stat calls) when we build
527     // the module. Regular inferred submodules are OK, as we need to look at all
528     // those header files anyway.
529     if (!(*MI)->IsInferred || (*MI)->IsFramework)
530       (*MI)->print(OS, Indent + 2);
531 
532   for (unsigned I = 0, N = Exports.size(); I != N; ++I) {
533     OS.indent(Indent + 2);
534     OS << "export ";
535     if (Module *Restriction = Exports[I].getPointer()) {
536       OS << Restriction->getFullModuleName(true);
537       if (Exports[I].getInt())
538         OS << ".*";
539     } else {
540       OS << "*";
541     }
542     OS << "\n";
543   }
544 
545   for (unsigned I = 0, N = UnresolvedExports.size(); I != N; ++I) {
546     OS.indent(Indent + 2);
547     OS << "export ";
548     printModuleId(OS, UnresolvedExports[I].Id);
549     if (UnresolvedExports[I].Wildcard)
550       OS << (UnresolvedExports[I].Id.empty() ? "*" : ".*");
551     OS << "\n";
552   }
553 
554   for (unsigned I = 0, N = DirectUses.size(); I != N; ++I) {
555     OS.indent(Indent + 2);
556     OS << "use ";
557     OS << DirectUses[I]->getFullModuleName(true);
558     OS << "\n";
559   }
560 
561   for (unsigned I = 0, N = UnresolvedDirectUses.size(); I != N; ++I) {
562     OS.indent(Indent + 2);
563     OS << "use ";
564     printModuleId(OS, UnresolvedDirectUses[I]);
565     OS << "\n";
566   }
567 
568   for (unsigned I = 0, N = LinkLibraries.size(); I != N; ++I) {
569     OS.indent(Indent + 2);
570     OS << "link ";
571     if (LinkLibraries[I].IsFramework)
572       OS << "framework ";
573     OS << "\"";
574     OS.write_escaped(LinkLibraries[I].Library);
575     OS << "\"";
576   }
577 
578   for (unsigned I = 0, N = UnresolvedConflicts.size(); I != N; ++I) {
579     OS.indent(Indent + 2);
580     OS << "conflict ";
581     printModuleId(OS, UnresolvedConflicts[I].Id);
582     OS << ", \"";
583     OS.write_escaped(UnresolvedConflicts[I].Message);
584     OS << "\"\n";
585   }
586 
587   for (unsigned I = 0, N = Conflicts.size(); I != N; ++I) {
588     OS.indent(Indent + 2);
589     OS << "conflict ";
590     OS << Conflicts[I].Other->getFullModuleName(true);
591     OS << ", \"";
592     OS.write_escaped(Conflicts[I].Message);
593     OS << "\"\n";
594   }
595 
596   if (InferSubmodules) {
597     OS.indent(Indent + 2);
598     if (InferExplicitSubmodules)
599       OS << "explicit ";
600     OS << "module * {\n";
601     if (InferExportWildcard) {
602       OS.indent(Indent + 4);
603       OS << "export *\n";
604     }
605     OS.indent(Indent + 2);
606     OS << "}\n";
607   }
608 
609   OS.indent(Indent);
610   OS << "}\n";
611 }
612 
613 LLVM_DUMP_METHOD void Module::dump() const {
614   print(llvm::errs());
615 }
616 
617 void VisibleModuleSet::setVisible(Module *M, SourceLocation Loc,
618                                   VisibleCallback Vis, ConflictCallback Cb) {
619   assert(Loc.isValid() && "setVisible expects a valid import location");
620   if (isVisible(M))
621     return;
622 
623   ++Generation;
624 
625   struct Visiting {
626     Module *M;
627     Visiting *ExportedBy;
628   };
629 
630   std::function<void(Visiting)> VisitModule = [&](Visiting V) {
631     // Nothing to do for a module that's already visible.
632     unsigned ID = V.M->getVisibilityID();
633     if (ImportLocs.size() <= ID)
634       ImportLocs.resize(ID + 1);
635     else if (ImportLocs[ID].isValid())
636       return;
637 
638     ImportLocs[ID] = Loc;
639     Vis(M);
640 
641     // Make any exported modules visible.
642     SmallVector<Module *, 16> Exports;
643     V.M->getExportedModules(Exports);
644     for (Module *E : Exports) {
645       // Don't recurse to unavailable submodules.
646       if (E->isAvailable())
647         VisitModule({E, &V});
648     }
649 
650     for (auto &C : V.M->Conflicts) {
651       if (isVisible(C.Other)) {
652         llvm::SmallVector<Module*, 8> Path;
653         for (Visiting *I = &V; I; I = I->ExportedBy)
654           Path.push_back(I->M);
655         Cb(Path, C.Other, C.Message);
656       }
657     }
658   };
659   VisitModule({M, nullptr});
660 }
661 
662 ASTSourceDescriptor::ASTSourceDescriptor(Module &M)
663     : Signature(M.Signature), ClangModule(&M) {
664   if (M.Directory)
665     Path = M.Directory->getName();
666   if (auto *File = M.getASTFile())
667     ASTFile = File->getName();
668 }
669 
670 std::string ASTSourceDescriptor::getModuleName() const {
671   if (ClangModule)
672     return ClangModule->Name;
673   else
674     return std::string(PCHModuleName);
675 }
676