| /********************************************************************** |
| * This file is part of "Object Teams Development Tooling"-Software |
| * |
| * Copyright 2005, 2006 Fraunhofer Gesellschaft, Munich, Germany, |
| * for its Fraunhofer Institute for Computer Architecture and Software |
| * Technology (FIRST), Berlin, Germany and Technical University Berlin, |
| * Germany. |
| * |
| * 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 |
| * $Id: RoleClassLiteralAccess.java 23401 2010-02-02 23:56:05Z stephan $ |
| * |
| * Please visit http://www.eclipse.org/objectteams for updates and contact. |
| * |
| * Contributors: |
| * Fraunhofer FIRST - Initial API and implementation |
| * Technical University Berlin - Initial API and implementation |
| **********************************************************************/ |
| package org.eclipse.objectteams.otdt.internal.core.compiler.ast; |
| |
| import static org.eclipse.jdt.internal.compiler.lookup.Binding.NO_METHODS; |
| import static org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers.AccVisibilityMASK; |
| import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.GET_CLASS_PREFIX; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess; |
| import org.eclipse.jdt.internal.compiler.ast.Expression; |
| import org.eclipse.jdt.internal.compiler.ast.MessageSend; |
| import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; |
| import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; |
| import org.eclipse.jdt.internal.compiler.ast.Statement; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.eclipse.jdt.internal.compiler.ast.Wildcard; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.codegen.CodeStream; |
| import org.eclipse.jdt.internal.compiler.flow.FlowContext; |
| import org.eclipse.jdt.internal.compiler.flow.FlowInfo; |
| import org.eclipse.jdt.internal.compiler.impl.Constant; |
| import org.eclipse.jdt.internal.compiler.lookup.Binding; |
| import org.eclipse.jdt.internal.compiler.lookup.BlockScope; |
| import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; |
| import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; |
| import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstEdit; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator; |
| |
| |
| /** |
| * NEW for OTDT. |
| * |
| * This class specializes MyType.class expressions for role types. |
| * Such expressions must be delegated to a dynamically bound team method, |
| * in order to implement late binding of the role type. |
| * |
| * @author stephan |
| * @version $Id: RoleClassLiteralAccess.java 23401 2010-02-02 23:56:05Z stephan $ |
| */ |
| public class RoleClassLiteralAccess extends ClassLiteralAccess { |
| |
| MessageSend send; // transformed version of this access |
| ReferenceBinding teamBinding; // the team to call |
| |
| /** |
| * Construct a role class literal from a given class literal. |
| * @param orig plain class literal |
| * @param expressionType pre-computed type of the expression |
| */ |
| public RoleClassLiteralAccess(ClassLiteralAccess orig, TypeBinding expressionType) { |
| super(orig.sourceEnd, orig.type); |
| this.constant = orig.constant; |
| generateMessageSend(expressionType); |
| } |
| |
| /** |
| * Constructor for use by the Parser. |
| */ |
| public RoleClassLiteralAccess(SingleTypeReference typeRef) { |
| super(typeRef.sourceEnd, typeRef); |
| } |
| |
| private void generateMessageSend(TypeBinding expressionType) { |
| AstGenerator gen = new AstGenerator(this.type.sourceStart, this.sourceEnd); |
| ReferenceBinding roleBinding = (ReferenceBinding)this.type.resolvedType; |
| this.teamBinding = roleBinding.enclosingType(); |
| if (!roleBinding.isValidBinding()) { |
| this.resolvedType= expressionType; // pre-computed type of getclass method |
| return; // don't generate illegal message send |
| } |
| Expression receiver; |
| if (RoleTypeBinding.isRoleWithExplicitAnchor(roleBinding)) { |
| // create some.anchor._OT$getClass$R() |
| char[][] tokens = ((RoleTypeBinding)roleBinding)._teamAnchor.tokens(); |
| if (tokens.length == 1) |
| receiver= gen.singleNameReference(tokens[0]); |
| else |
| receiver= gen.qualifiedNameReference(tokens); |
| } else { |
| // create MyTeam.this._OT$getClass$R() |
| receiver= gen.qualifiedThisReference(this.teamBinding); |
| } |
| this.send = gen.messageSend( |
| receiver, |
| CharOperation.concat(GET_CLASS_PREFIX, roleBinding.sourceName()), |
| new Expression[] {} |
| ); |
| } |
| |
| @Override |
| public FlowInfo analyseCode( |
| BlockScope currentScope, |
| FlowContext flowContext, |
| FlowInfo flowInfo) |
| { |
| // simply delegate: |
| if (this.send != null) |
| return this.send.analyseCode(currentScope, flowContext, flowInfo); |
| return flowInfo; |
| } |
| |
| @Override |
| public void generateCode( |
| BlockScope currentScope, |
| CodeStream codeStream, |
| boolean valueRequired) |
| { |
| // simply delegate: |
| if (this.send != null) |
| this.send.generateCode(currentScope, codeStream, valueRequired); |
| else if (valueRequired) |
| codeStream.aconst_null(); |
| } |
| |
| @Override |
| public TypeBinding resolveType(BlockScope scope) { |
| if (this.type instanceof ParameterizedSingleTypeReference) { |
| // directly created from parsing R<@t>.class |
| this.constant = Constant.NotAConstant; |
| |
| ParameterizedSingleTypeReference typeRef = (ParameterizedSingleTypeReference)this.type; |
| if (typeRef.token == null) { |
| // syntax error, however, we can still check the type anchor: |
| if (typeRef.typeArguments != null) |
| for (TypeReference anchors : typeRef.typeArguments) |
| ((TypeAnchorReference)anchors).resolveAnchor(scope); |
| return null; |
| } |
| TypeBinding roleType = this.type.resolveType(scope); |
| if (roleType != null && roleType.isValidBinding()) |
| generateMessageSend(roleType); |
| } |
| // clients might still use this (obsolete) field? |
| this.targetType = this.type.resolvedType; // is already resolved by the original ClassLiteralAccess |
| // and delegate: |
| if (this.send != null) |
| this.resolvedType = this.send.resolveType(scope); |
| return this.resolvedType; |
| } |
| |
| /** |
| * API for Dependencies: |
| * |
| * Generate for each role (bound or unbound): |
| * Class _OT$getClass$<roleName>() |
| * and add it to the enclosing team. |
| * @param teamModel team to add the method to |
| * @param roleModel the role whose class should be accessed |
| */ |
| public static TypeBinding ensureGetClassMethod( |
| TeamModel teamModel, |
| RoleModel roleModel) |
| { |
| TypeDeclaration teamDecl = teamModel.getAst(); |
| ReferenceBinding teamBinding = teamModel.getBinding(); |
| TypeDeclaration roleDecl = roleModel.getAst(); |
| ReferenceBinding roleBinding = roleModel.getBinding(); |
| char[] selector = CharOperation.concat(GET_CLASS_PREFIX, roleBinding.sourceName()); |
| TypeBinding result = ensureGetClassMethodPart(teamDecl, teamBinding, roleDecl, roleBinding, selector); |
| if (teamBinding.isRole()) { |
| TypeDeclaration teamIfc = teamBinding.roleModel.getInterfaceAst(); |
| if (teamIfc != null) |
| ensureGetClassMethodPart(teamIfc, teamIfc.binding, roleDecl, roleBinding, selector); |
| } |
| return result; |
| } |
| |
| private static TypeBinding ensureGetClassMethodPart(TypeDeclaration teamDecl, ReferenceBinding teamBinding, |
| TypeDeclaration roleDecl, ReferenceBinding roleBinding, char[] selector) |
| { |
| MethodBinding[] existingMethods = teamBinding.getMethods(selector); |
| if (existingMethods != NO_METHODS) |
| return existingMethods[0].returnType; // already generated |
| if (teamDecl == null) |
| throw new InternalCompilerError("Requesting to generate a method for binary type "+String.valueOf(teamBinding.readableName())); //$NON-NLS-1$ |
| AstGenerator gen; |
| if (roleDecl != null) |
| gen = new AstGenerator(roleDecl.scope.compilerOptions().sourceLevel, roleDecl.sourceStart, roleDecl.sourceEnd); |
| else |
| gen = new AstGenerator(teamDecl.scope.compilerOptions().sourceLevel, teamDecl.sourceStart, teamDecl.sourceEnd); |
| |
| // return type reference is either "Class<Role>" or "Class<Role<?,?...>>" |
| TypeVariableBinding[] typeVariables = roleBinding.typeVariables(); |
| TypeReference roleTypeRef; |
| if (typeVariables != Binding.NO_TYPE_VARIABLES) { |
| Wildcard[] wildcards = new Wildcard[typeVariables.length]; |
| for (int i = 0; i < typeVariables.length; i++) |
| wildcards[i] = gen.wildcard(Wildcard.UNBOUND); |
| roleTypeRef = gen.parameterizedSingleTypeReference(roleBinding.sourceName(), wildcards, 0); |
| } else { |
| roleTypeRef = gen.singleTypeReference(roleBinding.sourceName()); |
| } |
| TypeReference[] typeArguments = new TypeReference[] {roleTypeRef}; |
| |
| MethodDeclaration method = gen.method(teamDecl.compilationResult, |
| (teamBinding.isRole()) |
| ? ClassFileConstants.AccPublic // advertized via ifc, must be public |
| : roleBinding.modifiers & AccVisibilityMASK, |
| gen.parameterizedQualifiedTypeReference( // java.lang.Class<R> |
| TypeConstants.JAVA_LANG_CLASS, |
| typeArguments), |
| selector, |
| null); |
| if (teamBinding.isInterface()) |
| method.modifiers |= ClassFileConstants.AccAbstract|ExtraCompilerModifiers.AccSemicolonBody; |
| else |
| method.setStatements(new Statement[] { |
| gen.returnStatement(new ClassLiteralAccess(gen.sourceEnd, gen.singleTypeReference(roleBinding.sourceName()), true)) |
| }); |
| AstEdit.addMethod(teamDecl, method); |
| return method.returnType.resolvedType; |
| } |
| } |