| /******************************************************************************* |
| * Copyright (c) 2000, 2009 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 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.eval; |
| |
| import org.eclipse.jdt.internal.compiler.ast.Assignment; |
| import org.eclipse.jdt.internal.compiler.ast.CompoundAssignment; |
| import org.eclipse.jdt.internal.compiler.ast.Expression; |
| import org.eclipse.jdt.internal.compiler.ast.FieldReference; |
| import org.eclipse.jdt.internal.compiler.ast.IntLiteral; |
| import org.eclipse.jdt.internal.compiler.codegen.CodeStream; |
| import org.eclipse.jdt.internal.compiler.codegen.Opcodes; |
| import org.eclipse.jdt.internal.compiler.flow.FlowInfo; |
| import org.eclipse.jdt.internal.compiler.impl.Constant; |
| import org.eclipse.jdt.internal.compiler.lookup.BlockScope; |
| import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeIds; |
| |
| public class CodeSnippetFieldReference extends FieldReference implements ProblemReasons, EvaluationConstants { |
| |
| EvaluationContext evaluationContext; |
| FieldBinding delegateThis; |
| /** |
| * CodeSnippetFieldReference constructor comment. |
| * @param source char[] |
| * @param pos long |
| */ |
| public CodeSnippetFieldReference(char[] source, long pos, EvaluationContext evaluationContext) { |
| super(source, pos); |
| this.evaluationContext = evaluationContext; |
| } |
| public void generateAssignment(BlockScope currentScope, CodeStream codeStream, Assignment assignment, boolean valueRequired) { |
| FieldBinding codegenBinding = this.binding.original(); |
| if (codegenBinding.canBeSeenBy(this.actualReceiverType, this, currentScope)) { |
| this.receiver.generateCode(currentScope, codeStream, !codegenBinding.isStatic()); |
| assignment.expression.generateCode(currentScope, codeStream, true); |
| fieldStore(currentScope, codeStream, codegenBinding, null, this.actualReceiverType, this.receiver.isImplicitThis(), valueRequired); |
| } else { |
| codeStream.generateEmulationForField(codegenBinding); |
| this.receiver.generateCode(currentScope, codeStream, !codegenBinding.isStatic()); |
| if (codegenBinding.isStatic()) { // need a receiver? |
| codeStream.aconst_null(); |
| } |
| assignment.expression.generateCode(currentScope, codeStream, true); |
| if (valueRequired) { |
| switch (codegenBinding.type.id) { |
| case TypeIds.T_long : |
| case TypeIds.T_double : |
| codeStream.dup2_x2(); |
| break; |
| default : |
| codeStream.dup_x2(); |
| break; |
| } |
| } |
| codeStream.generateEmulatedWriteAccessForField(codegenBinding); |
| } |
| if (valueRequired){ |
| codeStream.generateImplicitConversion(assignment.implicitConversion); |
| } |
| } |
| /** |
| * Field reference code generation |
| * |
| * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope |
| * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream |
| * @param valueRequired boolean |
| */ |
| public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { |
| int pc = codeStream.position; |
| if (this.constant != Constant.NotAConstant) { |
| if (valueRequired) { |
| codeStream.generateConstant(this.constant, this.implicitConversion); |
| } |
| } else { |
| FieldBinding codegenBinding = this.binding.original(); |
| boolean isStatic = codegenBinding.isStatic(); |
| this.receiver.generateCode(currentScope, codeStream, !isStatic); |
| if (valueRequired) { |
| Constant fieldConstant = codegenBinding.constant(); |
| if (fieldConstant == Constant.NotAConstant) { |
| if (codegenBinding.declaringClass == null) { // array length |
| codeStream.arraylength(); |
| } else { |
| if (codegenBinding.canBeSeenBy(this.actualReceiverType, this, currentScope)) { |
| TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis()); |
| if (isStatic) { |
| codeStream.fieldAccess(Opcodes.OPC_getstatic , codegenBinding, constantPoolDeclaringClass); |
| } else { |
| codeStream.fieldAccess(Opcodes.OPC_getfield, codegenBinding, constantPoolDeclaringClass); |
| } |
| } else { |
| if (isStatic) { |
| // we need a null on the stack to use the reflect emulation |
| codeStream.aconst_null(); |
| } |
| codeStream.generateEmulatedReadAccessForField(codegenBinding); |
| } |
| } |
| codeStream.generateImplicitConversion(this.implicitConversion); |
| } else { |
| if (!isStatic) { |
| codeStream.invokeObjectGetClass(); // perform null check |
| codeStream.pop(); |
| } |
| codeStream.generateConstant(fieldConstant, this.implicitConversion); |
| } |
| } else { |
| if (!isStatic){ |
| codeStream.invokeObjectGetClass(); // perform null check |
| codeStream.pop(); |
| } |
| } |
| } |
| codeStream.recordPositionsFrom(pc, this.sourceStart); |
| } |
| |
| public void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired) { |
| boolean isStatic; |
| FieldBinding codegenBinding = this.binding.original(); |
| if (codegenBinding.canBeSeenBy(this.actualReceiverType, this, currentScope)) { |
| this.receiver.generateCode(currentScope, codeStream, !(isStatic = codegenBinding.isStatic())); |
| TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis()); |
| if (isStatic) { |
| codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenBinding, constantPoolDeclaringClass); |
| } else { |
| codeStream.dup(); |
| codeStream.fieldAccess(Opcodes.OPC_getfield, codegenBinding, constantPoolDeclaringClass); |
| } |
| int operationTypeID; |
| switch(operationTypeID = (this.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { |
| case T_JavaLangString : |
| case T_JavaLangObject : |
| case T_undefined : |
| codeStream.generateStringConcatenationAppend(currentScope, null, expression); |
| break; |
| default : |
| // promote the array reference to the suitable operation type |
| codeStream.generateImplicitConversion(this.implicitConversion); |
| // generate the increment value (will by itself be promoted to the operation value) |
| if (expression == IntLiteral.One){ // prefix operation |
| codeStream.generateConstant(expression.constant, this.implicitConversion); |
| } else { |
| expression.generateCode(currentScope, codeStream, true); |
| } |
| // perform the operation |
| codeStream.sendOperator(operator, operationTypeID); |
| // cast the value back to the array reference type |
| codeStream.generateImplicitConversion(assignmentImplicitConversion); |
| } |
| fieldStore(currentScope, codeStream, codegenBinding, null, this.actualReceiverType, this.receiver.isImplicitThis(), valueRequired); |
| } else { |
| this.receiver.generateCode(currentScope, codeStream, !(isStatic = codegenBinding.isStatic())); |
| if (isStatic) { |
| // used to store the value |
| codeStream.generateEmulationForField(codegenBinding); |
| codeStream.aconst_null(); |
| |
| // used to retrieve the actual value |
| codeStream.aconst_null(); |
| codeStream.generateEmulatedReadAccessForField(codegenBinding); |
| } else { |
| // used to store the value |
| codeStream.generateEmulationForField(this.binding); |
| this.receiver.generateCode(currentScope, codeStream, !isStatic); |
| |
| // used to retrieve the actual value |
| codeStream.dup(); |
| codeStream.generateEmulatedReadAccessForField(codegenBinding); |
| } |
| int operationTypeID; |
| if ((operationTypeID = (this.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) == T_JavaLangString) { |
| codeStream.generateStringConcatenationAppend(currentScope, null, expression); |
| } else { |
| // promote the array reference to the suitable operation type |
| codeStream.generateImplicitConversion(this.implicitConversion); |
| // generate the increment value (will by itself be promoted to the operation value) |
| if (expression == IntLiteral.One){ // prefix operation |
| codeStream.generateConstant(expression.constant, this.implicitConversion); |
| } else { |
| expression.generateCode(currentScope, codeStream, true); |
| } |
| // perform the operation |
| codeStream.sendOperator(operator, operationTypeID); |
| // cast the value back to the array reference type |
| codeStream.generateImplicitConversion(assignmentImplicitConversion); |
| } |
| // current stack is: |
| // field receiver value |
| if (valueRequired) { |
| if ((codegenBinding.type == TypeBinding.LONG) || (codegenBinding.type == TypeBinding.DOUBLE)) { |
| codeStream.dup2_x2(); |
| } else { |
| codeStream.dup_x2(); |
| } |
| } |
| // current stack is: |
| // value field receiver value |
| codeStream.generateEmulatedWriteAccessForField(codegenBinding); |
| } |
| } |
| public void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired) { |
| boolean isStatic; |
| FieldBinding codegenBinding = this.binding.original(); |
| if (codegenBinding.canBeSeenBy(this.actualReceiverType, this, currentScope)) { |
| super.generatePostIncrement(currentScope, codeStream, postIncrement, valueRequired); |
| } else { |
| this.receiver.generateCode(currentScope, codeStream, !(isStatic = codegenBinding.isStatic())); |
| if (isStatic) { |
| codeStream.aconst_null(); |
| } |
| // the actual stack is: receiver |
| codeStream.dup(); |
| // the actual stack is: receiver receiver |
| codeStream.generateEmulatedReadAccessForField(codegenBinding); |
| // the actual stack is: receiver value |
| // receiver value |
| // value receiver value dup_x1 or dup2_x1 if value required |
| // value value receiver value dup_x1 or dup2_x1 |
| // value value receiver pop or pop2 |
| // value value receiver field generateEmulationForField |
| // value value field receiver swap |
| // value field receiver value field receiver dup2_x1 or dup2_x2 |
| // value field receiver value pop2 |
| // value field receiver newvalue generate constant + op |
| // value store |
| int typeID; |
| switch (typeID = codegenBinding.type.id) { |
| case TypeIds.T_long : |
| case TypeIds.T_double : |
| if (valueRequired) { |
| codeStream.dup2_x1(); |
| } |
| codeStream.dup2_x1(); |
| codeStream.pop2(); |
| break; |
| default : |
| if (valueRequired) { |
| codeStream.dup_x1(); |
| } |
| codeStream.dup_x1(); |
| codeStream.pop(); |
| break; |
| } |
| codeStream.generateEmulationForField(codegenBinding); |
| codeStream.swap(); |
| switch (typeID) { |
| case TypeIds.T_long : |
| case TypeIds.T_double : |
| codeStream.dup2_x2(); |
| break; |
| default : |
| codeStream.dup2_x1(); |
| break; |
| } |
| codeStream.pop2(); |
| |
| codeStream.generateConstant(postIncrement.expression.constant, this.implicitConversion); |
| codeStream.sendOperator(postIncrement.operator, codegenBinding.type.id); |
| codeStream.generateImplicitConversion(postIncrement.preAssignImplicitConversion); |
| codeStream.generateEmulatedWriteAccessForField(codegenBinding); |
| } |
| } |
| /* |
| * No need to emulate access to protected fields since not implicitly accessed |
| */ |
| public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess){ |
| // The private access will be managed through the code generation |
| |
| if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return; |
| } |
| public TypeBinding resolveType(BlockScope scope) { |
| // Answer the signature type of the field. |
| // constants are propaged when the field is final |
| // and initialized with a (compile time) constant |
| |
| // regular receiver reference |
| this.actualReceiverType = this.receiver.resolveType(scope); |
| if (this.actualReceiverType == null){ |
| this.constant = Constant.NotAConstant; |
| return null; |
| } |
| // the case receiverType.isArrayType and token = 'length' is handled by the scope API |
| this.binding = scope.getField(this.actualReceiverType, this.token, this); |
| FieldBinding firstAttempt = this.binding; |
| boolean isNotVisible = false; |
| if (!this.binding.isValidBinding()) { |
| if (this.binding instanceof ProblemFieldBinding |
| && ((ProblemFieldBinding) this.binding).problemId() == NotVisible) { |
| isNotVisible = true; |
| if (this.evaluationContext.declaringTypeName != null) { |
| this.delegateThis = scope.getField(scope.enclosingSourceType(), DELEGATE_THIS, this); |
| if (this.delegateThis == null){ // if not found then internal error, field should have been found |
| this.constant = Constant.NotAConstant; |
| scope.problemReporter().invalidField(this, this.actualReceiverType); |
| return null; |
| } |
| this.actualReceiverType = this.delegateThis.type; |
| } else { |
| this.constant = Constant.NotAConstant; |
| scope.problemReporter().invalidField(this, this.actualReceiverType); |
| return null; |
| } |
| CodeSnippetScope localScope = new CodeSnippetScope(scope); |
| this.binding = localScope.getFieldForCodeSnippet(this.delegateThis.type, this.token, this); |
| } |
| } |
| |
| if (!this.binding.isValidBinding()) { |
| this.constant = Constant.NotAConstant; |
| if (isNotVisible) { |
| this.binding = firstAttempt; |
| } |
| scope.problemReporter().invalidField(this, this.actualReceiverType); |
| return null; |
| } |
| |
| if (isFieldUseDeprecated(this.binding, scope, (this.bits & IsStrictlyAssigned) !=0)) { |
| scope.problemReporter().deprecatedField(this.binding, this); |
| } |
| // check for this.x in static is done in the resolution of the receiver |
| this.constant = this.receiver.isImplicitThis() ? this.binding.constant() : Constant.NotAConstant; |
| if (!this.receiver.isThis()) { // TODO need to check if shouldn't be isImplicitThis check (and then removed) |
| this.constant = Constant.NotAConstant; |
| } |
| return this.resolvedType = this.binding.type; |
| } |
| } |