Bug 569756 - Revisit field initialization in roles - discriminate final
/ non-final
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
index 50ef142..9fd2f08 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
@@ -53,6 +53,7 @@
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.Lifting;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;
+import org.eclipse.objectteams.otdt.internal.core.compiler.model.TypeModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel.ProblemDetail;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.InsertTypeAdjustmentsVisitor;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;
@@ -346,7 +347,7 @@
&& this.constructorCall.accessMode != ExplicitConstructorCall.Tsuper) {
for (ReferenceBinding tsuperRole : roleType.roleModel.getTSuperRoleBindings()) {
RoleModel tsuperModel = tsuperRole.roleModel;
- if (tsuperModel != null && tsuperModel.hasFieldInit()) {
+ if (tsuperModel != null && tsuperModel.hasFinalFieldInit()) {
classScope.problemReporter().needToCallTSuper(this.constructorCall, tsuperRole);
break;
}
@@ -623,7 +624,7 @@
initializerScope.computeLocalVariablePositions(argSlotSize, codeStream); // offset by the argument size (since not linked to method scope)
boolean needFieldInitializations = this.constructorCall == null || this.constructorCall.accessMode != ExplicitConstructorCall.This;
-//{ObjectTeams: some more constructors do not initialize fields:
+//{ObjectTeams: some deviations regarding field initialization:
// copied team constructors (due to arg lifting) do not initialize fields
if ( !needFieldInitializations
&& this.constructorCall != null
@@ -653,6 +654,9 @@
if (!preInitSyntheticFields){
generateSyntheticFieldInitializationsIfNecessary(this.scope, codeStream, declaringClass);
}
+//{ObjectTeams: if a role can make use of _OT$InitFields, that is where all fields are initialized (only if all are non-final):
+ if (!isRoleUsingInitFields(declaringType, declaringClass)) {
+// orig:
// generate user field initialization
if (declaringType.fields != null) {
for (int i = 0, max = declaringType.fields.length; i < max; i++) {
@@ -662,6 +666,29 @@
}
}
}
+// :giro
+ } else
+ callInit:
+ {
+ // lifting ctor already contains the invoke statement
+ MethodBinding[] initMethods = declaringType.binding.getMethods(IOTConstants.INIT_METHOD_NAME);
+ if (initMethods.length >= 1)
+ {
+ int argCount = TSuperHelper.isTSuper(this.binding) ? 1 : 0;
+ for (int i = 0; i < initMethods.length; i++) {
+ if (initMethods[i].parameters.length == argCount) {
+ codeStream.aload_0(); // this
+ codeStream.invoke(Opcodes.OPC_invokevirtual, initMethods[i], declaringType.binding);
+ break callInit;
+ }
+ }
+ }
+ // no matching role init method should mean we had errors.
+ assert TypeModel.isIgnoreFurtherInvestigation(classScope.referenceContext)
+ || RoleModel.hasTagBit(declaringClass, RoleModel.BaseclassHasProblems)
+ || declaringClass.isTeam(); // might be the "turning constructor" of a nested team (see 2.1.11-otjld-*-1f)
+ }
+// SH}
}
// generate statements
if (this.statements != null) {
@@ -696,6 +723,23 @@
}
classFile.completeMethodInfo(this.binding, methodAttributeOffset, attributeNumber);
}
+private boolean isRoleUsingInitFields(TypeDeclaration typeDecl, ReferenceBinding typeBinding) {
+ if (!typeDecl.isRole()
+ || ( typeBinding.enclosingType() != null // also accept roles of o.o.Team
+ && typeBinding.enclosingType().id == IOTConstants.T_OrgObjectTeamsTeam))
+ return false;
+ if (typeDecl.fields == null)
+ return false;
+ boolean hasInitialization = false;
+ for (FieldDeclaration fieldDeclaration : typeDecl.fields) {
+ if (fieldDeclaration.initialization != null) {
+ if (fieldDeclaration.isFinal())
+ return false; // needs to be set inside a proper constructor!
+ hasInitialization = true;
+ }
+ }
+ return hasInitialization;
+}
@Override
protected AnnotationBinding[][] getPropagatedRecordComponentAnnotations() {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
index b5fc6b3..b8051a1 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
@@ -2123,7 +2123,7 @@
//{ObjectTeams: should we work at all?
Config config = Config.getConfig();
boolean fieldsAndMethods = config != null && config.verifyMethods;
- boolean hasFieldInit = false;
+ boolean hasFinalFieldInit = false;
if (fieldsAndMethods) {
// SH}
if (this.recordComponents != null) {
@@ -2160,8 +2160,8 @@
localMaxFieldCount++;
lastVisibleFieldID = field.binding.id;
//{ObjectTeams: has init?
- if (field.initialization != null)
- hasFieldInit = true;
+ if (field.initialization != null && field.isFinal())
+ hasFinalFieldInit = true;
// SH}
break;
@@ -2175,8 +2175,8 @@
//{ObjectTeams: also count type value parameters into maxFieldCount
if (this.typeParameters != null)
TypeValueParameter.updateMaxFieldCount(this);
- if (hasFieldInit && isRole())
- WordValueAttribute.addClassFlags(getRoleModel(), IOTConstants.OT_CLASS_HAS_FIELD_INITS);
+ if (hasFinalFieldInit && isRole())
+ WordValueAttribute.addClassFlags(getRoleModel(), IOTConstants.OT_CLASS_HAS_FINAL_FIELD_INITS);
}
// SH}
if (this.maxFieldCount < localMaxFieldCount) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
index 2f68012..a7ec918 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
@@ -1235,7 +1235,7 @@
123104 = Annotation '@{0}' can only be applied to role classes (OTJLD 2.3.1(d)).
123105 = Fields are discouraged in roles with InstantiationPolicy '{0}' (OTJLD 2.3.1(d)).
123106 = Roles with InstantiationPolicy '{0}' should define equals() and hashCode() methods (OTJLD 2.3.1(d)).
-123107 = Need to invoke a tsuper constructor because tsuper role ''{0}'' has field initializations.
+123107 = Need to invoke a tsuper constructor because tsuper role ''{0}'' has initializations for final fields.
123201 = Illegal type for argument {0}: declared lifting not allowed in static methods (OTJLD 2.3.2(a)).
123202 = Qualified type name not allowed here. Type after keyword "as" must be a role of the enclosing team {0} (OTJLD 2.3.2(a)).
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/core/compiler/IOTConstants.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/core/compiler/IOTConstants.java
index f025410..cd1ea31 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/core/compiler/IOTConstants.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/core/compiler/IOTConstants.java
@@ -96,6 +96,8 @@
public static final char[] OT_SETFIELD = (OT_DOLLAR+"set$").toCharArray();
public static final String OT_DOLLAR_ARG = OT_DOLLAR + "arg";
+ public static final char[] INIT_METHOD_NAME = (OT_DOLLAR + "InitFields").toCharArray();
+
// bytecode attributes:
public static final String PLAYEDBY = "PlayedBy";
public static final char[] PLAYEDBY_NAME = PLAYEDBY.toCharArray();
@@ -129,7 +131,7 @@
public static final int OT_CLASS_ROLE_FILE = 16;
public static final int OT_CLASS_FLAG_HAS_TSUPER = 32;
public static final int OT_CLASS_CONFINED = 64; // means: superclass Object should be removed on loading
- public static final int OT_CLASS_HAS_FIELD_INITS = 128;
+ public static final int OT_CLASS_HAS_FINAL_FIELD_INITS = 128;
public static final char[] CALLIN_FLAGS = "CallinFlags".toCharArray();
// possible values for CALLIN_FLAGS:
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/RoleInitializationMethod.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/RoleInitializationMethod.java
new file mode 100644
index 0000000..bf0ca8b
--- /dev/null
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/RoleInitializationMethod.java
@@ -0,0 +1,237 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ *
+ * Copyright 2004, 2006 Fraunhofer Gesellschaft, Munich, Germany,
+ * for its Fraunhofer Institute for Computer Architecture and Software
+ * Technology (FIRST), Berlin, Germany and Technical University Berlin,
+ * Germany.
+ *
+ * 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: RoleInitializationMethod.java 23401 2010-02-02 23:56:05Z stephan $
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Fraunhofer FIRST - Initial API and implementation
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otdt.internal.core.compiler.ast;
+
+import org.eclipse.jdt.internal.compiler.ClassFile;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
+import org.eclipse.jdt.internal.compiler.flow.InitializationFlowContext;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.jdt.internal.compiler.parser.Parser;
+import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
+import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;
+import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstEdit;
+import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;
+import org.eclipse.objectteams.otdt.internal.core.compiler.util.TSuperHelper;
+import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer;
+
+
+/**
+ * NEW for OTDT.
+ *
+ * A declaration for a generated method which performs all field initializations that
+ * otherwise each constructor would have to do.
+ *
+ * Example:
+ * int i = 5;
+ * yields:
+ * private void _OT$InitFields() {
+ * tsuper._OT$InitFields(); // if defined.
+ * i = 5;
+ * }
+ * Role () { // any constructor..
+ * super();
+ * _OT$InitFields();
+ * customStatements();
+ * }
+ *
+ * Why: This greatly helps to merge field initializations during copy-inheritance
+ * without modifying existing byte-code.
+ * How: endOfMethodHook() hooks into AbstractMethodDeclaration.generateCode().
+ *
+ * What: Insertion of calls to _OT$InitFields depends on the type of constructor:
+ * 1. Lifting constructors have an explicit MessageSend (generated by Lifting.genLiftToConstructorStatements)
+ * 2. Other constructors of roles invoke this method via ConstructorDeclaration.internalGenerateCode()
+ * Why: Lifting constructors must assign _OT$base before executing any user code from field initializations
+ * (_OT$base is needed for callouts!).
+ *
+ * @author stephan
+ * @version $Id: RoleInitializationMethod.java 23401 2010-02-02 23:56:05Z stephan $
+ */
+public class RoleInitializationMethod extends MethodDeclaration implements IOTConstants {
+
+ /**
+ * @param compilationResult
+ */
+ public RoleInitializationMethod(CompilationResult compilationResult) {
+ super(compilationResult);
+ this.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0, null);
+ this.selector = INIT_METHOD_NAME;
+ this.bits |= ASTNode.NeedFreeReturn;
+ this.isGenerated = true;
+ // set private to disable overriding along extends:
+ // (each constructor calls the corresponding init fields,
+ // which must stay within the constructor's class).
+ this.modifiers = ClassFileConstants.AccPrivate;
+ }
+
+
+ /*
+ * Override with empty implementation
+ * (non-Javadoc)
+ * @see org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration#analyseCode(org.eclipse.jdt.internal.compiler.lookup.ClassScope, org.eclipse.jdt.internal.compiler.flow.InitializationFlowContext, org.eclipse.jdt.internal.compiler.flow.FlowInfo)
+ */
+ public void analyseCode(
+ ClassScope classScope,
+ InitializationFlowContext initializationContext,
+ FlowInfo info)
+ {
+ if (this.statements != null) {
+ if (this.binding != null)
+ this.binding.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; // avoid unused-warning
+ super.analyseCode(classScope, initializationContext, info);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration#parseStatements(org.eclipse.jdt.internal.compiler.parser.Parser, org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration)
+ */
+ @Override
+ public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
+ // nothing to do, no statements to parse...
+ this.statements = new Statement[0];
+ }
+
+ /**
+ * Instead of parsing statements, possibly generate them just before resolving.
+ */
+ @Override
+ public void resolveStatements() {
+ if (this.ignoreFurtherInvestigation)
+ return;
+ MethodBinding tsuperInit = getTSuperInit(this.scope.referenceType().getRoleModel());
+ if (tsuperInit != null) {
+ AstGenerator gen = new AstGenerator(this.sourceStart, this.sourceEnd);
+ // there is a tsuper-version, so call it:
+ MessageSend tsuperCall = gen.messageSend(
+ ThisReference.implicitThis(),
+ this.selector,
+ null);
+ tsuperCall.arguments = TSuperHelper.addMarkerArgument(
+ null/*qualification*/, tsuperCall, tsuperCall.arguments, this.scope);
+ if (this.statements == null)
+ setStatements(new Statement[] { tsuperCall });
+ else {
+ // already have a casted local due to signature weakening
+ int len = this.statements.length;
+ Statement[] newStatements = new Statement[len+1];
+ System.arraycopy(
+ this.statements, 0,
+ newStatements, 0,
+ len);
+ newStatements[len] = tsuperCall;
+ setStatements(newStatements);
+ }
+ super.resolveStatements();
+ } else {
+ setStatements(new Statement[0]); // maybe remove useless casted local
+ }
+ }
+
+ /**
+ * During generateCode this is invoked right before generating the return opcode.
+ */
+ @Override
+ protected void endOfMethodHook (ClassFile classfile)
+ {
+ // basically taken from ConstructorDeclaration.internalGenerateCode
+ TypeDeclaration declaringType = this.scope.classScope().referenceContext;
+ // generate user field initialization
+ if (declaringType.fields != null) {
+ for (int i = 0, max = declaringType.fields.length; i < max; i++) {
+ FieldDeclaration fieldDecl;
+ if (!(fieldDecl = declaringType.fields[i]).isStatic()) {
+ fieldDecl.generateCode(this.scope, classfile.codeStream);
+ }
+ }
+ }
+ }
+
+ /**
+ * Create and setup a role initialization method.
+ *
+ * @param role the role class to augment (not an interface).
+ */
+ public static void setupRoleInitializationMethod(RoleModel role) {
+ if (TypeAnalyzer.isTopConfined(role.getBinding()))
+ return;
+ // Each split role has a method _OT$initFields instead of initializing non-final fields directly from all constructors
+ // (for final fields the VM requires initialization to happen in the constructor, though (since Java9))
+ TypeDeclaration roleClassDeclaration = role.getAst();
+ AstGenerator gen = new AstGenerator(roleClassDeclaration.sourceStart, roleClassDeclaration.sourceEnd);
+ RoleInitializationMethod initMethod = gen.roleInitializationMethod(
+ roleClassDeclaration.compilationResult);
+ // no arguments
+ AstEdit.addMethod(roleClassDeclaration, initMethod);
+ if (!role.getBinding().isDirectRole())
+ initMethod.resolve(role.getAst().scope);
+ }
+
+
+ /**
+ * Get the init fields method of the tsuper role of the current role OR null.
+ * Be sure not to return a method that is already a tsuper-copy.
+ *
+ * TODO (SH): support multiple tsupers, call all their init methods
+ *
+ * @param role the current role
+ * @return the tsuper version of this method.
+ */
+ private MethodBinding getTSuperInit(RoleModel role) {
+ if (!role.getBinding().isDirectRole())
+ return null;
+ ReferenceBinding tsuperRole = role.getTSuperRoleBinding(); // TODO(SH) multiple tsupers
+ if (tsuperRole != null) {
+ if (TypeAnalyzer.isSourceTypeWithErrors(tsuperRole))
+ return null;
+ MethodBinding[] tsuperInits = tsuperRole.getMethods(INIT_METHOD_NAME);
+ if (tsuperInits != null && tsuperInits.length >= 1) {
+ for (int i=0; i<tsuperInits.length; i++) {
+ if (tsuperInits[i].parameters.length == 0)
+ return tsuperInits[i];
+ }
+ }
+ }
+ return null;
+ }
+
+ public static Statement genInvokeInitMethod(Expression receiver, ReferenceBinding receiverType, AstGenerator gen)
+ {
+ if (TypeAnalyzer.isTopConfined(receiverType))
+ return gen.emptyStatement();
+ return gen.messageSend(receiver, INIT_METHOD_NAME, null);
+ }
+}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/ConstantPoolObjectMapper.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/ConstantPoolObjectMapper.java
index 0b47776..ffb1a2e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/ConstantPoolObjectMapper.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/ConstantPoolObjectMapper.java
@@ -474,6 +474,13 @@
assert(methods.length == 1);
return methods[0];
}
+ if ( role.isLocalType()
+ && CharOperation.equals(refMethod.selector, IOTConstants.INIT_METHOD_NAME))
+ {
+ // have no overriding along implicit inheritance due to scoping of locals,
+ // so just retrieve the method by its name:
+ return role.getMethods(IOTConstants.INIT_METHOD_NAME)[0];
+ }
int bestRank = Integer.MAX_VALUE;
MethodBinding bestMethod = null;
methods = role.getMethods(refMethod.selector);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Dependencies.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Dependencies.java
index 773c5b2..1a3817d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Dependencies.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/Dependencies.java
@@ -56,6 +56,7 @@
import org.eclipse.objectteams.otdt.core.compiler.Pair;
import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.RoleClassLiteralAccess;
+import org.eclipse.objectteams.otdt.internal.core.compiler.ast.RoleInitializationMethod;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.DeclaredLifting;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.Lifting.InstantiationPolicy;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.LiftingEnvironment;
@@ -608,6 +609,11 @@
success = establishFaultInTypes(model);
done = NESTED_TEAMS;
break;
+ case STATE_ROLE_INIT_METHODS:
+ if (teamBinding.isRole())
+ ensureRoleState(teamBinding.roleModel, nextState);
+ done = NONE;
+ break;
case STATE_METHODS_CREATED:
success = establishMethodsCreated(model);
done = NONE;
@@ -765,6 +771,9 @@
success &= ensureUnitState(model, nextState);
}
break;
+ case STATE_ROLE_INIT_METHODS:
+ success &= establishRoleInitializationMethod(model);
+ break;
case STATE_ROLE_FEATURES_COPIED:
success &= establishRoleFeaturesCopied(model);
break;
@@ -1016,6 +1025,7 @@
case STATE_NONE:
case STATE_ROLES_SPLIT:
case STATE_ROLES_LINKED:
+ case STATE_ROLE_INIT_METHODS:
case STATE_ROLE_FEATURES_COPIED:
case STATE_ROLE_HIERARCHY_ANALYZED:
case STATE_FULL_LIFTING:
@@ -1271,6 +1281,31 @@
* => Parser.getMethodBodies()
*/
+ /* **** STATE_ROLE_INIT_METHODS (OT/J) ****
+ * Create a method that holds all field initializations for a role.
+ *
+ * GENERATES:
+ * - _OT$InitFields()
+ */
+
+ /**
+ * BinaryTypes: nothing to do.
+ */
+ private static boolean establishRoleInitializationMethod(RoleModel role)
+ {
+ // must be done before role features copy
+ TypeDeclaration roleClassDeclaration = role.getAst();
+ if ( roleClassDeclaration != null
+ && !roleClassDeclaration.isInterface()
+ && needMethodBodies(roleClassDeclaration))
+ {
+ RoleInitializationMethod.setupRoleInitializationMethod(role);
+ }
+
+ role.setState(STATE_ROLE_INIT_METHODS);
+ return true;
+ }
+
/* **** STATE_ROLE_FEATURES_COPIED (OT/J) ****
* Copy all direct features (methods/ctors/fields/) from tsuper roles.
* Operate on AST and bindings. Link bindings of copied methods to
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/ITranslationStates.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/ITranslationStates.java
index 880e4ae..c4362c6 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/ITranslationStates.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/control/ITranslationStates.java
@@ -46,24 +46,22 @@
public static final int STATE_LENV_DONE_FIELDS_AND_METHODS= 7;//LookupEnvironment & Deps.
public static final int STATE_ROLES_LINKED = 8;//LookupEnvironment & RoleSplitter
public static final int STATE_METHODS_PARSED = 9;//Compiler
-// removed state:
-// public static final int STATE_ROLE_INIT_METHODS = 10;//RoleInitializationMethod
-//
public static final int STATE_ROLE_HIERARCHY_ANALYZED = 10;//Lifting
- public static final int STATE_ROLE_FEATURES_COPIED = 11;//Copy inheritance
- public static final int STATE_FULL_LIFTING = 12;//Lifting
- public static final int STATE_FAULT_IN_TYPES = 13;//Scope
- public static final int STATE_METHODS_CREATED = 14;//RoleTypeBinding, CopyInheritance, CalloutImplementor
- public static final int STATE_TYPES_ADJUSTED = 15;//RoleTypeBinding, CopyInheritance
- public static final int STATE_STATEMENTS_TRANSFORMED = 16;//TransformStatementsVisitor
- public static final int STATE_CALLINS_TRANSFORMED = 17;//CallinImplementor[Dyn]
- public static final int STATE_LATE_ATTRIBUTES_EVALUATED= 18; // ModelElement, TypeModel
- public static final int STATE_METHODS_VERIFIED = 19;//Scope
- public static final int STATE_RESOLVED = 20;//AST
- public static final int STATE_LATE_ELEMENTS_COPIED = 21;//CopyInheritance
- public static final int STATE_CODE_ANALYZED = 22;//AST
- public static final int STATE_BYTE_CODE_PREPARED = 23;//Dependencies
- public static final int STATE_BYTE_CODE_GENERATED = 24;//AST
+ public static final int STATE_ROLE_INIT_METHODS = 11;//RoleInitializationMethod
+ public static final int STATE_ROLE_FEATURES_COPIED = 12;//Copy inheritance
+ public static final int STATE_FULL_LIFTING = 13;//Lifting
+ public static final int STATE_FAULT_IN_TYPES = 14;//Scope
+ public static final int STATE_METHODS_CREATED = 15;//RoleTypeBinding, CopyInheritance, CalloutImplementor
+ public static final int STATE_TYPES_ADJUSTED = 16;//RoleTypeBinding, CopyInheritance
+ public static final int STATE_STATEMENTS_TRANSFORMED = 17;//TransformStatementsVisitor
+ public static final int STATE_CALLINS_TRANSFORMED = 18;//CallinImplementor[Dyn]
+ public static final int STATE_LATE_ATTRIBUTES_EVALUATED= 19; // ModelElement, TypeModel
+ public static final int STATE_METHODS_VERIFIED = 20;//Scope
+ public static final int STATE_RESOLVED = 21;//AST
+ public static final int STATE_LATE_ELEMENTS_COPIED = 22;//CopyInheritance
+ public static final int STATE_CODE_ANALYZED = 23;//AST
+ public static final int STATE_BYTE_CODE_PREPARED = 24;//Dependencies
+ public static final int STATE_BYTE_CODE_GENERATED = 25;//AST
/**
* Note, that this state does not trigger intermediate steps, in fact never use it
* with ensureState() but directly invoke Dependencies.cleanup!
@@ -83,8 +81,9 @@
"bindings: fields and methods [completed]",
"roles linked",
"method bodies parsed",
- "role hierarchy analyzed",
+ "role initialization method",
"role features copied",
+ "role hierarchy analyzed",
"full lifting",
"fault in types",
"methods created",
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lifting/Lifting.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lifting/Lifting.java
index bf181ca..d2db533 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lifting/Lifting.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lifting/Lifting.java
@@ -46,6 +46,7 @@
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError;
+import org.eclipse.objectteams.otdt.internal.core.compiler.ast.RoleInitializationMethod;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
@@ -413,8 +414,13 @@
new Expression[] {
gen.singleNameReference(baseArgName)
};
- // start with an empty statements list
- liftToConstructorDeclaration.setStatements(new Statement[0]);
+ // start with an empty statements list, except for this._OT$InitFields();
+ liftToConstructorDeclaration.setStatements(new Statement[] {
+ RoleInitializationMethod.genInvokeInitMethod(
+ gen.thisReference(),
+ roleType.binding,
+ gen)
+ });
}
private void genLiftToConstructorStatements(
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/RoleModel.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/RoleModel.java
index a04322b..35d6a2b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/RoleModel.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/RoleModel.java
@@ -36,6 +36,7 @@
import org.eclipse.jdt.internal.compiler.ClassFile;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
@@ -1134,7 +1135,10 @@
this._tsuperRoleBindings[this.numTSuperRoles++] = tsuperRole;
if (getAst() != null && getAst().isInterface())
TypeLevel.addImplicitInheritance(getAst(), tsuperRole);
- WordValueAttribute.addClassFlags(this, IOTConstants.OT_CLASS_FLAG_HAS_TSUPER);
+ int otClassFlags = IOTConstants.OT_CLASS_FLAG_HAS_TSUPER;
+ if (tsuperRole.roleModel.hasFinalFieldInit())
+ otClassFlags |= IOTConstants.OT_CLASS_HAS_FINAL_FIELD_INITS;
+ WordValueAttribute.addClassFlags(this, otClassFlags);
if (this._binding != null)
this._binding.modifiers |= AccOverriding;
}
@@ -1142,10 +1146,17 @@
this._state.inititalize(ITranslationStates.STATE_ROLES_SPLIT);
}
- public boolean hasFieldInit() {
+ public boolean hasFinalFieldInit() {
AbstractAttribute attribute = getAttribute(IOTConstants.OT_CLASS_FLAGS);
if (attribute instanceof WordValueAttribute) {
- return (((WordValueAttribute) attribute).getValue() & IOTConstants.OT_CLASS_HAS_FIELD_INITS) != 0;
+ if ((((WordValueAttribute) attribute).getValue() & IOTConstants.OT_CLASS_HAS_FINAL_FIELD_INITS) != 0)
+ return true;
+ }
+ if (this._classPart != null && this._classPart.fields != null) {
+ for (FieldDeclaration field : this._classPart.fields) {
+ if (field.initialization != null && field.isFinal())
+ return true;
+ }
}
return false;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/copyinheritance/CopyInheritance.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/copyinheritance/CopyInheritance.java
index 12438f1..7f27be0 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/copyinheritance/CopyInheritance.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/copyinheritance/CopyInheritance.java
@@ -2246,6 +2246,9 @@
}
}
}
+ if ( !binding.declaringClass.isDirectRole()
+ && CharOperation.equals(binding.selector, IOTConstants.INIT_METHOD_NAME))
+ return changed; // no statements, no casted locals.
if (!newLocalStats.isEmpty())
{
if (StateHelper.hasState(binding.declaringClass, ITranslationStates.STATE_RESOLVED))
@@ -2348,7 +2351,7 @@
public static boolean needsSuperCtorCall(RoleModel role) {
for (ReferenceBinding tsuperRole : role.getTSuperRoleBindings()) {
RoleModel tsuperModel = tsuperRole.roleModel;
- if (tsuperModel != null && tsuperModel.hasFieldInit())
+ if (tsuperModel != null && tsuperModel.hasFinalFieldInit())
return true;
}
return false;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/AstGenerator.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/AstGenerator.java
index 88f44b5..37c1582 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/AstGenerator.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/AstGenerator.java
@@ -65,6 +65,7 @@
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.PotentialLiftExpression;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.QualifiedBaseReference;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.ResultReference;
+import org.eclipse.objectteams.otdt.internal.core.compiler.ast.RoleInitializationMethod;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TSuperMessageSend;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TsuperReference;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TypeAnchorReference;
@@ -1291,6 +1292,13 @@
return lifter;
}
+ public RoleInitializationMethod roleInitializationMethod(CompilationResult compilationResult)
+ {
+ RoleInitializationMethod roleInit = new RoleInitializationMethod(compilationResult);
+ setMethodPositions(roleInit);
+ return roleInit;
+ }
+
public ThisReference thisReference() {
return new ThisReference(this.sourceStart, this.sourceEnd);
}
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/DevelopmentExamples.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/DevelopmentExamples.java
index c35f55a..746effe 100644
--- a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/DevelopmentExamples.java
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/DevelopmentExamples.java
@@ -5763,9 +5763,6 @@
" {\n" +
" buy <- replace book;\n" +
" String getName() -> String getName();\n" +
- " public Subscriber(flightbooking.model.Passenger p) {\n" +
- " tsuper();\n" +
- " }\n" +
" };\n" +
" @Override\n" +
" public class Item playedBy flightbooking.model.Segment {\n" +
@@ -5969,9 +5966,6 @@
" {\n" +
" buy <- replace book;\n" +
" String getName() -> String getName();\n" +
- " public Subscriber(Passenger p) {\n" +
- " tsuper();\n" +
- " }\n" +
" };\n" +
" @Override\n" +
" public class Item playedBy Segment {\n" +
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/ImplicitInheritance.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/ImplicitInheritance.java
index e87e644..c5339af 100644
--- a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/ImplicitInheritance.java
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/ImplicitInheritance.java
@@ -1478,6 +1478,7 @@
" }\n" +
" void test() {\n" +
" System.out.print(new R1().s1);\n" + // OK: has explicit tsuper() call
+ " System.out.print(new R1().s1Final);\n" + // OK: has explicit tsuper() call
" System.out.print(new R2().s2);\n" + // OK: fully inherited, all members are copied
" System.out.print(new R3().s3);\n" + // OK: constructor is copied
" }\n" +
@@ -1486,6 +1487,7 @@
"public team class SuperTeamFI4FI1 {\n" +
" protected class R1 {\n" +
" protected String s1 = \"s1\";\n" +
+ " final protected String s1Final = \"s1Final\";\n" +
" }\n" +
" protected class R2 {\n" +
" protected String s2 = \"s2\";\n" +
@@ -1495,7 +1497,51 @@
" }\n" +
"}\n",
},
- "s1s2s3");
+ "s1s1Finals2s3");
+ }
+
+ public void testFieldInitialization1_OK_3layers() throws Exception {
+ runConformTest(
+ new String[] {
+ "SubTeamFI1_3l.java",
+ "public team class SubTeamFI1_3l extends MidTeamFI4FI1_3l {\n" +
+ " @Override\n" +
+ " protected class R1 {\n" +
+ " protected R1() {\n" +
+ " tsuper();\n" +
+ " }\n" +
+ " }\n" +
+ " @Override\n" +
+ " protected class R3 {\n" +
+ " }\n" +
+ " public static void main(String... args) {\n" +
+ " new SubTeamFI1_3l().test();\n" +
+ " }\n" +
+ " void test() {\n" +
+ " System.out.print(new R1().s1);\n" + // OK: has explicit tsuper() call
+ " System.out.print(new R1().s1Final);\n" + // OK: has explicit tsuper() call
+ " System.out.print(new R2().s2);\n" + // OK: fully inherited, all members are copied
+ " System.out.print(new R3().s3);\n" + // OK: constructor is copied
+ " }\n" +
+ "}\n",
+ "MidTeamFI4FI1_3l.java",
+ "public team class MidTeamFI4FI1_3l extends SuperTeamFI4FI1_3l {\n" +
+ "}\n",
+ "SuperTeamFI4FI1_3l.java",
+ "public team class SuperTeamFI4FI1_3l {\n" +
+ " protected class R1 {\n" +
+ " protected String s1 = \"s1\";\n" +
+ " final protected String s1Final = \"s1Final\";\n" +
+ " }\n" +
+ " protected class R2 {\n" +
+ " protected String s2 = \"s2\";\n" +
+ " }\n" +
+ " protected class R3 {\n" +
+ " protected String s3 = \"s3\";\n" +
+ " }\n" +
+ "}\n",
+ },
+ "s1s1Finals2s3");
}
public void testFieldInitialization2_bound_OK() throws Exception {
@@ -1549,6 +1595,93 @@
"s1s2s3");
}
+ public void testFieldInitialization2_bound_OK_3layers() throws Exception {
+ runConformTest(
+ new String[] {
+ "SubTeamFI2_3l.java",
+ "public team class SubTeamFI2_3l extends MidTeamFI4FI2_3l {\n" +
+ " @Override\n" +
+ " protected class R1 {\n" +
+ " protected R1(Base1 b) {\n" +
+ " tsuper(b);\n" +
+ " }\n" +
+ " }\n" +
+ " @Override\n" +
+ " protected class R2 {\n" +
+ " }\n" +
+ " @Override\n" +
+ " protected class R3 {\n" +
+ " protected R3() {\n" +
+ " tsuper(base());\n" +
+ " }\n" +
+ " }\n" +
+ " public static void main(String... args) {\n" +
+ " new SubTeamFI2_3l().test();\n" +
+ " }\n" +
+ " void test() {\n" +
+ " System.out.print(new R1(new Base1()).s1);\n" + // OK: explicit tsuper() call (delegating lifting constructor)
+ " System.out.print(new R2(new Base2()).s2);\n" + // OK: copied lifting constructor
+ " System.out.print(new R3(new Base3()).s3);\n" + // OK: explicit tsuper() call (delegation from creating to lifting constructor)
+ " }\n" +
+ "}\n",
+ "MidTeamFI4FI2_3l.java",
+ "public team class MidTeamFI4FI2_3l extends SuperTeamFI4FI2_3l {\n" +
+ "}\n",
+ "SuperTeamFI4FI2_3l.java",
+ "public team class SuperTeamFI4FI2_3l {\n" +
+ " protected class R1 playedBy Base1 {\n" +
+ " protected String s1 = \"s1\";\n" +
+ " }\n" +
+ " protected class R2 playedBy Base2 {\n" +
+ " protected String s2 = \"s2\";\n" +
+ " }\n" +
+ " protected class R3 playedBy Base3 {\n" +
+ " protected String s3 = \"s3\";\n" +
+ " }\n" +
+ "}\n",
+ "Base1.java",
+ "public class Base1 {}\n",
+ "Base2.java",
+ "public class Base2 {}\n",
+ "Base3.java",
+ "public class Base3 {}\n",
+ },
+ "s1s2s3");
+ }
+
+ public void testFieldInitialization3_OK() throws Exception {
+ runNegativeTest(
+ new String[] {
+ "SubTeamFI3a.java",
+ "public team class SubTeamFI3a extends SuperTeamFI4FI3 {\n" +
+ " @Override\n" +
+ " protected class R1 {\n" +
+ " protected R1() {\n" +
+ " super();\n" +
+ " }\n" +
+ " }\n" +
+ " @Override\n" +
+ " protected class R2 {\n" +
+ " protected R2() {\n" +
+ " super();\n" +
+ " }\n" +
+ " protected R2(int i) {\n" +
+ " }\n" +
+ " }\n" +
+ "}\n",
+ "SuperTeamFI4FI3.java",
+ "public team class SuperTeamFI4FI3 {\n" +
+ " protected class R1 {\n" +
+ " String s1 = \"s1\";\n" +
+ " }\n" +
+ " protected class R2 extends R1 {\n" +
+ " String s2 = \"s2\";\n" +
+ " }\n" +
+ "}\n"
+ },
+ "");
+ }
+
public void testFieldInitialization3_NOK() throws Exception {
runNegativeTest(
new String[] {
@@ -1572,10 +1705,10 @@
"SuperTeamFI4FI3.java",
"public team class SuperTeamFI4FI3 {\n" +
" protected class R1 {\n" +
- " String s1 = \"s1\";\n" +
+ " final String s1 = \"s1\";\n" +
" }\n" +
" protected class R2 extends R1 {\n" +
- " String s2 = \"s2\";\n" +
+ " final String s2 = \"s2\";\n" +
" }\n" +
"}\n"
},
@@ -1583,20 +1716,114 @@
"1. ERROR in SubTeamFI3a.java (at line 5)\n" +
" super();\n" +
" ^^^^^^^^\n" +
- "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI4FI3.R1\' has field initializations.\n" +
+ "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI4FI3.R1\' has initializations for final fields.\n" +
"----------\n" +
"2. ERROR in SubTeamFI3a.java (at line 11)\n" +
" super();\n" +
" ^^^^^^^^\n" +
- "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI4FI3.R2\' has field initializations.\n" +
+ "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI4FI3.R2\' has initializations for final fields.\n" +
"----------\n" +
"3. ERROR in SubTeamFI3a.java (at line 13)\n" +
" protected R2(int i) {\n" +
" ^^^^^^^^^\n" +
- "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI4FI3.R2\' has field initializations.\n" +
+ "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI4FI3.R2\' has initializations for final fields.\n" +
"----------\n");
}
+ public void testFieldInitialization3_NOK_3layers() throws Exception {
+ runNegativeTest(
+ new String[] {
+ "SubTeamFI3a_3l.java",
+ "public team class SubTeamFI3a_3l extends MidTeamFI4FI3a_3l {\n" +
+ " @Override\n" +
+ " protected class R1 {\n" +
+ " protected R1() {\n" +
+ " super();\n" +
+ " }\n" +
+ " }\n" +
+ " @Override\n" +
+ " protected class R2 {\n" +
+ " protected R2() {\n" +
+ " super();\n" +
+ " }\n" +
+ " protected R2(int i) {\n" + // implicit super() is not acceptable
+ " }\n" +
+ " }\n" +
+ "}\n",
+ "MidTeamFI4FI3a_3l.java",
+ "public team class MidTeamFI4FI3a_3l extends SuperTeamFI4FI3a_3l {\n" +
+ "}\n",
+ "SuperTeamFI4FI3a_3l.java",
+ "public team class SuperTeamFI4FI3a_3l {\n" +
+ " protected class R1 {\n" +
+ " final String s1 = \"s1\";\n" +
+ " }\n" +
+ " protected class R2 extends R1 {\n" +
+ " final String s2 = \"s2\";\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in SubTeamFI3a_3l.java (at line 5)\n" +
+ " super();\n" +
+ " ^^^^^^^^\n" +
+ "Need to invoke a tsuper constructor because tsuper role \'MidTeamFI4FI3a_3l.R1\' has initializations for final fields.\n" +
+ "----------\n" +
+ "2. ERROR in SubTeamFI3a_3l.java (at line 11)\n" +
+ " super();\n" +
+ " ^^^^^^^^\n" +
+ "Need to invoke a tsuper constructor because tsuper role \'MidTeamFI4FI3a_3l.R2\' has initializations for final fields.\n" +
+ "----------\n" +
+ "3. ERROR in SubTeamFI3a_3l.java (at line 13)\n" +
+ " protected R2(int i) {\n" +
+ " ^^^^^^^^^\n" +
+ "Need to invoke a tsuper constructor because tsuper role \'MidTeamFI4FI3a_3l.R2\' has initializations for final fields.\n" +
+ "----------\n");
+ }
+
+ public void testFieldInitialization4_bound_OK() throws Exception {
+ // variants with regular constructors
+ runNegativeTest(
+ new String[] {
+ "SubTeamFI4.java",
+ "public team class SubTeamFI4 extends SuperTeamFI4 {\n" +
+ " @Override\n" +
+ " protected class R1 {\n" +
+ " protected R1() {\n" +
+ " tsuper(base());\n" +
+ " }\n" +
+ " }\n" +
+ " @Override\n" +
+ " protected class R2 {\n" +
+ " protected R2() {\n" +
+ " base();\n" +
+ " }\n" +
+ " }\n" +
+ " public static void main(String... args) {\n" +
+ " new SubTeamFI4().test();\n" +
+ " }\n" +
+ " void test() {\n" +
+ " System.out.print(new R1().s1);\n" +
+ " System.out.print(new R2().s2);\n" +
+ " }\n" +
+ "}\n",
+ "SuperTeamFI4.java",
+ "public team class SuperTeamFI4 {\n" +
+ " protected class R1 playedBy Base1 {\n" +
+ " protected String s1 = \"s1\";\n" +
+ " }\n" +
+ " protected class R2 playedBy Base2 {\n" +
+ " protected String s2 = \"s2\";\n" +
+ " }\n" +
+ "}\n",
+ "Base1.java",
+ "public class Base1 {}\n",
+ "Base2.java",
+ "public class Base2 {}\n",
+ },
+ "");
+ }
+
public void testFieldInitialization4_bound_NOK() throws Exception {
// variants with regular constructors
runNegativeTest(
@@ -1629,7 +1856,7 @@
" protected String s1 = \"s1\";\n" +
" }\n" +
" protected class R2 playedBy Base2 {\n" +
- " protected String s2 = \"s2\";\n" +
+ " final protected String s2 = \"s2\";\n" +
" }\n" +
"}\n",
"Base1.java",
@@ -1646,11 +1873,67 @@
"2. ERROR in SubTeamFI4.java (at line 10)\n" +
" protected R2() {\n" +
" ^^^^\n" +
- "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI4.R2\' has field initializations.\n" +
+ "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI4.R2\' has initializations for final fields.\n" +
"----------\n");
}
- public void testFieldInitialization5_bound_NOK() throws Exception {
+ public void testFieldInitialization4_bound_NOK_3layers() throws Exception {
+ // variants with regular constructors
+ runNegativeTest(
+ new String[] {
+ "SubTeamFI4_3l.java",
+ "public team class SubTeamFI4_3l extends MidTeamFI4_3l {\n" +
+ " @Override\n" +
+ " protected class R1 {\n" +
+ " protected R1() {\n" +
+ " tsuper();\n" +
+ " }\n" +
+ " }\n" +
+ " @Override\n" +
+ " protected class R2 {\n" +
+ " protected R2() {\n" +
+ " base();\n" +
+ " }\n" +
+ " }\n" +
+ " public static void main(String... args) {\n" +
+ " new SubTeamFI4_3l().test();\n" +
+ " }\n" +
+ " void test() {\n" +
+ " System.out.print(new R1().s1);\n" +
+ " System.out.print(new R2().s2);\n" +
+ " }\n" +
+ "}\n",
+ "MidTeamFI4_3l.java",
+ "public team class MidTeamFI4_3l extends SuperTeamFI4_3l {\n" +
+ "}\n",
+ "SuperTeamFI4_3l.java",
+ "public team class SuperTeamFI4_3l {\n" +
+ " protected class R1 playedBy Base1 {\n" +
+ " protected String s1 = \"s1\";\n" +
+ " }\n" +
+ " protected class R2 playedBy Base2 {\n" +
+ " final protected String s2 = \"s2\";\n" +
+ " }\n" +
+ "}\n",
+ "Base1.java",
+ "public class Base1 {}\n",
+ "Base2.java",
+ "public class Base2 {}\n",
+ },
+ "----------\n" +
+ "1. ERROR in SubTeamFI4_3l.java (at line 5)\n" +
+ " tsuper();\n" +
+ " ^^^^^^^^^\n" +
+ "The constructor MidTeamFI4_3l.R1() is undefined\n" +
+ "----------\n" +
+ "2. ERROR in SubTeamFI4_3l.java (at line 10)\n" +
+ " protected R2() {\n" +
+ " ^^^^\n" +
+ "Need to invoke a tsuper constructor because tsuper role \'MidTeamFI4_3l.R2\' has initializations for final fields.\n" +
+ "----------\n");
+ }
+
+ public void testFieldInitialization5_bound_OK() throws Exception {
// variants with lifting constructors
runNegativeTest(
new String[] {
@@ -1681,16 +1964,50 @@
"Base1.java",
"public class Base1 {}\n",
},
+ "");
+ }
+
+ public void testFieldInitialization5_bound_NOK() throws Exception {
+ // variants with lifting constructors
+ runNegativeTest(
+ new String[] {
+ "SubTeamFI5.java",
+ "public team class SubTeamFI5 extends SuperTeamFI5 {\n" +
+ " @Override\n" +
+ " protected class R1 {\n" +
+ " protected R1(Base1 b) {\n" +
+ " super();\n" +
+ " }\n" +
+ " }\n" +
+ " @Override\n" +
+ " protected class R2 {\n" +
+ " protected R2(Base1 b) {\n" +
+ " super(b);\n" +
+ " }\n" +
+ " }\n" +
+ "}\n",
+ "SuperTeamFI5.java",
+ "public team class SuperTeamFI5 {\n" +
+ " protected class R1 playedBy Base1 {\n" +
+ " final String s1 = \"s1\";\n" +
+ " }\n" +
+ " protected class R2 extends R1 {\n" +
+ " final String s2 = \"s2\";\n" +
+ " }\n" +
+ "}\n",
+ "Base1.java",
+ "public class Base1 {}\n",
+ },
"----------\n" +
"1. ERROR in SubTeamFI5.java (at line 5)\n" +
" super();\n" +
" ^^^^^^^^\n" +
- "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI5.R1\' has field initializations.\n" +
+ "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI5.R1\' has initializations for final fields.\n" +
"----------\n" +
"2. ERROR in SubTeamFI5.java (at line 11)\n" +
" super(b);\n" +
" ^^^^^^^^^\n" +
- "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI5.R2\' has field initializations.\n" +
+ "Need to invoke a tsuper constructor because tsuper role \'SuperTeamFI5.R2\' has initializations for final fields.\n" +
"----------\n");
}
}