Skip to main content
summaryrefslogtreecommitdiffstats
blob: 9ebd25fbd711fb4f0ea0bfbe70c177c6857d2c08 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/**********************************************************************
 * 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.
 *
 * 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: PotentialLiftExpression.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 org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.DeclaredLifting;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.Lifting;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;


/**
 * NEW for OTDT.
 *
 * A wrapper for an expression that might or might not require lifting.
 *
 * Note: all elements generated in StandardElementGenerator.liftCall() must be
 * resolved manually, since PotentialLiftExpression must already resolve the expression.
 * So the wrapping expression can not use resolveType(..) again.
 *
 * @author stephan
 */
public class PotentialLiftExpression extends PotentialTranslationExpression {

    private Expression teamExpr;

    public boolean requireReverseOperation = false;

    // if a PotentialLiftExpression was created on behalf of a parameter mapping (callin),
    // we remember the responsible baseMethodSpec so that our analysis whether lifting is
    // indeed needed can be propagated via the MethodSpec to the CallinMethodMappingsAttribute.
    // Update (1.3.0M3): This is now done by a registered job (callback):
	private Runnable liftingConfirmJob;
	
	private TypeReference expectedTypeReference = null;

	/** Job to perform if analysis confirms that lifting is required. */
	public void onLiftingRequired(Runnable job) {
		this.liftingConfirmJob = job;		
	}

    /**
     * Create a wrapper for an expression that might require lifting.
     *
     * @param teamExpr     expression suitable as call target for the lift call.
     * @param expression   expression to be wrapped
     * @param expectedType what the context expects from this expression
     */
	public PotentialLiftExpression(
			Expression  teamExpr,
			Expression  expression,
			TypeBinding expectedType)
	{
		super(expression, expectedType);
		this.operator = "lift"; //$NON-NLS-1$
		this.teamExpr = teamExpr;
	}
	
    /**
     * Create a wrapper for an expression that might require lifting.
     *
     * @param teamExpr        expression suitable as call target for the lift call.
     * @param expression      expression to be wrapped
     * @param expectedTypeRef what the context expects from this expression
     */
	public PotentialLiftExpression(
			Expression    teamExpr,
			Expression    expression,
			TypeReference expectedTypeRef)
	{
		super(expression, null);
		this.expectedTypeReference = expectedTypeRef;
		this.operator = "lift"; //$NON-NLS-1$
		this.teamExpr = teamExpr;
	}

