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 13KB

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