1 /* *******************************************************************
2 * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
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
10 * PARC initial implementation
11 * ******************************************************************/
13 package org.aspectj.ajdt.internal.compiler.ast;
15 import java.lang.reflect.Modifier;
17 import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
18 import org.aspectj.ajdt.internal.compiler.lookup.EclipseTypeMunger;
19 import org.aspectj.ajdt.internal.compiler.lookup.PrivilegedFieldBinding;
20 import org.aspectj.ajdt.internal.compiler.lookup.PrivilegedHandler;
21 import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
22 import org.aspectj.org.eclipse.jdt.internal.compiler.ClassFile;
23 import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
24 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument;
25 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression;
26 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
27 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
28 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Expression;
29 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
30 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Statement;
31 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference;
32 import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.CodeStream;
33 import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.Opcodes;
34 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
35 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope;
36 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
37 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
38 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
39 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Scope;
40 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
41 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
42 import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Parser;
43 import org.aspectj.weaver.AjAttribute;
44 import org.aspectj.weaver.AjcMemberMaker;
45 import org.aspectj.weaver.Member;
46 import org.aspectj.weaver.NameMangler;
47 import org.aspectj.weaver.NewFieldTypeMunger;
48 import org.aspectj.weaver.ResolvedMember;
49 import org.aspectj.weaver.ResolvedMemberImpl;
50 import org.aspectj.weaver.ResolvedType;
51 import org.aspectj.weaver.Shadow;
52 import org.aspectj.weaver.UnresolvedType;
55 * An inter-type field declaration.
57 * returnType encodes the type of the field selector encodes the name statements is null until resolution when it is filled in from
62 public class InterTypeFieldDeclaration extends InterTypeDeclaration {
63 public Expression initialization;
64 private TypeBinding realFieldType;
66 // public InterTypeFieldBinding interBinding;
68 public InterTypeFieldDeclaration(CompilationResult result, TypeReference onType) {
69 super(result, onType);
72 public TypeBinding getRealFieldType() {
76 public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
77 // we don't have a body to parse
80 protected char[] getPrefix() {
81 return (NameMangler.ITD_PREFIX + "interField$").toCharArray();
84 public void resolveOnType(ClassScope classScope) {
85 super.resolveOnType(classScope);
86 if (ignoreFurtherInvestigation) {
89 if (Modifier.isStatic(declaredModifiers) && onTypeBinding.isInterface()) {
90 scope.problemReporter().signalError(sourceStart, sourceEnd, "static inter-type field on interface not supported");
91 ignoreFurtherInvestigation = true;
93 if (Modifier.isStatic(declaredModifiers) && typeVariableAliases != null && typeVariableAliases.size() > 0
94 && onTypeBinding.isGenericType()) {
95 scope.problemReporter().signalError(sourceStart, sourceEnd,
96 "static intertype field declarations cannot refer to type variables from the target generic type");
101 public void resolve(ClassScope upperScope) {
102 if (munger == null) {
103 ignoreFurtherInvestigation = true;
105 if (ignoreFurtherInvestigation) {
109 EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(upperScope);
110 ResolvedMember sig = munger.getSignature();
111 UnresolvedType aspectType = world.fromBinding(upperScope.referenceContext.binding);
113 if (sig.getReturnType().equals(UnresolvedType.VOID)
114 || (sig.getReturnType().isArray() && (sig.getReturnType().getComponentType().equals(UnresolvedType.VOID)))) {
115 upperScope.problemReporter().signalError(sourceStart, sourceEnd, "field type can not be void");
119 // System.err.println("sig: " + sig);
120 // System.err.println("field: " + world.makeFieldBinding(
121 // AjcMemberMaker.interFieldClassField(sig, aspectType)));
123 if (initialization != null && initialization instanceof ArrayInitializer) {
124 // System.err.println("got initializer: " + initialization);
125 ArrayAllocationExpression aae = new ArrayAllocationExpression();
126 aae.initializer = (ArrayInitializer) initialization;
127 ArrayBinding arrayType = (ArrayBinding) world.makeTypeBinding(sig.getReturnType());
128 aae.type = AstUtil.makeTypeReference(arrayType.leafComponentType());
129 aae.sourceStart = initialization.sourceStart;
130 aae.sourceEnd = initialization.sourceEnd;
131 aae.dimensions = new Expression[arrayType.dimensions];
132 initialization = aae;
134 * else if (initialization!=null) { MethodScope initializationScope = this.scope; TypeBinding fieldType = realFieldType;
135 * TypeBinding initializationType; this.initialization.setExpectedType(fieldType); // needed in case of generic method
136 * invocation if (this.initialization instanceof ArrayInitializer) {
138 * if ((initializationType = this.initialization.resolveTypeExpecting(initializationScope, fieldType)) != null) {
139 * ((ArrayInitializer) this.initialization).binding = (ArrayBinding) initializationType;
140 * this.initialization.computeConversion(initializationScope, fieldType, initializationType); } } //
141 * System.err.println("i=>"+initialization); // System.err.println("sasuages=>"+initialization.resolvedType); //
142 * //initializationType = initialization.resolveType(initializationScope); //
143 * System.err.println("scope=>"+initializationScope);
145 * else if ((initializationType = this.initialization.resolveType(initializationScope)) != null) {
147 * if (fieldType != initializationType) // must call before computeConversion() and typeMismatchError()
148 * initializationScope.compilationUnitScope().recordTypeConversion(fieldType, initializationType); if
149 * (this.initialization.isConstantValueOfTypeAssignableToType(initializationType, fieldType) || (fieldType.isBaseType() &&
150 * BaseTypeBinding.isWidening(fieldType.id, initializationType.id)) || initializationType.isCompatibleWith(fieldType)) {
151 * initialization.computeConversion(initializationScope, fieldType, initializationType); if
152 * (initializationType.needsUncheckedConversion(fieldType)) {
153 * initializationScope.problemReporter().unsafeTypeConversion(this.initialization, initializationType, fieldType); } } else
154 * if (initializationScope.isBoxingCompatibleWith(initializationType, fieldType) || (initializationType.isBaseType() //
155 * narrowing then boxing ? && initializationScope.compilerOptions().sourceLevel >= JDK1_5 // autoboxing &&
156 * !fieldType.isBaseType() && initialization.isConstantValueOfTypeAssignableToType(initializationType,
157 * initializationScope.environment().computeBoxingType(fieldType)))) {
158 * this.initialization.computeConversion(initializationScope, fieldType, initializationType); } else {
159 * initializationScope.problemReporter().typeMismatchError(initializationType, fieldType, this); } // if
160 * (this.binding.isFinal()){ // cast from constant actual type to variable type //
161 * this.binding.setConstant(this.initialization.constant.castTo((this.binding.returnType.id << 4) +
162 * this.initialization.constant.typeID())); // } // } else { // this.binding.setConstant(NotAConstant); } // }
165 // ////////////////////
167 if (initialization == null) {
168 this.statements = new Statement[] { new ReturnStatement(null, 0, 0), };
169 } else if (!onTypeBinding.isInterface()) {
170 MethodBinding writeMethod = world.makeMethodBinding(AjcMemberMaker.interFieldSetDispatcher(sig, aspectType),
171 munger.getTypeVariableAliases());
172 // For the body of an intertype field initalizer, generate a call to the inter field set dispatcher
173 // method as that casts the shadow of a field set join point.
174 if (Modifier.isStatic(declaredModifiers)) {
175 this.statements = new Statement[] { new KnownMessageSend(writeMethod,
176 AstUtil.makeNameReference(writeMethod.declaringClass), new Expression[] { initialization }), };
178 this.statements = new Statement[] { new KnownMessageSend(writeMethod,
179 AstUtil.makeNameReference(writeMethod.declaringClass), new Expression[] {
180 AstUtil.makeLocalVariableReference(arguments[0].binding), initialization }), };
183 // XXX something is broken about this logic. Can we write to static interface fields?
184 MethodBinding writeMethod = world.makeMethodBinding(
185 AjcMemberMaker.interFieldInterfaceSetter(sig, sig.getDeclaringType().resolve(world.getWorld()), aspectType),
186 munger.getTypeVariableAliases());
187 if (Modifier.isStatic(declaredModifiers)) {
188 this.statements = new Statement[] { new KnownMessageSend(writeMethod,
189 AstUtil.makeNameReference(writeMethod.declaringClass), new Expression[] { initialization }), };
191 this.statements = new Statement[] { new KnownMessageSend(writeMethod,
192 AstUtil.makeLocalVariableReference(arguments[0].binding), new Expression[] { initialization }), };
196 super.resolve(upperScope);
199 public void setInitialization(Expression initialization) {
200 this.initialization = initialization;
205 * public void resolveStatements() { super.resolveStatements();
207 * // if (initialization!=null) { // MethodScope initializationScope = this.scope; // TypeBinding fieldType = realFieldType; //
208 * TypeBinding initializationType; // this.initialization.setExpectedType(fieldType); // needed in case of generic method
209 * invocation // if (this.initialization instanceof ArrayInitializer) { // // if ((initializationType =
210 * this.initialization.resolveTypeExpecting(initializationScope, fieldType)) != null) { // ((ArrayInitializer)
211 * this.initialization).binding = (ArrayBinding) initializationType; //
212 * this.initialization.computeConversion(initializationScope, fieldType, initializationType); // } // } ////
213 * System.err.println("i=>"+initialization); //// System.err.println("sasuages=>"+initialization.resolvedType); ////
214 * //initializationType = initialization.resolveType(initializationScope); ////
215 * System.err.println("scope=>"+initializationScope); // // else if ((initializationType =
216 * this.initialization.resolveType(initializationScope)) != null) { // // if (fieldType != initializationType) // must call
217 * before computeConversion() and typeMismatchError() //
218 * initializationScope.compilationUnitScope().recordTypeConversion(fieldType, initializationType); // if
219 * (this.initialization.isConstantValueOfTypeAssignableToType(initializationType, fieldType) // || (fieldType.isBaseType() &&
220 * BaseTypeBinding.isWidening(fieldType.id, initializationType.id)) // || initializationType.isCompatibleWith(fieldType)) { //
221 * initialization.computeConversion(initializationScope, fieldType, initializationType); // if
222 * (initializationType.needsUncheckedConversion(fieldType)) { //
223 * initializationScope.problemReporter().unsafeTypeConversion(this.initialization, initializationType, fieldType); // } // }
224 * else if (initializationScope.isBoxingCompatibleWith(initializationType, fieldType) // || (initializationType.isBaseType() //
225 * narrowing then boxing ? // && initializationScope.compilerOptions().sourceLevel >= JDK1_5 // autoboxing // &&
226 * !fieldType.isBaseType() // && initialization.isConstantValueOfTypeAssignableToType(initializationType,
227 * initializationScope.environment().computeBoxingType(fieldType)))) { //
228 * this.initialization.computeConversion(initializationScope, fieldType, initializationType); // } else { //
229 * initializationScope.problemReporter().typeMismatchError(initializationType, fieldType, this); // } // // if
230 * (this.binding.isFinal()){ // cast from constant actual type to variable type // //
231 * this.binding.setConstant(this.initialization.constant.castTo((this.binding.returnType.id << 4) +
232 * this.initialization.constant.typeID())); // // } // // } else { // // this.binding.setConstant(NotAConstant); // }}
236 public EclipseTypeMunger build(ClassScope classScope) {
237 EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(classScope);
238 resolveOnType(classScope);
240 if (ignoreFurtherInvestigation) {
244 binding = classScope.referenceContext.binding.resolveTypesFor(binding);
245 if (ignoreFurtherInvestigation) {
249 if (isTargetAnnotation(classScope, "field")) {
250 return null; // Error message output in isTargetAnnotation
252 if (isTargetEnum(classScope, "field")) {
253 return null; // Error message output in isTargetEnum
256 if (!Modifier.isStatic(declaredModifiers)) {
257 super.binding.parameters = new TypeBinding[] { onTypeBinding, };
258 this.arguments = new Argument[] { AstUtil.makeFinalArgument("ajc$this_".toCharArray(), onTypeBinding), };
261 // System.err.println("type: " + binding.returnType + ", " + returnType);
262 ResolvedType declaringType = world.fromBinding(onTypeBinding).resolve(world.getWorld());
263 if (declaringType.isRawType() || declaringType.isParameterizedType()) {
264 declaringType = declaringType.getGenericType();
267 if (interTypeScope == null) {
268 return null; // We encountered a problem building the scope, don't continue - error already reported
271 // Build a half correct resolvedmember (makeResolvedMember understands tvars) then build a fully correct sig from it
272 ResolvedMember sigtemp = world.makeResolvedMemberForITD(binding, onTypeBinding, interTypeScope.getRecoveryAliases());
273 UnresolvedType returnType = sigtemp.getReturnType();
274 // if (returnType.isParameterizedType() || returnType.isGenericType()) returnType = returnType.getRawType();
275 ResolvedMember sig = new ResolvedMemberImpl(Member.FIELD, declaringType, declaredModifiers, returnType, new String(
276 declaredSelector), UnresolvedType.NONE);
277 sig.setTypeVariables(sigtemp.getTypeVariables());
279 NewFieldTypeMunger myMunger = new NewFieldTypeMunger(sig, null, typeVariableAliases);
280 if (world.getItdVersion() == 1) {
281 myMunger.version = NewFieldTypeMunger.VersionOne;
284 ResolvedType aspectType = world.fromEclipse(classScope.referenceContext.binding);
285 ResolvedMember me = myMunger.getInitMethod(aspectType);
286 this.selector = binding.selector = me.getName().toCharArray();
287 this.realFieldType = this.binding.returnType;
288 this.binding.returnType = TypeBinding.VOID;
289 // ??? all other pieces should already match
291 return new EclipseTypeMunger(world, myMunger, aspectType, this);
294 private AjAttribute makeAttribute() {
295 return new AjAttribute.TypeMunger(munger);
298 public void generateCode(ClassScope classScope, ClassFile classFile) {
299 if (ignoreFurtherInvestigation) {
303 classFile.extraAttributes.add(new EclipseAttributeAdapter(makeAttribute()));
304 super.generateCode(classScope, classFile);
305 generateDispatchMethods(classScope, classFile);
306 // interBinding.reader.generateMethod(this, classScope, classFile);
307 // interBinding.writer.generateMethod(this, classScope, classFile);
310 private void generateDispatchMethods(ClassScope classScope, ClassFile classFile) {
311 EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(classScope);
312 ResolvedMember sig = munger.getSignature();
313 UnresolvedType aspectType = world.fromBinding(classScope.referenceContext.binding);
314 generateDispatchMethod(world, sig, aspectType, classScope, classFile, true);
315 generateDispatchMethod(world, sig, aspectType, classScope, classFile, false);
318 private void generateDispatchMethod(EclipseFactory world, ResolvedMember sig, UnresolvedType aspectType, ClassScope classScope,
319 ClassFile classFile, boolean isGetter) {
320 MethodBinding binding;
322 binding = world.makeMethodBinding(AjcMemberMaker.interFieldGetDispatcher(sig, aspectType),
323 munger.getTypeVariableAliases(), munger.getSignature().getDeclaringType());
325 binding = world.makeMethodBinding(AjcMemberMaker.interFieldSetDispatcher(sig, aspectType),
326 munger.getTypeVariableAliases(), munger.getSignature().getDeclaringType());
328 classFile.generateMethodInfoHeader(binding);
329 int methodAttributeOffset = classFile.contentsOffset;
330 int attributeNumber = classFile.generateMethodInfoAttributes(binding, makeEffectiveSignatureAttribute(sig, isGetter ? Shadow.FieldGet : Shadow.FieldSet, false));
331 int codeAttributeOffset = classFile.contentsOffset;
332 classFile.generateCodeAttributeHeader();
333 CodeStream codeStream = classFile.codeStream;
334 codeStream.reset(this, classFile);
336 NewFieldTypeMunger fieldMunger = (NewFieldTypeMunger) munger;
338 // Force use of version 1 if there is a field with that name on the type already
339 if (world.getItdVersion() == 1) {
340 fieldMunger.version = NewFieldTypeMunger.VersionOne;
342 if (!onTypeBinding.isInterface()) {
343 FieldBinding[] existingFields = onTypeBinding.fields();
344 for (int f = 0; f < existingFields.length; f++) {
345 FieldBinding fieldBinding = existingFields[f];
346 if (CharOperation.equals(fieldBinding.name, sig.getName().toCharArray())) {
347 fieldMunger.version = NewFieldTypeMunger.VersionOne;
353 FieldBinding classField = world.makeFieldBinding(
354 AjcMemberMaker.interFieldClassField(sig, aspectType, fieldMunger.version == NewFieldTypeMunger.VersionTwo),
355 munger.getTypeVariableAliases());
357 codeStream.initializeMaxLocals(binding);
359 if (onTypeBinding.isInterface()) {
360 UnresolvedType declaringTX = sig.getDeclaringType();
361 ResolvedType declaringRTX = world.getWorld().resolve(declaringTX, munger.getSourceLocation());
362 MethodBinding readMethod = world.makeMethodBinding(
363 AjcMemberMaker.interFieldInterfaceGetter(sig, declaringRTX, aspectType), munger.getTypeVariableAliases());
364 generateInterfaceReadBody(binding, readMethod, codeStream);
366 generateClassReadBody(binding, classField, codeStream);
369 if (onTypeBinding.isInterface()) {
370 MethodBinding writeMethod = world.makeMethodBinding(
371 AjcMemberMaker.interFieldInterfaceSetter(sig,
372 world.getWorld().resolve(sig.getDeclaringType(), munger.getSourceLocation()), aspectType),
373 munger.getTypeVariableAliases());
374 generateInterfaceWriteBody(binding, writeMethod, codeStream);
376 generateClassWriteBody(binding, classField, codeStream);
379 AstUtil.generateReturn(binding.returnType, codeStream);
381 classFile.completeCodeAttribute(codeAttributeOffset,scope);
383 classFile.completeMethodInfo(binding,methodAttributeOffset, attributeNumber);
386 private void generateInterfaceReadBody(MethodBinding binding, MethodBinding readMethod, CodeStream codeStream) {
387 codeStream.aload_0();
388 codeStream.invoke(Opcodes.OPC_invokeinterface,readMethod,null);
391 private void generateInterfaceWriteBody(MethodBinding binding, MethodBinding writeMethod, CodeStream codeStream) {
392 codeStream.aload_0();
393 codeStream.load(writeMethod.parameters[0], 1);
394 codeStream.invoke(Opcodes.OPC_invokeinterface, writeMethod, null);
397 private void generateClassReadBody(MethodBinding binding, FieldBinding field, CodeStream codeStream) {
398 if (field.isPrivate() || !field.canBeSeenBy(binding.declaringClass.fPackage)) {
400 PrivilegedHandler handler = (PrivilegedHandler) Scope.findPrivilegedHandler(binding.declaringClass);
401 if (handler == null) {
402 // one is now required!
403 ReferenceBinding typebinding = binding.declaringClass;
404 if (typebinding instanceof ReferenceBinding) {
405 SourceTypeBinding sourceBinding = (SourceTypeBinding) typebinding;
406 handler = new PrivilegedHandler((AspectDeclaration) sourceBinding.scope.referenceContext);
407 sourceBinding.privilegedHandler = handler;
410 PrivilegedFieldBinding fBinding = (PrivilegedFieldBinding) handler.getPrivilegedAccessField(field, null);
412 if (field.isStatic()) {
413 codeStream.invoke(Opcodes.OPC_invokestatic,fBinding.reader,null);
415 codeStream.aload_0();
416 codeStream.invoke(Opcodes.OPC_invokestatic,fBinding.reader,null);
420 if (field.isStatic()) {
421 codeStream.fieldAccess(Opcodes.OPC_getstatic,field,null);
423 codeStream.aload_0();
424 codeStream.fieldAccess(Opcodes.OPC_getfield,field,null);
428 private void generateClassWriteBody(MethodBinding binding, FieldBinding field, CodeStream codeStream) {
429 if (field.isPrivate() || !field.canBeSeenBy(binding.declaringClass.fPackage)) {
430 PrivilegedFieldBinding fBinding = (PrivilegedFieldBinding) Scope.findPrivilegedHandler(binding.declaringClass)
431 .getPrivilegedAccessField(field, null);
432 if (field.isStatic()) {
433 codeStream.load(field.type, 0);
434 codeStream.invoke(Opcodes.OPC_invokestatic,fBinding.writer,null);
436 codeStream.aload_0();
437 codeStream.load(field.type, 1);
438 codeStream.invoke(Opcodes.OPC_invokestatic,fBinding.writer,null);
442 if (field.isStatic()) {
443 codeStream.load(field.type, 0);
444 codeStream.fieldAccess(Opcodes.OPC_putstatic,field,null);
446 codeStream.aload_0();
447 codeStream.load(field.type, 1);
448 codeStream.fieldAccess(Opcodes.OPC_putfield,field,null);
452 protected Shadow.Kind getShadowKindForBody() {