| /********************************************************************** |
| * This file is part of "Object Teams Development Tooling"-Software |
| * |
| * Copyright 2004, 2006 Fraunhofer Gesellschaft, Munich, Germany, |
| * for its Fraunhofer Institute for Computer Architecture and Software |
| * Technology (FIRST), Berlin, Germany and Technical University Berlin, |
| * Germany. |
| * |
| * 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 |
| * $Id: AnchorListAttribute.java 23416 2010-02-03 19:59:31Z 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.bytecode; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.classfmt.MethodInfo; |
| import org.eclipse.jdt.internal.compiler.lookup.Binding; |
| import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; |
| 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.objectteams.otdt.core.compiler.IOTConstants; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.CallinCalloutScope; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.DependentTypeBinding; |
| 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.lookup.TThisBinding; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.DependentTypeBinding.IMethodProvider; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator; |
| |
| /** |
| * Keep a list of all type anchors of one method signature |
| * in order to make them persistent in class files. |
| * Later when reading the byte code, anchored types are restored from this list. |
| * Note that list positions are 1-based. 0 represents the return type. |
| * |
| * @author stephan |
| * @version $Id: AnchorListAttribute.java 23416 2010-02-03 19:59:31Z stephan $ |
| */ |
| public class AnchorListAttribute extends ListValueAttribute { |
| |
| private MethodBinding _method; |
| private char[][] _anchors; |
| private static final char[] NO_ANCHOR = "_OT$NoAnchor".toCharArray(); //$NON-NLS-1$ |
| private static final String ARG_ANCHOR_PREFIX = "_OT$param"; //$NON-NLS-1$ |
| |
| /** |
| * Create an attribute from source: |
| * @param method |
| */ |
| public AnchorListAttribute(MethodBinding method) { |
| super(IOTConstants.TYPE_ANCHOR_LIST, 0, 2); |
| this._method = method; |
| this._count = method.parameters.length+1; |
| } |
| |
| /** |
| * Create an attribute from byte code. |
| * |
| * (Invoked from AbstractAttribute.readAttribute(char[],MethodInfo,int,int,int[])) |
| */ |
| public AnchorListAttribute( |
| MethodInfo info, |
| int readOffset, |
| int structOffset, |
| int[] constantPoolOffsets) |
| { |
| super(IOTConstants.TYPE_ANCHOR_LIST, 0, 2); |
| this._methodInfo = info; |
| readList(info, readOffset, structOffset, constantPoolOffsets); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.ListValueAttribute#writeElementValue(int) |
| */ |
| @Override |
| void writeElementValue(int i) { |
| if (i==0) { |
| writeElement(this._method.returnType); |
| } else { |
| writeElement(this._method.parameters[i-1]); |
| } |
| } |
| |
| /** |
| * Write one list element: encode the type anchor as this: |
| * (a) NO_ANCHOR -> not an externalized role type |
| * (b) name -> best-name (path) to be resolved to a variable binding (see TeamAnchor.getBestName()) |
| * (c) _OT$param'n' -> argument 'n' of this method is the type anchor. |
| * |
| * @param type |
| */ |
| private void writeElement(TypeBinding type) { |
| if (type.isArrayType()) |
| type = type.leafComponentType(); |
| if (type instanceof DependentTypeBinding) { |
| DependentTypeBinding roleType = (DependentTypeBinding)type; |
| ITeamAnchor anchor = roleType._teamAnchor; |
| if (anchor instanceof TThisBinding) |
| writeName(NO_ANCHOR); |
| else { |
| if (roleType._argumentPosition > -1) |
| writeName((ARG_ANCHOR_PREFIX+roleType._argumentPosition).toCharArray()); |
| else |
| writeName(anchor.getBestName()); |
| } |
| } else { |
| writeName(NO_ANCHOR); |
| } |
| } |
| |
| /** |
| * PHASE 1 of reading: |
| * Read one list element from byte code: Read names and store |
| * them in our array _anchors. |
| * |
| * (Invoked from MethodInfo.readDeprecatedAndSyntheticAttributes |
| * .. -> <init>(MethodInfo,int,int,int[]) -> ListValueAttribute.readList() |
| * ) |
| */ |
| @Override |
| void read(int i) |
| { |
| if (i==0) |
| this._anchors = new char[this._count][]; |
| |
| char[] name = consumeName(); |
| if (!CharOperation.equals(name, NO_ANCHOR)) |
| this._anchors[i] = name; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.AbstractAttribute#evaluate(org.eclipse.jdt.internal.compiler.lookup.Binding, org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment) |
| */ |
| @Override |
| public void evaluate(Binding binding, LookupEnvironment environment, char[][][] missingTypeNames) { |
| assert(false); |
| } |
| |
| /** |
| * PHASE 2 or reading: |
| * Once the method binding is created, store the anchor list into the method binding. |
| */ |
| @Override |
| public boolean evaluate(MethodInfo info, MethodBinding methodBinding, LookupEnvironment environment) |
| { |
| if (info != this._methodInfo) return false; |
| this._method = methodBinding; |
| this._method.anchorList = this; |
| return true; |
| } |
| |
| /** |
| * PHASE 3 of reading: |
| * When types in the method signature are resolved, wrap them. |
| * |
| * (Hooked into RoleTypeBinding.wrapTypesInMethodSignature(..)). |
| */ |
| public void wrapTypes(LookupEnvironment environment) { |
| for (int i=0;i<this._count;i++) { |
| if (this._anchors[i] != null) { |
| if (i == 0) |
| this._method.returnType = |
| getWrappedType( |
| this._method.returnType, this._anchors[i], this._method.declaringClass, this._method, environment); |
| else |
| this._method.parameters[i-1] = |
| getWrappedType( |
| this._method.parameters[i-1], this._anchors[i], this._method.declaringClass, this._method, environment); |
| } |
| } |
| } |
| |
| /** |
| * @param typeToWrap ReferenceBinding or array thereof |
| * @param anchorName |
| * @param site |
| * @param declaringMethod where to look for arguments being used as type anchor. |
| * @param environment |
| * @return a wrapped version of typeToWrap |
| */ |
| private TypeBinding getWrappedType( |
| TypeBinding typeToWrap, char[] anchorName, |
| ReferenceBinding site, final MethodBinding declaringMethod, LookupEnvironment environment) |
| { |
| assert !CharOperation.equals(anchorName, NO_ANCHOR) : "NO_ANCHOR should have been filtered out"; //$NON-NLS-1$ |
| |
| ReferenceBinding type = (ReferenceBinding)typeToWrap.leafComponentType(); |
| if (CharOperation.prefixEquals(ARG_ANCHOR_PREFIX.toCharArray(), anchorName)) { |
| // Type anchored to another argument: |
| LocalVariableBinding anchor = new LocalVariableBinding( |
| anchorName, type.enclosingType(), 0, true); // name is irrelevant. |
| // make sure this anchor can answer `anchor.declaringScope.referenceMethodBinding()`: |
| anchor.declaringScope = new CallinCalloutScope(null, null) { |
| @Override |
| public MethodBinding referenceMethodBinding() { |
| return declaringMethod; |
| } |
| }; |
| TypeBinding wrappedType = anchor.getRoleTypeBinding(type, typeToWrap.dimensions()); |
| |
| // argument position is relevant: |
| char[] tail = CharOperation.subarray(anchorName, ARG_ANCHOR_PREFIX.length(), -1); |
| RoleTypeBinding wrappedRole = (RoleTypeBinding)wrappedType.leafComponentType(); |
| wrappedRole._argumentPosition = Integer.parseInt(String.valueOf(tail)); |
| wrappedRole._declaringMethod = new IMethodProvider() { |
| @Override |
| public MethodBinding getMethod() { return declaringMethod; } |
| }; |
| |
| return wrappedType; |
| } else { |
| return RoleTypeCreator.wrapTypeWithAnchorFromName(typeToWrap, anchorName, site, environment); |
| } |
| } |
| |
| /** |
| * API: after resolving a method, check whether we need to store an |
| * anchor list. |
| * |
| * @param decl |
| */ |
| public static void checkAddAnchorList(AbstractMethodDeclaration decl) { |
| if (decl.ignoreFurtherInvestigation) |
| return; |
| MethodBinding binding = decl.binding; |
| if (binding.anchorList != null) |
| return; |
| boolean relevantAnchorFound = false; |
| if (binding.returnType != null) |
| relevantAnchorFound = hasRelevantAnchor(binding.returnType); |
| int i=0; |
| while (!relevantAnchorFound && i<binding.parameters.length) { |
| relevantAnchorFound |= hasRelevantAnchor(binding.parameters[i++]); |
| } |
| if (relevantAnchorFound) { |
| MethodModel model = MethodModel.getModel(decl); |
| model.addAttribute(new AnchorListAttribute(binding)); |
| } |
| } |
| |
| /** |
| * Is `binding' an anchored type, whose anchor is not a TThisBinding? |
| */ |
| private static boolean hasRelevantAnchor(TypeBinding binding) { |
| if (binding.isArrayType()) |
| binding = binding.leafComponentType(); |
| if (binding.isBaseType()) |
| return false; |
| ReferenceBinding refBinding = (ReferenceBinding)binding; |
| if (!(refBinding instanceof DependentTypeBinding)) |
| return false; |
| ITeamAnchor anchor = ((DependentTypeBinding)refBinding)._teamAnchor; |
| return !(anchor instanceof TThisBinding); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.ListValueAttribute#toString(int) |
| */ |
| @Override |
| String toString(int i) { |
| if (this._anchors != null) { |
| if (this._anchors[i] == null) |
| return "<>"; //$NON-NLS-1$ |
| return new String(this._anchors[i]); |
| } |
| if (i==0) |
| return toString(this._method.returnType); |
| else |
| return toString(this._method.parameters[i-1]); |
| } |
| |
| private String toString(TypeBinding type) { |
| if (type instanceof RoleTypeBinding) |
| return ((RoleTypeBinding)type)._teamAnchor.toString(); |
| return "<>"; //$NON-NLS-1$ |
| } |
| } |