Skip to main content
summaryrefslogtreecommitdiffstats
blob: d332642fa02f60d924c099a38fd73d7564308aaa (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
232
233
234
235
236
237
238
239
/**********************************************************************
 * This file is part of "Object Teams Development Tooling"-Software
 *
 * Copyright 2003, 2011 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: Lowering.java 23417 2010-02-03 20:13:55Z 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.lifting;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
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.LocalVariableBinding;
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.RoleTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.StandardElementGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;

/**
 * Helper that generates the lowering statements necessary for the callout compiler-feature.
 *
 * Nested classes PushingExpression and PopExpression are used to store a value on
 * the stack that must be evaluated only once but is first used for a null-check
 * and later the same value is used for the actual translation.
 *
 * @author haebor
 * @version $Id: Lowering.java 23417 2010-02-03 20:13:55Z stephan $
 */
public class Lowering implements IOTConstants {

	/**
	 * This method generates a lowering translation for a given expression.
	 * We have three distinct alternatives:
	 * (1) arrays are translated via a method call (see ArrayLowering).
	 * (2) field access (applicable for same and sibling roles).
	 * (3) _OT$getBase() method (else).
	 *
	 * @param scope for lookup
	 * @param expression original
	 * @param unloweredType either a RoleTypeBinding or an ArrayBinding thereof.
	 * @param requiredType  the base type (or ArrayBinding thereof).
	 * @param teamExpression can be used as a receiver for array lowering method calls
	 * @param needNullCheck if expression maybe null, a null-check is required at runtime.
	 * @return translation expression
	 */
	public Expression lowerExpression(
              BlockScope  scope,
              final Expression  expression,
		      TypeBinding unloweredType,
			  TypeBinding requiredType,
			  Expression  teamExpression,
			  boolean     needNullCheck)
	{
        // Note, this method is responsible for 'resolving' all AST nodes it generates!

        int sourceStart = expression.sourceStart;
        int sourceEnd   = expression.sourceEnd;
    	AstGenerator gen = new AstGenerator(sourceStart, sourceEnd);

    	unloweredType = TeamModel.strengthenRoleType(scope.enclosingSourceType(), unloweredType);
        Expression loweringExpr = null;

        TypeBinding expressionType = expression.resolvedType;
        // this assertion needed for pushing/casting using unchecked one-byte opcodes.
        assert expressionType == null || expressionType.leafComponentType() instanceof ReferenceBinding;

    	LocalVariableBinding localVar = null;
    	Expression unloweredExpression = expression;
    	if (expression instanceof ThisReference || expression instanceof AllocationExpression)
    		needNullCheck = false;
    	if (needNullCheck) {
        	localVar = makeNewLocal(scope, unloweredType, sourceStart, sourceEnd);
        	SingleNameReference varRef = gen.singleNameReference(localVar.name);
        	varRef.binding = localVar;
        	varRef.bits = Binding.LOCAL;
        	varRef.constant = Constant.NotAConstant;
        	varRef.resolvedType = unloweredType;
			unloweredExpression = varRef;
    	}

        if(unloweredType.isArrayType())
		{
        	// (1) array translation:
			ArrayLowering trans = new ArrayLowering(teamExpression);
			loweringExpr =  trans.lowerArray(
								scope,
								unloweredExpression,
								unloweredType,
								requiredType);
		}
		else
		{
            RoleTypeBinding roleType = (RoleTypeBinding)unloweredType;
			boolean needMethod =   roleType.isRegularInterface()
								|| !roleType.isSiblingRole(scope.enclosingSourceType());
            if (needMethod) {
            	// (3) translation using _OT$.getBase() method:
				MessageSend callLower = gen.messageSend(
						unloweredExpression,
                		IOTConstants._OT_GETBASE,
						new Expression[0]);

                // manual resolving:
                callLower.actualReceiverType = unloweredType;
                callLower.constant       = Constant.NotAConstant;
                callLower.resolvedType   = roleType.baseclass();

                callLower.binding =
                            StandardElementGenerator.getGetBaseMethod(
                            		scope,
                                    roleType.roleModel,
                                    roleType.baseclass());
                loweringExpr = callLower;
            } else {
                // (2) translation using field _OT$base:
                FieldReference invokeBaseOnRole =
                    new FieldReference(IOTConstants._OT_BASE, (((long)sourceStart) << 32) + sourceEnd);
                ReferenceBinding roleClass = roleType.roleModel.getClassPartBinding();
                TypeReference classRef = gen.typeReference(roleClass);
                if (classRef != null) {
                	// field access needs cast to the role-class.
                	// FIXME(SH): use synthetic role field accessor instead!
                	CastExpression unloweredExpr;
                    unloweredExpr = new CastExpression(unloweredExpression, classRef, CastExpression.NEED_CLASS);
                    unloweredExpr.constant     = Constant.NotAConstant;
                    unloweredExpr.resolvedType = roleClass;
					unloweredExpr.bits        |= ASTNode.GenerateCheckcast;
	                invokeBaseOnRole.receiver  = unloweredExpr;
                } else {
                	invokeBaseOnRole.receiver  = unloweredExpression;
                }

                invokeBaseOnRole.actualReceiverType = roleClass;
                invokeBaseOnRole.resolvedType       = roleClass.baseclass();
                invokeBaseOnRole.binding = scope.findField(
                                        			  roleClass,
                                        			  IOTConstants._OT_BASE,
                                        			  invokeBaseOnRole,
                                        			  true);
                invokeBaseOnRole.constant           = Constant.NotAConstant;
               	loweringExpr = invokeBaseOnRole;
            }
		}
        if (needNullCheck) {
        	// ((local = (expression)) == null) ? (RequiredType)local : lower(local));
        	@SuppressWarnings("null") // needNullCheck => localVar != null
			SingleNameReference lhs = gen.singleNameReference(localVar.name);
			lhs.binding = localVar;
			lhs.resolvedType = unloweredType;
			lhs.bits = Binding.LOCAL|ASTNode.FirstAssignmentToLocal;
			Assignment assignment = gen.assignment(lhs, expression);
			assignment.constant = Constant.NotAConstant;
			loweringExpr = new CheckedLoweringExpression(expression, gen.nullCheck(assignment), gen.nullLiteral(), loweringExpr, localVar);
			loweringExpr.constant = Constant.NotAConstant;
			loweringExpr.resolvedType = requiredType;
        }
        return loweringExpr;
	}
	
	@NonNull LocalVariableBinding makeNewLocal(BlockScope scope, TypeBinding variableType, int sourceStart, int sourceEnd) {
		char[] name = ("_OT$unlowered$"+sourceStart).toCharArray(); //$NON-NLS-1$
		LocalVariableBinding varBinding = new LocalVariableBinding(name, variableType, 0, false);
		varBinding.declaration = new LocalDeclaration(name, sourceStart, sourceEnd); // needed for BlockScope.computeLocalVariablePositions() -> CodeStream.record()
		scope.addLocalVariable(varBinding);
		varBinding.setConstant(Constant.NotAConstant);
		varBinding.useFlag = LocalVariableBinding.USED;
		return varBinding;
	}
	
	/** A conditional expression that checks for null before performing the actual lowering. */
	static class CheckedLoweringExpression extends ConditionalExpression {
		
		private final LocalVariableBinding localVar;
		Expression origExpression;

		CheckedLoweringExpression(Expression origExpression,
								  Expression condition,
								  Expression valueIfTrue,
								  Expression valueIfFalse,
								  LocalVariableBinding localVar)
		{
			super(condition, valueIfTrue, valueIfFalse);
			this.localVar = localVar;
			this.origExpression = origExpression;
		}

		@Override
		public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
			codeStream.addVisibleLocalVariable(this.localVar);
			super.generateCode(currentScope, codeStream, valueRequired);
		}
	}

	public static boolean isLoweringConditional(Expression expr) {
		return expr instanceof CheckedLoweringExpression;
	}

	/**
	 * Extract the actual value, considering wrapping by CheckedLoweringExpression.
	 *
	 * @param expression
	 * @return the actual value to be lowered
	 */
	public static Expression unwrapExpression(Expression expression) {
		if (expression instanceof CheckedLoweringExpression)
			return ((CheckedLoweringExpression)expression).origExpression;
		return null;
	}
}

Back to the top