/* ******************************************************************* * Copyright (c) 2010 Contributors * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Eclipse Public License v 2.0 * which accompanies this distribution and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt * * Contributors: * Andy Clement - SpringSource * ******************************************************************/ package org.aspectj.ajdt.internal.compiler.ast; import java.lang.reflect.Modifier; import java.util.Collections; import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory; import org.aspectj.ajdt.internal.compiler.lookup.EclipseSourceLocation; import org.aspectj.ajdt.internal.compiler.lookup.EclipseTypeMunger; import org.aspectj.ajdt.internal.compiler.lookup.InterTypeScope; import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation; import org.aspectj.org.eclipse.jdt.internal.compiler.ClassFile; import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.NestedTypeBinding; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import org.aspectj.weaver.AjAttribute; import org.aspectj.weaver.NewMemberClassTypeMunger; import org.aspectj.weaver.ResolvedType; import org.aspectj.weaver.ResolvedTypeMunger; /** * Represents an intertype member class declaration. * * @author Andy Clement * @since 1.6.9 */ public class IntertypeMemberClassDeclaration extends TypeDeclaration { // The target type for this inner class private TypeReference onType; private ReferenceBinding onTypeResolvedBinding; private NewMemberClassTypeMunger newMemberClassTypeMunger; protected InterTypeScope interTypeScope; // When set to true, the scope hierarchy for the field/method declaration has been correctly modified to include an intertype // scope which resolves things relative to the targeted type. private boolean scopeSetup = false; public IntertypeMemberClassDeclaration(CompilationResult compilationResult) { super(compilationResult); } public ResolvedTypeMunger getMunger() { return newMemberClassTypeMunger; } @Override public void resolve(ClassScope aspectScope) { resolveOnType(aspectScope); ensureScopeSetup(); super.resolve(aspectScope); } /** * Bytecode generation for a member inner type */ /* * public void generateCode(ClassScope classScope, ClassFile enclosingClassFile) { if ((this.bits & ASTNode.HasBeenGenerated) != * 0) { return; } try { Field f = ReferenceBinding.class.getDeclaredField("constantPoolName"); char[] name = * CharOperation.concat(onTypeResolvedBinding.constantPoolName(), binding.sourceName, '$'); f.setAccessible(true); * f.set(this.binding, name); } catch (Exception e) { e.printStackTrace(); } if (this.binding != null) { ((NestedTypeBinding) * this.binding).computeSyntheticArgumentSlotSizes(); } generateCode(enclosingClassFile); } */ @Override public void resolve() { super.resolve(); } @Override public void resolve(BlockScope blockScope) { throw new IllegalStateException(); } @Override public void resolve(CompilationUnitScope upperScope) { throw new IllegalStateException(); } @SuppressWarnings("unchecked") @Override protected void generateAttributes(ClassFile classFile) { // classFile.extraAttributes.add(new EclipseAttributeAdapter(makeAttribute())); super.generateAttributes(classFile); } public AjAttribute getAttribute() { // if there were problems then there is nothing to return if (newMemberClassTypeMunger == null) { return null; } return new AjAttribute.TypeMunger(newMemberClassTypeMunger); } /** * Called just before the compiler is going to start resolving elements of a declaration, this method adds an intertype scope so * that elements of the type targeted by the ITD can be resolved. For example, if type variables are referred to in the ontype * for the ITD, they have to be resolved against the ontype, not the aspect containing the ITD. */ public void ensureScopeSetup() { if (scopeSetup) { return; // don't do it again } ClassScope scope = this.scope; // TODO [inner] ton of stuff related to parameterization support // if (ot instanceof ParameterizedQualifiedTypeReference) { // pr132349 // ParameterizedQualifiedTypeReference pref = (ParameterizedQualifiedTypeReference) ot; // if (pref.typeArguments != null && pref.typeArguments.length != 0) { // boolean usingNonTypeVariableInITD = false; // // Check if any of them are not type variables // for (int i = 0; i < pref.typeArguments.length; i++) { // TypeReference[] refs = pref.typeArguments[i]; // for (int j = 0; refs != null && j < refs.length; j++) { // TypeBinding tb = refs[j].getTypeBindingPublic(scope.parent); // if (!tb.isTypeVariable() && !(tb instanceof ProblemReferenceBinding)) { // usingNonTypeVariableInITD = true; // } // // } // } // if (usingNonTypeVariableInITD) { // scope.problemReporter().signalError(sourceStart, sourceEnd, // "Cannot make inter-type declarations on parameterized types"); // // to prevent disgusting cascading errors after this problem - lets null out what leads to them (pr105038) // this.arguments = null; // this.returnType = new SingleTypeReference(TypeReference.VOID, 0L); // // this.ignoreFurtherInvestigation = true; // ReferenceBinding closestMatch = null; // rb = new ProblemReferenceBinding(ot.getParameterizedTypeName(), closestMatch, 0); // onType = null; // } // } // // } // // Work out the real base type // if (onType instanceof ParameterizedSingleTypeReference) { // ParameterizedSingleTypeReference pref = (ParameterizedSingleTypeReference) ot; // long pos = (((long) pref.sourceStart) << 32) | pref.sourceEnd; // ot = new SingleTypeReference(pref.token, pos); // } else if (ot instanceof ParameterizedQualifiedTypeReference) { // ParameterizedQualifiedTypeReference pref = (ParameterizedQualifiedTypeReference) ot; // long pos = (((long) pref.sourceStart) << 32) | pref.sourceEnd; // ot = new QualifiedTypeReference(pref.tokens, new long[] { pos });// SingleTypeReference(pref.Quatoken,pos); // } // resolve it // if (rb == null) { // rb = (ReferenceBinding) ot.getTypeBindingPublic(scope.parent); // } // pr203646 - if we have ended up with the raw type, get back to the underlying generic one. // if (rb.isRawType() && rb.isMemberType()) { // // if the real target type used a type variable alias then we can do this OK, but need to switch things around, we want // // the generic type // rb = ((RawTypeBinding) rb).type; // } // if (rb instanceof TypeVariableBinding) { // scope.problemReporter().signalError(sourceStart, sourceEnd, // "Cannot make inter-type declarations on type variables, use an interface and declare parents"); // // to prevent disgusting cascading errors after this problem - lets null out what leads to them (pr105038) // this.arguments = null; // this.returnType = new SingleTypeReference(TypeReference.VOID, 0L); // // this.ignoreFurtherInvestigation = true; // ReferenceBinding closestMatch = null; // if (((TypeVariableBinding) rb).firstBound != null) { // closestMatch = ((TypeVariableBinding) rb).firstBound.enclosingType(); // } // rb = new ProblemReferenceBinding(rb.compoundName, closestMatch, 0); // } // if resolution failed, give up - someone else is going to report an error // if (rb instanceof ProblemReferenceBinding) { // return; // } if (scope != null) { interTypeScope = new InterTypeScope(scope.parent, onTypeResolvedBinding, Collections.emptyList()); // FIXME asc verify the choice of lines here... // Two versions of this next line. // First one tricks the JDT variable processing code so that it won't complain if // you refer to a type variable from a static ITD - it *is* a problem and it *will* be caught, but later and // by the AJDT code so we can put out a much nicer message. // scope.isStatic = (typeVariableAliases != null ? false : Modifier.isStatic(declaredModifiers)); // this is the original version in case tricking the JDT causes grief (if you reinstate this variant, you // will need to change the expected messages output for some of the generic ITD tests) // scope.isStatic = Modifier.isStatic(declaredModifiers); // Use setter in order to also update member 'compilationUnitScope' scope.setParent(interTypeScope); } scopeSetup = true; } public void setOnType(TypeReference onType) { this.onType = onType; } private void resolveOnType(ClassScope cuScope) { if (onType == null || onTypeResolvedBinding != null) { return; } // error reported elsewhere. onTypeResolvedBinding = (ReferenceBinding) onType.getTypeBindingPublic(cuScope); if (!onTypeResolvedBinding.isValidBinding()) { cuScope.problemReporter().invalidType(onType, onTypeResolvedBinding); ignoreFurtherInvestigation = true; } else { // fix up the ITD'd type? if (this.binding != null) { ((NestedTypeBinding) this.binding).enclosingType = (SourceTypeBinding) onTypeResolvedBinding; } // this done at build type for the nested type now: // ((NestedTypeBinding) this.binding).compoundName = CharOperation.splitOn('.', "Basic$_".toCharArray()); } } public EclipseTypeMunger build(ClassScope classScope) { EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(classScope); resolveOnType(classScope); ensureScopeSetup(); if (ignoreFurtherInvestigation) { return null; } if (onTypeResolvedBinding.isInterface() || onTypeResolvedBinding.isEnum() || onTypeResolvedBinding.isAnnotationType()) { scope.problemReporter().signalError( sourceStart, sourceEnd, "Cannot declare new member type on '" + onType.toString() + "'. New member types can only be specified on classes (compiler limitation)"); return null; } if (!Modifier.isStatic(modifiers)) { scope.problemReporter().signalError(sourceStart, sourceEnd, "Intertype declared member types can only be static (compiler limitation)"); return null; } ResolvedType declaringType = world.fromBinding(onTypeResolvedBinding).resolve(world.getWorld()); if (declaringType.isRawType() || declaringType.isParameterizedType()) { declaringType = declaringType.getGenericType(); } if (interTypeScope == null) { return null; // We encountered a problem building the scope, don't continue - error already reported } // TODO [inner] use the interTypeScope.getRecoveryAliases // TODO [inner] should mark it in the aspect as unreachable - it is not to be considered part of the aspect newMemberClassTypeMunger = new NewMemberClassTypeMunger(declaringType, new String(this.name)); newMemberClassTypeMunger.setSourceLocation(new EclipseSourceLocation(compilationResult, sourceStart, sourceEnd)); ResolvedType aspectType = world.fromEclipse(classScope.referenceContext.binding); return new EclipseTypeMunger(world, newMemberClassTypeMunger, aspectType, null); } public char[] alternativeName() { return CharOperation.concatWith(onType.getTypeName(),'.');//onType.getLastToken(); } }