Skip to main content
summaryrefslogtreecommitdiffstats
blob: ee157de8ec46888029bcd0a9734368c485e00431 (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
/*******************************************************************************
 * Copyright (c) 2000, 2012 IBM Corporation and others.
 * 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: Reference.java 23404 2010-02-03 14:10:22Z stephan $
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Stephan Herrmann <stephan@cs.tu-berlin.de> - Contribution for bug 185682 - Increment/decrement operators mark local variables as read
 *     Fraunhofer FIRST - extended API and implementation
 *     Technical University Berlin - extended API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;

/**
 * OTDT changes:
 * What: Utility for determining the nesting depth of a referred field as seen from a particular type.
 *
 * What: support notion of expected type
 * Why:  inferred callout to field must be created with proper type, possibly involving lifting.
 * 
 * @author stephan
 * @version $Id: Reference.java 23404 2010-02-03 14:10:22Z stephan $
 */
public abstract class Reference extends Expression  {
//{ObjectTeams: store expected type to support infering callout-to-field
	public TypeBinding expectedType;
	@Override
	public void setExpectedType(TypeBinding expectedType) {
		this.expectedType = expectedType;
	}
// SH}
/**
 * BaseLevelReference constructor comment.
 */
public Reference() {
	super();
}
public abstract FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Assignment assignment, boolean isCompound);

public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
	return flowInfo;
}

public FieldBinding fieldBinding() {
	//this method should be sent one FIELD-tagged references
	//  (ref.bits & BindingIds.FIELD != 0)()
	return null ;
}

public void fieldStore(Scope currentScope, CodeStream codeStream, FieldBinding fieldBinding, MethodBinding syntheticWriteAccessor, TypeBinding receiverType, boolean isImplicitThisReceiver, boolean valueRequired) {
	int pc = codeStream.position;
	if (fieldBinding.isStatic()) {
		if (valueRequired) {
			switch (fieldBinding.type.id) {
				case TypeIds.T_long :
				case TypeIds.T_double :
					codeStream.dup2();
					break;
				default : 
					codeStream.dup();
					break;
			}
		}
		if (syntheticWriteAccessor == null) {
			TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, fieldBinding, receiverType, isImplicitThisReceiver);
			codeStream.fieldAccess(Opcodes.OPC_putstatic, fieldBinding, constantPoolDeclaringClass);
		} else {
			codeStream.invoke(Opcodes.OPC_invokestatic, syntheticWriteAccessor, null /* default declaringClass */);
		}
	} else { // Stack:  [owner][new field value]  ---> [new field value][owner][new field value]
		if (valueRequired) {
			switch (fieldBinding.type.id) {
				case TypeIds.T_long :
				case TypeIds.T_double :
					codeStream.dup2_x1();
					break;
				default : 
					codeStream.dup_x1();
					break;
			}
		}
		if (syntheticWriteAccessor == null) {
			TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, fieldBinding, receiverType, isImplicitThisReceiver);
			codeStream.fieldAccess(Opcodes.OPC_putfield, fieldBinding, constantPoolDeclaringClass);
		} else {
			codeStream.invoke(Opcodes.OPC_invokestatic, syntheticWriteAccessor, null /* default declaringClass */);
		}
	}
	codeStream.recordPositionsFrom(pc, this.sourceStart);
}

public abstract void generateAssignment(BlockScope currentScope, CodeStream codeStream, Assignment assignment, boolean valueRequired);

public abstract void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired);

public abstract void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired);

/* report if a private field is only read from a 'special operator',
 * i.e., in a postIncrement expression or a compound assignment,
 * where the information is never flowing out off the field. */
