aboutsummaryrefslogtreecommitdiffstats
path: root/org.aspectj.matcher/src/main/java/org/aspectj/weaver/ShadowMunger.java
blob: 3a2a3a74071f41463a64bd60c8db03c80ef1613f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/* *******************************************************************
 * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
 * All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 *  
 * Contributors: 
 *     PARC     initial implementation 
 * ******************************************************************/

package org.aspectj.weaver;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.bridge.SourceLocation;
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.util.PartialOrder;
import org.aspectj.weaver.patterns.PerClause;
import org.aspectj.weaver.patterns.Pointcut;
import org.aspectj.weaver.patterns.TypePattern;

/**
 * For every shadow munger, nothing can be done with it until it is concretized. Then...
 * 
 * (Then we call fast match.)
 * 
 * For every shadow munger, for every shadow, first match is called, then (if match returned true) the shadow munger is specialized
 * for the shadow, which may modify state. Then implement is called.
 */
public abstract class ShadowMunger implements PartialOrder.PartialComparable, IHasPosition {

	public static final ShadowMunger[] NONE = new ShadowMunger[0];

	private static int VERSION_1 = 1; // ShadowMunger version for serialization

	protected static final int ShadowMungerAdvice = 1;
	protected static final int ShadowMungerDeow = 2;

	public String handle = null;

	private int shadowMungerKind;

	protected int start, end;
	protected ISourceContext sourceContext;
	private ISourceLocation sourceLocation;
	private ISourceLocation binarySourceLocation;
	private File binaryFile;
	private ResolvedType declaringType;
	private boolean isBinary;
	private boolean checkedIsBinary;

	protected Pointcut pointcut;

	protected ShadowMunger() {
	}

	public ShadowMunger(Pointcut pointcut, int start, int end, ISourceContext sourceContext, int shadowMungerKind) {
		this.shadowMungerKind = shadowMungerKind;
		this.pointcut = pointcut;
		this.start = start;
		this.end = end;
		this.sourceContext = sourceContext;
	}

