| /********************************************************************** |
| * This file is part of "Object Teams Development Tooling"-Software |
| * |
| * Copyright 2004, 2013 Fraunhofer Gesellschaft, Munich, Germany, |
| * for its Fraunhofer Institute for Computer Architecture and Software |
| * Technology (FIRST), Berlin, Germany and Technical University Berlin, |
| * Germany and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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.mappings; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.Argument; |
| import org.eclipse.jdt.internal.compiler.ast.CastExpression; |
| import org.eclipse.jdt.internal.compiler.ast.Expression; |
| import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; |
| import org.eclipse.jdt.internal.compiler.ast.Statement; |
| import org.eclipse.jdt.internal.compiler.ast.TypeParameter; |
| import org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme; |
| import org.eclipse.jdt.internal.compiler.lookup.Binding; |
| import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.Scope; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; |
| import org.eclipse.objectteams.otdt.core.compiler.IOTConstants; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.AbstractMethodMappingDeclaration; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CallinMappingDeclaration; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CalloutMappingDeclaration; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.FieldAccessSpec; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec.ImplementationStrategy; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TypeAnchorReference; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor; |
| 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.statemachine.transformer.MethodSignatureEnhancer; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.ReplaceResultReferenceVisitor; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstClone; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator; |
| |
| /** |
| * moved here from package org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.resolve |
| * |
| * Generalize some functions of implementing callout and callin. |
| * |
| * @author stephan |
| */ |
| public abstract class MethodMappingImplementor { |
| |
| // discriminate callout/in by this flag: |
| int bindingDirection; |
| |
| // use this if source positions must be mapped between roleFile and enclosing team: |
| protected AstGenerator synthGen; |
| |
| protected RoleModel _role; |
| |
| private MethodSignatureEnhancer methodSignatureEnhancer; |
| |
| public MethodMappingImplementor(RoleModel role) { |
| this._role = role; |
| } |
| // for subclass CallinImplementorDyn, which uses one role per transform call. |
| protected MethodMappingImplementor() { |
| // empty |
| } |
| |
| MethodSignatureEnhancer getMethodSignatureEnhancer() { |
| if (this.methodSignatureEnhancer == null) |
| this.methodSignatureEnhancer = MethodSignatureEnhancer.variants[this._role.getWeavingScheme().ordinal()]; |
| return this.methodSignatureEnhancer; |
| } |
| |
| /** |
| * Make arguments for message send which implements a callout or a callin wrapper. |
| * For a callout mapping this method is used only if signatures are given |
| * (and possible param-mappings). |
| * |
| * @param methodMapping here the method mapping is declared. |
| * @param wrapperMethodDeclaration provided parameters are taken from here |
| * @param sourceMethodSpec <ul> |
| * <li>look here for param-positions (see AbstractMethodMappingDeclaration.positions) |
| * <li>and check the need of lifting.</ul> |
| * @param baseFieldSpec |
| * if we don't call a method but access a field |
| * which is emulated by a static base method. |
| * @param hasResultArgument |
| * has a "result" argument been added to the wrapper? |
| * (used to access the base-method-result within a after-callin (param-mapping+predicate)) |
| */ |
| Expression[] makeWrapperCallArguments( |
| AbstractMethodMappingDeclaration methodMapping, |
| MethodDeclaration wrapperMethodDeclaration, |
| MethodSpec sourceMethodSpec, |
| FieldAccessSpec baseFieldSpec, |
| boolean hasResultArgument) |
| { |
| // prepare parameter mappings: |
| if (methodMapping.mappings != null) { |
| methodMapping.traverse(new ReplaceResultReferenceVisitor(methodMapping), methodMapping.scope.classScope()); |
| if (methodMapping.isReplaceCallin()) |
| ((CallinMappingDeclaration)methodMapping).checkResultMapping(); |
| } |
| |
| Argument[] wrapperMethodArguments = wrapperMethodDeclaration.arguments; |
| Expression[] arguments = null; |
| |
| boolean hasArgError = false; |
| |
| // parameters of the implemented method drive the loop, |
| // additional provided arguments may be ignored. |
| |
| // (Typechecking is left to regular resolve methods.) |
| |
| TypeBinding[] implParameters = getImplementationParamters( |
| methodMapping, wrapperMethodDeclaration); |
| |
| int implementationArgLen = implParameters.length; |
| |
| int expressionsOffset = 0; |
| if (baseFieldSpec != null) { |
| if (baseFieldSpec.implementationStrategy == ImplementationStrategy.DYN_ACCESS) { |
| // in decapsulation scenarios OTREDyn uses non-static accessor for non-static fields |
| if (!baseFieldSpec.isSetter()) |
| implementationArgLen = 0; // if resolved to a 4-arg _OT$access, don't consider these args during AST gen. |
| } else if (this._role.getWeavingScheme() == WeavingScheme.OTRE |
| && !((FieldAccessSpec)methodMapping.getBaseMethodSpecs()[0]).isStatic()) { |
| // for OTRE, non-static field access is mapped to static method with additional first parameter _OT$base: |
| expressionsOffset = 1; |
| |
| ReferenceBinding baseType = methodMapping.scope.enclosingSourceType().baseclass(); |
| arguments = new Expression[implementationArgLen+expressionsOffset]; |
| // TODO(SH): generalize this and the corresponding statement in |
| // CalloutImplementor.makeArguments(). |
| // cast needed against weakened _OT$base reference. |
| MethodSpec baseSpec = ((CalloutMappingDeclaration)methodMapping).baseMethodSpec; |
| AstGenerator gen = new AstGenerator(baseSpec); |
| arguments[0] = new CastExpression( |
| gen.singleNameReference(IOTConstants._OT_BASE), |
| gen.baseclassReference(baseType), |
| baseType.isRole() ? CastExpression.NEED_CLASS : CastExpression.RAW); // FIXME (see also CalloutImplementor.makeArguments) |
| } |
| } |
| if (arguments == null) { |
| arguments = new Expression[implementationArgLen]; |
| } |
| |
| // bind argument because getArgument might want to link bestnames |
| // (need to bind all arguments in order!) |
| if ( wrapperMethodArguments != null |
| && wrapperMethodDeclaration.binding.parameters != Binding.NO_PARAMETERS) |
| for (int idx = 0; idx < wrapperMethodArguments.length; idx++) |
| wrapperMethodArguments[idx].bind( |
| wrapperMethodDeclaration.scope, |
| wrapperMethodDeclaration.binding.parameters[idx], |
| /*used*/false); |
| |
| for(int idx = 0; idx < implementationArgLen; idx++) |
| { |
| arguments[idx+expressionsOffset] = getArgument( |
| methodMapping, |
| wrapperMethodDeclaration, |
| implParameters, |
| idx, |
| hasResultArgument, |
| sourceMethodSpec); |
| if (arguments[idx+expressionsOffset] == null) |
| hasArgError = true; // keep going to check usage of arguments in mapping |
| } |
| |
| if (hasArgError) { |
| wrapperMethodDeclaration.statements = new Statement[0]; // leave a clean state. |
| return null; |
| } |
| return arguments; |
| } |
| /** |
| * Get the parameters of the implemented method to be called from the wrapper. |
| * Overridden in CallinImplementor to account for signature enhancing |
| * |
| * @param methodMapping |
| * @param wrapperMethod |
| * @return types array |
| */ |
| TypeBinding[] getImplementationParamters(AbstractMethodMappingDeclaration methodMapping, MethodDeclaration wrapperMethod) |
| { |
| return methodMapping.getImplementationMethodSpec().resolvedParameters(); |
| } |
| |
| /** If original is a type variable or contains a type variable replace that type variable |
| * with a corresponding type variable from variables. |
| * This method is used to adjust the scope of type variables that originally were |
| * resolved in the method mappings scope, but should be resolved in the wrapper method scope. |
| */ |
| TypeBinding substituteVariables(TypeBinding original, TypeVariableBinding[] variables) { |
| if (original.isTypeVariable()) { |
| for (int i = 0; i < variables.length; i++) |
| if (CharOperation.equals(original.internalName(), variables[i].sourceName)) |
| return variables[i]; |
| } else if (original.isParameterizedType()) { |
| ParameterizedTypeBinding pt= (ParameterizedTypeBinding)original; |
| TypeBinding[] args= pt.arguments; |
| if (args != null) { |
| int l= args.length; |
| System.arraycopy(args, 0, args= new TypeBinding[l], 0, l); |
| boolean changed= false; |
| for (int i= 0; i < l; i++) { |
| TypeBinding tb= substituteVariables(args[i], variables); |
| if (TypeBinding.notEquals(tb, args[i])) { |
| args[i] = tb; |
| changed= true; |
| } |
| } |
| if (changed) |
| return new ParameterizedTypeBinding((ReferenceBinding)pt.erasure(), args, pt.enclosingType(), pt.environment); |
| } |
| } |
| return original; |
| } |
| |
| /** |
| * Map one argument to yield the expression to pass to the implemented method. |
| * |
| * @param methodMapping lookup method spec and parameter mapping here |
| * @param wrapperDeclaration use args from this method if no mapping is involved |
| * @param implParameters parameters of the implemented method to invoke |
| * @param idx argument position on the target side |
| * @param hasResultArgument as a 'result' argument been prepended to the wrapper args? |
| * @param sourceMethodSpec this signature defines the provided args |
| * @return an argument expression |
| */ |
| Expression getArgument( |
| AbstractMethodMappingDeclaration methodMapping, |
| MethodDeclaration wrapperDeclaration, |
| TypeBinding[] implParameters, |
| int idx, |
| boolean hasResultArgument, |
| MethodSpec sourceMethodSpec) |
| {return null;} |
| |
| /** |
| * Make a copy of the given "arguments" |
| * @param arguments types for arguments to be generated |
| * @param declaredMethodSpec try to lookup names and positions from here. |
| * @return array of arguments with given types an names: |
| * methodSpec-argument name if available or OT$argN (N ascending) |
| */ |
| protected Argument[] copyArguments(AstGenerator gen, Scope scope, TypeBinding[] arguments, MethodSpec declaredMethodSpec) |
| { |
| if(arguments == null || arguments.length == 0) |
| return null; |
| |
| ITeamAnchor baseSideAnchor = RoleTypeCreator.getPlayedByAnchor(scope); |
| |
| boolean useDeclaredArgs = (declaredMethodSpec.arguments != null) && (declaredMethodSpec.arguments.length == arguments.length); |
| Argument[] result = new Argument[arguments.length]; |
| for (int idx = 0; idx < arguments.length; idx++) |
| { |
| char[] argName; |
| long pos = (((long)declaredMethodSpec.sourceStart)<<32) + declaredMethodSpec.sourceEnd; |
| int argModifiers = ClassFileConstants.AccFinal; // in case an argument serves as a type anchor. |
| TypeReference argTypeReference = null; |
| if(useDeclaredArgs) { |
| Argument argument = declaredMethodSpec.arguments[idx]; |
| argName = argument.name; |
| pos = (((long)argument.sourceStart) << 32) + argument.sourceEnd; |
| argModifiers = argument.modifiers; |
| // for callout prefer source-level args (from method spec) to avoid resolved/unresolved conflicts downstream |
| if (this.bindingDirection == AbstractMethodMappingDeclaration.BindingDirectionOut) |
| argTypeReference = AstClone.copyTypeReference(argument.type); |
| } else { |
| argName = (IOTConstants.OT_DOLLAR_ARG + idx).toCharArray(); |
| } |
| TypeBinding argType = arguments[idx]; |
| if (this.bindingDirection == AbstractMethodMappingDeclaration.BindingDirectionIn) { // only relevant for callin |
| argTypeReference = getAnchoredTypeReference(gen, baseSideAnchor, argType); |
| } |
| if (argTypeReference == null) { |
| argTypeReference = gen.typeReference(argType); |
| } |
| // don't use AstGenerator: hand-crafted source position: |
| result[idx] = new Argument(argName, pos, argTypeReference, argModifiers); |
| } |
| return result; |
| } |
| /** |
| * Try whether type is a role type to be interpreted relative to the anchor of a "playedBy t.R" |
| * @param gen |
| * @param baseSideAnchor |
| * @param type |
| * @return an anchored type reference or null |
| */ |
| TypeReference getAnchoredTypeReference(AstGenerator gen, ITeamAnchor baseSideAnchor, TypeBinding type) |
| { |
| TypeReference argTypeReference = null; |
| int dims = type.dimensions(); |
| type = type.leafComponentType(); |
| if (type instanceof RoleTypeBinding) { |
| RoleTypeBinding roleTypeArg = (RoleTypeBinding)type; |
| if (roleTypeArg.hasExplicitAnchor()) |
| { |
| // originally anchored to _OT$base? change to 'base' now: |
| if (roleTypeArg._teamAnchor.isBaseAnchor()) |
| { |
| // witness: 4.3.1-otjld-parameter-mapping-11a |
| TypeReference anchorRef = new TypeAnchorReference( |
| gen.singleNameReference(IOTConstants.BASE), gen.sourceStart); |
| argTypeReference = new ParameterizedSingleTypeReference( |
| roleTypeArg.internalName(), |
| new TypeReference[] {anchorRef}, |
| dims, |
| gen.pos); |
| } |
| } else if (baseSideAnchor != null) { |
| // witness in 1.1.22-otjld-layered-teams-1, B.1.1-otjld-sh-42 |
| ReferenceBinding baseSideTeam = (ReferenceBinding)baseSideAnchor.getResolvedType(); |
| if (TeamModel.isTeamContainingRole(baseSideTeam, roleTypeArg)) |
| argTypeReference = gen.roleTypeReference(baseSideAnchor, roleTypeArg, dims); |
| } |
| } |
| return argTypeReference; |
| } |
| |
| /** |
| * Generate a single name argument expression. |
| * @param argName |
| * @param targetMethodSpec (just for source positions ..) |
| * @return an argument reference |
| */ |
| Expression genSimpleArgExpr(char[] argName, MethodSpec targetMethodSpec) { |
| AstGenerator gen = this.synthGen != null |
| ? this.synthGen |
| : new AstGenerator(targetMethodSpec.sourceStart, targetMethodSpec.sourceEnd); |
| return gen.singleNameReference(argName); |
| } |
| protected TypeParameter[] getTypeParameters(boolean hasSignature, MethodBinding rrrBinding, MethodSpec roleMethodSpec, |
| final AstGenerator gen) { |
| TypeParameter[] typeParams = null; |
| if (hasSignature) { |
| // from method spec: |
| typeParams= roleMethodSpec.typeParameters; |
| if (typeParams != null) |
| typeParams= AstClone.copyTypeParameters(typeParams); |
| } else { |
| // from role method: |
| TypeVariableBinding[] typeVariables = rrrBinding.typeVariables; |
| if (typeVariables != Binding.NO_TYPE_VARIABLES) { |
| typeParams= new TypeParameter[typeVariables.length]; |
| for (int i = 0; i < typeVariables.length; i++) |
| typeParams[i]= gen.typeParameter(typeVariables[i]); |
| } |
| } |
| return typeParams; |
| } |
| |
| |
| } |