You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DeclareParents.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /* *******************************************************************
  2. * Copyright (c) 2002-2019 Contributors
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v 2.0
  6. * which accompanies this distribution and is available at
  7. * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
  8. * ******************************************************************/
  9. package org.aspectj.weaver.patterns;
  10. import java.io.IOException;
  11. import java.util.ArrayList;
  12. import java.util.Collections;
  13. import java.util.Iterator;
  14. import java.util.List;
  15. import java.util.Map;
  16. import org.aspectj.bridge.IMessage;
  17. import org.aspectj.bridge.ISourceLocation;
  18. import org.aspectj.bridge.Message;
  19. import org.aspectj.weaver.CompressingDataOutputStream;
  20. import org.aspectj.weaver.ISourceContext;
  21. import org.aspectj.weaver.ResolvedType;
  22. import org.aspectj.weaver.UnresolvedType;
  23. import org.aspectj.weaver.VersionedDataInputStream;
  24. import org.aspectj.weaver.WeaverMessages;
  25. import org.aspectj.weaver.World;
  26. /**
  27. * @author Thomas Kiviaho
  28. * @author Andy Clement
  29. * @author PARC
  30. */
  31. public class DeclareParents extends Declare {
  32. protected TypePattern child;
  33. protected TypePatternList parents;
  34. private boolean isWildChild = false;
  35. protected boolean isExtends = true;
  36. public DeclareParents(TypePattern child, List<TypePattern> parents, boolean isExtends) {
  37. this(child, new TypePatternList(parents), isExtends);
  38. }
  39. protected DeclareParents(TypePattern child, TypePatternList parents, boolean isExtends) {
  40. this.child = child;
  41. this.parents = parents;
  42. this.isExtends = isExtends;
  43. WildChildFinder wildChildFinder = new WildChildFinder();
  44. child.accept(wildChildFinder, null);
  45. isWildChild = wildChildFinder.containedWildChild();
  46. }
  47. public boolean match(ResolvedType typeX) {
  48. if (!child.matchesStatically(typeX)) {
  49. return false;
  50. }
  51. if (typeX.getWorld().getLint().typeNotExposedToWeaver.isEnabled() && !typeX.isExposedToWeaver()) {
  52. typeX.getWorld().getLint().typeNotExposedToWeaver.signal(typeX.getName(), getSourceLocation());
  53. }
  54. return true;
  55. }
  56. @Override
  57. public Object accept(PatternNodeVisitor visitor, Object data) {
  58. return visitor.visit(this, data);
  59. }
  60. @Override
  61. public Declare parameterizeWith(Map<String,UnresolvedType> typeVariableBindingMap, World w) {
  62. DeclareParents ret = new DeclareParents(child.parameterizeWith(typeVariableBindingMap, w), parents.parameterizeWith(
  63. typeVariableBindingMap, w), isExtends);
  64. ret.copyLocationFrom(this);
  65. return ret;
  66. }
  67. @Override
  68. public String toString() {
  69. StringBuilder buf = new StringBuilder();
  70. buf.append("declare parents: ");
  71. buf.append(child);
  72. buf.append(isExtends ? " extends " : " implements "); // extends and implements are treated equivalently
  73. buf.append(parents);
  74. buf.append(";");
  75. return buf.toString();
  76. }
  77. @Override
  78. public boolean equals(Object other) {
  79. if (!(other instanceof DeclareParents)) {
  80. return false;
  81. }
  82. DeclareParents o = (DeclareParents) other;
  83. return o.child.equals(child) && o.parents.equals(parents);
  84. }
  85. // ??? cache this
  86. @Override
  87. public int hashCode() {
  88. int result = 23;
  89. result = 37 * result + child.hashCode();
  90. result = 37 * result + parents.hashCode();
  91. return result;
  92. }
  93. @Override
  94. public void write(CompressingDataOutputStream s) throws IOException {
  95. s.writeByte(Declare.PARENTS);
  96. child.write(s);
  97. parents.write(s);
  98. writeLocation(s);
  99. }
  100. public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException {
  101. DeclareParents ret = new DeclareParents(TypePattern.read(s, context), TypePatternList.read(s, context), true);
  102. ret.readLocation(context, s);
  103. return ret;
  104. }
  105. public boolean parentsIncludeInterface(World w) {
  106. for (int i = 0; i < parents.size(); i++) {
  107. if (parents.get(i).getExactType().resolve(w).isInterface()) {
  108. return true;
  109. }
  110. }
  111. return false;
  112. }
  113. public boolean parentsIncludeClass(World w) {
  114. for (int i = 0; i < parents.size(); i++) {
  115. if (parents.get(i).getExactType().resolve(w).isClass()) {
  116. return true;
  117. }
  118. }
  119. return false;
  120. }
  121. @Override
  122. public void resolve(IScope scope) {
  123. TypePattern resolvedChild = child.resolveBindings(scope, Bindings.NONE, false, false);
  124. if (!resolvedChild.equals(child)) {
  125. WildChildFinder wildChildFinder = new WildChildFinder();
  126. resolvedChild.accept(wildChildFinder, null);
  127. isWildChild = wildChildFinder.containedWildChild();
  128. this.child = resolvedChild;
  129. }
  130. parents = parents.resolveBindings(scope, Bindings.NONE, false, true);
  131. // Could assert this ...
  132. // for (int i=0; i < parents.size(); i++) {
  133. // parents.get(i).assertExactType(scope.getMessageHandler());
  134. // }
  135. }
  136. public TypePatternList getParents() {
  137. return parents;
  138. }
  139. public TypePattern getChild() {
  140. return child;
  141. }
  142. // note - will always return true after deserialization, this doesn't affect weaver
  143. public boolean isExtends() {
  144. return this.isExtends;
  145. }
  146. @Override
  147. public boolean isAdviceLike() {
  148. return false;
  149. }
  150. private ResolvedType maybeGetNewParent(ResolvedType targetType, TypePattern typePattern, World world, boolean reportErrors) {
  151. if (typePattern == TypePattern.NO) {
  152. return null; // already had an error here
  153. }
  154. // isWildChild = (child instanceof WildTypePattern);
  155. UnresolvedType iType = typePattern.getExactType();
  156. ResolvedType parentType = iType.resolve(world);
  157. if (targetType.equals(world.getCoreType(UnresolvedType.OBJECT))) {
  158. world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.DECP_OBJECT), this.getSourceLocation(), null);
  159. return null;
  160. }
  161. // Ensure the target doesn't already have an
  162. // alternate parameterization of the generic type on it
  163. if (parentType.isParameterizedType() || parentType.isRawType()) {
  164. // Let's take a look at the parents we already have
  165. boolean isOK = verifyNoInheritedAlternateParameterization(targetType, parentType, world);
  166. if (!isOK) {
  167. return null;
  168. }
  169. }
  170. if (parentType.isAssignableFrom(targetType)) {
  171. return null; // already a parent
  172. }
  173. // Enum types that are targetted for decp through a wild type pattern get linted
  174. if (reportErrors && isWildChild && targetType.isEnum()) {
  175. world.getLint().enumAsTargetForDecpIgnored.signal(targetType.toString(), getSourceLocation());
  176. }
  177. // Annotation types that are targetted for decp through a wild type pattern get linted
  178. if (reportErrors && isWildChild && targetType.isAnnotation()) {
  179. world.getLint().annotationAsTargetForDecpIgnored.signal(targetType.toString(), getSourceLocation());
  180. }
  181. // 1. Can't use decp to make an enum/annotation type implement an interface
  182. if (targetType.isEnum() && parentType.isInterface()) {
  183. if (reportErrors && !isWildChild) {
  184. world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ENUM_TO_IMPL_INTERFACE,
  185. targetType), getSourceLocation(), null);
  186. }
  187. return null;
  188. }
  189. if (targetType.isAnnotation() && parentType.isInterface()) {
  190. if (reportErrors && !isWildChild) {
  191. world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ANNOTATION_TO_IMPL_INTERFACE,
  192. targetType), getSourceLocation(), null);
  193. }
  194. return null;
  195. }
  196. // 2. Can't use decp to change supertype of an enum/annotation
  197. if (targetType.isEnum() && parentType.isClass()) {
  198. if (reportErrors && !isWildChild) {
  199. world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ENUM_TO_EXTEND_CLASS,
  200. targetType), getSourceLocation(), null);
  201. }
  202. return null;
  203. }
  204. if (targetType.isAnnotation() && parentType.isClass()) {
  205. if (reportErrors && !isWildChild) {
  206. world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ANNOTATION_TO_EXTEND_CLASS,
  207. targetType), getSourceLocation(), null);
  208. }
  209. return null;
  210. }
  211. // 3. Can't use decp to declare java.lang.Enum/java.lang.annotation.Annotation as the parent of a type
  212. if (parentType.getSignature().equals(UnresolvedType.ENUM.getSignature())) {
  213. if (reportErrors && !isWildChild) {
  214. world.showMessage(IMessage.ERROR, WeaverMessages
  215. .format(WeaverMessages.CANT_DECP_TO_MAKE_ENUM_SUPERTYPE, targetType), getSourceLocation(), null);
  216. }
  217. return null;
  218. }
  219. if (parentType.getSignature().equals(UnresolvedType.ANNOTATION.getSignature())) {
  220. if (reportErrors && !isWildChild) {
  221. world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_TO_MAKE_ANNOTATION_SUPERTYPE,
  222. targetType), getSourceLocation(), null);
  223. }
  224. return null;
  225. }
  226. if (parentType.isAssignableFrom(targetType)) {
  227. return null; // already a parent
  228. }
  229. if (targetType.isAssignableFrom(parentType)) {
  230. world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_EXTEND_SELF, targetType.getName()), this
  231. .getSourceLocation(), null);
  232. return null;
  233. }
  234. if (parentType.isClass()) {
  235. if (targetType.isInterface()) {
  236. world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.INTERFACE_CANT_EXTEND_CLASS), this
  237. .getSourceLocation(), null);
  238. return null;
  239. // how to handle xcutting errors???
  240. }
  241. if (!targetType.getSuperclass().isAssignableFrom(parentType)) {
  242. world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.DECP_HIERARCHY_ERROR, iType.getName(),
  243. targetType.getSuperclass().getName()), this.getSourceLocation(), null);
  244. return null;
  245. } else {
  246. return parentType;
  247. }
  248. } else {
  249. return parentType;
  250. }
  251. }
  252. /**
  253. * This method looks through the type hierarchy for some target type - it is attempting to find an existing parameterization
  254. * that clashes with the new parent that the user wants to apply to the type. If it finds an existing parameterization that
  255. * matches the new one, it silently completes, if it finds one that clashes (e.g. a type already has A<String> when the user
  256. * wants to add A<Number>) then it will produce an error.
  257. *
  258. * It uses recursion and exits recursion on hitting 'jlObject'
  259. *
  260. * Related bugzilla entries: pr110788
  261. */
  262. private boolean verifyNoInheritedAlternateParameterization(ResolvedType typeToVerify, ResolvedType newParent, World world) {
  263. if (typeToVerify.equals(ResolvedType.OBJECT)) {
  264. return true;
  265. }
  266. ResolvedType newParentGenericType = newParent.getGenericType();
  267. Iterator<ResolvedType> iter = typeToVerify.getDirectSupertypes();
  268. while (iter.hasNext()) {
  269. ResolvedType supertype = iter.next();
  270. if (((supertype.isRawType() && newParent.isParameterizedType()) || (supertype.isParameterizedType() && newParent
  271. .isRawType()))
  272. && newParentGenericType.equals(supertype.getGenericType())) {
  273. // new parent is a parameterized type, but this is a raw type
  274. world.getMessageHandler().handleMessage(
  275. new Message(WeaverMessages.format(WeaverMessages.CANT_DECP_MULTIPLE_PARAMETERIZATIONS, newParent.getName(),
  276. typeToVerify.getName(), supertype.getName()), getSourceLocation(), true,
  277. new ISourceLocation[] { typeToVerify.getSourceLocation() }));
  278. return false;
  279. }
  280. if (supertype.isParameterizedType()) {
  281. ResolvedType generictype = supertype.getGenericType();
  282. // If the generic types are compatible but the parameterizations aren't then we have a problem
  283. if (generictype.isAssignableFrom(newParentGenericType) && !supertype.isAssignableFrom(newParent)) {
  284. world.getMessageHandler().handleMessage(
  285. new Message(WeaverMessages.format(WeaverMessages.CANT_DECP_MULTIPLE_PARAMETERIZATIONS, newParent
  286. .getName(), typeToVerify.getName(), supertype.getName()), getSourceLocation(), true,
  287. new ISourceLocation[] { typeToVerify.getSourceLocation() }));
  288. return false;
  289. }
  290. }
  291. if (!verifyNoInheritedAlternateParameterization(supertype, newParent, world)) {
  292. return false;
  293. }
  294. }
  295. return true;
  296. }
  297. public List<ResolvedType> findMatchingNewParents(ResolvedType onType, boolean reportErrors) {
  298. if (onType.isRawType()) {
  299. onType = onType.getGenericType();
  300. }
  301. if (!match(onType)) {
  302. return Collections.emptyList();
  303. }
  304. List<ResolvedType> ret = new ArrayList<>();
  305. for (int i = 0; i < parents.size(); i++) {
  306. ResolvedType t = maybeGetNewParent(onType, parents.get(i), onType.getWorld(), reportErrors);
  307. if (t != null) {
  308. ret.add(t);
  309. }
  310. }
  311. return ret;
  312. }
  313. @Override
  314. public String getNameSuffix() {
  315. return "parents";
  316. }
  317. public boolean isMixin() {
  318. return false;
  319. }
  320. }