void reportOnlyUselesslyReadPrivateField(BlockScope currentScope, FieldBinding fieldBinding, boolean valueRequired) {
	if (valueRequired) {
		// access is relevant, turn compound use into real use:
		fieldBinding.compoundUseFlag = 0;
		fieldBinding.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
	} else {
		if (fieldBinding.isUsedOnlyInCompound()) {
			fieldBinding.compoundUseFlag--; // consume one
			if (fieldBinding.compoundUseFlag == 0					// report only the last usage
					&& fieldBinding.isOrEnclosedByPrivateType() 
					&& (this.implicitConversion & TypeIds.UNBOXING) == 0) // don't report if unboxing is involved (might cause NPE)
			{
				// compoundAssignment/postIncrement is the only usage of this field
				currentScope.problemReporter().unusedPrivateField(fieldBinding.sourceField());
				fieldBinding.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; // don't report again
			}
		}
	}
}
/* report a local/arg that is only read from a 'special operator',
 * i.e., in a postIncrement expression or a compound assignment,
 * where the information is never flowing out off the local/arg. */
static void reportOnlyUselesslyReadLocal(BlockScope currentScope, LocalVariableBinding localBinding, boolean valueRequired) {
	if (localBinding.declaration == null)
		return;  // secret local
	if ((localBinding.declaration.bits & ASTNode.IsLocalDeclarationReachable) == 0)
		return;  // declaration is unreachable
	if (localBinding.useFlag >= LocalVariableBinding.USED)
		return;  // we're only interested in cases with only compound access (negative count)

	if (valueRequired) {
		// access is relevant
		localBinding.useFlag = LocalVariableBinding.USED;
		return;
	} else {
		localBinding.useFlag++;
		if (localBinding.useFlag != LocalVariableBinding.UNUSED) // have all negative counts been consumed?
			return; // still waiting to see more usages of this kind
	}
	// at this point we know we have something to report
	if (localBinding.declaration instanceof Argument) {
		// check compiler options to report against unused arguments
		MethodScope methodScope = currentScope.methodScope();
		if (methodScope != null) {
			MethodBinding method = ((AbstractMethodDeclaration)methodScope.referenceContext()).binding;
			
			boolean shouldReport = !method.isMain();
			if (method.isImplementing()) {
				shouldReport &= currentScope.compilerOptions().reportUnusedParameterWhenImplementingAbstract;
			} else if (method.isOverriding()) {
				shouldReport &= currentScope.compilerOptions().reportUnusedParameterWhenOverridingConcrete;
			}
			
			if (shouldReport) {
				// report the case of an argument that is unread except through a special operator
				currentScope.problemReporter().unusedArgument(localBinding.declaration);
			}
		}
	} else {
		// report the case of a local variable that is unread except for a special operator
		currentScope.problemReporter().unusedLocalVariable(localBinding.declaration);
	}
	localBinding.useFlag = LocalVariableBinding.USED; // don't report again
}

//{ObjectTeams: references to the enclosing team need synthetic accessors.
/**
 * @param fieldBinding
 * @param enclosingSourceType
 * @return depth of field's declaring class as seen from enclosingSourceType or -1.
 */
protected int getDepthForSynthFieldAccess(FieldBinding fieldBinding, SourceTypeBinding enclosingSourceType) {
	int depth = (this.bits & DepthMASK) >> DepthSHIFT;

	if (fieldBinding.isPrivate())
		return depth;
	if (fieldBinding.isPublic())
		return -1;
	if (fieldBinding.declaringClass.getPackage() == enclosingSourceType.getPackage()) {
		depth = TeamModel.levelFromEnclosingTeam(fieldBinding.declaringClass, enclosingSourceType);
		// through copy inheritance this code could be executed within a different package!
		if (depth == 0)
			return -1; // neither a team field, nor an access across packages
	}
	return depth;
}
/** 
 * If this reference is an inferred call to a c-t-f to static, synthetic args (int,Team) must be generated. 
 * @return true if synthetic args have been generated
 */
protected boolean checkGeneratedSynthArgsForFieldAccess(MethodBinding[] accessors, CodeStream codeStream, BlockScope scope) {
	if (accessors != null && SyntheticMethodBinding.isCalloutToStaticField(accessors[SingleNameReference.WRITE]))
	{
		SyntheticMethodBinding syntheticMethodBinding = (SyntheticMethodBinding)accessors[SingleNameReference.WRITE];
		syntheticMethodBinding.generateStaticCTFArgs(codeStream, scope, this, (this.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT);
		return true;
	}
	return false; // nothing generated
}
// SH}
}

Back to the top