Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 1 | /******************************************************************************* |
Stephan Herrmann | 53b3b92 | 2015-02-14 22:37:15 +0100 | [diff] [blame] | 2 | * Copyright (c) 2000, 2015 IBM Corporation and others. |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 3 | * 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 Herrmann | 53b3b92 | 2015-02-14 22:37:15 +0100 | [diff] [blame] | 10 | * Stephan Herrmann - Contributions for |
| 11 | * Bug 185682 - Increment/decrement operators mark local variables as read |
| 12 | * Bug 458396 - NPE in CodeStream.invoke() |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 13 | *******************************************************************************/ |
| 14 | package org.eclipse.jdt.internal.eval; |
| 15 | |
| 16 | import org.eclipse.jdt.internal.compiler.ast.Assignment; |
| 17 | import org.eclipse.jdt.internal.compiler.ast.CompoundAssignment; |
| 18 | import org.eclipse.jdt.internal.compiler.ast.Expression; |
| 19 | import org.eclipse.jdt.internal.compiler.ast.FieldReference; |
| 20 | import org.eclipse.jdt.internal.compiler.ast.IntLiteral; |
| 21 | import org.eclipse.jdt.internal.compiler.codegen.CodeStream; |
| 22 | import org.eclipse.jdt.internal.compiler.codegen.Opcodes; |
| 23 | import org.eclipse.jdt.internal.compiler.flow.FlowInfo; |
| 24 | import org.eclipse.jdt.internal.compiler.impl.Constant; |
| 25 | import org.eclipse.jdt.internal.compiler.lookup.BlockScope; |
| 26 | import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| 27 | import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding; |
| 28 | import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; |
| 29 | import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| 30 | import org.eclipse.jdt.internal.compiler.lookup.TypeIds; |
| 31 | |
| 32 | public 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 | */ |
| 41 | public CodeSnippetFieldReference(char[] source, long pos, EvaluationContext evaluationContext) { |
| 42 | super(source, pos); |
| 43 | this.evaluationContext = evaluationContext; |
| 44 | } |
Stephan Herrmann | e472188 | 2018-01-25 20:43:01 +0100 | [diff] [blame] | 45 | @Override |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 46 | public 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 Herrmann | e472188 | 2018-01-25 20:43:01 +0100 | [diff] [blame] | 83 | @Override |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 84 | public 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 Herrmann | e472188 | 2018-01-25 20:43:01 +0100 | [diff] [blame] | 133 | @Override |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 134 | public 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 Herrmann | 1bb1007 | 2014-01-03 14:36:07 +0100 | [diff] [blame] | 207 | if ((TypeBinding.equalsEquals(codegenBinding.type, TypeBinding.LONG)) || (TypeBinding.equalsEquals(codegenBinding.type, TypeBinding.DOUBLE))) { |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 208 | 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 Herrmann | e472188 | 2018-01-25 20:43:01 +0100 | [diff] [blame] | 218 | @Override |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 219 | public 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 Herrmann | e472188 | 2018-01-25 20:43:01 +0100 | [diff] [blame] | 284 | @Override |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 285 | public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess){ |
| 286 | // The private access will be managed through the code generation |
| 287 | |
Stephan Herrmann | 0e64ff6 | 2011-03-10 18:42:36 +0000 | [diff] [blame] | 288 | if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) return; |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 289 | } |
Stephan Herrmann | e472188 | 2018-01-25 20:43:01 +0100 | [diff] [blame] | 290 | @Override |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 291 | public 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 Herrmann | e880bb6 | 2010-11-06 15:10:25 +0000 | [diff] [blame] | 337 | if (isFieldUseDeprecated(this.binding, scope, this.bits)) { |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 338 | scope.problemReporter().deprecatedField(this.binding, this); |
| 339 | } |
| 340 | // check for this.x in static is done in the resolution of the receiver |
Stephan Herrmann | 53b3b92 | 2015-02-14 22:37:15 +0100 | [diff] [blame] | 341 | this.constant = this.receiver.isImplicitThis() ? this.binding.constant(scope) : Constant.NotAConstant; |
Stephan Herrmann | 7b7062f | 2010-04-01 19:56:59 +0000 | [diff] [blame] | 342 | 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 | } |