blob: 755cd2a3d5936f78ffe3aa1c7994a6bbbf04a614 [file] [log] [blame]
package org.eclipse.jdt.internal.eval;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
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.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
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) {
if (this.codegenBinding.canBeSeenBy(receiverType, this, currentScope)) {
receiver.generateCode(currentScope, codeStream, !this.codegenBinding.isStatic());
assignment.expression.generateCode(currentScope, codeStream, true);
fieldStore(codeStream, this.codegenBinding, null, valueRequired);
} else {
((CodeSnippetCodeStream) codeStream).generateEmulationForField(this.codegenBinding);
receiver.generateCode(currentScope, codeStream, !this.codegenBinding.isStatic());
if (this.codegenBinding.isStatic()) { // need a receiver?
codeStream.aconst_null();
}
assignment.expression.generateCode(currentScope, codeStream, true);
if (valueRequired) {
if ((this.codegenBinding.type == LongBinding) || (this.codegenBinding.type == DoubleBinding)) {
codeStream.dup2_x2();
} else {
codeStream.dup_x2();
}
}
((CodeSnippetCodeStream) codeStream).generateEmulatedWriteAccessForField(this.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 (constant != NotAConstant) {
if (valueRequired) {
codeStream.generateConstant(constant, implicitConversion);
}
} else {
boolean isStatic = this.codegenBinding.isStatic();
receiver.generateCode(currentScope, codeStream, valueRequired && (!isStatic) && (this.codegenBinding.constant == NotAConstant));
if (valueRequired) {
if (this.codegenBinding.constant == NotAConstant) {
if (this.codegenBinding.declaringClass == null) { // array length
codeStream.arraylength();
} else {
if (this.codegenBinding.canBeSeenBy(receiverType, this, currentScope)) {
if (isStatic) {
codeStream.getstatic(this.codegenBinding);
} else {
codeStream.getfield(this.codegenBinding);
}
} else {
if (isStatic) {
// we need a null on the stack to use the reflect emulation
codeStream.aconst_null();
}
((CodeSnippetCodeStream) codeStream).generateEmulatedReadAccessForField(this.codegenBinding);
}
}
codeStream.generateImplicitConversion(implicitConversion);
} else {
codeStream.generateConstant(this.codegenBinding.constant, implicitConversion);
}
}
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
public void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired) {
boolean isStatic;
if (this.codegenBinding.canBeSeenBy(receiverType, this, currentScope)) {
receiver.generateCode(currentScope, codeStream, !(isStatic = this.codegenBinding.isStatic()));
if (isStatic) {
codeStream.getstatic(this.codegenBinding);
} else {
codeStream.dup();
codeStream.getfield(this.codegenBinding);
}
int operationTypeID;
if ((operationTypeID = implicitConversion >> 4) == T_String) {
codeStream.generateStringAppend(currentScope, null, expression);
} else {
// promote the array reference to the suitable operation type
codeStream.generateImplicitConversion(implicitConversion);
// generate the increment value (will by itself be promoted to the operation value)
if (expression == IntLiteral.One){ // prefix operation
codeStream.generateConstant(expression.constant, 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(codeStream, this.codegenBinding, null, valueRequired);
} else {
receiver.generateCode(currentScope, codeStream, !(isStatic = this.codegenBinding.isStatic()));
if (isStatic) {
// used to store the value
((CodeSnippetCodeStream) codeStream).generateEmulationForField(this.codegenBinding);
codeStream.aconst_null();
// used to retrieve the actual value
codeStream.aconst_null();
((CodeSnippetCodeStream) codeStream).generateEmulatedReadAccessForField(this.codegenBinding);
} else {
// used to store the value
((CodeSnippetCodeStream) codeStream).generateEmulationForField(binding);
receiver.generateCode(currentScope, codeStream, !(isStatic = this.codegenBinding.isStatic()));
// used to retrieve the actual value
codeStream.dup();
((CodeSnippetCodeStream) codeStream).generateEmulatedReadAccessForField(this.codegenBinding);
}
int operationTypeID;
if ((operationTypeID = implicitConversion >> 4) == T_String) {
codeStream.generateStringAppend(currentScope, null, expression);
} else {
// promote the array reference to the suitable operation type
codeStream.generateImplicitConversion(implicitConversion);
// generate the increment value (will by itself be promoted to the operation value)
if (expression == IntLiteral.One){ // prefix operation
codeStream.generateConstant(expression.constant, 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 ((this.codegenBinding.type == LongBinding) || (this.codegenBinding.type == DoubleBinding)) {
codeStream.dup2_x2();
} else {
codeStream.dup_x2();
}
}
// current stack is:
// value field receiver value
((CodeSnippetCodeStream) codeStream).generateEmulatedWriteAccessForField(this.codegenBinding);
}
}
public void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired) {
boolean isStatic;
if (this.codegenBinding.canBeSeenBy(receiverType, this, currentScope)) {
receiver.generateCode(currentScope, codeStream, !(isStatic = this.codegenBinding.isStatic()));
if (isStatic) {
codeStream.getstatic(this.codegenBinding);
} else {
codeStream.dup();
codeStream.getfield(this.codegenBinding);
}
if (valueRequired) {
if (isStatic) {
if ((this.codegenBinding.type == LongBinding) || (this.codegenBinding.type == DoubleBinding)) {
codeStream.dup2();
} else {
codeStream.dup();
}
} else { // Stack: [owner][old field value] ---> [old field value][owner][old field value]
if ((this.codegenBinding.type == LongBinding) || (this.codegenBinding.type == DoubleBinding)) {
codeStream.dup2_x1();
} else {
codeStream.dup_x1();
}
}
}
codeStream.generateConstant(postIncrement.expression.constant, implicitConversion);
codeStream.sendOperator(postIncrement.operator, this.codegenBinding.type.id);
codeStream.generateImplicitConversion(postIncrement.assignmentImplicitConversion);
fieldStore(codeStream, this.codegenBinding, null, false);
} else {
receiver.generateCode(currentScope, codeStream, !(isStatic = this.codegenBinding.isStatic()));
if (this.codegenBinding.isStatic()) {
codeStream.aconst_null();
}
// the actual stack is: receiver
codeStream.dup();
// the actual stack is: receiver receiver
((CodeSnippetCodeStream) codeStream).generateEmulatedReadAccessForField(this.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
if (valueRequired) {
if ((this.codegenBinding.type == LongBinding) || (this.codegenBinding.type == DoubleBinding)) {
codeStream.dup2_x1();
} else {
codeStream.dup_x1();
}
}
if ((this.codegenBinding.type == LongBinding) || (this.codegenBinding.type == DoubleBinding)) {
codeStream.dup2_x1();
codeStream.pop2();
} else {
codeStream.dup_x1();
codeStream.pop();
}
((CodeSnippetCodeStream) codeStream).generateEmulationForField(this.codegenBinding);
codeStream.swap();
if ((this.codegenBinding.type == LongBinding) || (this.codegenBinding.type == DoubleBinding)) {
codeStream.dup2_x2();
} else {
codeStream.dup2_x1();
}
codeStream.pop2();
codeStream.generateConstant(postIncrement.expression.constant, implicitConversion);
codeStream.sendOperator(postIncrement.operator, this.codegenBinding.type.id);
codeStream.generateImplicitConversion(postIncrement.assignmentImplicitConversion);
((CodeSnippetCodeStream) codeStream).generateEmulatedWriteAccessForField(this.codegenBinding);
}
}
/*
* No need to emulate access to protected fields since not implicitly accessed
*/
public void manageSyntheticReadAccessIfNecessary(BlockScope currentScope){
// The private access will be managed through the code generation
// if the binding declaring class is not visible, need special action
// for runtime compatibility on 1.2 VMs : change the declaring class of the binding
// NOTE: from 1.4 on, field's declaring class is touched if any different from receiver type
if (binding.declaringClass != this.receiverType
&& !this.receiverType.isArrayType()
&& binding.declaringClass != null // array.length
&& binding.constant == NotAConstant
&& ((currentScope.environment().options.complianceLevel >= CompilerOptions.JDK1_4
&& binding.declaringClass.id != T_Object) //no change for Object fields (in case there was)
|| !binding.declaringClass.canBeSeenBy(currentScope))){
this.codegenBinding = currentScope.enclosingSourceType().getUpdatedFieldBinding(binding, (ReferenceBinding) this.receiverType);
}
}
/*
* No need to emulate access to protected fields since not implicitly accessed
*/
public void manageSyntheticWriteAccessIfNecessary(BlockScope currentScope){
// The private access will be managed through the code generation
// if the binding declaring class is not visible, need special action
// for runtime compatibility on 1.2 VMs : change the declaring class of the binding
// NOTE: from 1.4 on, field's declaring class is touched if any different from receiver type
if (binding.declaringClass != this.receiverType
&& !this.receiverType.isArrayType()
&& binding.declaringClass != null // array.length
&& binding.constant == NotAConstant
&& ((currentScope.environment().options.complianceLevel >= CompilerOptions.JDK1_4
&& binding.declaringClass.id != T_Object) //no change for Object fields (in case there was)
|| !binding.declaringClass.canBeSeenBy(currentScope))){
this.codegenBinding = currentScope.enclosingSourceType().getUpdatedFieldBinding(binding, (ReferenceBinding) this.receiverType);
}
}
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
receiverType = receiver.resolveType(scope);
if (receiverType == null){
constant = NotAConstant;
return null;
}
// the case receiverType.isArrayType and token = 'length' is handled by the scope API
this.codegenBinding = this.binding = scope.getField(receiverType, token, this);
FieldBinding firstAttempt = binding;
boolean isNotVisible = false;
if (!binding.isValidBinding()) {
if (binding instanceof ProblemFieldBinding
&& ((ProblemFieldBinding) binding).problemId() == NotVisible) {
isNotVisible = true;
if (this.evaluationContext.declaringTypeName != null) {
delegateThis = scope.getField(scope.enclosingSourceType(), DELEGATE_THIS, this);
if (delegateThis == null){ ; // if not found then internal error, field should have been found
constant = NotAConstant;
scope.problemReporter().invalidField(this, receiverType);
return null;
}
} else {
constant = NotAConstant;
scope.problemReporter().invalidField(this, receiverType);
return null;
}
CodeSnippetScope localScope = new CodeSnippetScope(scope);
this.codegenBinding = this.binding = localScope.getFieldForCodeSnippet(delegateThis.type, token, this);
}
}
if (!binding.isValidBinding()) {
constant = NotAConstant;
if (isNotVisible) {
this.codegenBinding = this.binding = firstAttempt;
}
scope.problemReporter().invalidField(this, receiverType);
return null;
}
if (isFieldUseDeprecated(binding, scope))
scope.problemReporter().deprecatedField(binding, this);
// check for this.x in static is done in the resolution of the receiver
constant = FieldReference.getConstantFor(binding, receiver == ThisReference.ThisImplicit, this, scope, 0);
if (!receiver.isThis())
constant = NotAConstant;
return binding.type;
}
}