	/**
	 * All overriding methods should call super
	 */
	public boolean match(Shadow shadow, World world) {
		if (world.isXmlConfigured() && world.isAspectIncluded(declaringType)) {
			TypePattern scoped = world.getAspectScope(declaringType);
			if (scoped != null) {
				// Check the 'cached' exclusion map
				Set<ResolvedType> excludedTypes = world.getExclusionMap().get(declaringType);
				ResolvedType type = shadow.getEnclosingType().resolve(world);
				if (excludedTypes != null && excludedTypes.contains(type)) {
					return false;
				}
				boolean b = scoped.matches(type, TypePattern.STATIC).alwaysTrue();
				if (!b) {
					if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) {
						world.getMessageHandler().handleMessage(
								MessageUtil.info("Type '" + type.getName() + "' not woven by aspect '" + declaringType.getName()
										+ "' due to scope exclusion in XML definition"));
					}
					if (excludedTypes == null) {
						excludedTypes = new HashSet<>();
						excludedTypes.add(type);
						world.getExclusionMap().put(declaringType, excludedTypes);
					} else {
						excludedTypes.add(type);
					}
					return false;
				}
			}
		}
		if (world.areInfoMessagesEnabled() && world.isTimingEnabled()) {
			long starttime = System.nanoTime();
			FuzzyBoolean isMatch = pointcut.match(shadow);
			long endtime = System.nanoTime();
			world.record(pointcut, endtime - starttime);
			return isMatch.maybeTrue();
		} else {
			FuzzyBoolean isMatch = pointcut.match(shadow);
			return isMatch.maybeTrue();
		}
	}

	public int fallbackCompareTo(Object other) {
		return toString().compareTo(toString());
	}

	public int getEnd() {
		return end;
	}

	public int getStart() {
		return start;
	}

	public ISourceLocation getSourceLocation() {
		if (sourceLocation == null) {
			if (sourceContext != null) {
				sourceLocation = sourceContext.makeSourceLocation(this);
			}
		}
		if (isBinary()) {
			if (binarySourceLocation == null) {
				binarySourceLocation = getBinarySourceLocation(sourceLocation);
			}
			return binarySourceLocation;
		}
		return sourceLocation;
	}

	public Pointcut getPointcut() {
		return pointcut;
	}

	// pointcut may be updated during rewriting...
	public void setPointcut(Pointcut pointcut) {
		this.pointcut = pointcut;
	}

	/**
	 * Invoked when the shadow munger of a resolved type are processed.
	 * 
	 * @param aType
	 */
	public void setDeclaringType(ResolvedType aType) {
		declaringType = aType;
	}

	public ResolvedType getDeclaringType() {
		return declaringType;
	}

	public abstract ResolvedType getConcreteAspect();

	/**
	 * Returns the binarySourceLocation for the given sourcelocation. This isn't cached because it's used when faulting in the
	 * binary nodes and is called with ISourceLocations for all advice, pointcuts and deows contained within the
	 * resolvedDeclaringAspect.
	 */
	public ISourceLocation getBinarySourceLocation(ISourceLocation sl) {
		if (sl == null) {
			return null;
		}
		String sourceFileName = null;
		if (getDeclaringType() instanceof ReferenceType) {
			String s = ((ReferenceType) getDeclaringType()).getDelegate().getSourcefilename();
			int i = s.lastIndexOf('/');
			if (i != -1) {
				sourceFileName = s.substring(i + 1);
			} else {
				sourceFileName = s;
			}
		}
		ISourceLocation sLoc = new SourceLocation(getBinaryFile(), sl.getLine(), sl.getEndLine(),
				((sl.getColumn() == 0) ? ISourceLocation.NO_COLUMN : sl.getColumn()), sl.getContext(), sourceFileName);
		return sLoc;
	}

	/**
	 * Returns the File with pathname to the class file, for example either:<br>
	 * C:\temp \ajcSandbox\workspace\ajcTest16957.tmp\simple.jar!pkg\BinaryAspect.class if the class file is in a jar file, or <br>
	 * C:\temp\ajcSandbox\workspace\ajcTest16957.tmp!pkg\BinaryAspect.class if the class file is in a directory
	 */
	private File getBinaryFile() {
		if (binaryFile == null) {
			String binaryPath = getDeclaringType().getBinaryPath();
			if (binaryPath == null) {
				// Looks like an aspect that has been picked up from the classpath (likely an abstract one
				// being extended). As it didn't come in via inpath or aspectpath the binarypath has not
				// yet been constructed.

				// We can't discover where the file came from now, that info has been lost. So just
				// use "classpath" for now - until we discover we need to get this right.

				binaryPath = "classpath";
				getDeclaringType().setBinaryPath(binaryPath);
				// ReferenceTypeDelegate delegate = ((ReferenceType) getDeclaringType()).getDelegate();
				// if (delegate instanceof BcelObjectType) {
				// grab javaclass... but it doesnt know the originating file
				// }
			}
			if (!binaryPath.contains("!")) {
				File f = getDeclaringType().getSourceLocation().getSourceFile();
				// Replace the source file suffix with .class
				int i = f.getPath().lastIndexOf('.');
				String path = null;
				if (i != -1) {
					path = f.getPath().substring(0, i) + ".class";
				} else {
					path = f.getPath() + ".class";
				}
				binaryFile = new File(binaryPath + "!" + path);
			} else {
				binaryFile = new File(binaryPath);
			}
		}
		return binaryFile;
	}

	/**
	 * Returns whether or not this shadow munger came from a binary aspect - keep a record of whether or not we've checked if we're
	 * binary otherwise we keep calculating the same thing many times
	 */
	public boolean isBinary() {
		if (!checkedIsBinary) {
			ResolvedType rt = getDeclaringType();
			if (rt != null) {
				isBinary = ((rt.getBinaryPath() == null) ? false : true);
			}
			checkedIsBinary = true;
		}
		return isBinary;
	}

	public abstract ShadowMunger concretize(ResolvedType fromType, World world, PerClause clause);

	public abstract void specializeOn(Shadow shadow);

	/**
	 * Implement this munger at the specified shadow, returning a boolean to indicate success.
	 * 
	 * @param shadow the shadow where this munger should be applied
	 * @return true if the implement was successful
	 */
	public abstract boolean implementOn(Shadow shadow);

	public abstract ShadowMunger parameterizeWith(ResolvedType declaringType, Map<String, UnresolvedType> typeVariableMap);

	/**
	 * @return a Collection of ResolvedTypes for all checked exceptions that might be thrown by this munger
	 */
	public abstract Collection<ResolvedType> getThrownExceptions();

	/**
	 * Does the munger have to check that its exception are accepted by the shadow ? It is not the case for annotation style around
	 * advice, for example: that can throw Throwable, even if the advised method does not throw any exceptions.
	 * 
	 * @return true if munger has to check that its exceptions can be thrown based on the shadow
	 */
	public abstract boolean mustCheckExceptions();

	public void write(CompressingDataOutputStream stream) throws IOException {
		stream.writeInt(VERSION_1);
		stream.writeInt(shadowMungerKind); // determines real subclass
		stream.writeInt(start);
		stream.writeInt(end);
		PersistenceSupport.write(stream, sourceContext);
		PersistenceSupport.write(stream, sourceLocation);
		PersistenceSupport.write(stream, binarySourceLocation);
		PersistenceSupport.write(stream, binaryFile);
		declaringType.write(stream);
		stream.writeBoolean(isBinary);
		stream.writeBoolean(checkedIsBinary);
		pointcut.write(stream);
	}

	//
	// public static ShadowMunger read(VersionedDataInputStream stream, World world) throws IOException {
	// stream.readInt();
	// int kind = stream.readInt();
	// ShadowMunger newShadowMunger = null;
	// switch (kind) {
	// case ShadowMungerAdvice:
	// // world.getWeavingSupport().createAdviceMunger(attribute, pointcut, signature)
	// case ShadowMungerDeow:
	// newShadowMunger = Checker.read(stream, world);
	// default:
	// throw new IllegalStateException("Unexpected type of shadow munger found on deserialization: " + kind);
	// }
	// newShadowMunger.binaryFile = null;
	// }
	
	public boolean bindsProceedingJoinPoint() {
		return false;
	}
	
	public boolean isAroundAdvice() {
		return false;
	}

}