| /********************************************************************** |
| * 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) |
| */ |
| 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); |
| } |
| } |