| /********************************************************************** |
| * This file is part of "Object Teams Development Tooling"-Software |
| * |
| * Copyright 2010, 2015 Stephan Herrmann |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Please visit http://www.eclipse.org/objectteams for updates and contact. |
| * |
| * Contributors: |
| * Stephan Herrmann - Initial API and implementation |
| **********************************************************************/ |
| package org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer; |
| |
| |
| import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccFinal; |
| import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccPrivate; |
| import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccProtected; |
| import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccPublic; |
| import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccSynchronized; |
| import static org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers.AccVisibilityMASK; |
| import static org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CallinImplementorDyn.*; |
| |
| import java.io.File; |
| import java.io.IOException; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ClassFile; |
| import org.eclipse.jdt.internal.compiler.CompilationResult; |
| import org.eclipse.jdt.internal.compiler.ast.Argument; |
| import org.eclipse.jdt.internal.compiler.ast.Expression; |
| import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; |
| import org.eclipse.jdt.internal.compiler.ast.Statement; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; |
| import org.eclipse.jdt.internal.compiler.classfmt.MethodInfo; |
| import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; |
| import org.eclipse.jdt.internal.compiler.env.IBinaryType; |
| import org.eclipse.jdt.internal.compiler.flow.FlowContext; |
| import org.eclipse.jdt.internal.compiler.flow.FlowInfo; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme; |
| import org.eclipse.jdt.internal.compiler.lookup.Binding; |
| import org.eclipse.jdt.internal.compiler.lookup.ClassScope; |
| import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; |
| import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; |
| import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; |
| import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; |
| import org.eclipse.objectteams.otdt.core.compiler.IOTConstants; |
| import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.BytecodeTransformer; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.ConstantPoolObject; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.ConstantPoolObjectMapper; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.ConstantPoolObjectReader; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.control.Dependencies; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.CallinCalloutBinding; |
| 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.util.AstEdit; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator; |
| |
| /** |
| * This class adds methods and fields from org.objectteams.Team to those teams that |
| * extend a non-team class and thus cannot inherit these necessary members. |
| * Methods are copied via byte-code copy, fields are generated at AST-level (incl. initialization). |
| * |
| * Technical note: the way this generator is constructed it assumes that org.objectteams.Team |
| * exists in binary form, i.e., a team leveraging this generator cannot be compiled together |
| * with org.objectteams.Team. |
| * |
| * @since 1.4.0 |
| */ |
| public class TeamMethodGenerator { |
| static final char[][] JAVA_LANG_WEAKHASHMAP = new char[][] {"java".toCharArray(), "util".toCharArray(), "WeakHashMap".toCharArray()}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| static final char[][] JAVA_LANG_THREAD = new char[][] {"java".toCharArray(), "lang".toCharArray(), "Thread".toCharArray()}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| static final char[][] JAVA_LANG_THREADLOCAL = new char[][] {"java".toCharArray(), "lang".toCharArray(), "ThreadLocal".toCharArray()}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| |
| /** Simple structure to represent one method of o.o.Team. */ |
| static class MethodDescriptor { |
| String selector; |
| String signature; |
| Type args; |
| Type returnType; |
| int modifiers; |
| |
| // store from binary method of o.o.Team: |
| int methodCodeOffset; |
| ReferenceBinding declaringClass; |
| MethodBinding binding; |
| |
| MethodDescriptor(String selector, String signature, Type args, Type returnType, int modifiers) { |
| super(); |
| this.selector = selector; |
| this.signature = signature; |
| this.args = args; |
| this.returnType = returnType; |
| this.modifiers = modifiers; |
| } |
| |
| @SuppressWarnings("nls") |
| Argument[] makeArgs(AstGenerator gen) { |
| switch (this.args) { |
| case THREAD: |
| return new Argument[] { |
| gen.argument("thread".toCharArray(), gen.qualifiedTypeReference(JAVA_LANG_THREAD)) |
| }; |
| case BOOLEAN: |
| return new Argument[] { |
| gen.argument("flag".toCharArray(), gen.baseTypeReference(TypeConstants.BOOLEAN)) |
| }; |
| case INT: |
| return new Argument[] { |
| gen.argument("flags".toCharArray(), gen.baseTypeReference(TypeConstants.INT)) |
| }; |
| case OTDYNARGS1: |
| return new Argument[] { |
| gen.argument("base".toCharArray(), gen.qualifiedTypeReference(IOTConstants.ORG_OBJECTTEAMS_IBOUNDBASE2)), |
| gen.argument("teams".toCharArray(), gen.qualifiedArrayTypeReference(IOTConstants.ORG_OBJECTTEAMS_ITEAM, 1)), |
| gen.argument("idx".toCharArray(), gen.baseTypeReference(TypeConstants.INT)), |
| gen.argument("callinIds".toCharArray(), gen.arrayTypeReference(TypeConstants.INT, 1)), |
| gen.argument("boundMethodId".toCharArray(), gen.baseTypeReference(TypeConstants.INT)), |
| gen.argument("args".toCharArray(), gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1)) |
| }; |
| case OTDYNARGS2: |
| return new Argument[] { |
| gen.argument("base".toCharArray(), gen.qualifiedTypeReference(IOTConstants.ORG_OBJECTTEAMS_IBOUNDBASE2)), |
| gen.argument("teams".toCharArray(), gen.qualifiedArrayTypeReference(IOTConstants.ORG_OBJECTTEAMS_ITEAM, 1)), |
| gen.argument("idx".toCharArray(), gen.baseTypeReference(TypeConstants.INT)), |
| gen.argument("callinIds".toCharArray(), gen.arrayTypeReference(TypeConstants.INT, 1)), |
| gen.argument("boundMethodId".toCharArray(), gen.baseTypeReference(TypeConstants.INT)), |
| gen.argument("args".toCharArray(), gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1)), |
| gen.argument("baseCallArgs".toCharArray(), gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1)), |
| gen.argument("baseCallFlags".toCharArray(), gen.baseTypeReference(TypeConstants.INT)) |
| }; |
| case OTDYNARGS3: |
| return new Argument[] { |
| gen.argument("base".toCharArray(), gen.qualifiedTypeReference(IOTConstants.ORG_OBJECTTEAMS_IBOUNDBASE2)), |
| gen.argument("callinId".toCharArray(), gen.baseTypeReference(TypeConstants.INT)), |
| gen.argument("boundMethodId".toCharArray(), gen.baseTypeReference(TypeConstants.INT)), |
| gen.argument("args".toCharArray(), gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1)) |
| }; |
| case OTDYNARGS4: |
| return new Argument[] { |
| gen.argument("base".toCharArray(), gen.qualifiedTypeReference(IOTConstants.ORG_OBJECTTEAMS_IBOUNDBASE2)), |
| gen.argument("callinId".toCharArray(), gen.baseTypeReference(TypeConstants.INT)), |
| gen.argument("boundMethodId".toCharArray(), gen.baseTypeReference(TypeConstants.INT)), |
| gen.argument("args".toCharArray(), gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1)), |
| gen.argument("result".toCharArray(), gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT)) |
| }; |
| case OTDYNARGS5: |
| return new Argument[] { |
| gen.argument("callinId".toCharArray(), gen.baseTypeReference(TypeConstants.INT)), |
| gen.argument("boundMethodId".toCharArray(), gen.baseTypeReference(TypeConstants.INT)), |
| gen.argument("args".toCharArray(), gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1)) |
| }; |
| default: |
| return null; |
| } |
| } |
| |
| TypeReference makeReturnRef(AstGenerator gen) { |
| switch (this.returnType) { |
| case BOOLEAN: |
| return gen.baseTypeReference(TypeConstants.BOOLEAN); |
| case INT: |
| return gen.baseTypeReference(TypeConstants.INT); |
| case NONE: |
| return gen.baseTypeReference(TypeConstants.VOID); |
| case OBJECT: |
| return gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT); |
| default: |
| throw new InternalCompilerError("Unexpected return type "+this.returnType); //$NON-NLS-1$ |
| } |
| } |
| } |
| enum Type { |
| NONE { |
| @Override public int length() { return 0; } |
| }, |
| THREAD, BOOLEAN, INT, OBJECT, |
| OTDYNARGS1 { @Override int length() { return 6; } }, |
| OTDYNARGS2 { @Override int length() { return 8; } }, |
| OTDYNARGS3 { @Override int length() { return 4; } }, |
| OTDYNARGS4 { @Override int length() { return 5; } }, |
| OTDYNARGS5 { @Override int length() { return 3; } }; |
| int length() { |
| return 1; |
| } |
| } |
| /** Public methods to copy from o.o.Team: */ |
| @SuppressWarnings("nls") |
| MethodDescriptor[] methodDescriptors = new MethodDescriptor[] { |
| new MethodDescriptor("activate", "()V", Type.NONE, Type.NONE, AccPublic), |
| new MethodDescriptor("activate", "(Ljava/lang/Thread;)V",Type.THREAD, Type.NONE, AccPublic), |
| new MethodDescriptor("deactivate", "()V", Type.NONE, Type.NONE, AccPublic), |
| new MethodDescriptor("deactivate", "(Ljava/lang/Thread;)V",Type.THREAD, Type.NONE, AccPublic), |
| new MethodDescriptor("isActive", "()Z", Type.NONE, Type.BOOLEAN, AccPublic|AccFinal), |
| new MethodDescriptor("isActive", "(Ljava/lang/Thread;)Z",Type.THREAD, Type.BOOLEAN, AccPublic|AccFinal), |
| new MethodDescriptor("isExecutingCallin", "()Z", Type.NONE, Type.BOOLEAN, AccPublic), |
| new MethodDescriptor("deactivateForEndedThread", "(Ljava/lang/Thread;)V",Type.THREAD, Type.NONE, AccPublic), |
| new MethodDescriptor("internalIsActiveSpecificallyFor", "(Ljava/lang/Thread;)Z",Type.THREAD, Type.BOOLEAN, AccPublic), |
| new MethodDescriptor("_OT$setExecutingCallin", "(Z)Z", Type.BOOLEAN, Type.BOOLEAN, AccPublic), |
| new MethodDescriptor("_OT$activateForAllThreads", "()V", Type.NONE, Type.NONE, AccPrivate), |
| new MethodDescriptor("_OT$saveActivationState", "()I", Type.NONE, Type.INT, AccPublic|AccSynchronized), |
| new MethodDescriptor("_OT$restoreActivationState", "(I)V", Type.INT, Type.NONE, AccPublic), |
| new MethodDescriptor("doRegistration", "()V", Type.NONE, Type.NONE, AccPrivate), |
| new MethodDescriptor("doUnregistration", "()V", Type.NONE, Type.NONE, AccPrivate), |
| }; |
| @SuppressWarnings("nls") |
| final MethodDescriptor[] methodDescriptorsDyn = new MethodDescriptor[] { |
| new MethodDescriptor("_OT$callAllBindings", "(Lorg/objectteams/IBoundBase2;[Lorg/objectteams/ITeam;I[II[Ljava/lang/Object;)Ljava/lang/Object;", |
| Type.OTDYNARGS1, Type.OBJECT, AccPublic), |
| new MethodDescriptor("_OT$callNext", "(Lorg/objectteams/IBoundBase2;[Lorg/objectteams/ITeam;I[II[Ljava/lang/Object;[Ljava/lang/Object;I)Ljava/lang/Object;", |
| Type.OTDYNARGS2, Type.OBJECT, AccPublic), |
| new MethodDescriptor("_OT$callReplace", "(Lorg/objectteams/IBoundBase2;[Lorg/objectteams/ITeam;I[II[Ljava/lang/Object;)Ljava/lang/Object;", |
| Type.OTDYNARGS1, Type.OBJECT, AccPublic), |
| new MethodDescriptor("_OT$callBefore", "(Lorg/objectteams/IBoundBase2;II[Ljava/lang/Object;)V", |
| Type.OTDYNARGS3, Type.NONE, AccPublic), |
| new MethodDescriptor("_OT$callAfter", "(Lorg/objectteams/IBoundBase2;II[Ljava/lang/Object;Ljava/lang/Object;)V", |
| Type.OTDYNARGS4, Type.NONE, AccPublic), |
| new MethodDescriptor("_OT$callOrigStatic", "(II[Ljava/lang/Object;)Ljava/lang/Object;", |
| Type.OTDYNARGS5, Type.OBJECT, AccPublic) |
| }; |
| |
| // ==== Currently, this is where we globally store the byte code of org.objectteams.Team: ==== |
| public byte[] classBytes; |
| public int[] constantPoolOffsets; |
| // --- variant if o.o.Team is a SourceTypeBinding: |
| public SourceTypeBinding ooTeamBinding; |
| // ==== ==== |
| |
| public TeamMethodGenerator(WeavingScheme weavingScheme) { |
| if (weavingScheme == WeavingScheme.OTDRE) { |
| int l1 = this.methodDescriptors.length; |
| int l2 = this.methodDescriptorsDyn.length; |
| MethodDescriptor[] all = new MethodDescriptor[l1+l2]; |
| System.arraycopy(this.methodDescriptors, 0, all, 0, l1); |
| System.arraycopy(this.methodDescriptorsDyn, 0, all, l1, l2); |
| this.methodDescriptors = all; |
| } |
| } |
| |
| public void registerTeamMethodWithoutBytes(IBinaryType binaryType, IBinaryMethod method, MethodBinding methodBinding, |
| LookupEnvironment environment) { |
| try { |
| char[] selector = method.getSelector(); |
| char[] descriptor = method.getMethodDescriptor(); |
| File file = new File(String.valueOf(binaryType.getFileName())); |
| ClassFileReader reader = ClassFileReader.read(file, true); |
| for (IBinaryMethod binMethod : reader.getMethods()) { |
| if (CharOperation.equals(selector, binMethod.getSelector()) |
| && CharOperation.equals(descriptor, binMethod.getMethodDescriptor())) |
| { |
| registerTeamMethod(binMethod, methodBinding); |
| break; |
| } |
| } |
| } catch (IOException | ClassFormatException e) { |
| // TODO: handle exception |
| } |
| } |
| |
| /** When binary methods for o.o.Team are created record the relevant method bindings. */ |
| public void registerTeamMethod(IBinaryMethod method, MethodBinding methodBinding) { |
| String selector = String.valueOf(method.getSelector()); |
| String descriptor = String.valueOf(method.getMethodDescriptor()); |
| registerTeamMethod(methodBinding.declaringClass, methodBinding, selector, descriptor, -1/*structOffset not yet known*/); |
| } |
| |
| /** When o.o.Team is read from .class file, record the byte code here. */ |
| public synchronized void maybeRegisterTeamClassBytes(ClassFileReader teamClass, ReferenceBinding teamClassBinding) { |
| if (this.classBytes != null) |
| return; |
| this.classBytes = teamClass.getBytes(); |
| this.constantPoolOffsets = teamClass.getConstantPoolOffsets(); |
| for (IBinaryMethod method : teamClass.getMethods()) { |
| if (this.classBytes == null && method instanceof MethodInfo) { |
| // repair if class already nulled its byte reference: |
| this.classBytes = ((MethodInfo)method).reference; |
| this.constantPoolOffsets = ((MethodInfo)method).constantPoolOffsets; |
| } |
| String selector = String.valueOf(method.getSelector()); |
| String descriptor = String.valueOf(method.getMethodDescriptor()); |
| int structOffset = ((MethodInfo)method).getStructOffset(); |
| // relevant new info is structOffset, everything has already been registered from registerTeamMethod(IBinaryMethod,MethodBinding) |
| registerTeamMethod(teamClassBinding, null, selector, descriptor, structOffset); |
| } |
| } |
| private boolean registerTeamMethod(ReferenceBinding declaringClass, MethodBinding methodBinding, String selector, String descriptor, int structOffset) { |
| for (int s = 0; s < this.methodDescriptors.length; s++) { |
| if ( selector.equals(this.methodDescriptors[s].selector) |
| && descriptor.equals(this.methodDescriptors[s].signature)) |
| { |
| if (methodBinding != null) |
| this.methodDescriptors[s].binding = methodBinding; |
| this.methodDescriptors[s].declaringClass = declaringClass; |
| this.methodDescriptors[s].methodCodeOffset = structOffset; |
| return true; |
| } |
| } |
| return false; |
| } |
| // --- alternative initialization when compiling o.o.Team from source: --- |
| public synchronized void registerOOTeamClass(SourceTypeBinding orgObjectteamsTeamBinding) { |
| if (this.classBytes != null) |
| return; |
| this.ooTeamBinding = orgObjectteamsTeamBinding; |
| } |
| public boolean requestBytes() { |
| if (this.classBytes != null) |
| return true; |
| if (this.ooTeamBinding != null) { |
| if (isOOTConverted()) |
| return false; |
| boolean result = Dependencies.ensureBindingState(this.ooTeamBinding, ITranslationStates.STATE_BYTE_CODE_GENERATED); |
| // the above causes callbacks to registerSourceMethodBytes(), info will be stored in corresponding MethodModels |
| // finish collecting info: |
| MethodModel model = this.methodDescriptors[0].binding.model; |
| this.classBytes = model.getBytes(); |
| this.constantPoolOffsets = model.getConstantPoolOffsets(); |
| return result; |
| } |
| return false; // shouldn't |
| } |
| boolean isOOTConverted() { |
| if (this.ooTeamBinding == null) |
| return false; |
| ClassScope scope = this.ooTeamBinding.scope; |
| if (scope == null) |
| return false; |
| TypeDeclaration typeDecl = scope.referenceContext; |
| return typeDecl.isConverted; |
| } |
| public boolean registerSourceMethodBytes(MethodBinding method) { |
| String selector = String.valueOf(method.selector); |
| String signature = String.valueOf(method.signature()); |
| return registerTeamMethod(method.declaringClass, method, selector, signature, -1/*no structOffset before class is complete*/); |
| } |
| // ============== |
| /** |
| * Add the AST representing all relevant methods and fields from o.o.Team, |
| * and prepare methods for byte-code copy. |
| */ |
| public void addMethodsAndFields(TypeDeclaration teamDecl) { |
| // FIXME(SH): test subteams of teams already treated by this generator! |
| AstGenerator gen = new AstGenerator(teamDecl); |
| |
| boolean hasBoundRole = false; |
| // check what will be generated: |
| boolean hasCallinBefore = false, hasCallinAfter = false, hasCallinReplace = false; |
| for (RoleModel role : teamDecl.getTeamModel().getRoles(false)) { |
| if (role.isBound()) |
| hasBoundRole = true; |
| ReferenceBinding roleBinding = role.getBinding(); |
| if (roleBinding == null) continue; // FIXME(SH): check if this is OK |
| if (roleBinding.callinCallouts != null) { |
| for (CallinCalloutBinding mappingBinding : roleBinding.callinCallouts) { |
| if (mappingBinding.isCallin()) { |
| switch (mappingBinding.callinModifier) { |
| case TerminalTokens.TokenNamebefore: hasCallinBefore = true; break; |
| case TerminalTokens.TokenNameafter: hasCallinAfter = true; break; |
| case TerminalTokens.TokenNamereplace: hasCallinReplace = true; break; |
| } |
| // note: we cannot detect static base methods, because the mapping has not yet been resolved. |
| // hence we overwrite _OT$callOrigStatic after the fact in CallinImplementorDyn.generateCallOrigStatic(). |
| } |
| } |
| } |
| } |
| |
| // methods: |
| for (MethodDescriptor methodDescriptor : this.methodDescriptors) { |
| MethodDeclaration newMethod = null; |
| // don't duplicate methods that will be generated later |
| if (willBeGenerated(methodDescriptor, teamDecl, hasCallinBefore, hasCallinAfter, hasCallinReplace)) |
| continue; |
| if ((methodDescriptor.modifiers & AccVisibilityMASK) == AccPublic) { |
| // public methods are always copied |
| newMethod = new CopiedTeamMethod(teamDecl.compilationResult, methodDescriptor, gen); |
| } else { |
| // privates are only copied if bound roles exist, else created as empty |
| if (hasBoundRole) { |
| newMethod = new CopiedTeamMethod(teamDecl.compilationResult, methodDescriptor, gen); |
| } else { |
| newMethod = gen.method(teamDecl.compilationResult, |
| methodDescriptor.modifiers, |
| methodDescriptor.makeReturnRef(gen), |
| methodDescriptor.selector.toCharArray(), |
| null, |
| new Statement[0]); // regular empty method. |
| } |
| } |
| AstEdit.addGeneratedMethod(teamDecl, newMethod); |
| } |
| // fields: |
| addFields(teamDecl, gen); |
| } |
| |
| boolean willBeGenerated(MethodDescriptor methodDescriptor, TypeDeclaration teamDecl, |
| boolean hasCallinBefore, boolean hasCallinAfter, boolean hasCallinReplace) |
| { |
| char[] selector = methodDescriptor.selector.toCharArray(); |
| if (CharOperation.equals(OT_CALL_BEFORE, selector)) |
| return hasCallinBefore; |
| else if (CharOperation.equals(OT_CALL_AFTER, selector)) |
| return hasCallinAfter; |
| else if (CharOperation.equals(OT_CALL_REPLACE, selector) || CharOperation.equals(OT_CALL_NEXT, selector)) |
| return hasCallinReplace; |
| return false; |
| } |
| |
| /** create field ASTs. */ |
| @SuppressWarnings("nls") |
| void addFields(TypeDeclaration teamDecl, AstGenerator gen) { |
| // private WeakHashMap<Thread, Boolean> _OT$activatedThreads = new WeakHashMap<Thread, Boolean>(); |
| addPrivateField(teamDecl, gen, |
| weakHashMapTypeReference(gen), |
| "_OT$activatedThreads".toCharArray(), |
| gen.allocation(weakHashMapTypeReference(gen), null)); |
| |
| // private Object _OT$registrationLock= new Object(); |
| addPrivateField(teamDecl, gen, |
| gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT), |
| "_OT$registrationLock".toCharArray(), |
| gen.allocation(gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT), null)); |
| |
| // private boolean _OT$lazyGlobalActiveFlag = false; |
| addPrivateField(teamDecl, gen, |
| gen.baseTypeReference(TypeConstants.BOOLEAN), |
| "_OT$lazyGlobalActiveFlag".toCharArray(), |
| gen.booleanLiteral(false)); |
| |
| // private boolean _OT$isExecutingCallin = false; |
| addPrivateField(teamDecl, gen, |
| gen.baseTypeReference(TypeConstants.BOOLEAN), |
| "_OT$isExecutingCallin".toCharArray(), |
| gen.booleanLiteral(false)); |
| |
| // private int _OT$registrationState = _OT$UNREGISTERED; |
| addPrivateField(teamDecl, gen, |
| gen.baseTypeReference(TypeConstants.INT), |
| "_OT$registrationState".toCharArray(), |
| gen.intLiteral(0)); |
| |
| // private boolean _OT$globalActive = false; |
| addPrivateField(teamDecl, gen, |
| gen.baseTypeReference(TypeConstants.BOOLEAN), |
| "_OT$globalActive".toCharArray(), |
| gen.booleanLiteral(false)); |
| |
| // private ThreadLocal<Integer> _OT$implicitActivationsPerThread = new ThreadLocal<Integer>() { |
| // protected synchronized Integer initialValue() { |
| // return Integer.valueOf(0); |
| // } |
| // }; |
| TypeDeclaration anonThreadLocal = gen.anonymousType(teamDecl.compilationResult); |
| anonThreadLocal.methods = new MethodDeclaration[] { |
| gen.method( teamDecl.compilationResult, |
| AccProtected|AccSynchronized, |
| gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_INTEGER), |
| "initialValue".toCharArray(), |
| null/*arguments*/, |
| new Statement[] { |
| gen.returnStatement(gen.intLiteral(0)) // rely on autoboxing |
| }) |
| }; |
| addPrivateField(teamDecl, gen, |
| threadLocalReference(gen), |
| "_OT$implicitActivationsPerThread".toCharArray(), |
| gen.anonymousAllocation(threadLocalReference(gen), null/*arguments*/, anonThreadLocal)); |
| } |
| void addPrivateField(TypeDeclaration teamDecl, AstGenerator gen, TypeReference type, char[] name, Expression init) { |
| FieldDeclaration field = gen.field(AccPrivate, type, name, init); |
| boolean teamHasProblems = teamDecl.ignoreFurtherInvestigation; |
| AstEdit.addField(teamDecl, field, !teamHasProblems, false, false); |
| if (!teamHasProblems) |
| field.binding.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; |
| } |
| QualifiedTypeReference weakHashMapTypeReference(AstGenerator gen) { |
| return gen.parameterizedQualifiedTypeReference( |
| JAVA_LANG_WEAKHASHMAP, |
| new TypeReference[]{ |
| gen.qualifiedTypeReference(JAVA_LANG_THREAD), |
| gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_BOOLEAN) |
| }); |
| } |
| QualifiedTypeReference threadLocalReference(AstGenerator gen) { |
| return gen.parameterizedQualifiedTypeReference( |
| JAVA_LANG_THREADLOCAL, |
| new TypeReference[] { |
| gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_INTEGER) |
| }); |
| } |
| |
| /** |
| * Add fake method bindings for methods that will be generated by the OTRE. |
| * These bindings are needed by the TeamConstantPoolMapper. |
| */ |
| public static void addFakedTeamRegistrationMethods(ReferenceBinding teamBinding) { |
| MethodBinding registrationMethod; |
| registrationMethod = new MethodBinding(AccPublic, |
| "_OT$registerAtBases".toCharArray(), //$NON-NLS-1$ |
| TypeBinding.VOID, // return type |
| Binding.NO_PARAMETERS, |
| Binding.NO_EXCEPTIONS, // exceptions |
| teamBinding); |
| teamBinding.addMethod(registrationMethod); |
| MethodModel.getModel(registrationMethod)._fakeKind = MethodModel.FakeKind.TEAM_REGISTRATION_METHOD; |
| registrationMethod = new MethodBinding(AccPublic, |
| "_OT$unregisterFromBases".toCharArray(), //$NON-NLS-1$ |
| TypeBinding.VOID, // return type |
| Binding.NO_PARAMETERS, |
| Binding.NO_EXCEPTIONS, // exceptions |
| teamBinding); |
| teamBinding.addMethod(registrationMethod); |
| MethodModel.getModel(registrationMethod)._fakeKind = MethodModel.FakeKind.TEAM_REGISTRATION_METHOD; |
| } |
| |
| /** Special crippled method declarations that only serve as place-holder for copy-inheritance. */ |
| public class CopiedTeamMethod extends MethodDeclaration { |
| MethodDescriptor descriptor; |
| public CopiedTeamMethod(CompilationResult compilationResult, MethodDescriptor descriptor, AstGenerator gen) { |
| super(compilationResult); |
| this.selector = descriptor.selector.toCharArray(); |
| this.descriptor = descriptor; |
| this.isCopied = true; |
| this.modifiers = descriptor.modifiers; |
| this.arguments = descriptor.makeArgs(gen); |
| this.returnType = descriptor.makeReturnRef(gen); |
| } |
| @Override |
| public void resolve(ClassScope upperScope) { |
| // nop |
| } |
| @Override |
| public void analyseCode(ClassScope classScope, FlowContext flowContext, FlowInfo flowInfo) { |
| // nop |
| } |
| @Override |
| public void generateCode(ClassScope classScope, ClassFile classFile) { |
| if (isOOTConverted()) |
| return; // don't actually try to generate, o.o.Team has no byte codes in this scenario |
| this.binding.copyInheritanceSrc = this.descriptor.binding; |
| ConstantPoolObjectMapper mapper = new TeamConstantPoolMapper(this.descriptor.binding, this.binding); |
| byte[] bytes; |
| int[] offsets; |
| int structOffset; |
| if (this.descriptor.methodCodeOffset == -1) { |
| MethodModel srcModel = this.descriptor.binding.model; |
| bytes = srcModel.getBytes(); |
| offsets = srcModel.getConstantPoolOffsets(); |
| structOffset = srcModel.getStructOffset(); |
| } else { |
| bytes = TeamMethodGenerator.this.classBytes; |
| offsets = TeamMethodGenerator.this.constantPoolOffsets; |
| structOffset = this.descriptor.methodCodeOffset; |
| } |
| ConstantPoolObjectReader reader = new ConstantPoolObjectReader( bytes, |
| offsets, |
| this.descriptor.declaringClass.getTeamModel(), |
| this.scope.environment()); |
| new BytecodeTransformer().doCopyMethodCode( null /*srcRoleModel*/, this.binding, // source |
| (SourceTypeBinding)this.binding.declaringClass, this, // destination |
| bytes, // source bytes |
| offsets, |
| structOffset, |
| reader, mapper, // mapping strategies |
| classFile); // final destination |
| } |
| } |
| |
| /** Simple constant mapper: only translate from o.o.Team to current team type. */ |
| static class TeamConstantPoolMapper extends ConstantPoolObjectMapper { |
| |
| ReferenceBinding srcType, dstType; |
| public TeamConstantPoolMapper(MethodBinding srcMethodBinding, MethodBinding dstMethodBinding) { |
| super(srcMethodBinding, dstMethodBinding); |
| this.srcType = srcMethodBinding.declaringClass; |
| this.dstType = dstMethodBinding.declaringClass; |
| } |
| @Override |
| public ConstantPoolObject mapConstantPoolObject(ConstantPoolObject src_cpo, boolean addMarkerArgAllowed) { |
| return mapConstantPoolObject(src_cpo); |
| } |
| @Override |
| public ConstantPoolObject mapConstantPoolObject(ConstantPoolObject src_cpo) { |
| int type=src_cpo.getType(); |
| |
| TypeBinding clazz = null; |
| switch (type) { |
| case MethodRefTag: |
| clazz = src_cpo.getMethodRef().declaringClass; |
| break; |
| case FieldRefTag: |
| clazz = src_cpo.getFieldRef().declaringClass; |
| break; |
| case ClassTag: |
| clazz = src_cpo.getClassObject(); |
| } |
| if (TypeBinding.notEquals(clazz, this.srcType)) |
| return src_cpo; // only map references to o.o.Team |
| |
| // perform the mapping: |
| switch(type){ |
| case FieldRefTag: |
| return new ConstantPoolObject( |
| FieldRefTag, |
| mapField(src_cpo.getFieldRef())); |
| case MethodRefTag: |
| return new ConstantPoolObject( |
| MethodRefTag, |
| mapMethod(src_cpo.getMethodRef())); |
| case ClassTag: |
| return new ConstantPoolObject( |
| ClassTag, |
| this.dstType); // don't search further, we already have it. |
| } |
| //if no mapping is needed, return original ConstantPoolObject |
| return src_cpo; |
| } |
| private MethodBinding mapMethod(MethodBinding methodRef) { |
| for (MethodBinding dstBinding : this.dstType.methods()) |
| if ( CharOperation.equals(dstBinding.selector, methodRef.selector) |
| && CharOperation.equals(dstBinding.signature(), methodRef.signature())) |
| return dstBinding; |
| return methodRef; |
| } |
| private FieldBinding mapField(FieldBinding fieldRef) { |
| for (FieldBinding dstBinding : this.dstType.fields()) |
| if ( CharOperation.equals(dstBinding.name, fieldRef.name) |
| && TypeBinding.equalsEquals(dstBinding.type, fieldRef.type)) |
| return dstBinding; |
| return fieldRef; |
| } |
| } |
| } |