	public TypeBinding resolveType(BlockScope scope)
    {
    	this.constant = Constant.NotAConstant;
        TypeBinding providedType = this.expression.resolveType(scope);
        if (providedType == null)
        	return null; // no chance
        if (providedType.isParameterizedType())
        	providedType = providedType.erasure();
        
        if (this.expectedType == null)
        	this.expectedType = this.expectedTypeReference.resolvedType;
        this.resolvedType = this.expectedType; // be optimistic.
        this.checked = true;
        TypeBinding compatibleType = compatibleType(scope, providedType);
        if (compatibleType != null)
        	return compatibleType;

        TypeBinding roleSideType;
        TypeBinding baseType;
        if (providedType.isArrayType()) {
            if (!(this.expectedType instanceof ArrayBinding))
                throw new InternalCompilerError("mapping array to scalar"); //$NON-NLS-1$
            baseType = providedType.leafComponentType();
            roleSideType     = this.expectedType.leafComponentType();
        } else {
            baseType = providedType;
            roleSideType     = this.expectedType;
        }
        ReferenceBinding roleType = null;
        if (!(baseType instanceof ReferenceBinding)) {
        	return reportIncompatibility(scope, providedType);
        } else {
            roleType = (ReferenceBinding)roleSideType;
            if (   !roleType.isDirectRole()
                || !(baseType instanceof ReferenceBinding))
            	return reportIncompatibility(scope, providedType);
        }

        // reset Config, because below we want to check loweringRequired.
        Config oldConfig = Config.createOrResetConfig(this);
        try {
	        ReferenceBinding expectedBase = roleType.baseclass();
	        if(expectedBase != null && expectedBase.isParameterizedType())
	        	expectedBase = (ReferenceBinding) expectedBase.erasure();
			if (   expectedBase == null							// roleType is assigned unless incompatibilityFound, in which case we return above
	        	|| !baseType.isCompatibleWith(expectedBase))
	        {
	        	TypeBinding adjustedRole = TeamModel.getRoleToLiftTo(scope, baseType, roleType, true, this.expression);
	        	if (adjustedRole != null) {
	        		this.expectedType = adjustedRole;
	        	} else {
	        		if (providedType.isTypeVariable()) {
	        			ReferenceBinding roleBound = ((TypeVariableBinding)providedType).roletype;
	        			if (roleBound != null && roleBound.isRole()) {
	        				generateDynamicLiftCall(scope, roleBound);
	        				return this.resolvedType;
	        			}
	        		}

		            scope.problemReporter().typeMismatchErrorPotentialLift(
		                    this.expression, providedType, this.expectedType, baseType);
		            this.resolvedType = null;
		            return null;
	        	}
	        }
	        if (   roleType.isHierarchyInconsistent()
	        	|| roleType.roleModel.hasBaseclassProblem())
	        {
	        	// don't install unresolvable liftcall
	        	scope.problemReporter().referenceContext.tagAsHavingErrors();
	        	return this.resolvedType;
	        }
	        if (Config.getLoweringRequired())
	            throw new InternalCompilerError("Lifting would also require lowering!"); //$NON-NLS-1$
        } finally {
        	Config.removeOrRestore(oldConfig, this);
        }

        TypeBinding expectedBaseclass = ((ReferenceBinding)this.expectedType.leafComponentType()).baseclass();
        if (this.expectedType.isArrayType())
        	expectedBaseclass = new ArrayBinding(expectedBaseclass, this.expectedType.dimensions(), scope.environment());
		// further conversions (cast for generic)?
		checkOtherConversions(scope, expectedBaseclass, providedType);

        // successfully recognized the need for lifting, create the lift-call now:

		// propagate the need for translation:
		if (this.liftingConfirmJob != null)
			this.liftingConfirmJob.run();

        this.rawExpression = this.expression;
        this.operator = "lift"; // redundant; //$NON-NLS-1$
        this.expression = genLiftCall(scope, providedType);
        return this.resolvedType;
    }

	private MessageSend genLiftCall(BlockScope scope, TypeBinding providedType) {
        MessageSend liftCall = Lifting.liftCall(scope, this.teamExpr, this.expression, providedType, this.expectedType, this.requireReverseOperation);
        liftCall.actualReceiverType = this.teamExpr.resolveType(scope);
        if (this.teamExpr.resolvedType instanceof ReferenceBinding) // i.e. not-null
        	liftCall.binding = ((ReferenceBinding)this.teamExpr.resolvedType).getMethod(scope, liftCall.selector);
        if (liftCall.binding == null) // can't process (analyze,generate) if lift method is missing
        {
        	if (liftCall.actualReceiverType != null && TeamModel.hasRoFiCache((ReferenceBinding)liftCall.actualReceiverType))
        		scope.problemReporter().unresolvedLifting(this, providedType, this.expectedType);
        	else
        		scope.problemReporter().referenceContext.tagAsHavingErrors();
        }
        liftCall.constant = Constant.NotAConstant;
        liftCall.resolvedType =
      	    this.resolvedType = RoleTypeCreator.maybeWrapUnqualifiedRoleType(this.expectedType, scope, this);
		return liftCall;
	}

	/* Translating parameterized declared lifting requires delegation to lift_dynamic method: */
	private void generateDynamicLiftCall(BlockScope scope, ReferenceBinding roleType) {
		// lift_dynamic method has already been generated from DeclaredLifting.transformMethodWithDeclaredLifting

		AstGenerator gen = new AstGenerator(this.expression);

        this.rawExpression = this.expression;
		Expression[] args = new Expression[] { this.expression };
		this.expression = gen.messageSend(this.teamExpr, DeclaredLifting.dynamicLiftSelector(roleType), args);
		this.resolvedType = this.expression.resolveType(scope); // or resolve bits individually ? (expression is already resolved?)
	}
}

Back to the top