diff options
| author | Stephan Herrmann | 2020-12-17 17:53:22 +0000 |
|---|---|---|
| committer | Stephan Herrmann | 2020-12-17 17:53:22 +0000 |
| commit | 2629b6df1a73f1b7aa63a0f5f1a922446da662db (patch) | |
| tree | 0d3bee888f7a2246bdc0d7c8f817f1b66d25172e | |
| parent | e8df4ac059126fd502a6ee04fe904ae3960d1684 (diff) | |
| download | org.eclipse.objectteams-2629b6df1a73f1b7aa63a0f5f1a922446da662db.tar.gz org.eclipse.objectteams-2629b6df1a73f1b7aa63a0f5f1a922446da662db.tar.xz org.eclipse.objectteams-2629b6df1a73f1b7aa63a0f5f1a922446da662db.zip | |
Bug 569756 - Revisit field initialization in roles - discriminate final
/ non-final
14 files changed, 713 insertions, 50 deletions
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 50ef142e3..9fd2f0862 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.control.Config; 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 @@ public void analyseCode(ClassScope classScope, InitializationFlowContext initial && 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 @@ private void internalGenerateCode(ClassScope classScope, ClassFile classFile) { 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 @@ private void internalGenerateCode(ClassScope classScope, ClassFile classFile) { 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 @@ private void internalGenerateCode(ClassScope classScope, ClassFile classFile) { } } } +// :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 @@ private void internalGenerateCode(ClassScope classScope, ClassFile classFile) { } 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 b5fc6b30e..b8051a1e4 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 @@ public void resolve() { //{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 @@ public void resolve() { 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 @@ public void resolve() { //{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 2f680126f..a7ec9181f 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 f0254107e..cd1ea31bf 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 interface IOTConstants 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 interface IOTConstants 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 000000000..bf0ca8b03 --- /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 0b4777680..ffb1a2e8f 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 @@ public class ConstantPoolObjectMapper implements ClassFileConstants{ 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 773c5b20f..1a3817d4a 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.OTNameUtils; 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 @@ public class Dependencies implements ITranslationStates { 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 @@ public class Dependencies implements ITranslationStates { 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 @@ public class Dependencies implements ITranslationStates { 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 @@ public class Dependencies implements ITranslationStates { * => 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 880e4aea1..c4362c6af 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 interface ITranslationStates { 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 @@ public interface ITranslationStates { "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 bf181ca39..d2db533ef 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.TypeIds; 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 @@ public class Lifting extends SwitchOnBaseTypeGenerator 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 a04322bd5..35d6a2b26 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.core.compiler.CharOperation; 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 @@ public class RoleModel extends TypeModel 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 @@ public class RoleModel extends TypeModel 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 12438f159..7f27be0bc 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 @@ public class CopyInheritance implements IOTConstants, ClassFileConstants, ExtraC } } } + 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 class CopyInheritance implements IOTConstants, ClassFileConstants, ExtraC 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 88f44b5ca..37c1582bb 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.MethodSpec; 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 @@ public class AstGenerator extends AstFactory { 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 c35f55a6d..746effeda 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 @@ public class DevelopmentExamples extends AbstractOTJLDTest { " {\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 @@ public class DevelopmentExamples extends AbstractOTJLDTest { " {\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 e87e64487..c5339afd0 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 @@ public class ImplicitInheritance extends AbstractOTJLDTest { " }\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 class ImplicitInheritance extends AbstractOTJLDTest { "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 @@ public class ImplicitInheritance extends AbstractOTJLDTest { " }\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,7 +1595,61 @@ public class ImplicitInheritance extends AbstractOTJLDTest { "s1s2s3"); } - public void testFieldInitialization3_NOK() throws Exception { + 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", @@ -1565,7 +1665,7 @@ public class ImplicitInheritance extends AbstractOTJLDTest { " protected R2() {\n" + " super();\n" + " }\n" + - " protected R2(int i) {\n" + // implicit super() is not acceptable + " protected R2(int i) {\n" + " }\n" + " }\n" + "}\n", @@ -1579,24 +1679,151 @@ public class ImplicitInheritance extends AbstractOTJLDTest { " }\n" + "}\n" }, + ""); + } + + public void testFieldInitialization3_NOK() 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" + // implicit super() is not acceptable + " }\n" + + " }\n" + + "}\n", + "SuperTeamFI4FI3.java", + "public team class SuperTeamFI4FI3 {\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.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 @@ public class ImplicitInheritance extends AbstractOTJLDTest { " 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 @@ public class ImplicitInheritance extends AbstractOTJLDTest { "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 @@ public class ImplicitInheritance extends AbstractOTJLDTest { "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"); } } |
