blob: c51f0fc80ae21d86c43321aa207f8de2105bb6e3 [file] [log] [blame]
/**********************************************************************
* 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);
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)
*/
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.
*/
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.
*/
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 fields directly from all constructors
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);
}
}