blob: 695887b02ad7a768cebe5881005cbe27cb4bb0c5 [file] [log] [blame]
Stephan Herrmann7b7062f2010-04-01 19:56:59 +00001/*******************************************************************************
Stephan Herrmann53b3b922015-02-14 22:37:15 +01002 * Copyright (c) 2000, 2015 IBM Corporation and others.
Stephan Herrmann7b7062f2010-04-01 19:56:59 +00003 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
Stephan Herrmann53b3b922015-02-14 22:37:15 +010010 * Stephan Herrmann - Contributions for
11 * Bug 185682 - Increment/decrement operators mark local variables as read
12 * Bug 458396 - NPE in CodeStream.invoke()
Stephan Herrmann7b7062f2010-04-01 19:56:59 +000013 *******************************************************************************/
14package org.eclipse.jdt.internal.eval;
15
16import org.eclipse.jdt.internal.compiler.ast.Assignment;
17import org.eclipse.jdt.internal.compiler.ast.CompoundAssignment;
18import org.eclipse.jdt.internal.compiler.ast.Expression;
19import org.eclipse.jdt.internal.compiler.ast.FieldReference;
20import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
21import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
22import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
23import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
24import org.eclipse.jdt.internal.compiler.impl.Constant;
25import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
26import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
27import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding;
28import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
29import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
30import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
31
32public class CodeSnippetFieldReference extends FieldReference implements ProblemReasons, EvaluationConstants {
33
34 EvaluationContext evaluationContext;
35 FieldBinding delegateThis;
36/**
37 * CodeSnippetFieldReference constructor comment.
38 * @param source char[]
39 * @param pos long
40 */
41public CodeSnippetFieldReference(char[] source, long pos, EvaluationContext evaluationContext) {
42 super(source, pos);
43 this.evaluationContext = evaluationContext;
44}
Stephan Herrmanne4721882018-01-25 20:43:01 +010045@Override
Stephan Herrmann7b7062f2010-04-01 19:56:59 +000046public void generateAssignment(BlockScope currentScope, CodeStream codeStream, Assignment assignment, boolean valueRequired) {
47 FieldBinding codegenBinding = this.binding.original();
48 if (codegenBinding.canBeSeenBy(this.actualReceiverType, this, currentScope)) {
49 this.receiver.generateCode(currentScope, codeStream, !codegenBinding.isStatic());
50 assignment.expression.generateCode(currentScope, codeStream, true);
51 fieldStore(currentScope, codeStream, codegenBinding, null, this.actualReceiverType, this.receiver.isImplicitThis(), valueRequired);
52 } else {
53 codeStream.generateEmulationForField(codegenBinding);
54 this.receiver.generateCode(currentScope, codeStream, !codegenBinding.isStatic());
55 if (codegenBinding.isStatic()) { // need a receiver?
56 codeStream.aconst_null();
57 }
58 assignment.expression.generateCode(currentScope, codeStream, true);
59 if (valueRequired) {
60 switch (codegenBinding.type.id) {
61 case TypeIds.T_long :
62 case TypeIds.T_double :
63 codeStream.dup2_x2();
64 break;
65 default :
66 codeStream.dup_x2();
67 break;
68 }
69 }
70 codeStream.generateEmulatedWriteAccessForField(codegenBinding);
71 }
72 if (valueRequired){
73 codeStream.generateImplicitConversion(assignment.implicitConversion);
74 }
75}
76/**
77 * Field reference code generation
78 *
79 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
80 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
81 * @param valueRequired boolean
82 */
Stephan Herrmanne4721882018-01-25 20:43:01 +010083@Override
Stephan Herrmann7b7062f2010-04-01 19:56:59 +000084public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
85 int pc = codeStream.position;
86 if (this.constant != Constant.NotAConstant) {
87 if (valueRequired) {
88 codeStream.generateConstant(this.constant, this.implicitConversion);
89 }
90 } else {
91 FieldBinding codegenBinding = this.binding.original();
92 boolean isStatic = codegenBinding.isStatic();
93 this.receiver.generateCode(currentScope, codeStream, !isStatic);
94 if (valueRequired) {
95 Constant fieldConstant = codegenBinding.constant();
96 if (fieldConstant == Constant.NotAConstant) {
97 if (codegenBinding.declaringClass == null) { // array length
98 codeStream.arraylength();
99 } else {
100 if (codegenBinding.canBeSeenBy(this.actualReceiverType, this, currentScope)) {
101 TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis());
102 if (isStatic) {
103 codeStream.fieldAccess(Opcodes.OPC_getstatic , codegenBinding, constantPoolDeclaringClass);
104 } else {
105 codeStream.fieldAccess(Opcodes.OPC_getfield, codegenBinding, constantPoolDeclaringClass);
106 }
107 } else {
108 if (isStatic) {
109 // we need a null on the stack to use the reflect emulation
110 codeStream.aconst_null();
111 }
112 codeStream.generateEmulatedReadAccessForField(codegenBinding);
113 }
114 }
115 codeStream.generateImplicitConversion(this.implicitConversion);
116 } else {
117 if (!isStatic) {
118 codeStream.invokeObjectGetClass(); // perform null check
119 codeStream.pop();
120 }
121 codeStream.generateConstant(fieldConstant, this.implicitConversion);
122 }
123 } else {
124 if (!isStatic){
125 codeStream.invokeObjectGetClass(); // perform null check
126 codeStream.pop();
127 }
128 }
129 }
130 codeStream.recordPositionsFrom(pc, this.sourceStart);
131}
132
Stephan Herrmanne4721882018-01-25 20:43:01 +0100133@Override
Stephan Herrmann7b7062f2010-04-01 19:56:59 +0000134public void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired) {
135 boolean isStatic;
136 FieldBinding codegenBinding = this.binding.original();
137 if (codegenBinding.canBeSeenBy(this.actualReceiverType, this, currentScope)) {
138 this.receiver.generateCode(currentScope, codeStream, !(isStatic = codegenBinding.isStatic()));
139 TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis());
140 if (isStatic) {
141 codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenBinding, constantPoolDeclaringClass);
142 } else {
143 codeStream.dup();
144 codeStream.fieldAccess(Opcodes.OPC_getfield, codegenBinding, constantPoolDeclaringClass);
145 }
146 int operationTypeID;
147 switch(operationTypeID = (this.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) {
148 case T_JavaLangString :
149 case T_JavaLangObject :
150 case T_undefined :
151 codeStream.generateStringConcatenationAppend(currentScope, null, expression);
152 break;
153 default :
154 // promote the array reference to the suitable operation type
155 codeStream.generateImplicitConversion(this.implicitConversion);
156 // generate the increment value (will by itself be promoted to the operation value)
157 if (expression == IntLiteral.One){ // prefix operation
158 codeStream.generateConstant(expression.constant, this.implicitConversion);
159 } else {
160 expression.generateCode(currentScope, codeStream, true);
161 }
162 // perform the operation
163 codeStream.sendOperator(operator, operationTypeID);
164 // cast the value back to the array reference type
165 codeStream.generateImplicitConversion(assignmentImplicitConversion);
166 }
167 fieldStore(currentScope, codeStream, codegenBinding, null, this.actualReceiverType, this.receiver.isImplicitThis(), valueRequired);
168 } else {
169 this.receiver.generateCode(currentScope, codeStream, !(isStatic = codegenBinding.isStatic()));
170 if (isStatic) {
171 // used to store the value
172 codeStream.generateEmulationForField(codegenBinding);
173 codeStream.aconst_null();
174
175 // used to retrieve the actual value
176 codeStream.aconst_null();
177 codeStream.generateEmulatedReadAccessForField(codegenBinding);
178 } else {
179 // used to store the value
180 codeStream.generateEmulationForField(this.binding);
181 this.receiver.generateCode(currentScope, codeStream, !isStatic);
182
183 // used to retrieve the actual value
184 codeStream.dup();
185 codeStream.generateEmulatedReadAccessForField(codegenBinding);
186 }
187 int operationTypeID;
188 if ((operationTypeID = (this.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) == T_JavaLangString) {
189 codeStream.generateStringConcatenationAppend(currentScope, null, expression);
190 } else {
191 // promote the array reference to the suitable operation type
192 codeStream.generateImplicitConversion(this.implicitConversion);
193 // generate the increment value (will by itself be promoted to the operation value)
194 if (expression == IntLiteral.One){ // prefix operation
195 codeStream.generateConstant(expression.constant, this.implicitConversion);
196 } else {
197 expression.generateCode(currentScope, codeStream, true);
198 }
199 // perform the operation
200 codeStream.sendOperator(operator, operationTypeID);
201 // cast the value back to the array reference type
202 codeStream.generateImplicitConversion(assignmentImplicitConversion);
203 }
204 // current stack is:
205 // field receiver value
206 if (valueRequired) {
Stephan Herrmann1bb10072014-01-03 14:36:07 +0100207 if ((TypeBinding.equalsEquals(codegenBinding.type, TypeBinding.LONG)) || (TypeBinding.equalsEquals(codegenBinding.type, TypeBinding.DOUBLE))) {
Stephan Herrmann7b7062f2010-04-01 19:56:59 +0000208 codeStream.dup2_x2();
209 } else {
210 codeStream.dup_x2();
211 }
212 }
213 // current stack is:
214 // value field receiver value
215 codeStream.generateEmulatedWriteAccessForField(codegenBinding);
216 }
217}
Stephan Herrmanne4721882018-01-25 20:43:01 +0100218@Override
Stephan Herrmann7b7062f2010-04-01 19:56:59 +0000219public void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired) {
220 boolean isStatic;
221 FieldBinding codegenBinding = this.binding.original();
222 if (codegenBinding.canBeSeenBy(this.actualReceiverType, this, currentScope)) {
223 super.generatePostIncrement(currentScope, codeStream, postIncrement, valueRequired);
224 } else {
225 this.receiver.generateCode(currentScope, codeStream, !(isStatic = codegenBinding.isStatic()));
226 if (isStatic) {
227 codeStream.aconst_null();
228 }
229 // the actual stack is: receiver
230 codeStream.dup();
231 // the actual stack is: receiver receiver
232 codeStream.generateEmulatedReadAccessForField(codegenBinding);
233 // the actual stack is: receiver value
234 // receiver value
235 // value receiver value dup_x1 or dup2_x1 if value required
236 // value value receiver value dup_x1 or dup2_x1
237 // value value receiver pop or pop2
238 // value value receiver field generateEmulationForField
239 // value value field receiver swap
240 // value field receiver value field receiver dup2_x1 or dup2_x2
241 // value field receiver value pop2
242 // value field receiver newvalue generate constant + op
243 // value store
244 int typeID;
245 switch (typeID = codegenBinding.type.id) {
246 case TypeIds.T_long :
247 case TypeIds.T_double :
248 if (valueRequired) {
249 codeStream.dup2_x1();
250 }
251 codeStream.dup2_x1();
252 codeStream.pop2();
253 break;
254 default :
255 if (valueRequired) {
256 codeStream.dup_x1();
257 }
258 codeStream.dup_x1();
259 codeStream.pop();
260 break;
261 }
262 codeStream.generateEmulationForField(codegenBinding);
263 codeStream.swap();
264 switch (typeID) {
265 case TypeIds.T_long :
266 case TypeIds.T_double :
267 codeStream.dup2_x2();
268 break;
269 default :
270 codeStream.dup2_x1();
271 break;
272 }
273 codeStream.pop2();
274
275 codeStream.generateConstant(postIncrement.expression.constant, this.implicitConversion);
276 codeStream.sendOperator(postIncrement.operator, codegenBinding.type.id);
277 codeStream.generateImplicitConversion(postIncrement.preAssignImplicitConversion);
278 codeStream.generateEmulatedWriteAccessForField(codegenBinding);
279 }
280}
281/*
282 * No need to emulate access to protected fields since not implicitly accessed
283 */
Stephan Herrmanne4721882018-01-25 20:43:01 +0100284@Override
Stephan Herrmann7b7062f2010-04-01 19:56:59 +0000285public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess){
286 // The private access will be managed through the code generation
287
Stephan Herrmann0e64ff62011-03-10 18:42:36 +0000288 if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) return;
Stephan Herrmann7b7062f2010-04-01 19:56:59 +0000289}
Stephan Herrmanne4721882018-01-25 20:43:01 +0100290@Override
Stephan Herrmann7b7062f2010-04-01 19:56:59 +0000291public TypeBinding resolveType(BlockScope scope) {
292 // Answer the signature type of the field.
293 // constants are propaged when the field is final
294 // and initialized with a (compile time) constant
295
296 // regular receiver reference
297 this.actualReceiverType = this.receiver.resolveType(scope);
298 if (this.actualReceiverType == null){
299 this.constant = Constant.NotAConstant;
300 return null;
301 }
302 // the case receiverType.isArrayType and token = 'length' is handled by the scope API
303 this.binding = scope.getField(this.actualReceiverType, this.token, this);
304 FieldBinding firstAttempt = this.binding;
305 boolean isNotVisible = false;
306 if (!this.binding.isValidBinding()) {
307 if (this.binding instanceof ProblemFieldBinding
308 && ((ProblemFieldBinding) this.binding).problemId() == NotVisible) {
309 isNotVisible = true;
310 if (this.evaluationContext.declaringTypeName != null) {
311 this.delegateThis = scope.getField(scope.enclosingSourceType(), DELEGATE_THIS, this);
312 if (this.delegateThis == null){ // if not found then internal error, field should have been found
313 this.constant = Constant.NotAConstant;
314 scope.problemReporter().invalidField(this, this.actualReceiverType);
315 return null;
316 }
317 this.actualReceiverType = this.delegateThis.type;
318 } else {
319 this.constant = Constant.NotAConstant;
320 scope.problemReporter().invalidField(this, this.actualReceiverType);
321 return null;
322 }
323 CodeSnippetScope localScope = new CodeSnippetScope(scope);
324 this.binding = localScope.getFieldForCodeSnippet(this.delegateThis.type, this.token, this);
325 }
326 }
327
328 if (!this.binding.isValidBinding()) {
329 this.constant = Constant.NotAConstant;
330 if (isNotVisible) {
331 this.binding = firstAttempt;
332 }
333 scope.problemReporter().invalidField(this, this.actualReceiverType);
334 return null;
335 }
336
Stephan Herrmanne880bb62010-11-06 15:10:25 +0000337 if (isFieldUseDeprecated(this.binding, scope, this.bits)) {
Stephan Herrmann7b7062f2010-04-01 19:56:59 +0000338 scope.problemReporter().deprecatedField(this.binding, this);
339 }
340 // check for this.x in static is done in the resolution of the receiver
Stephan Herrmann53b3b922015-02-14 22:37:15 +0100341 this.constant = this.receiver.isImplicitThis() ? this.binding.constant(scope) : Constant.NotAConstant;
Stephan Herrmann7b7062f2010-04-01 19:56:59 +0000342 if (!this.receiver.isThis()) { // TODO need to check if shouldn't be isImplicitThis check (and then removed)
343 this.constant = Constant.NotAConstant;
344 }
345 return this.resolvedType = this.binding.type;
346}
347}