PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Boo.Lang.Compiler/TypeSystem/NameResolutionService.cs

https://github.com/boo/boo-lang
C# | 646 lines | 529 code | 84 blank | 33 comment | 148 complexity | ca624caddb48a82830853c332783a135 MD5 | raw file
Possible License(s): GPL-2.0
  1. #region license
  2. // Copyright (c) 2004, Rodrigo B. de Oliveira (rbo@acm.org)
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without modification,
  6. // are permitted provided that the following conditions are met:
  7. //
  8. // * Redistributions of source code must retain the above copyright notice,
  9. // this list of conditions and the following disclaimer.
  10. // * Redistributions in binary form must reproduce the above copyright notice,
  11. // this list of conditions and the following disclaimer in the documentation
  12. // and/or other materials provided with the distribution.
  13. // * Neither the name of Rodrigo B. de Oliveira nor the names of its
  14. // contributors may be used to endorse or promote products derived from this
  15. // software without specific prior written permission.
  16. //
  17. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  18. // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  21. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  23. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  24. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  25. // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. #endregion
  28. namespace Boo.Lang.Compiler.TypeSystem
  29. {
  30. using System;
  31. using System.Collections;
  32. using Boo.Lang.Compiler.Ast;
  33. using System.Reflection;
  34. using System.Collections.Generic;
  35. public class NameResolutionService
  36. {
  37. public static readonly char[] DotArray = new char[] { '.' };
  38. protected CompilerContext _context;
  39. protected INamespace _current;
  40. protected INamespace _global = NullNamespace.Default;
  41. protected List _buffer = new List();
  42. protected List _innerBuffer = new List();
  43. public NameResolutionService(CompilerContext context)
  44. {
  45. if (null == context) throw new ArgumentNullException("context");
  46. _context = context;
  47. }
  48. public INamespace GlobalNamespace
  49. {
  50. get { return _global; }
  51. set
  52. {
  53. if (null == value)
  54. {
  55. throw new ArgumentNullException("GlobalNamespace");
  56. }
  57. _global = value;
  58. }
  59. }
  60. public void EnterNamespace(INamespace ns)
  61. {
  62. if (null == ns) throw new ArgumentNullException("ns");
  63. _current = ns;
  64. }
  65. public INamespace CurrentNamespace
  66. {
  67. get { return _current; }
  68. }
  69. public void Reset()
  70. {
  71. EnterNamespace(_global);
  72. }
  73. public void Restore(INamespace saved)
  74. {
  75. if (null == saved) throw new ArgumentNullException("saved");
  76. _current = saved;
  77. }
  78. public void LeaveNamespace()
  79. {
  80. _current = _current.ParentNamespace;
  81. }
  82. public IEntity Resolve(string name)
  83. {
  84. return Resolve(name, EntityType.Any);
  85. }
  86. public IEntity Resolve(string name, EntityType flags)
  87. {
  88. _buffer.Clear();
  89. Resolve(_buffer, name, flags);
  90. return GetEntityFromBuffer();
  91. }
  92. public bool Resolve(List targetList, string name)
  93. {
  94. return Resolve(targetList, name, EntityType.Any);
  95. }
  96. public bool Resolve(List targetList, string name, EntityType flags)
  97. {
  98. IEntity entity = _context.TypeSystemServices.ResolvePrimitive(name);
  99. if (null != entity)
  100. {
  101. targetList.Add(entity);
  102. return true;
  103. }
  104. INamespace ns = _current;
  105. while (null != ns)
  106. {
  107. if (ns.Resolve(targetList, name, flags))
  108. {
  109. return true;
  110. }
  111. ns = ns.ParentNamespace;
  112. }
  113. return false;
  114. }
  115. public IEntity ResolveExtension(INamespace ns, string name)
  116. {
  117. IType type = ns as IType;
  118. if (null == type) return null;
  119. INamespace current = _current;
  120. while (null != current)
  121. {
  122. IEntity found = ResolveExtensionForType(current, type, name);
  123. if (null != found) return found;
  124. current = current.ParentNamespace;
  125. }
  126. return null;
  127. }
  128. class IsNotExtensionOf
  129. {
  130. private IType _type;
  131. public IsNotExtensionOf(IType type)
  132. {
  133. _type = type;
  134. }
  135. public bool Match(object item)
  136. {
  137. IExtensionEnabled e = item as IExtensionEnabled;
  138. if (e == null) return true;
  139. if (!e.IsExtension) return true;
  140. IParameter[] parameters = e.GetParameters();
  141. if (parameters.Length == 0) return true;
  142. return !parameters[0].Type.IsAssignableFrom(_type);
  143. }
  144. }
  145. private IEntity ResolveExtensionForType(INamespace ns, IType type, string name)
  146. {
  147. _buffer.Clear();
  148. if (!ns.Resolve(_buffer, name, EntityType.Method|EntityType.Property)) return null;
  149. _buffer.RemoveAll(new IsNotExtensionOf(type).Match);
  150. return GetEntityFromBuffer();
  151. }
  152. public IEntity ResolveQualifiedName(string name)
  153. {
  154. _buffer.Clear();
  155. ResolveQualifiedName(_buffer, name);
  156. return GetEntityFromBuffer();
  157. }
  158. public bool ResolveQualifiedName(List targetList, string name)
  159. {
  160. return ResolveQualifiedName(targetList, name, EntityType.Any);
  161. }
  162. public bool ResolveQualifiedName(List targetList, string name, EntityType flags)
  163. {
  164. if (!IsQualifiedName(name))
  165. {
  166. return Resolve(targetList, name, flags);
  167. }
  168. string[] parts = name.Split(DotArray);
  169. string topLevel = parts[0];
  170. _innerBuffer.Clear();
  171. if (Resolve(_innerBuffer, topLevel) && 1 == _innerBuffer.Count)
  172. {
  173. INamespace ns = _innerBuffer[0] as INamespace;
  174. if (null != ns)
  175. {
  176. int last = parts.Length-1;
  177. for (int i=1; i<last; ++i)
  178. {
  179. _innerBuffer.Clear();
  180. if (!ns.Resolve(_innerBuffer, parts[i], EntityType.Any) ||
  181. 1 != _innerBuffer.Count)
  182. {
  183. return false;
  184. }
  185. ns = _innerBuffer[0] as INamespace;
  186. if (null == ns)
  187. {
  188. return false;
  189. }
  190. }
  191. return ns.Resolve(targetList, parts[last], flags);
  192. }
  193. }
  194. return false;
  195. }
  196. public void ResolveTypeReference(TypeReference node)
  197. {
  198. if (null != node.Entity) return;
  199. switch (node.NodeType)
  200. {
  201. case NodeType.ArrayTypeReference:
  202. ResolveArrayTypeReference((ArrayTypeReference) node);
  203. break;
  204. case NodeType.CallableTypeReference:
  205. //not needed? (late resolution)
  206. //ResolveCallableTypeReference((CallableTypeReference) node);
  207. break;
  208. default:
  209. ResolveSimpleTypeReference((SimpleTypeReference) node);
  210. break;
  211. }
  212. }
  213. public void ResolveArrayTypeReference(ArrayTypeReference node)
  214. {
  215. if (null != node.Entity) return;
  216. ResolveTypeReference(node.ElementType);
  217. IType elementType = TypeSystemServices.GetType(node.ElementType);
  218. if (TypeSystemServices.IsError(elementType))
  219. {
  220. node.Entity = TypeSystemServices.ErrorEntity;
  221. }
  222. else
  223. {
  224. int rank = null == node.Rank ? 1 : (int)node.Rank.Value;
  225. node.Entity = _context.TypeSystemServices.GetArrayType(elementType, rank);
  226. }
  227. }
  228. private void ResolveTypeReferenceCollection(TypeReferenceCollection collection)
  229. {
  230. foreach (TypeReference tr in collection)
  231. {
  232. ResolveTypeReference(tr);
  233. }
  234. }
  235. public void ResolveSimpleTypeReference(SimpleTypeReference node)
  236. {
  237. if (null != node.Entity) return;
  238. IEntity entity = ResolveTypeName(node);
  239. if (null == entity)
  240. {
  241. node.Entity = NameNotType(node);
  242. return;
  243. }
  244. if (EntityType.Type != entity.EntityType)
  245. {
  246. if (EntityType.Ambiguous == entity.EntityType)
  247. {
  248. entity = AmbiguousReference(node, (Ambiguous)entity);
  249. }
  250. else
  251. {
  252. entity = NameNotType(node);
  253. }
  254. }
  255. else
  256. {
  257. GenericTypeReference gtr = node as GenericTypeReference;
  258. if (null != gtr)
  259. {
  260. ResolveTypeReferenceCollection(gtr.GenericArguments);
  261. entity = ResolveGenericTypeReference(gtr, entity);
  262. }
  263. GenericTypeDefinitionReference gtdr = node as GenericTypeDefinitionReference;
  264. if (null != gtdr)
  265. {
  266. IType type = (IType)entity;
  267. if (gtdr.GenericPlaceholders != type.GenericInfo.GenericParameters.Length)
  268. {
  269. entity = GenericArgumentsCountMismatch(gtdr, type);
  270. }
  271. }
  272. node.Name = entity.FullName;
  273. }
  274. node.Entity = entity;
  275. }
  276. private IEntity ResolveTypeName(SimpleTypeReference node)
  277. {
  278. _buffer.Clear();
  279. if (IsQualifiedName(node.Name))
  280. {
  281. ResolveQualifiedName(_buffer, node.Name);
  282. }
  283. else
  284. {
  285. Resolve(_buffer, node.Name, EntityType.Type);
  286. }
  287. // Remove from the buffer types that do not match requested generity
  288. FilterGenericTypes(_buffer, node);
  289. return GetEntityFromBuffer();
  290. }
  291. public IType ResolveGenericTypeReference(GenericTypeReference gtr, IEntity definition)
  292. {
  293. ResolveTypeReferenceCollection(gtr.GenericArguments);
  294. IType[] typeArguments = GetTypes(gtr.GenericArguments);
  295. return (IType)_context.TypeSystemServices.GenericsServices.ConstructEntity(
  296. gtr, definition, typeArguments);
  297. }
  298. public IEntity ResolveGenericReferenceExpression(GenericReferenceExpression gre, IEntity definition)
  299. {
  300. ResolveTypeReferenceCollection(gre.GenericArguments);
  301. IType[] typeArguments = GetTypes(gre.GenericArguments);
  302. return _context.TypeSystemServices.GenericsServices.ConstructEntity(
  303. gre, definition, typeArguments);
  304. }
  305. private IType[] GetTypes(TypeReferenceCollection typeReferences)
  306. {
  307. return Array.ConvertAll<TypeReference, IType>(
  308. typeReferences.ToArray(),
  309. TypeSystemServices.GetType);
  310. }
  311. private void FilterGenericTypes(List types, SimpleTypeReference node)
  312. {
  313. bool genericRequested = (node is GenericTypeReference || node is GenericTypeDefinitionReference);
  314. for (int i = 0; i < types.Count; i++)
  315. {
  316. IType type = types[i] as IType;
  317. if (type == null) continue;
  318. // Remove type from list of matches if it doesn't match requested generity
  319. if (type.GenericInfo != null ^ genericRequested)
  320. {
  321. types.RemoveAt(i);
  322. i--;
  323. }
  324. }
  325. }
  326. private IEntity NameNotType(SimpleTypeReference node)
  327. {
  328. string suggestion = GetMostSimilarTypeName(node.Name);
  329. _context.Errors.Add(CompilerErrorFactory.NameNotType(node, node.ToCodeString(), suggestion));
  330. return TypeSystemServices.ErrorEntity;
  331. }
  332. private IEntity AmbiguousReference(SimpleTypeReference node, Ambiguous entity)
  333. {
  334. _context.Errors.Add(CompilerErrorFactory.AmbiguousReference(node, node.Name, entity.Entities));
  335. return TypeSystemServices.ErrorEntity;
  336. }
  337. private IEntity GenericArgumentsCountMismatch(TypeReference node, IType type)
  338. {
  339. _context.Errors.Add(CompilerErrorFactory.GenericDefinitionArgumentCount(node, type.FullName, type.GenericInfo.GenericParameters.Length));
  340. return TypeSystemServices.ErrorEntity;
  341. }
  342. public static IField ResolveField(IType type, string name)
  343. {
  344. return (IField)ResolveMember(type, name, EntityType.Field);
  345. }
  346. public static IMethod ResolveMethod(IType type, string name)
  347. {
  348. return (IMethod)ResolveMember(type, name, EntityType.Method);
  349. }
  350. public static IProperty ResolveProperty(IType type, string name)
  351. {
  352. return (IProperty)ResolveMember(type, name, EntityType.Property);
  353. }
  354. public static IEntity ResolveMember(IType type, string name, EntityType elementType)
  355. {
  356. foreach (IEntity member in type.GetMembers())
  357. {
  358. if (elementType == member.EntityType && name == member.Name)
  359. {
  360. return member;
  361. }
  362. }
  363. return null;
  364. }
  365. public IEntity Resolve(INamespace ns, string name, EntityType elementType)
  366. {
  367. _buffer.Clear();
  368. ns.Resolve(_buffer, name, elementType);
  369. return GetEntityFromList(_buffer);
  370. }
  371. public IEntity Resolve(INamespace ns, string name)
  372. {
  373. return Resolve(ns, name, EntityType.Any);
  374. }
  375. IEntity GetEntityFromBuffer()
  376. {
  377. return GetEntityFromList(_buffer);
  378. }
  379. public static IEntity GetEntityFromList(IList list)
  380. {
  381. IEntity element = null;
  382. if (list.Count > 0)
  383. {
  384. if (list.Count > 1)
  385. {
  386. element = new Ambiguous(list);
  387. }
  388. else
  389. {
  390. element = (IEntity)list[0];
  391. }
  392. list.Clear();
  393. }
  394. return element;
  395. }
  396. static bool IsQualifiedName(string name)
  397. {
  398. return name.IndexOf('.') > 0;
  399. }
  400. public static bool IsFlagSet(EntityType flags, EntityType flag)
  401. {
  402. return flag == (flags & flag);
  403. }
  404. public void OrganizeAssemblyTypes(Assembly asm)
  405. {
  406. CatalogPublicTypes(asm.GetTypes());
  407. }
  408. private void CatalogPublicTypes(Type[] types)
  409. {
  410. string lastNs = "!!not a namespace!!";
  411. NamespaceEntity lastNsEntity = null;
  412. foreach (Type type in types)
  413. {
  414. if (!type.IsPublic) continue;
  415. string ns = type.Namespace ?? string.Empty;
  416. //retrieve the namespace only if we don't have it handy already
  417. //usually we'll have it since GetExportedTypes() seems to export
  418. //types in a sorted fashion.
  419. if (ns != lastNs)
  420. {
  421. lastNs = ns;
  422. lastNsEntity = GetNamespace(ns);
  423. lastNsEntity.Add(type);
  424. }
  425. else
  426. {
  427. lastNsEntity.Add(type);
  428. }
  429. }
  430. }
  431. public NamespaceEntity GetNamespace(string ns)
  432. {
  433. string[] namespaceHierarchy = ns.Split('.');
  434. string topLevelName = namespaceHierarchy[0];
  435. NamespaceEntity topLevel = GetTopLevelNamespace(topLevelName);
  436. NamespaceEntity current = topLevel;
  437. for (int i=1; i<namespaceHierarchy.Length; ++i)
  438. {
  439. current = current.GetChildNamespace(namespaceHierarchy[i]);
  440. }
  441. return current;
  442. }
  443. NamespaceEntity GetTopLevelNamespace(string topLevelName)
  444. {
  445. GlobalNamespace globalNS = GetGlobalNamespace();
  446. if (globalNS == null) return null;
  447. NamespaceEntity entity = (NamespaceEntity)globalNS.GetChild(topLevelName);
  448. if (null == entity)
  449. {
  450. entity = new NamespaceEntity(null, _context.TypeSystemServices, topLevelName);
  451. globalNS.SetChild(topLevelName, entity);
  452. }
  453. return entity;
  454. }
  455. GlobalNamespace GetGlobalNamespace()
  456. {
  457. INamespace ns = _global;
  458. GlobalNamespace globals = ns as GlobalNamespace;
  459. while (globals == null && ns != null)
  460. {
  461. ns = ns.ParentNamespace;
  462. globals = ns as GlobalNamespace;
  463. }
  464. return globals;
  465. }
  466. private static void FlattenChildNamespaces(List<INamespace> list, INamespace ns)
  467. {
  468. foreach (IEntity ent in ns.GetMembers())
  469. {
  470. if (EntityType.Namespace != ent.EntityType) continue;
  471. list.Add((INamespace) ent);
  472. FlattenChildNamespaces(list, (INamespace) ent);
  473. }
  474. }
  475. public string GetMostSimilarTypeName(string name)
  476. {
  477. string[] nsHierarchy = name.Split('.');
  478. int nshLen = nsHierarchy.Length;
  479. string suggestion = null;
  480. if (nshLen > 1)
  481. {
  482. INamespace ns = null;
  483. INamespace prevNs = null;
  484. for (int i = 1; i < nshLen; i++)
  485. {
  486. string currentNsName = string.Join(".", nsHierarchy, 0, i);
  487. ns = ResolveQualifiedName(currentNsName) as INamespace;
  488. if (null == ns)
  489. {
  490. nsHierarchy[i-1] = GetMostSimilarMemberName(prevNs, nsHierarchy[i-1], EntityType.Namespace);
  491. if (null == nsHierarchy[i-1]) break;
  492. i--; continue; //reloop to resolve step
  493. }
  494. prevNs = ns;
  495. }
  496. suggestion = GetMostSimilarMemberName(ns, nsHierarchy[nshLen-1], EntityType.Type);
  497. if (null != suggestion)
  498. {
  499. nsHierarchy[nshLen-1] = suggestion;
  500. return string.Join(".", nsHierarchy);
  501. }
  502. }
  503. List<INamespace> nsList = new List<INamespace>();
  504. FlattenChildNamespaces(nsList, GetGlobalNamespace());
  505. nsList.Reverse();//most recently added namespaces first
  506. foreach (INamespace nse in nsList)
  507. {
  508. suggestion = GetMostSimilarMemberName(nse, nsHierarchy[nshLen-1], EntityType.Type);
  509. if (null != suggestion) return nse.ToString()+"."+suggestion;
  510. }
  511. return GetMostSimilarMemberName(GetGlobalNamespace(), nsHierarchy[nshLen-1], EntityType.Type);
  512. }
  513. public string GetMostSimilarMemberName(INamespace ns, string name, EntityType elementType)
  514. {
  515. if (null == ns) return null;
  516. string expectedSoundex = ToSoundex(name);
  517. string lastMemberName = null;
  518. foreach (IEntity member in ns.GetMembers())
  519. {
  520. if (EntityType.Any != elementType && elementType != member.EntityType)
  521. continue;
  522. if (lastMemberName == member.Name)
  523. continue;//no need to check this name again
  524. //TODO: try Levenshtein distance or Metaphone instead of Soundex.
  525. if (expectedSoundex == ToSoundex(member.Name))
  526. {
  527. return member.Name;
  528. }
  529. lastMemberName = member.Name;
  530. }
  531. return null;
  532. }
  533. private static string ToSoundex(string s)
  534. {
  535. if (s.Length < 2) return null;
  536. char[] code = "?0000".ToCharArray();
  537. string ws = s.ToLowerInvariant();
  538. int wsLen = ws.Length;
  539. char lastChar = ' ';
  540. int lastCharPos = 1;
  541. code[0] = ws[0];
  542. for (int i = 1; i < wsLen; i++)
  543. {
  544. char wsc = ws[i];
  545. char c = ' ';
  546. if (wsc == 'b' || wsc == 'f' || wsc == 'p' || wsc == 'v') c = '1';
  547. if (wsc == 'c' || wsc == 'g' || wsc == 'j' || wsc == 'k' || wsc == 'q' || wsc == 's' || wsc == 'x' || wsc == 'z') c = '2';
  548. if (wsc == 'd' || wsc == 't') c = '3';
  549. if (wsc == 'l') c = '4';
  550. if (wsc == 'm' || wsc == 'n') c = '5';
  551. if (wsc == 'r') c = '6';
  552. if (c == lastChar) continue;
  553. lastChar = c;
  554. if (c == ' ') continue;
  555. code[lastCharPos] = c;
  556. lastCharPos++;
  557. if (lastCharPos > 4) break;
  558. }
  559. return new string(code);
  560. }
  561. }
  562. }