diff options
author | Stephan Herrmann | 2010-03-23 20:58:33 +0000 |
---|---|---|
committer | Stephan Herrmann | 2010-03-23 20:58:33 +0000 |
commit | ef9e3857117068868ecd2278543e615a9579bd39 (patch) | |
tree | de3d3b68d6f31d60607103a41b353e06b3891ff3 /othersrc | |
parent | 0e1c7e39bb736d0ea8da718c541a4fa30d2db539 (diff) | |
download | org.eclipse.objectteams-ef9e3857117068868ecd2278543e615a9579bd39.tar.gz org.eclipse.objectteams-ef9e3857117068868ecd2278543e615a9579bd39.tar.xz org.eclipse.objectteams-ef9e3857117068868ecd2278543e615a9579bd39.zip |
initial contribution "Object Teams Runtime Environment" as approved in CQ 3786
Diffstat (limited to 'othersrc')
55 files changed, 13796 insertions, 0 deletions
diff --git a/othersrc/OTRE/.classpath b/othersrc/OTRE/.classpath new file mode 100644 index 000000000..b98640e7b --- /dev/null +++ b/othersrc/OTRE/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry combineaccessrules="false" kind="src" path="/bcel4-4-1"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/othersrc/OTRE/.project b/othersrc/OTRE/.project new file mode 100644 index 000000000..9f6b8319b --- /dev/null +++ b/othersrc/OTRE/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>OTRE</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/othersrc/OTRE/.settings/org.eclipse.jdt.core.prefs b/othersrc/OTRE/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..1aa712415 --- /dev/null +++ b/othersrc/OTRE/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,70 @@ +#Thu Sep 18 21:57:35 CEST 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/othersrc/OTRE/MANIFEST.MF b/othersrc/OTRE/MANIFEST.MF new file mode 100644 index 000000000..c759b23fd --- /dev/null +++ b/othersrc/OTRE/MANIFEST.MF @@ -0,0 +1,5 @@ +Manifest-Version: 1.0
+Premain-Class: org.eclipse.objectteams.otre.jplis.otreAgent
+Can-Redefine-Classes: true
+Built-By: resix
+
diff --git a/othersrc/OTRE/about.html b/othersrc/OTRE/about.html new file mode 100644 index 000000000..47048bd42 --- /dev/null +++ b/othersrc/OTRE/about.html @@ -0,0 +1,28 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/> +<title>About</title> +</head> +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>Feb 3, 2010</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p> + +</body> +</html>
\ No newline at end of file diff --git a/othersrc/OTRE/otre-src-jar.jardesc b/othersrc/OTRE/otre-src-jar.jardesc new file mode 100644 index 000000000..755677629 --- /dev/null +++ b/othersrc/OTRE/otre-src-jar.jardesc @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<jardesc> + <jar path="org.eclipse.objectteams.runtime/lib/otre.jar"/> + <options buildIfNeeded="true" compress="true" descriptionLocation="/OTRE/otre-src-jar.jardesc" exportErrors="false" exportWarnings="true" includeDirectoryEntries="false" overwrite="true" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/> + <storedRefactorings deprecationInfo="true" structuralOnly="false"/> + <selectedProjects/> + <manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true"> + <sealing sealJar="false"> + <packagesToSeal/> + <packagesToUnSeal/> + </sealing> + </manifest> + <selectedElements exportClassFiles="false" exportJavaFiles="true" exportOutputFolder="true"> + <javaElement handleIdentifier="=OTRE/src"/> + </selectedElements> +</jardesc> diff --git a/othersrc/OTRE/otre_agent.jardesc b/othersrc/OTRE/otre_agent.jardesc new file mode 100644 index 000000000..cf1515f36 --- /dev/null +++ b/othersrc/OTRE/otre_agent.jardesc @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<jardesc> + <jar path="org.eclipse.objectteams.otdt/lib/otre_agent.jar"/> + <options overwrite="false" compress="true" exportErrors="false" exportWarnings="true" saveDescription="true" descriptionLocation="/OTRE/otre_agent.jardesc" useSourceFolders="false" buildIfNeeded="true"/> + <manifest manifestVersion="1.0" usesManifest="true" reuseManifest="false" saveManifest="false" generateManifest="false" manifestLocation="/OTRE/MANIFEST.MF"> + <sealing sealJar="false"> + <packagesToSeal/> + <packagesToUnSeal/> + </sealing> + </manifest> + <selectedElements exportClassFiles="true" exportOutputFolder="false" exportJavaFiles="false"> + <javaElement handleIdentifier="=OTRE/src<org.eclipse.objectteams.otre.jplis{otreAgent.java"/> + </selectedElements> +</jardesc> diff --git a/othersrc/OTRE/otre_min.jardesc b/othersrc/OTRE/otre_min.jardesc new file mode 100644 index 000000000..73086e6fe --- /dev/null +++ b/othersrc/OTRE/otre_min.jardesc @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<jardesc> + <jar path="org.objectteams.otdt/lib/otre_min.jar"/> + <options buildIfNeeded="true" compress="true" descriptionLocation="/OTRE/otre_min.jardesc" exportErrors="false" exportWarnings="true" includeDirectoryEntries="false" overwrite="true" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/> + <storedRefactorings deprecationInfo="true" structuralOnly="false"/> + <selectedProjects/> + <manifest generateManifest="false" manifestLocation="/OTRE/MANIFEST.MF" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true"> + <sealing sealJar="false"> + <packagesToSeal/> + <packagesToUnSeal/> + </sealing> + </manifest> + <selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false"> + <javaElement handleIdentifier="=OTRE/src<org.objectteams"/> + </selectedElements> +</jardesc> diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/BaseCallRedirection.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/BaseCallRedirection.java new file mode 100644 index 000000000..567a422de --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/BaseCallRedirection.java @@ -0,0 +1,1446 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: BaseCallRedirection.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import de.fub.bytecode.classfile.*; +import de.fub.bytecode.generic.*; +import de.fub.bytecode.*; + +import java.util.*; + +import org.eclipse.objectteams.otre.util.*; + +/** + * for callin-role-methods with recursive method-calls (base calls) we add a + * method with extendend signature. + * Within the extendend method recursive calls are replaced by the corresponding + * (chaining)base-call.<p> + * For example: + * <pre> + * callin m1() { m1(); } --> + * callin m1(Team _OT$teams[], int _OT$teamIDs[], + * int _OT$idx, int _OT$baseMethTag){ + * liftToRole(b1._OT$m1$chain(Team _OT$teams[], + * int _OT$teamIDs[], + * int _OT$idx, + * int _OT$baseMethTag)); + * } + * </pre> + * + * @version $Id: BaseCallRedirection.java 23408 2010-02-03 18:07:35Z stephan $ + * @author Christine Hundt + * @author Stephan Herrmann + */ +public class BaseCallRedirection extends ObjectTeamsTransformation { + + static class IHPair { + private InstructionHandle _ih1, _ih2; + public IHPair (InstructionHandle ih1, InstructionHandle ih2) { + _ih1 = ih1; + _ih2 = ih2; + } + public InstructionHandle fst() {return _ih1; } + public InstructionHandle snd() {return _ih2; } + } + + public BaseCallRedirection(SharedState state) { this(null, state); } + public BaseCallRedirection(ClassLoader loader, SharedState state) { + super(loader, state); + } + + /** + * @param ce + * @param cg + */ + public void doTransformInterface(ClassEnhancer ce, ClassGen cg) { + factory = new InstructionFactory(cg); + String class_name = cg.getClassName(); + + if (state.interfaceTransformedClasses.contains(class_name)) { + return; //already transformed! + } + + ConstantPoolGen cpg = cg.getConstantPool(); + checkReadClassAttributes(ce, cg, class_name, cpg); + + if (!CallinBindingManager.isRole(class_name)) { + return; + } + + if (!cg.isInterface()) { + Set<String> boundBaseMethods = CallinBindingManager.getBoundRoleMethods(class_name); + addBaseCallSurrogatesForReplaceBindings(ce, boundBaseMethods, cg); + } + + Method[] methods = cg.getMethods(); + for (int i=0; i<methods.length; i++) { + Method m = methods[i]; + String method_name = m.getName(); + String method_signature = m.getSignature(); + + if (candidateForImplicitActivation(m, cg, cpg)) { // TODO: check the other preconditions, like not abstact etc. + cg.replaceMethod(m, genImplicitActivation(m, class_name, cpg, true)); + } + + if (!isCallin(m, cg)) + continue; + if (logging) printLogMessage(method_name + " in " + class_name + + " IS A CALLIN-METHOD!"); + if (method_name.startsWith(OT_PREFIX)) { + method_name = revertToOriginalName(method_name); + if (logging) printLogMessage("Reverted tsuper name to " + method_name); + } + // code SHOULD contain at least one base call. + if(logging) printLogMessage("----->will add another method " + method_name + + " with enhanced signature"); + +//{SH: retrench signature because otherwise the binding will not be found, +// as a result an empty basecall surrogate will be generated leading to repetetive methods... +// TODO(SH) is this the proper way to retrench?? + String enhancedPrefix = "([Lorg/objectteams/Team;[IIII[Ljava/lang/Object;"; + if (method_signature.startsWith(enhancedPrefix)) + method_signature = "("+method_signature.substring(enhancedPrefix.length()); +// SH} + + boolean roleMethodIsBound = CallinBindingManager.roleMethodHasBinding(class_name, + method_name, + method_signature); + + //if (mbs.isEmpty()) { + if (!roleMethodIsBound) { + if (logging) printLogMessage("callin method " + method_name + + " was not bound in this class!!!"); + } + MethodGen baseCallSurrogate = null; + if (!IS_COMPILER_13X_PLUS) { // since 1.3.0 this part is legacy: + if (!roleMethodIsBound && !methodHasCallinFlags(m, cg, OVERRIDING) && !m.isStatic()) { + // method not bound in current class and doesn't inherit a base call surrogate + baseCallSurrogate = generateEmptyBaseCallSurrogate(cg, m); + } + if (baseCallSurrogate != null) + ce.addMethod(baseCallSurrogate.getMethod(), cg); + } + + } + state.interfaceTransformedClasses.add(class_name); + } + + /** + * Generates an "empty" base call surrogate method, which just throws an 'Error'. + * This method should normaly never be called, but overwritten in a subclass. + * It has to be generated just for the wellformedness of the class file. + * @param cg + * @param m + * @param mbs + * @param baseClassName + * @return + */ + private MethodGen generateEmptyBaseCallSurrogate(ClassGen cg, Method m/*, List mbs*/) { + if (m.getName().startsWith(OT_PREFIX)) + return null; // ot-internal methods don't need this? + + ConstantPoolGen cpg = cg.getConstantPool(); + String class_name = cg.getClassName(); + MethodGen mg = new MethodGen(m, class_name, cpg); + + if (isTSuperWrapper(mg)) { + // tsuper wrapper do not need base call surrogates + return null; + } + + // role method already was enhanced by compiler + Type[] enhancedArgumentTypes = mg.getArgumentTypes(); + if (IS_COMPILER_GREATER_123) { + // add super flag between enhancement and real arguments: + int len = enhancedArgumentTypes.length; + Type[] newArgumentTypes = new Type[len+1]; + System.arraycopy(enhancedArgumentTypes, 0, newArgumentTypes, 0, EXTRA_ARGS); + newArgumentTypes[EXTRA_ARGS] = Type.BOOLEAN; + System.arraycopy(enhancedArgumentTypes, EXTRA_ARGS, newArgumentTypes, EXTRA_ARGS+1, len-EXTRA_ARGS); + enhancedArgumentTypes = newArgumentTypes; + } + Type enhancedReturnType = mg.getReturnType(); + +// {SH: interface method has no argument names? generate dummy names: + String[] argumentNames = new String[enhancedArgumentTypes.length]; + for (int i = 0; i < argumentNames.length; i++) { + argumentNames[i] = "arg"+i; + } + + // role method already was enhanced by compiler: + String[] enhancedArgumentNames = argumentNames; +// } + InstructionList il = new InstructionList(); + //int accessFlags = m.getAccessFlags(); + int accessFlags = Constants.ACC_PROTECTED; // no unanticipated calls possible + +// {SH: interface methods must be public abstract: + if (cg.isInterface()) + accessFlags = Constants.ACC_ABSTRACT|Constants.ACC_PUBLIC; +// } + + MethodGen baseCallSurrogate = new MethodGen(accessFlags, + enhancedReturnType, + enhancedArgumentTypes, + enhancedArgumentNames, + getBaseCallSurrogateName(m.getName(), m.isStatic(), class_name /*genRoleInterfaceName(class_name)*/), + class_name, + il, cpg); +// {SH: no code for interface method: + if (!cg.isInterface()) { + // orig: + if (logging) + printLogMessage("Exception has to be thrown!"); + + createThrowInternalError(cpg, il, new InstructionList(new PUSH(cpg, "Binding-Error: base-call impossible!"))); + + il.append(InstructionFactory.createNull(enhancedReturnType)); + il.append(InstructionFactory.createReturn(enhancedReturnType)); + + if (debugging) + baseCallSurrogate.addLineNumber(il.getStart(), STEP_OVER_LINENUMBER); + } +// } + + il.setPositions(); + baseCallSurrogate.removeNOPs(); + baseCallSurrogate.setMaxStack(); + baseCallSurrogate.setMaxLocals(); + return baseCallSurrogate; + } + + /** + * @param mg + * @return + */ + private static boolean isTSuperWrapper(MethodGen mg) { + Type[] argTypes = mg.getArgumentTypes(); + if (argTypes.length == 0) { + return false; // no tsuper marker interface argument existing + } + String lastArgument = (argTypes[argTypes.length - 1]).toString(); + return lastArgument.contains(OTDT_PREFIX); + } + + /** + * Adds base call surrogate method for all role method bindings in the current role class. + * Thereby method bindings which are defined in super roles are accumulated and + * considered as well. + * @param ce the ClassEnhancer to which the new method has to be added + * @param boundRoleMethods the bound methods of the role class + * @param cg the ClassGen for the role class + */ + private void addBaseCallSurrogatesForReplaceBindings(ClassEnhancer ce, Set<String> boundRoleMethods, ClassGen cg) + { + Iterator<String> it = boundRoleMethods.iterator(); + while (it.hasNext()) { + String nameAndSignature = it.next(); + int dotIndex = nameAndSignature.indexOf('.'); + String methodName = nameAndSignature.substring(0, dotIndex); + String methodSignature = nameAndSignature.substring(dotIndex + 1); + List<MethodBinding> mbs = CallinBindingManager.getBindingsForRoleMethod(cg.getClassName(), + methodName, + methodSignature); + MethodBinding anyMethodBinding = mbs.get(0); + if (!anyMethodBinding.isReplace()) { + continue; + } + mbs.addAll(CallinBindingManager.getInheritedRoleMethodBindings(cg.getClassName(), + methodName, + methodSignature)); + + if (anyMethodBinding.hasStaticRoleMethod()) + continue; // base call surrogates for static methods are generated within the enclosing team class + // TODO: remove this check as soon as static replace method bindings are no longer in 'CallinMethodMappings' + MethodGen baseCallSurrogate = genBaseCallSurrogate(cg, mbs); + ce.addOrReplaceMethod(baseCallSurrogate.getMethod(), cg); + } + } + + /** + * Generates base call surrogate method for the role method for which the method bindings 'mbs' are. + * Thereby a switch-case for each bound base method is generated. + * This method is only for nonstatic role methods. Base call surrogates for static methods + * are generated within the enclosing team class + * @param cg the ClassGen for the role class + * @param mbs the method bindings for one role method + * @param baseClassName the name of the base class + */ + MethodGen genBaseCallSurrogate(ClassGen cg, List<MethodBinding> mbs) { + + //baseClassName would not be needed here, if I could find out the root-base-class-type... + ConstantPoolGen cpg = cg.getConstantPool(); + String class_name = cg.getClassName(); + + MethodBinding anyBindingForRoleMethod = mbs.get(0); + String baseClassName = anyBindingForRoleMethod.getRootBoundBase(); + String roleMethodSignature = anyBindingForRoleMethod.getRoleMethodSignature(); + + Type[] enhancedArgumentTypes; + { + Type[] argTypesTail = Type.getArgumentTypes(roleMethodSignature); + if (IS_COMPILER_GREATER_123) { + // add super flag between enhancement and real arguments: + int len = argTypesTail.length; + System.arraycopy(argTypesTail, 0, argTypesTail=new Type[len+1], 1, len); + argTypesTail[0] = Type.BOOLEAN; + } + enhancedArgumentTypes = enhanceArgumentTypes(argTypesTail); + } + Type enhancedReturnType = generalizeReturnType(Type.getReturnType(roleMethodSignature)); + String methodName = anyBindingForRoleMethod.getRoleMethodName(); + InstructionList il = new InstructionList(); + int accessFlags = Constants.ACC_PROTECTED; + + MethodGen baseCallSurrogate = new MethodGen(accessFlags, + enhancedReturnType, + enhancedArgumentTypes, + null, // no explicit names + getBaseCallSurrogateName(methodName, false, + genRoleInterfaceName(class_name)), + class_name, + il, cpg); + + ObjectType baseClass = new ObjectType(baseClassName); + ObjectType outerClass; + { + String outerClassName = getOuterClassName(class_name); + outerClass = new ObjectType(outerClassName); + } + + LocalVariableGen otResult = null; + + otResult = baseCallSurrogate.addLocalVariable("_OT$result", + enhancedReturnType, null, null); + + il.insert(InstructionFactory.createStore(enhancedReturnType, + otResult.getIndex())); + il.insert(new ACONST_NULL()); + il.setPositions(); // about to retrieve instruction handles. + + if (logging) printLogMessage("base-call switch has to be inserted!"); + InstructionList loading = new InstructionList(); + loading.append(InstructionFactory.createThis()); + int index = 1; + for (int i = 0; i < enhancedArgumentTypes.length; i++) { + loading.append(InstructionFactory.createLoad(enhancedArgumentTypes[i],index)); + index += enhancedArgumentTypes[i].getSize(); + } + + Type[] argumentTypes = Type.getArgumentTypes(roleMethodSignature); + Type returnType = Type.getReturnType(roleMethodSignature); + + if (debugging) { + baseCallSurrogate.addLineNumber(il.getStart(), STEP_OVER_LINENUMBER); + } + + boolean generateSuperAccess = false; + List<SuperMethodDescriptor> superAccesses = IS_COMPILER_GREATER_123 ? CallinBindingManager.getSuperAccesses(baseClassName) : null; + if (superAccesses != null) { + outer: for (SuperMethodDescriptor superMethod : superAccesses) { + for (MethodBinding methodBinding : mbs) { + if ( superMethod.methodName.equals(methodBinding.getBaseMethodName()) + && superMethod.signature.equals(methodBinding.getBaseMethodSignature())) + { + generateSuperAccess = true; + break outer; + } + } + } + } + + BranchInstruction ifSuper = new IFEQ(null); + GotoInstruction skipElse = new GOTO(null); + if (generateSuperAccess) { + // gen: if (isSuperAccess) { _OT$base._OT$m$super(args); } else ... + il.append(InstructionFactory.createLoad(Type.BOOLEAN, EXTRA_ARGS+1)); // last synthetic arg is super-flag + il.append(ifSuper); + il.append(genBaseCallSwitch(cpg, mbs, baseCallSurrogate, + argumentTypes, + outerClass, baseClass, + returnType, + otResult, loading, true)); + il.append(skipElse); + } + InstructionList + basecall = genBaseCallSwitch(cpg, mbs, baseCallSurrogate, + argumentTypes, + outerClass, baseClass, + returnType, + otResult, loading, false); + InstructionHandle callStart = basecall.getStart(); // store handle before append eats the list + il.append(basecall); + if (generateSuperAccess) { + ifSuper.setTarget(callStart); + skipElse.setTarget(il.append(new NOP())); + } + + il.append(InstructionFactory.createLoad(enhancedReturnType, otResult.getIndex())); + il.append(InstructionFactory.createReturn(enhancedReturnType)); + + il.setPositions(); + baseCallSurrogate.removeNOPs(); + baseCallSurrogate.setMaxStack(); + baseCallSurrogate.setMaxLocals(); + return baseCallSurrogate; + } + +/* + Type findBaseFieldType(JavaClass c, ConstantPoolGen cpg) { + Field[] fields = c.getFields(); + for (int l=0; l<fields.length; l++) { + Field f = fields[l]; + if (f.getName().equals(BASE)) { + FieldGen fg = new FieldGen(f, cpg); + return fg.getType(); + } + } + JavaClass superClass = Repository.lookupClass(c.getSuperclassName()); + // BCEL bug: super class of "Object" is "Object": + //System.err.println("Superclass of "+ c.getClassName()+" is " +c.getSuperclassName()); + if (!superClass.getClassName().equals("java.lang.Object")) { + return findBaseFieldType(superClass, cpg); + } + return null; + }*/ + + /** + * Iterate through the instructions of a callin method. + * <ul> + * <li>Adjust local variable instructions due to inserted extra arguments. + * <li>Replace base-calls and calls to activate. + * </ul> + * @param cg + * @param m + * @param mbs List<MethodBinding> + * @return MethodGen + */ + MethodGen replaceBaseCalls(ClassGen cg, Method m, List<MethodBinding> mbs) { + + int indexOffset = m.isStatic() ? -1 : 0; // argument indices are decremented for static methods, + // because of the missing 'this' + + ConstantPoolGen cpg = cg.getConstantPool(); + String class_name = cg.getClassName(); + String method_name = m.getName(); + + MethodGen mg = new MethodGen(m, class_name, cpg); + + Type[] argumentTypes = mg.getArgumentTypes(); + Type returnType = mg.getReturnType(); + + Type[] enhancedArgumentTypes = enhanceArgumentTypes(mg.getArgumentTypes()); +// {SH abstract methods may not have argument names?? + String[] argumentNames; + if (m.isAbstract()) { + argumentNames = new String[argumentTypes.length]; + int index = 0; + for (int i = 0; i < argumentNames.length; i++) { + argumentNames[i] = "arg" + index/* i */; + index += argumentTypes[i].getSize(); + } + +/* +// load regular arguments: + int index = 1; + for (int i=0; i<argTypes.length; i++) { + il.append(InstructionFactory.createLoad(argTypes[i],index)); + index += argTypes[i].getSize(); + } +//*/ + + } else { + argumentNames = mg.getArgumentNames(); + } + String[] enhancedArgumentNames = enhanceArgumentNames(argumentNames); +// orig: String[] enhancedArgumentNames = enhanceArgumentNames(mg.getArgumentNames()); +// SH} + + Type enhancedMethodReturnType = generalizeReturnType(m.getSignature()); + +// {SH instruction list may be null for abstract method +// orig: InstructionList il = mg.getInstructionList().copy(); + InstructionList il = mg.getInstructionList(); + if (il != null) + il = il.copy(); + else + il = new InstructionList(); +// SH} + + MethodGen enhancedMethod = new MethodGen(m.getAccessFlags(), + enhancedMethodReturnType, + enhancedArgumentTypes, + enhancedArgumentNames, + method_name, class_name, + il, cpg); + //not needed??: + // or not?????? + copyLocalVariables(mg, enhancedMethod); + copyLineNumbers(mg, enhancedMethod); + + boolean returnValueAdded = (returnType == Type.VOID) + && (enhancedMethodReturnType != Type.VOID); + + // all exception handlers of this method, which have to be updated later. + CodeExceptionGen [] handlers = copyExceptionHandlers(mg, enhancedMethod, il); + // list of instruction handles (old and new) that are replaced in the sequel + ArrayList<IHPair> replacedInstructions = new ArrayList<IHPair>(); // of IHPair; + // set of instruction handles which signal TargetLostException during delete(). + HashSet<InstructionHandle> targetLost = new HashSet<InstructionHandle>(); // of InstructionHandle + + // create LocalVariable Object _OT$result: + LocalVariableGen otResult = null; + + int slot = mg.getMaxLocals() + EXTRA_ARGS-indexOffset; + otResult = enhancedMethod.addLocalVariable("_OT$result", enhancedMethodReturnType, + slot, null, null); + + // subtract EXTRA_ARGS since this offset will be added again below. + il.insert(InstructionFactory.createStore(enhancedMethodReturnType, + otResult.getIndex() - EXTRA_ARGS)); + + il.insert(new ACONST_NULL()); + il.setPositions(); // about to retrieve instruction handles. + + InstructionHandle[] ihs = il.getInstructionHandles(); + //printLogMessage("every call of base." + method_name + "(...) will be replaced by " + // + liftMethodName + BASE + "."+baseChainMethodName+"(...))"); + + int actInstruction = 0; + int offset = EXTRA_ARGS; + while (actInstruction < ihs.length) { + Instruction act_instruction = ihs[actInstruction].getInstruction(); +/****************************** variable index adaption: **********************************/ + if(act_instruction instanceof LocalVariableInstruction) { + // add offset to the index of every variable load or store instruction, + // because of the inserted EXTRA_ARGS arguments: + LocalVariableInstruction localVariableInstruction = (LocalVariableInstruction) act_instruction; + if (localVariableInstruction.getIndex() != 0 || (enhancedMethod.isStatic())) { // 'this' stays at index 0 + if (localVariableInstruction instanceof StoreInstruction) { + localVariableInstruction = + InstructionFactory.createStore(localVariableInstruction.getType(cpg), + offset+localVariableInstruction.getIndex()); + } else if (localVariableInstruction instanceof LoadInstruction) { + localVariableInstruction = + InstructionFactory.createLoad(localVariableInstruction.getType(cpg), + offset+localVariableInstruction.getIndex()); + } else if (localVariableInstruction instanceof IINC) { + localVariableInstruction.setIndex(offset+localVariableInstruction.getIndex()); + // TODO: check, if this is enough for all kinds of LocalVariableInstructions + // and if there are more instructions which use variable indizes!! + } + ihs[actInstruction].setInstruction(localVariableInstruction); + } + } else if (act_instruction instanceof RET) { + RET ret = (RET)act_instruction; + if (ret.getIndex() != 0) + ihs[actInstruction].setInstruction(new RET(offset + ret.getIndex())); + +/*************************** "super"- & "tsuper"-call enhancement: **********************************/ + } else if (super_or_tsuper_instruction(act_instruction, method_name, cpg) ) { + + InvokeInstruction ii = (InvokeInstruction)act_instruction; + InstructionHandle next = ihs[actInstruction+1]; + InstructionList changedArea; + InstructionHandle[] delim = new InstructionHandle[2]; + int stackDepth = computeArgumentStackDepth(cpg, ii); + InstructionList loading = pruneLoading(il, ihs, actInstruction, + stackDepth, cpg, + targetLost, delim, true); + if (loading == null) { + actInstruction++; + continue; + } + + changedArea = genEnhancedSuperCall(cpg, ii, enhancedMethod, loading); + if (returnValueAdded) { + changedArea.append(InstructionFactory.createStore( + enhancedMethodReturnType, otResult.getIndex())); + } else { + InstructionHandle ih = adjustValue(changedArea, changedArea.getEnd(), enhancedMethodReturnType, returnType); + if (debugging && ih != null) + mg.addLineNumber(ih, STEP_OVER_LINENUMBER); + } + replacedInstructions.add(new IHPair(delim[0], changedArea.getStart())); + replacedInstructions.add(new IHPair(delim[1], changedArea.getEnd())); + + il.insert(next, changedArea); + actInstruction++; + continue; + +/*************************** "activate" substitution and base-call generation: ************/ + //} else if (act_instruction instanceof INVOKEVIRTUAL) { + // INVOKEVIRTUAL iv = (INVOKEVIRTUAL)act_instruction; + } else if (act_instruction instanceof InvokeInstruction) { + InvokeInstruction iv = (InvokeInstruction)act_instruction; + String iv_name = iv.getName(cpg); + + // FIXME(SH): is this still needed? + // - activate is commented-out, + // - base call is now generated by the compiler. + if(!(iv_name.equals(method_name) || + iv_name.equals("activate"))) + { + actInstruction++; + continue; + } + InstructionHandle next = ihs[actInstruction+1]; + InstructionList changedArea; + InstructionHandle[] delim = new InstructionHandle[2]; + /* + if (iv_name.equals("activate")) { + // blank original invokevirtual: + ihs[actInstruction].setInstruction(new NOP()); + Type [] ivArgTypes = iv.getArgumentTypes(cpg); + int activateArgCount = ivArgTypes.length; + InstructionList loading = null; + if (activateArgCount == 1) + loading = pruneLoading (il, ihs, actInstruction, + ivArgTypes[0].getSize(), cpg, + targetLost, delim, false); + changedArea = enhanceActivateCall(factory, cpg, loading, iv); + if (activateArgCount == 1) { + replacedInstructions.add(new IHPair(delim[0], + changedArea.getStart())); + replacedInstructions.add(new IHPair(delim[1], + changedArea.getEnd())); + } + } else*/ { // base call: + int stackDepth = computeArgumentStackDepth(cpg, iv); + boolean deleteThis = true; + + if(enhancedMethod.isStatic()) + deleteThis = false; + + /* + if (iv.getOpcode()==Constants.INVOKESTATIC) + deleteThis = false; + */ + InstructionList loading = pruneLoading(il, ihs, actInstruction, + stackDepth, cpg, + targetLost, delim, /*true*/deleteThis); + //System.err.println(loading); + if (loading == null) { + actInstruction++; + continue; + } + + // insert call of base-call surrogate method: + String roleInterfaceName = genRoleInterfaceName(cg.getClassName()); + + String calleeClassName = null; + if(m.isStatic()) { + calleeClassName = extractTeamName(roleInterfaceName); + } + changedArea = genBaseCallSurrogateCall(cpg, iv, enhancedMethod, loading, extractRoleName(roleInterfaceName), calleeClassName); + + if (returnValueAdded) { + changedArea.append(InstructionFactory.createStore(enhancedMethodReturnType, + otResult.getIndex())); + } else { + InstructionHandle ih = adjustValue(changedArea, changedArea.getEnd(), enhancedMethodReturnType, returnType); + if (debugging && ih != null) + mg.addLineNumber(ih, STEP_OVER_LINENUMBER); + } + replacedInstructions.add(new IHPair(delim[0], changedArea.getStart())); + replacedInstructions.add(new IHPair(delim[1], changedArea.getEnd())); + + } // if (activate or base call) + + il.insert(next, changedArea); + + +/*************************** "return" enhancements: ************/ + } else if (act_instruction instanceof ReturnInstruction) { + // replace return statement by result preparation and a new return statement + // construct back to front, to keep the insertion position! + InstructionHandle oldReturn = ihs[actInstruction]; + InstructionHandle replacedPos = oldReturn; + il.append(oldReturn, InstructionFactory.createReturn(enhancedMethodReturnType)); + if (returnValueAdded) { + // load ot_result: + oldReturn.setInstruction(InstructionFactory.createLoad(enhancedMethodReturnType, + otResult.getIndex())); + } else { + oldReturn.setInstruction(new NOP()); + replacedPos = + adjustValue(il, oldReturn, returnType, enhancedMethodReturnType); + if (debugging && replacedPos != null) + mg.addLineNumber(replacedPos, STEP_OVER_LINENUMBER); + } + if (replacedPos != null) + replacedInstructions.add(new IHPair(oldReturn, replacedPos)); + } // conditional over instruction types + actInstruction++; + } //end while + + // tidy: + checkUpdate(handlers, replacedInstructions, targetLost); + enhancedMethod.removeNOPs(); + il.setPositions(); + enhancedMethod.setMaxStack(); + enhancedMethod.setMaxLocals(); + + return enhancedMethod; + } + + /** + * Given an invokevirtual compute the space its arguments use on the stack. + * @param cpg + * @param iv + * @return int stack size. + */ + static int computeArgumentStackDepth(ConstantPoolGen cpg, InvokeInstruction ii) { + Type [] iiargs = ii.getArgumentTypes(cpg); + int depth=0; + for (int i=0; i<iiargs.length; i++) + depth += iiargs[i].getSize(); + return depth; + } + + /** + * Copy all local variables from <tt>src</tt> to <tt>dest</tt>. + * While doing so, increment their index by EXTRA_ARGS. + */ + static void copyLocalVariables(MethodGen src, MethodGen dest) { + Type[] argumentTypes = src.getArgumentTypes(); + LocalVariableGen[] lvgs = src.getLocalVariables(); + for (int l=argumentTypes.length; l<lvgs.length; l++) { + LocalVariableGen lvg = lvgs[l]; + if (lvg.getIndex() > 0) { + dest.addLocalVariable(lvg.getName(), + lvg.getType(), + lvg.getIndex()+(EXTRA_ARGS+1), // +1????? + null, null); + //System.err.println("adding:" +src.getClassName() +" "+src.getName()+" "+lvg.getName() +" " + lvg.getType() +" "+ (lvg.getIndex()+(EXTRA_ARGS+1))); + } + } + } + /** Copy all line numbers from <tt>src</tt> to <tt>dest</tt>. */ + static void copyLineNumbers(MethodGen src, MethodGen dest) { + InstructionList il_dest = dest.getInstructionList(); + il_dest.setPositions(); + LineNumberGen[] src_lng = src.getLineNumbers(); + for (int i=0; i<src_lng.length; i++) { + int position = src_lng[i].getInstruction().getPosition(); + InstructionHandle ih = il_dest.findHandle(position); + dest.addLineNumber(ih, src_lng[i].getSourceLine()); + } + } + /** + * Prune a invokevirtual portion from a given instruction list. + * Note, that arguments don't include 'this', which is not pruned but blanked + * (need to keep as possible jump target). + * @param il the source list + * @param ihs array of handles of this list + * @param idx points to a invokevirtual that shall be removed + * @param stackDepth size of the called method's arguments on the stack. + * This is how deep we need to cut into the stack. + * @param cpg + * @param targetLost set of lost InstructionHandles to be filled + * @param delim array of two handles, which should be filled with start and end of + * the pruned region. + * @param blankThis should the 'this' call target be overwritten? + * @return InstructionList a copy of the original value loading. + */ + static InstructionList pruneLoading (InstructionList il, InstructionHandle[] ihs, int idx, + int stackDepth, ConstantPoolGen cpg, + HashSet<InstructionHandle> targetLost, InstructionHandle[] delim, + boolean blankThis) + { + InstructionList nlist = new InstructionList(); + InstructionHandle start = ihs[idx]; + InstructionHandle end = ihs[idx--]; + while (stackDepth > 0) { + start = ihs[idx--]; + Instruction instr = start.getInstruction(); + stackDepth -= stackDiff(instr, cpg); + nlist.insert(instr); + } + if (blankThis) { + if (!isALoad0(ihs[idx].getInstruction())) + return null; + ihs[idx].setInstruction(new NOP()); // keep as jump target but delete 'this' + } + delim[0] = start; + delim[1] = end; + safeDelete(il, start, end, targetLost); + return nlist; + } + + static boolean isALoad0(Instruction i) { + if (!(i instanceof ALOAD)) return false; + return ((ALOAD)i).getIndex() == 0; + } + + /** Get the lenght of the longest base method signature in mbs. + * @param mbs List of {@link MethodBinding MethodBinding} + */ +// static int getMaxBaseArgLen (List mbs) { +// int max=0; +// Iterator it = mbs.iterator(); +// while (it.hasNext()) { +// MethodBinding mb = (MethodBinding)it.next(); +// String sign = mb.getBaseMethodSignature(); +// int len = Type.getArgumentTypes(sign).length; +// if (len>max) max = len; +// } +// return max; +// } + + /** + * Generate a dispatching switch statement which calls the proper base method. + * @param cpg + * @param mbs list of MethodBinding that applies to this callin method + * @param enhancedMethod the enhanced callin method + * @param roleArgumentTypes arg types of the callin method + * @param outerClass the Team + * @param baseClass the base bound to this role + * @param returnType the return type of the original callin method + * @param otResult the local variable storing the base call result + * @param loading an instruction list holding the original instructions for + * loading parameters + * @return InstructionList the complete replacement implementing the base call. + */ + InstructionList genBaseCallSwitch (ConstantPoolGen cpg, + List<MethodBinding> mbs, MethodGen enhancedMethod, + Type[] roleArgumentTypes, + ObjectType outerClass, ObjectType baseClass, + Type returnType, LocalVariableGen otResult, + InstructionList loading, + boolean isSuperAccess) + { + + String className = enhancedMethod.getClassName(); + Type enhancedMethodReturnType = enhancedMethod.getReturnType(); + boolean callinHasReturnValue = returnType != Type.VOID; + + InstructionList il = new InstructionList(); + + // Setup a variable which holds the result of this base call. + // This variabel is local to this segment of code and used only + // to transport this result out off the switch statement. + int localResult = -1; + LocalVariableGen lg = null; + if (callinHasReturnValue) { + lg = enhancedMethod.addLocalVariable("_OT$tmpResult", returnType, + null, null); + localResult = lg.getIndex(); + il.append(InstructionFactory.createNull (returnType)); + il.append(InstructionFactory.createStore(returnType, localResult)); + } + + // ---- Prepare the switch: ---- + InstructionHandle switchStart = il.append + (InstructionFactory.createLoad(Type.INT, BASE_METH_ARG)); + // generated: _OT$baseMethTag + + removeDuplicatedBaseMethodTags(mbs); + + // one break for each case clause + int numberOfCases = mbs.size(); + GOTO[] breaks = new GOTO[numberOfCases]; + for (int i=0; i<numberOfCases; i++) + breaks[i] = new GOTO(null); + + int[] matches = new int[numberOfCases]; + InstructionHandle[] targets = new InstructionHandle[numberOfCases]; + int caseCounter = 0; + + Iterator<MethodBinding> it = mbs.iterator(); + while (it.hasNext()) { + + MethodBinding mb = it.next(); + + String wrapperName = mb.getWrapperName(); + int[] paramPositions = CallinBindingManager.getParamPositions(outerClass.getClassName(), + wrapperName); + if (logging) printLogMessage("param pos(" + wrapperName + ")=" + paramPositions); + + matches[caseCounter] = CallinBindingManager.getBaseCallTag(mb.getBaseClassName(), + mb.getBaseMethodName(), + mb.getBaseMethodSignature()); + InstructionHandle nextBranch = il.append(new NOP()); + + String baseMethodName = mb.getBaseMethodName(); + String baseMethodSignature = mb.getBaseMethodSignature(); + Type[] baseMethodArgumentTypes = Type.getArgumentTypes(baseMethodSignature); + Type baseMethodReturnType = Type.getReturnType (baseMethodSignature); + + String baseChainMethodName; + Type baseChainReturnType; + Type[] enhancedBaseArgumentTypes; + if (isSuperAccess) { + baseChainMethodName = OT_PREFIX+baseMethodName+"$super"; + baseChainReturnType = returnType; + // base arguments are un-enhanced but have a leading base instance: + int len = baseMethodArgumentTypes.length; + System.arraycopy(baseMethodArgumentTypes, 0, enhancedBaseArgumentTypes=new Type[len+1], 1, len); + enhancedBaseArgumentTypes[0] = baseClass; + } else { + baseChainMethodName = genChainMethName(baseMethodName); + baseChainReturnType = object; // ALWAYS + enhancedBaseArgumentTypes = enhanceArgumentTypes(baseMethodArgumentTypes); + } + + // --- call target: --- + + // if base class type is a role type _OT$base field has role interface type: + { + String baseClassName = baseClass.toString(); + if (baseClassName.indexOf(OTDT_PREFIX) != -1) { + baseClass = new ObjectType(ObjectTeamsTransformation.genRoleInterfaceName(baseClassName)); + if(logging) printLogMessage(baseClassName + " --> " + ObjectTeamsTransformation.genRoleInterfaceName(baseClassName)); + } + } + + // load '_OT$base' field: + InstructionHandle baseCallLine = il.append(InstructionFactory.createThis()); + il.append(factory.createFieldAccess(className, BASE, baseClass, Constants.GETFIELD)); + + if (!baseClass.getClassName().equals(mb.getBaseClassName())) { + // playedBy has been refined in the sub role; + // create a cast to the sub base class: + il.append(factory.createCast(baseClass, new ObjectType(mb.getBaseClassName()))); + } + + // --- load arguments of the new method: --- + // (letters refer to document parameter-passing.odg) + + // (u) generate extra arguments (indices are equal at role and base): + if (!isSuperAccess) + for (int idx = 0; idx < EXTRA_ARGS; idx++) + il.append(InstructionFactory.createLoad(enhancedMethod.getArgumentTypes()[idx], + idx+1/*translating non-static*/)); + + // (v)(w)(x) split loading sequence and transfer source-level arguments + // (includes reverse-application of parameter mappings): + + // Start at EXTRA_ARGS, because one set of enhancement has already been loaded, + // except when doing super access which only has one extra arg: base instance. + int start = isSuperAccess ? 1 : EXTRA_ARGS; + il.append(translateLoads(splitLoading(cpg, + loading.copy(), + roleArgumentTypes), + enhancedMethod.getArgumentTypes(), + enhancedBaseArgumentTypes, + paramPositions, + extractTeamName(enhancedMethod.getClassName()), + className, + new BaseMethodInfo(mb.baseMethodIsCallin(), false/*static*/, mb.getTranslationFlags()), + start, + cpg)); + // --- done loading --- + + // invoke the chaining method of the base class (base-call!): + il.append(factory.createInvoke(mb.getBaseClassName(), + baseChainMethodName, + baseChainReturnType, + enhancedBaseArgumentTypes, + isSuperAccess + ? Constants.INVOKESTATIC + : Constants.INVOKEVIRTUAL + )); + + boolean resultLiftingNecessary = ((mb.getTranslationFlags()&1)!=0); + + if (resultLiftingNecessary) { // call the static lift-method: + // STATIC_PARTS_OK: in role: lift method call +//TODO: lift method args! + String liftMethodName = mb.getLiftMethodName(); + Type liftMethodReturnType = Type.getReturnType(mb.getLiftMethodSignature()); + Type[] liftMethodArgs = Type.getArgumentTypes(mb.getLiftMethodSignature()); + + il.append(factory.createCast(baseChainReturnType, baseMethodReturnType)); + il.append(InstructionFactory.createThis()); + + int nestingDepth = countOccurrences(className, '$') -1; + il.append(factory.createGetField(className, "this$" + nestingDepth, outerClass )); + + il.append(new SWAP()); // -> .., this$0, (BaseType)result + //il.append(new ICONST(LIFT_ARG_RES)); + + il.append(factory.createInvoke(outerClass.getClassName(), + liftMethodName, + liftMethodReturnType, + liftMethodArgs, + Constants.INVOKEVIRTUAL)); + } + + InstructionHandle afterBaseCallLine = il.append(new NOP()); + + if (baseChainReturnType != Type.VOID) { + // adjust the return value to the type expected by the WRAPPER: + il.append(new DUP()); // keep for adjustment below + if (!resultLiftingNecessary) + adjustValue(il, null, baseChainReturnType, enhancedMethodReturnType); + il.append(InstructionFactory.createStore(enhancedMethodReturnType, + otResult.getIndex())); // store "globally" + // this store is needed to tunnel unused results through the callin. + + // adjust the return value to the type expected by the ORIGINAL CALLIN: + adjustValue(il, null, baseChainReturnType, returnType); + if (callinHasReturnValue) + il.append(InstructionFactory.createStore(returnType, localResult)); // store "locally" + // this store is useful for callins which make use of the result. + } + + targets[caseCounter] = nextBranch; + il.append(breaks[caseCounter]); + // generated: break; + + caseCounter++; + + if (debugging) { + enhancedMethod.addLineNumber(baseCallLine, STEP_INTO_LINENUMBER); + enhancedMethod.addLineNumber(afterBaseCallLine, STEP_OVER_LINENUMBER); + } + } + // Default case: throw an exception reporting the situation: + // create: String msg = ("Unhandled base-call case!"+base_method_tag) + InstructionList messagePush = new InstructionList(); + messagePush.append(factory.createNew(OTConstants.STRING_BUFFER_NAME)); + messagePush.append(new DUP()); + messagePush.append(factory.createInvoke(OTConstants.STRING_BUFFER_NAME, Constants.CONSTRUCTOR_NAME, Type.VOID, new Type[0], Constants.INVOKESPECIAL)); + messagePush.append(new PUSH(cpg, "Unhandled base-call case: ")); + messagePush.append(factory.createInvoke(OTConstants.STRING_BUFFER_NAME, "append", Type.STRINGBUFFER, new Type[]{Type.STRING}, Constants.INVOKEVIRTUAL)); + messagePush.append(InstructionFactory.createLoad(Type.INT, BASE_METH_ARG)); + messagePush.append(factory.createInvoke(OTConstants.STRING_BUFFER_NAME, "append", Type.STRINGBUFFER, new Type[]{Type.INT}, Constants.INVOKEVIRTUAL)); + messagePush.append(factory.createInvoke(OTConstants.STRING_BUFFER_NAME, "toString", Type.STRING, new Type[0], Constants.INVOKEVIRTUAL)); + // create: throw new OTREInternalError(msg) + InstructionHandle defaultCase = createThrowInternalError(cpg, il, messagePush); + + InstructionHandle afterSwitch = il.append(new NOP()); // all breaks point here. + + il.append(switchStart, createLookupSwitch(matches, targets, breaks, + defaultCase, afterSwitch)); + + // retrieve locally stored result: + if (callinHasReturnValue) { + il.append(InstructionFactory.createLoad(returnType, localResult)); + lg.setStart(il.getStart()); // restrict local variable to this segment. + lg.setEnd(il.getEnd()); + } + + return il; + } + + /** + * Removes duplicated method bindings with the same base call tag from the list, to avoid duplicated + * cases in the base call surrogate-switch. + * @param mbs + */ + private static void removeDuplicatedBaseMethodTags(List<MethodBinding> mbs) { + if (mbs.size() < 2) // nothing to remove + return; + + MethodBinding[] mbArray = mbs.toArray(new MethodBinding[mbs.size()]); + + Comparator<MethodBinding> baseCallTagComparator = new Comparator<MethodBinding>() { + public int compare(MethodBinding firstMB, MethodBinding secondMB) { + int firstBaseTag = CallinBindingManager.getBaseCallTag(firstMB.getBaseClassName(), + firstMB.getBaseMethodName(), + firstMB.getBaseMethodSignature()); + int secondBaseTag = CallinBindingManager.getBaseCallTag(secondMB.getBaseClassName(), + secondMB.getBaseMethodName(), + secondMB.getBaseMethodSignature()); + + if (firstBaseTag < secondBaseTag) + return -1; + if (firstBaseTag > secondBaseTag) + return 1; + return 0; + } + }; + Arrays.sort(mbArray, baseCallTagComparator); + for (int i = 0; i + 1 < mbArray.length; i++) { + if (baseCallTagComparator.compare(mbArray[i], mbArray[i + 1]) == 0) { + mbs.remove(mbArray[i + 1]); + } + } + } + + + /** + * @param className + * @return + */ + private static String extractTeamName(String roleClassName) { + int lastDollarIndex = roleClassName.lastIndexOf('$'); + return roleClassName.substring(0, lastDollarIndex); + } + + /** + * @param className + * @return + */ + private static String extractRoleName(String roleClassName) { + int lastDollarIndex = roleClassName.lastIndexOf('$'); + return roleClassName.substring(lastDollarIndex+1, roleClassName.length()); + } + + /** + * FIXME(SH): obsolete! + * @param baseMethodReturnType + * @param returnType + * @return + */ +// private static boolean returnTypeCompatible(Type from, Type to) { +// System.out.println("test for " + from + "->" + to); +// if (from.equals(to)) +// return true; +// if (from instanceof ObjectType && to instanceof ObjectType) {// how to handle compatible basic types?? +// ObjectType otFrom = (ObjectType) from; +// ObjectType otTo = (ObjectType) to; +// if (otFrom.subclassOf(otTo)) +// return true; +// } +// return false; +// } + + /** + * Copy all exception handlers of a method. + * @param source the method from where to copy + * @param dest the method where to copy to + * @param il instructions of `dest' which must still have the same positions + * as the instructions in `source'. + * @return an array of handler generators, which still has to be maintained, + * whenever instructions are replaced in the methods instruction list. + */ + static CodeExceptionGen[] copyExceptionHandlers(MethodGen source, + MethodGen dest, + InstructionList il) { + il.setPositions(); // needed to retrieve handles by position. + CodeExceptionGen[] excGens = source.getExceptionHandlers(); + CodeExceptionGen[] newGens = new CodeExceptionGen[excGens.length]; + if ((excGens != null) && excGens.length > 0) { + for (int hcount=0; hcount<excGens.length; hcount++) { + CodeExceptionGen excGen = excGens[hcount]; + InstructionHandle excStart = il.findHandle(excGen.getStartPC().getPosition()); + InstructionHandle excEnd = il.findHandle(excGen.getEndPC().getPosition()); + InstructionHandle excHandler = il.findHandle(excGen.getHandlerPC().getPosition()); + ObjectType catchType = excGen.getCatchType(); + newGens[hcount] = + dest.addExceptionHandler(excStart, excEnd, excHandler, catchType); + } + } + return newGens; + } + + /** + * Update the positions of all exception handlers. + * @param handlers the handlers of this method. + * @param replaced a pair of InstructionHandles, the first is replaced by the second. + */ + static void updateHandlers (CodeExceptionGen[] handlers, IHPair replaced) { + InstructionHandle old = replaced.fst(); + InstructionHandle neu = replaced.snd(); + for (int i=0; i<handlers.length; i++) { + // System.out.println("handler "+handlers[i]); + if (handlers[i].containsTarget(old) && (old != neu)) { + // System.out.println("update "+old+"->"+neu); + handlers[i].updateTarget(old, neu); + } + } + } + + /** + * Delete a range of instructions, whithout throwing TargetLostException. + * @param il the list to delete from + * @param start handle to first instruction to delete. + * @param end handle to last instruction to delete. + * @param collect a set of InstructionHandle which are still targeted. + */ + static void safeDelete(InstructionList il, + InstructionHandle start, InstructionHandle end, + HashSet<InstructionHandle> collect) + { + try { + il.delete(start, end); + } catch(TargetLostException e) { + // System.out.print("Loosing:"+e+" "); + InstructionHandle [] targets = e.getTargets(); + for (int tcount = 0; tcount < targets.length; tcount++) { + collect.add(targets[tcount]); + // System.out.println(targets[tcount]+"!!"); + } + } + } + + /** + * Update all exceptions handlers with respect to all instructions that were replaced. + * Check that this covers all instruction handles in 'lost'. + * @param handles the exception handlers of this method. + * @param replacedList list of IHPairs describing what has been modified. + * @param lost set of instruction handles that were still referred to when + * they were deleted. All these handles should be updated. + */ + static void checkUpdate(CodeExceptionGen[] handlers, ArrayList<IHPair> replacedList, HashSet<InstructionHandle> lost) { + // System.out.println("Update "+replacedList+"/"+lost); + Iterator<IHPair> iter = replacedList.iterator(); + while (iter.hasNext()) { + IHPair replaced = iter.next(); + updateHandlers(handlers, replaced); + lost.remove(replaced.fst()); + } + if (!lost.isEmpty()) { + System.err.println("Warning: "+lost.size()+" target(s) lost: "); + Iterator<InstructionHandle> it = lost.iterator(); + while (it.hasNext()) + System.out.println(it.next()); + } + } + + /** + * While adding extra args to an activate call: + * <ul> + * <li>Check whether an activationLevel was passed (single argument) + * <li>if so, insert the loading sequence for that expression. + * <li>also the return differs (void or int), should however be + * consistent between plain and enhanced version. + * <li>decrement "idx" so this team is the currently active in the chain. + * </ul> + * @param factory + * @param cpg + * @param loading sequence, which loads "level" argument, else null. + * @param iv the invokevirtual for "activate", used to inspect the + * original signature. + * @return the full sequence for loading the arguments, but not the + * call target (because that's the enclosing team and is kept + * unmodified in the original instruction list). + */ +// static InstructionList enhanceActivateCall (final InstructionFactory factory, +// ConstantPoolGen cpg, +// InstructionList loading, +// INVOKEVIRTUAL iv) { +// InstructionList changedArea = new InstructionList(); +// +// // load arguments of the new method: +// int index = 1; +// Type[] enhancedArgumentTypes = enhanceArgumentTypes(iv.getArgumentTypes(cpg), +// 0, false, false); +// Type returnType = Type.VOID; +// int kount = enhancedArgumentTypes.length; +// if (iv.getArgumentTypes(cpg).length == 1) { +// kount--; // "level" loaded separately via 'loading' +// returnType = Type.INT; +// } +// +// for (int k=0; k<kount; k++) { +// changedArea.append(InstructionFactory.createLoad(enhancedArgumentTypes[k], index)); +// index += enhancedArgumentTypes[k].getSize(); +// } +// if (loading != null) +// changedArea.append(loading); +// +// // invoke the overloaded activate method: +// changedArea.append(factory.createInvoke("org.objectteams.Team", +// "activate", +// returnType, +// enhancedArgumentTypes, +// Constants.INVOKEVIRTUAL)); +// +// // generate: idx--; +// changedArea.append(InstructionFactory.createLoad(Type.INT, IDX_ARG)); +// changedArea.append(new ICONST(1)); +// changedArea.append(new ISUB()); +// changedArea.append(InstructionFactory.createStore(Type.INT, IDX_ARG)); +// +// return changedArea; +// } + + /** + * Generates the instructions to call the enhanced version of the 'super' respectively 'tsuper' + * call in a role method. + * @param cpg the constant pool + * @param ii the original invoke instruction + * @param enhancedMethod the enhanced role method + * @param loading the originally loaded arguments + * @return the instruction list containing the method call ingredients + */ + InstructionList genEnhancedSuperCall(ConstantPoolGen cpg, InvokeInstruction ii, + MethodGen enhancedMethod, InstructionList loading) + { + Type returnType = enhancedMethod.getReturnType(); + //Type[] argTypes = enhancedMethod.getArgumentTypes(); + Type[] argTypes = enhanceArgumentTypes(ii.getArgumentTypes(cpg)); + InstructionList il = new InstructionList(); + + il.append(InstructionFactory.createThis()); + // load all additional arguments of the enhanced method (first EXTRA_ARGS): + int index = 1; + for (int i=0; i<EXTRA_ARGS; i++) { + il.append(InstructionFactory.createLoad(argTypes[i],index)); + index += argTypes[i].getSize(); + } + // load arguments of the originally call: + il.append(loading); + + // call super.<enhancedMethod>: + short kind=0; + if (ii instanceof INVOKESPECIAL) + kind = Constants.INVOKESPECIAL; + else + kind = Constants.INVOKEVIRTUAL; + + il.append(factory.createInvoke(ii.getClassName(cpg), + ii.getMethodName(cpg), + returnType, + argTypes, + kind)); + return il; + } + + /** + * Generates the instructions to call the base call surrogate method. + * @param cpg the constant pool + * @param iv the original invoke instruction + * @param enhancedMethod the enhanced role method + * @param loading the originally loaded arguments + * @return the instruction list containing the method call ingredients + */ + InstructionList genBaseCallSurrogateCall(ConstantPoolGen cpg, InvokeInstruction/*INVOKEVIRTUAL*/ iv, + MethodGen enhancedMethod, InstructionList loading, + String roleClassName, String calleeClassName) //JU: added String roleClassName and teamClassName to the method signature + { + int indexOffset = enhancedMethod.isStatic()?-1:0; // argument indexes are decremented for static methods, + // because of the missing 'this' + // for static methods callee is 'null' -> substitute by current class (JU) + if(calleeClassName == null) { + calleeClassName = iv.getClassName(cpg); + } + + Type returnType = enhancedMethod.getReturnType(); + //Type[] argTypes = enhancedMethod.getArgumentTypes(); + Type[] argTypes = enhanceArgumentTypes(iv.getArgumentTypes(cpg)); + InstructionList il = new InstructionList(); + + String methodName = getBaseCallSurrogateName(enhancedMethod.getName(), + enhancedMethod.isStatic(), + roleClassName); + short invokeKind; + + if (!enhancedMethod.isStatic()) { + il.append(InstructionFactory.createThis()); + invokeKind = Constants.INVOKEVIRTUAL; + + } else { // role method is static: + invokeKind = Constants.INVOKESTATIC; + } + + // load all additional arguments of the enhanced method (first EXTRA_ARGS): + int index = 1; + for (int i = 0; i < argTypes.length; i++) { + if(i < EXTRA_ARGS){ // skip original arguments + il.append(InstructionFactory.createLoad(argTypes[i], index+indexOffset)); + } + //calculate the next index + index += argTypes[i].getSize(); + } + // load arguments of the originally call: + il.append(loading); + + // call _OT$<enhancedMethod>$base(): + il.append(factory.createInvoke(calleeClassName, + methodName, + returnType, + argTypes, + invokeKind/*Constants.INVOKEVIRTUAL*/)); + return il; + } + + + /** + * Checks, if a given instuction is a super or a tsuper call + * @param instr the instruction to check + * @param method_name the name of the method from wich the 'inst' came + * @param cpg the constant pool + * @return true, if 'inst' is a super or tsuper call + */ + private static boolean super_or_tsuper_instruction(Instruction instr, String method_name, ConstantPoolGen cpg) { + if (isTSuperCall(instr, method_name, cpg)) + return true; + if (isSuperCall(instr, method_name, cpg)) + return true; + return false; + } + + private static boolean isTSuperCall(Instruction instr, String method_name, ConstantPoolGen cpg) { + if (instr instanceof INVOKEVIRTUAL) { + INVOKEVIRTUAL iv = (INVOKEVIRTUAL)instr; + String iv_name = iv.getName(cpg); + Type[] argTypes = iv.getArgumentTypes(cpg); + if (argTypes.length<1) // no tsuper marker interface parameter! + return false; + String lastArgument = (argTypes[argTypes.length-1]).toString(); + if (iv_name.equals(method_name) && (lastArgument.indexOf(TSUPER_PREFIX)!=-1)) { + // if iv == <method_name>(..., TSuper__OT__<SuperTeamName>) --> + if(logging) printLogMessage("tsuper-call to " + iv_name + " has to be enhanced!"); + return true; + } + } + return false; + } + + private static boolean isSuperCall(Instruction instr, String method_name, ConstantPoolGen cpg) { + if (instr instanceof INVOKESPECIAL) { + INVOKESPECIAL is = (INVOKESPECIAL)instr; + String is_name = is.getName(cpg); + + if(is_name.equals(method_name)) { + if(logging) printLogMessage("super-call to " + is_name + " has to be enhanced!"); + return true; + } + } + return false; + } + + /** + * Generates the base call surrogate name for a given method name. + * @param method_name the name of the role method + * @param staticFlag + * @param roleClassName the name of the role method + * @return the base call surrogate name + */ + private static String getBaseCallSurrogateName(String method_name, boolean staticFlag, String roleClassName) { + //JU: for static methods the role class name should be inserted + if(staticFlag) { + return OT_PREFIX+roleClassName+"$"+method_name+"$base"; + } + return OT_PREFIX+method_name+"$base"; + } + + /** + * Reverts a method name to its original by returning the substring after the last '$' + * until the end. + * @param method_name method name to be adjusted + * @return the original method name + */ + private static String revertToOriginalName(String method_name) { + int p = method_name.lastIndexOf('$'); + return method_name.substring(p + 1); + } + +/* + * (non-Javadoc) + * + * @see org.eclipse.objectteams.otre.common.ObjectTeamsTransformation#doTransformCode(de.fub.bytecode.generic.ClassGen) + */ + public void doTransformCode(ClassGen cg) { + // nothing to do + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/BaseMethodTransformation.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/BaseMethodTransformation.java new file mode 100644 index 000000000..06878cb78 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/BaseMethodTransformation.java @@ -0,0 +1,1861 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: BaseMethodTransformation.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import static org.eclipse.objectteams.otre.StaticSliceBaseTransformation._OT_ACTIVE_TEAMS; +import static org.eclipse.objectteams.otre.StaticSliceBaseTransformation._OT_ACTIVE_TEAM_IDS; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.StringTokenizer; +import java.util.Map.Entry; + +import org.eclipse.objectteams.otre.util.CallinBindingManager; +import org.eclipse.objectteams.otre.util.DebugUtil; +import org.eclipse.objectteams.otre.util.ListValueHashMap; +import org.eclipse.objectteams.otre.util.MethodBinding; +import org.eclipse.objectteams.otre.util.TeamIdDispenser; + +import de.fub.bytecode.Constants; +import de.fub.bytecode.classfile.Field; +import de.fub.bytecode.classfile.LineNumber; +import de.fub.bytecode.classfile.LineNumberTable; +import de.fub.bytecode.classfile.Method; +import de.fub.bytecode.generic.AASTORE; +import de.fub.bytecode.generic.ACONST_NULL; +import de.fub.bytecode.generic.ALOAD; +import de.fub.bytecode.generic.ANEWARRAY; +import de.fub.bytecode.generic.ARRAYLENGTH; +import de.fub.bytecode.generic.ATHROW; +import de.fub.bytecode.generic.ArrayType; +import de.fub.bytecode.generic.BasicType; +import de.fub.bytecode.generic.BranchInstruction; +import de.fub.bytecode.generic.ClassGen; +import de.fub.bytecode.generic.ConstantPoolGen; +import de.fub.bytecode.generic.DUP; +import de.fub.bytecode.generic.DUP_X1; +import de.fub.bytecode.generic.FieldGen; +import de.fub.bytecode.generic.GOTO; +import de.fub.bytecode.generic.IADD; +import de.fub.bytecode.generic.ICONST; + +import de.fub.bytecode.generic.IFNE; +import de.fub.bytecode.generic.IFNONNULL; +import de.fub.bytecode.generic.IF_ICMPLT; +import de.fub.bytecode.generic.IINC; +import de.fub.bytecode.generic.INVOKESPECIAL; +import de.fub.bytecode.generic.Instruction; +import de.fub.bytecode.generic.InstructionConstants; +import de.fub.bytecode.generic.InstructionFactory; +import de.fub.bytecode.generic.InstructionHandle; +import de.fub.bytecode.generic.InstructionList; +import de.fub.bytecode.generic.InvokeInstruction; +import de.fub.bytecode.generic.LDC; +import de.fub.bytecode.generic.LocalVariableGen; +import de.fub.bytecode.generic.MONITOREXIT; +import de.fub.bytecode.generic.MethodGen; +import de.fub.bytecode.generic.NOP; +import de.fub.bytecode.generic.ObjectType; +import de.fub.bytecode.generic.POP; +import de.fub.bytecode.generic.PUSH; +import de.fub.bytecode.generic.TABLESWITCH; +import de.fub.bytecode.generic.Type; + + +/** + * Insert dispatch code into base methods affected by callin bindings. <p> + * If a loaded class contains binding declarations (transmitted by attributes) + * these are stored for further determination of necessity for base-method-transforming. + * Classes for which a callin binding exists will be transformed: + * (base-class)methods <i>m()</i> which are changed by a callin will be copied to + * <tt>_OT$<i>m</i>$orig()</tt>. + * Hereafter the original method <i>m()</i> will be transformend depending on the + * callin-modifier: + * <ul> + * <li> replace: only the (role-)callin-method is called + * <li> after: first the original-method <tt>_OT$<i>m</i>$orig()</tt> is called and + * then the callin-method + * <li> before: first the the callin-method is called and then original-method + * <tt>_OT$<i>m</i>$orig()</tt> + * </ul> + * + * @version $Id: BaseMethodTransformation.java 23408 2010-02-03 18:07:35Z stephan $ + * @author Christine Hundt + * @author Stephan Herrmann + */ + +public class BaseMethodTransformation + extends ObjectTeamsTransformation +{ + // configurability for stepping behavior of the chaining wrapper: + private static boolean SHOW_ORIG_CALL = true; + private static boolean SHOW_RECURSIVE_CALL = true; + private static boolean SHOW_ROLE_CALL = true; + static { + String callinStepping = System.getProperty("ot.debug.callin.stepping"); + if (callinStepping != null) { + SHOW_ORIG_CALL = SHOW_RECURSIVE_CALL = SHOW_ROLE_CALL = false; + StringTokenizer tokens = new StringTokenizer(callinStepping, ","); + while (tokens.hasMoreTokens()) { + String token = tokens.nextToken(); + if ("orig".equals(token)) + SHOW_ORIG_CALL = true; + else if ("recurse".equals(token)) + SHOW_RECURSIVE_CALL = true; + else if ("role".equals(token)) + SHOW_ROLE_CALL = true; + } + } + } + + // method of o.o.Team: + private static final String IS_ACTIVE = "isActive"; //$NON-NLS-1$ + + private final static int NORESULT = -1; + + // FIXME(SH): once we remove JMangler support, these maps can be reduced to their RHS because then we'll consistently have one transformer per class: + private HashMap /* class_name -> HashSet(method_name) */<String, HashSet<String>> transformableMethods = new HashMap<String, HashSet<String>>(); + private HashMap /* class_name -> HashSet(method_name) */<String, HashSet<String>> overridableMethods = new HashMap<String, HashSet<String>>(); + + public boolean useReflection = false; + + public BaseMethodTransformation(SharedState state) { + this(null, state); + } + + public BaseMethodTransformation(ClassLoader loader, SharedState state) { + super(loader, state); + } + /** + * The code transformer only replaces the original code with + * the initial wrapper. + */ + public void doTransformCode(ClassGen cg) { + factory = new InstructionFactory(cg); + ConstantPoolGen cpg = cg.getConstantPool(); + String class_name = cg.getClassName(); + + Method[] methods = cg.getMethods(); + for (int i=0; i<methods.length; i++) { + Method m = methods[i]; + //if (m.isNative()) + // continue; + if (m.isVolatile()) + continue; // don't touch bridge methods + + String method_name = m.getName(); + + if (CallinBindingManager.isBoundBaseClass(class_name) + && !CallinBindingManager.hasBoundBaseParent(class_name) + && method_name.equals(Constants.CONSTRUCTOR_NAME)) + { + addToConstructor(m, getInitializedRoleSet(cg.getClassName(), false), cg, cpg); + continue; + } + + String method_signature = m.getSignature(); + + if (state.interfaceTransformedClasses.contains(class_name)) { + HashSet<String> transformable = transformableMethods.get(class_name); + HashSet<String> overridable = overridableMethods.get(class_name); + if (transformable.contains(method_name + '.' + method_signature)) + cg.replaceMethod(m, m = generateInitialWrapper(m, class_name, cg.getMajor(), cpg)); + else if (overridable.contains(method_name + '.' + method_signature)) + cg.replaceMethod(m, m = generateSuperCall(m, cg, cpg)); + + Method replacement = checkReplaceWickedSuper(class_name, m, cpg); + if (replacement != null) + cg.replaceMethod(m, replacement); + } + } + } + + /* + * If a base method m1 has a super-call super.m2() and if that method m2 is callin-bound + * we currently bypass aspect dispatch to avoid infinite recursions. + */ + private Method checkReplaceWickedSuper(String className, Method m, ConstantPoolGen cpg) + { + if (m.isAbstract() || m.isNative()) + return null; + MethodGen mg = new MethodGen(m, className, cpg); + String method_name = m.getName(); + InstructionHandle[] ihs = mg.getInstructionList().getInstructionHandles(); + boolean found = false; + for (InstructionHandle ih : ihs) { + if (ih.getInstruction() instanceof INVOKESPECIAL) { + Instruction actInstruction = ih.getInstruction(); + INVOKESPECIAL is = (INVOKESPECIAL)actInstruction; + String is_name = is.getName(cpg); + if ( !is_name.equals(method_name) // not same method + && !is_name.equals("<init>")) // not ctor call + { + String superClassName = is.getClassName(cpg); + if ( !superClassName.equals(className) // not private method of same class + && CallinBindingManager.isBoundBaseMethod( // target method is callin-affected + superClassName, + is_name, + is.getSignature(cpg))) + { + found = true; + if(logging) printLogMessage("wicked super-call to " + is_name //$NON-NLS-1$ + + " has to be redirected to the orig-version!"); //$NON-NLS-1$ + ih.setInstruction(factory.createInvoke(superClassName, + "_OT$"+is_name+"$orig", + is.getReturnType(cpg), + is.getArgumentTypes(cpg), + Constants.INVOKESPECIAL)); + } + } + } + } + if (found) + return mg.getMethod(); + return null; + } + + /** + * Main entry for this transformer. + */ + public void doTransformInterface(ClassEnhancer ce, ClassGen cg) { + String class_name = cg.getClassName(); + //SourceMapGeneration sourceMapGen = new SourceMapGeneration(cg); + + ConstantPoolGen cpg = cg.getConstantPool(); + factory = new InstructionFactory(cg); + if (CallinBindingManager.isBoundBaseClass(class_name) && !cg.isInterface()) { + // TODO: where to add the role set infrastructure, if only an interface is bound? Implementing classes? + if (cg.containsField(OTConstants.ROLE_SET) == null && !CallinBindingManager.hasBoundBaseParent(class_name)) { + ce.addField(generateRoleSet(cpg, class_name), cg); + ce.addMethod(generateAddRole(cpg, class_name), cg); + ce.addMethod(generateRemoveRole(cpg, class_name), cg); + ce.addImplements(OTConstants.IBOUND_BASE, cg); + } + } + + HashSet<String> transformedMethods = transformableMethods.get(class_name); + if (transformedMethods == null) { + transformedMethods = new HashSet<String>(); + transformableMethods.put(class_name, transformedMethods); + } + HashSet<String> renamedMethods = overridableMethods.get(class_name); + if (renamedMethods == null) { + renamedMethods = new HashSet<String>(); + overridableMethods.put(class_name, renamedMethods); + } + + checkReadClassAttributes(ce, cg, class_name, cpg); + + //JU: change the class modifier to 'public' if decapsulation required + if(CallinBindingManager.checkBaseClassModifierChange(class_name) && !cg.isPublic()) { + cg.setAccessFlags(makePublicFlags(cg.getAccessFlags())); + } + + Collection<MethodBinding> inheritedBindings = CallinBindingManager.getInheritedCallinBindings(class_name); + +// if (inheritedSigns!=null && !inheritedSigns.isEmpty()) { +// Iterator itx = inheritedSigns.iterator(); +// while (itx.hasNext()) { +// System.err.print(class_name+" : "); +// String[] next = (String[])itx.next(); +// System.err.println(next[0] +" " +next[1]) ; +// } +// } + /* + String[] interfaceNames = cg.getInterfaceNames(); + Collection interfaceInheritedSigns = new LinkedList(); + //System.err.println("searching inherited bindings for: "+class_name); + for (int i=0; i<interfaceNames.length;i++) { + if (!interfaceNames[i].equals(class_name)) + interfaceInheritedSigns.addAll(CallinBindingManager.getInterfaceInheritedCallinBindings(interfaceNames[i])); + }*/ + /* + if (!interfaceInheritedSigns.isEmpty()) { + System.err.println("BMT: searching inherited bindings for: "+class_name); + for (Iterator iterator = interfaceInheritedSigns.iterator(); iterator.hasNext();) { + String[] element = (String[]) iterator.next(); + System.err.println(element[0]+element[1]); + } + }*/ + //inheritedSigns.addAll(interfaceInheritedSigns); + + boolean haveDirectCallin = + CallinBindingManager.isBoundBaseClass(class_name); + // IMPLICIT_INHERITANCE + if (inheritedBindings.size() == 0 && !haveDirectCallin /*&& interfaceInheritedSigns.size()==0*/) { + if(logging) printLogMessage("\nCallins: nothing to do for class " + class_name); //$NON-NLS-1$ + return; // nothing to do + } + // if class is already transformed by this transformer + /* + if (interfaceTransformedClasses.contains(class_name)) + continue; + */ + + if(cg.isInterface()) { + //CallinBindingManager.addBoundBaseInterface(class_name); // <- this is to late, implementing class may be loaded before!! + return; // No transfomations neccessary for interfaces. + } + + if(logging) printLogMessage("\nCallin bindings may be changing class " //$NON-NLS-1$ + + class_name + ':'); + + // A field to optimize class-literal in initial wrapper: + if (cg.getMajor() < 49) {// pre 1.5? + if (cg.containsField(OTConstants.SELF_CLASS) == null) + ce.addField(new FieldGen(Constants.ACC_PROTECTED|Constants.ACC_STATIC, + classType, + OTConstants.SELF_CLASS, + cpg) + .getField(), + cg); + } + + Method[] methods = cg.getMethods(); + for (int i=0; i<methods.length; i++) { + Method m = methods[i]; + if (m.isVolatile()) // bridge method! + continue; + String method_name = m.getName(); + String method_signature = m.getSignature(); + + Collection<MethodBinding> bindingsForMethod = null; + // IMPLICIT_INHERITANCE + if (haveDirectCallin) + bindingsForMethod = CallinBindingManager . + getBindingForBaseMethod(class_name, method_name, m.getSignature()); + + //JU: added the following statement to determine overridden static base methods + //Collection inheritedCallinBindings = CallinBindingManager.getInheritedCallinBindings(class_name); + //CH: removed it again, because it is the same as 'inheritedSigns'! + + MethodGen mg = null; + int firstLine = STEP_OVER_LINENUMBER; + String original_signature = method_signature; + /*if (bindingsForMethod != null || containsSign(inheritedSigns, m)*/ /*|| containsSign(interfaceInheritedSigns, m)*/ //) { + MethodBinding match= matchingBinding(inheritedBindings, m, false); + if (bindingsForMethod != null || (match!= null && !m.isStatic() && !m.isPrivate())) { + + mg = new MethodGen(m, class_name, cpg); + Method orig_method; + + String name_orig = genOrigMethName(method_name); + if (cg.containsMethod(name_orig, m.getSignature())!=null) { + continue;// method was already copied to orig-version! + } + + if (debugging) + firstLine = findFirstLineNumber(m); + + mg.setName(name_orig); + + // TODO(SH): store this match, or keep previous? + if (matchingBinding(inheritedBindings, m, true) != null) // this method was adapted in a super class + replaceSuperCalls(mg, method_name, cpg); + + orig_method = mg.getMethod(); + ce.addMethod(orig_method, cg); + if(logging) printLogMessage("Method " + method_name + " was backuped as " //$NON-NLS-1$ //$NON-NLS-2$ + + name_orig + '.'); + + if (match == null || method_signature.equals(match.getBaseMethodSignature())) + renamedMethods.add(method_name+'.'+method_signature); + else // override with covariant return: at the VM-level this is a *new* method. + transformedMethods.add(method_name+'.'+method_signature); + } + + /*if (bindingsForMethod != null || (containsSign(inheritedSigns,m) && m.isStatic())*/ /*|| containsSign(interfaceInheritedSigns, m)*/ //) { + //CH: changed 'inheritedCallinBindings' to 'inheritedSigns', because it was the same. + if (bindingsForMethod != null) { + //add method '_OT$<method_name>$chain' : + Method chain; + mg = getConcretMethodGen(m, class_name, cpg); + chain = generateChainingWrapper(mg, method_name, + original_signature/*method_signature*/, class_name, cpg, cg, firstLine); + + if (cg.containsMethod(chain.getName(), chain.getSignature()) == null) + ce.addMethod(chain, cg); + + transformedMethods.add(method_name + '.' + method_signature); + } + if (mg == null) + if (logging) printLogMessage("No method binding (direct or inherited) found for " //$NON-NLS-1$ + + method_name); + } + state.interfaceTransformedClasses.add(class_name); + } + + private int findFirstLineNumber(Method m) { + LineNumberTable lnt = m.getLineNumberTable(); + if (lnt != null && lnt.getTableLength() > 0) { + LineNumber[] lineNumberTable = lnt.getLineNumberTable(); + for (int i=0; i<lineNumberTable.length; i++) { + int lineNumber = lineNumberTable[i].getLineNumber(); + if (lineNumber != OTConstants.STEP_OVER_LINENUMBER) + return lineNumber; + } + return lineNumberTable[0].getLineNumber(); + } + return STEP_OVER_LINENUMBER; // make it a valid line number + } + + /** + * "super"-calls in callin bound base methods have to be redirected to the _OT$...$orig version, if the + * super-method is bound (and thus renamed to _OT$..$orig) too. + * + * @param orig_method the copied method + * @param method_name the prior name of the copied method + * @param cpg the corresponding constang pool + */ + private void replaceSuperCalls(MethodGen orig_method, String method_name, ConstantPoolGen cpg) { + // search for super calls: + InstructionList il = orig_method.getInstructionList(); + InstructionHandle[] ihs = il.getInstructionHandles(); + int actInstrIndex = 0; + while (actInstrIndex < ihs.length) { + if (ihs[actInstrIndex].getInstruction() instanceof INVOKESPECIAL) { + Instruction actInstruction = ihs[actInstrIndex].getInstruction(); + INVOKESPECIAL is = (INVOKESPECIAL)actInstruction; + String is_name = is.getName(cpg); + if(is_name.equals(method_name)) { + String superClassName = is.getClassName(cpg); + if(logging) printLogMessage("super-call to " + is_name //$NON-NLS-1$ + + " has to be redirected to the orig-version!"); //$NON-NLS-1$ + // generate and set an instruction calling the orig-version of the super method: + InvokeInstruction superOrigCall = factory.createInvoke(superClassName, + orig_method.getName(), + orig_method.getReturnType(), + orig_method.getArgumentTypes(), + Constants.INVOKESPECIAL); + ihs[actInstrIndex].setInstruction(superOrigCall); + } + } + actInstrIndex++; + } + // redirect them ( change called method name to _OT$..$orig ) if super-method has been renamed: + // --> + } + + /** + * Is method `m' contained in `baseMethodBindings'? + * @param nameSigns list of MethodBinding + * @param m + * @param strict if true covariance must not be considered + * @return the matching binding from baseMethodBindings or null. + */ + static MethodBinding matchingBinding (Collection<MethodBinding> baseMethodBindings, Method m, boolean strict) + { + for (MethodBinding binding: baseMethodBindings) + if (binding.matchesMethod(m.getName(), m.getSignature(), strict)) + return binding; + + return null; + } + + /** + * Get a MethodGen for `m'. + * If `m' is abstract setup a new concrete method. + */ + static MethodGen getConcretMethodGen (Method m, String class_name, ConstantPoolGen cpg) { + MethodGen mg; + String signature = m.getSignature(); + Type[] argTypes = Type.getArgumentTypes(signature); + if (m.isAbstract()) { + Type returnType = Type.getReturnType(signature); + InstructionList il = new InstructionList(); + il.append(new NOP()); + mg = new MethodGen(m.getAccessFlags()&~Constants.ACC_ABSTRACT, + returnType, argTypes, + null, // names are unknown + m.getName(), class_name, + il, cpg); + } else { + mg = wipeMethod(m, class_name, cpg); + } + if (debugging) { + mg.removeLocalVariables(); + mg.removeLocalVariableTypes(); + int slot = 0; + // create local variable table for "this" and arguments: + if (!m.isAbstract()) + mg.addLocalVariable("this", new ObjectType(class_name), slot++, null, null); + for (int i=0; i<argTypes.length; i++) + mg.addLocalVariable("arg"+i, argTypes[i], slot++, null, null); + mg.setMaxLocals(); + } + return mg; + } + + /** + * Generate the initial wrapper for the passed mehtod. + * It binds callin dispatch to Team-unaware client code. + * @param m the original method + * @param class_name the name of the appropriate class + * @param major + * @param cpg the ConstantPoolGen of the class + * @return the generated method + */ + private Method generateInitialWrapper(Method m, + String class_name, + int major, + ConstantPoolGen cpg) + { + MethodGen mg = getConcretMethodGen(m, class_name, cpg); + + String method_name = m.getName(); + Type returnType = mg.getReturnType(); + Type chainReturnType = object; + Type[] argTypes = mg.getArgumentTypes(); + + String name_chain = genChainMethName(method_name); + + InstructionList il = mg.getInstructionList(); + + InstructionHandle startSynchronized; + InstructionHandle chainCall; + int monitor; + // start generating + { + LocalVariableGen lg; // used for several local variables + + // Team[] _OT$teams; + int teams; + lg = mg.addLocalVariable(TEAMS, teamArray, null, null); + teams = lg.getIndex(); + + // synchronized (TopMostBoundBaseClass.class) { + Pair<Integer,InstructionHandle> monitorResult = addClassMonitorEnter(mg, il, CallinBindingManager.getTopmostBoundBase(class_name), major, cpg); + monitor = monitorResult.first; + if (debugging) + // no natural lines in this method: step-over until chain call, which has step-into: debugging => addLineNumber + mg.addLineNumber(monitorResult.second, STEP_OVER_LINENUMBER); + + // _OT$teams= new Teams[_OT$activeTeams.length]; + startSynchronized= // begin area protected by exception handler + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(InstructionConstants.ARRAYLENGTH); + il.append((Instruction)factory.createNewArray(teamType, (short) 1)); + il.append(InstructionFactory.createStore(Type.OBJECT, teams)); + + // _OT$teamIDs=new int[_OT$activeTeamIDs.length]; + int teamIDs; + lg = mg.addLocalVariable(TEAMIDS, intArray, null, null); + teamIDs = lg.getIndex(); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAM_IDS, intArray, Constants.GETSTATIC)); + il.append(InstructionConstants.ARRAYLENGTH); + il.append((Instruction)factory.createNewArray(Type.INT, (short) 1)); + il.append(InstructionFactory.createStore(Type.OBJECT, teamIDs)); + + // for (int i=0; i<_OT$activeTeams.length; ... + int for_index; + lg = mg.addLocalVariable("i", Type.INT, null, null); //$NON-NLS-1$ + for_index = lg.getIndex(); + il.append(new PUSH(cpg, 0)); + il.append(InstructionFactory.createStore(Type.INT, for_index)); + InstructionHandle for_start = + il.append(InstructionFactory.createLoad(Type.INT, for_index)); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(InstructionConstants.ARRAYLENGTH); + BranchInstruction if_loop_finished = + InstructionFactory.createBranchInstruction(Constants.IF_ICMPGE, null); + il.append(if_loop_finished); + + // if (!_OT$activeTeams[i].isActive()) { + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(InstructionFactory.createLoad(Type.INT, for_index)); + il.append(InstructionConstants.AALOAD); + il.append(factory.createInvoke(OTConstants.teamName, IS_ACTIVE, Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEINTERFACE)); + BranchInstruction if_team_isactive = + InstructionFactory.createBranchInstruction(Constants.IFNE, null); + il.append(if_team_isactive); + + // invalidate activation of current team: + // _OT$teams[i]= null; + il.append(InstructionFactory.createLoad(Type.OBJECT, teams)); + il.append(InstructionFactory.createLoad(Type.INT, for_index)); + il.append(InstructionConstants.ACONST_NULL); + il.append(InstructionConstants.AASTORE); + + // _OT$teamIDs[i]= -1; + il.append(InstructionFactory.createLoad(Type.OBJECT, teamIDs)); + il.append(InstructionFactory.createLoad(Type.INT, for_index)); + il.append(new PUSH(cpg, -1)); + il.append(InstructionConstants.IASTORE); + BranchInstruction goto_continue = + InstructionFactory.createBranchInstruction(Constants.GOTO, null); + il.append(goto_continue); + + // } else { adopt activation of current team: + // _OT$teams[i]= _OT$activeTeams[i]; + InstructionHandle adopt_activation = + il.append(InstructionFactory.createLoad(Type.OBJECT, teams)); + il.append(InstructionFactory.createLoad(Type.INT, for_index)); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(InstructionFactory.createLoad(Type.INT, for_index)); + il.append(InstructionConstants.AALOAD); + il.append(InstructionConstants.AASTORE); + + // _OT$teamIDs[i]= _OT$activeTeamIDs[i]; + il.append(InstructionFactory.createLoad(Type.OBJECT, teamIDs)); + il.append(InstructionFactory.createLoad(Type.INT, for_index)); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAM_IDS, intArray, Constants.GETSTATIC)); + il.append(InstructionFactory.createLoad(Type.INT, for_index)); + il.append(InstructionConstants.IALOAD); + il.append(InstructionConstants.IASTORE); + + // closing the above for: i++ and jump back: + InstructionHandle do_continue = + il.append(new IINC(for_index, 1)); + il.append(InstructionFactory.createBranchInstruction(Constants.GOTO, for_start)); + + // link jump instructions to their targets: + InstructionHandle loop_finished = il.append(new NOP()); // synthetic jump target + if_loop_finished.setTarget(loop_finished); + if_team_isactive.setTarget(adopt_activation); + goto_continue.setTarget(do_continue); + + // No more access to array fields, release monitor: + il.append(InstructionFactory.createLoad(Type.OBJECT, monitor)); + il.append(new MONITOREXIT()); + + // load special arguments: + if(!m.isStatic()) { // "this" cannot be accessed by static methods + chainCall= il.append(InstructionFactory.createThis()); // this + } else { + chainCall= il.append(new NOP()); + } + + if (debugging) + mg.addLineNumber(chainCall, STEP_INTO_LINENUMBER); + // ih now points to end of area protected by exception handler + + il.append(InstructionFactory.createLoad(teamArray, teams)); // _OT$teams + il.append(InstructionFactory.createLoad(intArray, teamIDs)); // _OT$teamIDs + il.append(new ICONST(0)); // _OT$idx = 0 + il.append(new ICONST(0)); // _OT$bindIdx = 0 + il.append(new ICONST(-1)); // _OT$baseMethTag is unused here + il.append(new ACONST_NULL()); // _OT$unusedArgs = null + } + + + // load regular arguments: + int index = m.isStatic()?0:1; + // chaining wrapper is always public, so never user INVOKESPECIAL: + short invocationKind = m.isStatic()?Constants.INVOKESTATIC:Constants.INVOKEVIRTUAL; + + for (int i=0; i<argTypes.length; i++) { + il.append(InstructionFactory.createLoad(argTypes[i],index)); + index += argTypes[i].getSize(); + } + // + il.append(factory.createInvoke(class_name, name_chain, + chainReturnType, + enhanceArgumentTypes(argTypes), + invocationKind)); + // generated invoke: _OT$<method_name>$chain (a1,.. aN) + + + adjustValue(il, null, chainReturnType, returnType); + il.append(InstructionFactory.createReturn(returnType)); + + // handler for exception within synchronized: + LocalVariableGen ex= mg.addLocalVariable("exceptionInSynchronized", Type.THROWABLE, il.getEnd(), null); //$NON-NLS-1$ + InstructionHandle handler= + il.append(InstructionFactory.createStore(Type.THROWABLE, ex.getIndex())); + il.append(InstructionFactory.createLoad(Type.OBJECT, monitor)); + il.append(new MONITOREXIT()); + il.append(InstructionFactory.createLoad(Type.THROWABLE, ex.getIndex())); + il.append(new ATHROW()); + mg.addExceptionHandler(startSynchronized, chainCall, handler, Type.THROWABLE); + + // tidy: + mg.setMaxStack(); + mg.setMaxLocals(); + Method generatedMethod = mg.getMethod(); + il.dispose(); + return generatedMethod; + } + + /** + * Generate a version of the passed method which only calls its super method + * (which will eventually call an initial-wrapper in a directly bound class). + * @param m the original method + * @param cg the ClassGen of the appropriate class + * @param cpg the ConstantPoolGen of the class + * @return the generated method + */ + Method generateSuperCall (Method m, + ClassGen cg, + ConstantPoolGen cpg) + { + short invocationKind = m.isStatic() ? Constants.INVOKESTATIC : Constants.INVOKESPECIAL; + MethodGen mg = getConcretMethodGen(m, cg.getClassName(), cpg); + + String method_name = m.getName(); + Type returnType = mg.getReturnType(); + Type[] argTypes = mg.getArgumentTypes(); + InstructionList il = mg.getInstructionList(); + + if(logging) printLogMessage("\nReplacing with call to super: " + method_name); //$NON-NLS-1$ + // start generating + if(!m.isStatic()){ //JU + il.append(InstructionFactory.createThis()); + } + int index = 1; + for (int i=0; i<argTypes.length; i++) { + il.append(InstructionFactory.createLoad(argTypes[i],index)); + index += argTypes[i].getSize(); + } + il.append(factory.createInvoke(cg.getSuperclassName(), method_name, + returnType, argTypes, + invocationKind)); + il.append(InstructionFactory.createReturn(returnType)); + // generated invoke: return super.<method_name> (a1,.. aN) + + // tidy: + mg.setMaxStack(); + mg.setMaxLocals(); + Method generatedMethod = mg.getMethod(); + il.dispose(); + return generatedMethod; + } + + + /** + * Generate a chaining wrapper for the original method described by the passed arguments. + * This includes dispatch code and the termination condition for the recursion. + * @param mg the MethodGen of the original method + * @param method_name the name of the original method + * @param method_signature the signature of the original method + * @param class_name the name of the appropriate class + * @param cpg the ConstantPoolGen of the class + * @param cg the ClassGen of the class + * @param firstLine the first real source line of this method + * @return + */ + Method generateChainingWrapper(MethodGen mg, + String method_name, + String method_signature, + String class_name, + ConstantPoolGen cpg, + ClassGen cg, + int firstLine) + { + Type origReturnType = mg.getReturnType(); + Type[] argumentTypes = mg.getArgumentTypes(); + String[] argumentNames = mg.getArgumentNames(); + Type[] enhancedArgumentTypes = enhanceArgumentTypes(argumentTypes); + String[] enhancedArgumentNames = enhanceArgumentNames(argumentNames); + Type enhancedReturnType = object; // ALWAYS! + + InstructionList il = new InstructionList(); + + // the chaining wrapper has to be 'public' because it will be called by base calls: + int accessFlags = makePublicFlags(mg.getAccessFlags()); + + MethodGen chainMethod = new MethodGen(accessFlags, + enhancedReturnType, + enhancedArgumentTypes, + enhancedArgumentNames, + genChainMethName(method_name), + class_name, + il, cpg); + + // All chaining calls return an Object. + // Need to store this in a local variable to keep the stack + // balanced, because each section (before, replace, after) + // is guarded by its own exception handler, and they don't + // like pending objects on the stack. + InstructionHandle ih; + int result, ot_team; + { + LocalVariableGen lg = + chainMethod.addLocalVariable("_OT$result", enhancedReturnType, //$NON-NLS-1$ + null, null); + result = lg.getIndex(); + ih = il.append(InstructionFactory.createNull(enhancedReturnType)); + if (debugging) + chainMethod.addLineNumber(ih, STEP_OVER_LINENUMBER); + lg.setStart(il.append(InstructionFactory.createStore(enhancedReturnType, result))); + // generated: RType _OT$result = null; + + lg = chainMethod.addLocalVariable("_OT$team", teamType, null, null); //$NON-NLS-1$ + ot_team = lg.getIndex(); + // generated: Team _OT$team; + } + + int indexOffset = chainMethod.isStatic() ? -1 : 0; // argument indizes are decremented for static methods, + // because of the missing 'this' + short invocationKind = getInvocationType(mg); + + il.append(InstructionFactory.createLoad(Type.INT, IDX_ARG + indexOffset)); + il.append(InstructionFactory.createLoad(teamArray, TEAMS_ARG + indexOffset)); + il.append(new ARRAYLENGTH()); + IF_ICMPLT recursionNotYetTerminated = new IF_ICMPLT(null); + il.append(recursionNotYetTerminated); + // generated: if (_OT$teams.length < _OT$idx) { + + + // load arguments: + if (!chainMethod.isStatic()) { + ih = il.append(InstructionFactory.createThis()); + } else { + ih = il.append(new NOP()); + } + if (debugging) + chainMethod.addLineNumber(ih, SHOW_ORIG_CALL ? firstLine : STEP_INTO_LINENUMBER); // show orig call at method header ("dispatching") + + int index = EXTRA_ARGS + 1; + for (int i = 0; i < argumentTypes.length; i++) { + il.append(InstructionFactory.createLoad(argumentTypes[i], index + indexOffset)); + index += argumentTypes[i].getSize(); + } + // + il.append(factory.createInvoke(class_name, genOrigMethName(method_name), + origReturnType, argumentTypes, + invocationKind)); + // generated: this._OT$<method_name>$orig (a1,.., aN) for nonstatic case + // _OT$<method_name>$orig (a1,.., aN) for static case + + if (debugging) + chainMethod.addLineNumber(il.append(new NOP()), STEP_OVER_LINENUMBER); + + adjustValue(il, null, origReturnType, enhancedReturnType); + il.append(InstructionFactory.createReturn(enhancedReturnType)); + // generated: return _OT$result; + + ih = il.append(new NOP()); + recursionNotYetTerminated.setTarget(ih); + // generated: ; (end of the if part) + + il.append(InstructionFactory.createLoad(teamArray, TEAMS_ARG + indexOffset)); + il.append(InstructionFactory.createLoad(Type.INT, IDX_ARG + indexOffset)); + il.append(InstructionFactory.createArrayLoad(teamType)); + il.append(InstructionFactory.createStore(teamType, ot_team)); + // generated: _OT$team = _OT$teams[_OT$idx]; + + // --------------------------------------------- + createDispatchCode(chainMethod, il, + class_name, method_name, + method_signature, result, ot_team, cg, firstLine); + // --------------------------------------------- + + ih = il.append(InstructionFactory.createLoad(enhancedReturnType, result)); + il.append(InstructionFactory.createReturn(enhancedReturnType)); + // generated: return _OT$result; + if (debugging) + chainMethod.addLineNumber(ih, STEP_OVER_LINENUMBER); + + // tidy: + chainMethod.removeNOPs(); + try { // [SH]: overcautious: I once saw this CCE with no clue, why it happened :( + chainMethod.setMaxStack(); + } catch (ClassCastException cce) { + System.err.println(chainMethod); + cce.printStackTrace(); + } + chainMethod.setMaxLocals(); + //chainMethod.removeNOPs(); + Method generated = chainMethod.getMethod(); + il.dispose(); + return generated; + } + + private short getInvocationType(MethodGen chainMethod) { + if (chainMethod.isStatic()) + return Constants.INVOKESTATIC; + if (chainMethod.isPrivate()) + return Constants.INVOKESPECIAL; + else + return Constants.INVOKEVIRTUAL; + } + + /** + * @param i + * @return + */ + private static int makePublicFlags(int flags) { + if ((flags & Constants.ACC_PUBLIC) != 0) { + return flags; + } + if ((flags & Constants.ACC_PRIVATE) != 0) { + flags &= ~Constants.ACC_PRIVATE; + } else if ((flags & Constants.ACC_PROTECTED) != 0) { + flags &= ~Constants.ACC_PROTECTED; + } + flags |= Constants.ACC_PUBLIC; + return flags; + } + + /** + * Generate the dispatch code by which a chaining wrapper invokes the + * callin method(s). + * This consists of three switch blocks: before, replace, after. + * @param chainMethod the chaining wrapper method + * @param il the InstructionList of the chaining wrapper + * @param class_name the name of the appropriate class + * @param method_name the name of the original method + * @param method_signature the signature of the original method + * @param result the index of the '_OT$result' variable + * @param ot_team the index of the variable containing the team currently processed by the chaining wrapper + * @param cg the ClassGen of the appropriate class + * @param firstLine the first real source line of this method + */ + void createDispatchCode(MethodGen chainMethod, InstructionList il, + String class_name, String method_name, + String method_signature, int result, int ot_team, ClassGen cg, int firstLine) + { + + // get bindings for actually modified methods and sort them by callin-modifier: + // modifier -> ArrayList<MethodBinding> + HashMap<String, ArrayList<MethodBinding>> sortedMethodBindings = new HashMap<String, ArrayList<MethodBinding>>(); + Collection<MethodBinding> callinsForMethod; + callinsForMethod = CallinBindingManager.getBindingForBaseMethod(class_name, + method_name, method_signature); + //System.err.println(class_name +" : "+callinsForMethod); + List<MethodBinding> inheritedMethodBindings = CallinBindingManager.getInheritedBaseMethodBindings(class_name, method_name, method_signature); + //System.err.println(inheritedMethodBindings); + + //-------------------------------------------------------------------------------------------- + //JU: initialize callinsForMethod for overridden static base methods (begin) +// if(chainMethod.isStatic()){ +// if(callinsForMethod == null) { +// callinsForMethod = new LinkedList(); +// } +// +// Collection classesDefBindingsToStaticMethods = CallinBindingManager.getInheritedCallinBindingsForStaticMethods(class_name, method_name, method_signature); +// Iterator classesIt = classesDefBindingsToStaticMethods.iterator(); +// while(classesIt.hasNext()){ +// String superClassName = (String) classesIt.next(); +// Collection callinsFromSuperClasses = CallinBindingManager.getBindingForBaseMethod(superClassName, method_name, method_signature); +// callinsForMethod.addAll(callinsFromSuperClasses); +// } +// } + //JU: (end) + //---------------------------------------------------------------------------------------------- + if (!chainMethod.isStatic()) + callinsForMethod.addAll(inheritedMethodBindings); + + /* + String[] interfaceNames = cg.getInterfaceNames(); + Collection interfaceInheritedMethodBindings = new LinkedList(); + for (int i=0; i<interfaceNames.length;i++) { + if (!interfaceNames[i].equals(class_name)) + interfaceInheritedMethodBindings.addAll( + CallinBindingManager.getInterfaceInheritedMethodBindings(method_name, + method_signature, + interfaceNames[i])); + } + + if (callinsForMethod == null) + callinsForMethod = new LinkedList(); + + callinsForMethod.addAll(interfaceInheritedMethodBindings); + */ + ListValueHashMap<MethodBinding> beforeBindings = new ListValueHashMap<MethodBinding>(); + ListValueHashMap<MethodBinding> replaceBindings = new ListValueHashMap<MethodBinding>(); + ListValueHashMap<MethodBinding> afterBindings = new ListValueHashMap<MethodBinding>(); + + Iterator<MethodBinding> it = callinsForMethod.iterator(); + while (it.hasNext()) { + MethodBinding methodBinding = it.next(); + //sourceMapGen.addSourceMapInfo(methodBinding); + String modifier = methodBinding.getModifier(); + ArrayList<MethodBinding> bindings = sortedMethodBindings.get(modifier); + if (bindings == null) { + bindings = new ArrayList<MethodBinding>(); + sortedMethodBindings.put(modifier, bindings); + } + bindings.add(methodBinding); + // ----> added for predecedence purpose: + String teamName = methodBinding.getTeamClassName(); + if (modifier.equals("before")) { //$NON-NLS-1$ + beforeBindings.put(teamName, methodBinding); + } else if (modifier.equals("replace")) { //$NON-NLS-1$ + replaceBindings.put(teamName, methodBinding); + } else if (modifier.equals("after")) { //$NON-NLS-1$ + afterBindings.put(teamName, methodBinding); + } + // <--- + } + + // if any team has multiple bindings, we need to insert additional checks to avoid duplicate + // invocation of before/after causes during recursion. + boolean useBindingIdx = false; + for (LinkedList<MethodBinding> perTeamMethods : replaceBindings.valueSet()) + if (perTeamMethods.size() > 1) { + useBindingIdx = true; + break; + } + + /****************************************************************************/ + // before callin : + if (sortedMethodBindings.containsKey("before")) { //$NON-NLS-1$ + if(logging) printLogMessage("before bindings will be applied..."); //$NON-NLS-1$ + il.append(createSwitch( + beforeBindings, + chainMethod, ot_team, + NORESULT, firstLine, cg.getMajor(), useBindingIdx)); + if(logging) printLogMessage("before bindings: " //$NON-NLS-1$ + + sortedMethodBindings.get("before")); //$NON-NLS-1$ + } + /****************************************************************************/ + // replacement callin or direct recursion : + if (sortedMethodBindings.containsKey("replace")) { //$NON-NLS-1$ + if(logging) printLogMessage("recursive call and replace bindings will be applied..."); //$NON-NLS-1$ + il.append(createSwitch( + replaceBindings, + chainMethod, ot_team, + result, firstLine, cg.getMajor(), useBindingIdx)); + if(logging) printLogMessage("replace bindings: " //$NON-NLS-1$ + + sortedMethodBindings.get("replace")); //$NON-NLS-1$ + } else { + if(logging) printLogMessage("recursive chain-method call will be done..."); //$NON-NLS-1$ + // recursive call: + + createRecursiveCall(il, chainMethod, result, 1, 0, method_name, method_signature, firstLine); + } + /****************************************************************************/ + // after callin : + if (sortedMethodBindings.containsKey("after")) { //$NON-NLS-1$ + if(logging) printLogMessage("after bindings will be applied..."); //$NON-NLS-1$ + il.append(createSwitch( + afterBindings, + chainMethod, ot_team, + /*NORESULT*/result, firstLine, cg.getMajor(), useBindingIdx)); + if(logging) printLogMessage("after bindings: " //$NON-NLS-1$ + + sortedMethodBindings.get("after")); //$NON-NLS-1$ + } + } + + /** + * Create a switch statement which contains one case for each MethodBinding + * in a given list. + * The switch block is furthermore wrapped in a try-catch block. + * Herein all {@link org.objectteams.LiftingVetoException LiftingVetoException} + * are caught, and possibly reported (if Dot.log.lift ist set). + * @param methodBindings hash map of team names to 'MethodBinding' lists + * @param mg method being generated. + * @param ot_team index of local variable <tt>_OT$team</tt> + * @param ot_result index of local variable <tt>_OT$result</tt> + */ + private InstructionList createSwitch(ListValueHashMap<MethodBinding> methodBindings, MethodGen mg, + int ot_team, int ot_result, + int firstLine, + int major, + boolean useBindingIdx) + { + InstructionList il = new InstructionList(); + + boolean handlesReplacement = false; + + int indexOffset = mg.isStatic()?-1:0; // argument indizes are decremented for static methods, + // because of the missing 'this' + + // load value to be switched: + il.append(InstructionFactory.createLoad(intArray, TEAMIDS_ARG+indexOffset)); + il.append(InstructionFactory.createLoad(Type.INT, IDX_ARG+indexOffset)); + InstructionHandle switchStart = il.append(InstructionFactory.createArrayLoad(Type.INT)); + // generated: _OT$teamIDs[_OT$idx] + + int numberOfCases = methodBindings.size(); + + // one break for each case clause + GOTO[] breaks = new GOTO[numberOfCases]; + for (int i = 0; i < numberOfCases; i++) + breaks[i] = new GOTO(null); + + int[] matches = new int[numberOfCases]; + InstructionHandle[] targets = new InstructionHandle[numberOfCases]; + + int caseCounter = 0; + + List<MethodBinding> methodBindingsForTeam = null; + MethodBinding mb = null; + Iterator <Entry<String, LinkedList<MethodBinding>>> teamIterator = methodBindings.entrySet().iterator(); + while (teamIterator.hasNext()) { + Entry<String, LinkedList<MethodBinding>> entry = teamIterator.next(); + String teamName = entry.getKey(); + methodBindingsForTeam = entry.getValue(); + //System.out.println(methodBindings.get(teamName)); + + /*MethodBinding*/ mb = methodBindingsForTeam.get(0); + + matches[caseCounter] = TeamIdDispenser.getTeamId(teamName); + InstructionHandle nextBranch = il.append(new NOP()); + + // generate (_OT$teamID == teamId) branch here: + + // ========== create Cases: ========== + if (mb.isReplace()) { // distinct treatment of replacement: + handlesReplacement = true; + createReplaceCase(mg, il, + teamName, methodBindingsForTeam, + ot_result, ot_team, major, firstLine); + } else { // before or after callin: + BranchInstruction ifBindingIdx = null; + if (useBindingIdx) { + // only if bindingIdx == 0 + il.append(InstructionFactory.createLoad(Type.INT, BIND_IDX_ARG+indexOffset)); + ifBindingIdx= new IFNE(null); + il.append(ifBindingIdx); + } + createBeforeAfterCase(mg, il, teamName, + methodBindingsForTeam, ot_result, ot_team, major, firstLine); + if (useBindingIdx) { + ifBindingIdx.setTarget(il.append(new NOP())); + } + } + + // =================================== + + targets[caseCounter] = nextBranch; + /*InstructionHandle break_instr =*/ il.append(breaks[caseCounter]); + // generated: break; + + caseCounter++; + } // end of while + + // generate default branch here: + InstructionHandle defaultBranch = il.append(new NOP()); + if (handlesReplacement) + createRecursiveCall(il, mg, ot_result, 1, 0, mb.getBaseMethodName(), mb.getBaseMethodSignature(), firstLine); + + InstructionHandle afterSwitch = il.append(new NOP()); // all breaks point here. + + // ===== assemble the switch ==== + il.append(switchStart, + createLookupSwitch(matches, targets, breaks, + defaultBranch, afterSwitch) + ); + // ============================== + + // wrap everything in a try {} catch (LiftingVetoException e) {..} + InstructionHandle endTry = il.getEnd(); + + GOTO skipHdlr = null; + skipHdlr = new GOTO(null); + il.append(skipHdlr); + // generated: goto normal exit + + InstructionHandle hdlr = il.append(new NOP()); + il.append(DebugUtil.createReportExc(factory)); + + // catch: proceed with recursion if caught in a replace call + if (handlesReplacement) + createRecursiveCall(il, mg, ot_result, 1, 0, mb.getBaseMethodName(), mb.getBaseMethodSignature(), firstLine); + + mg.addExceptionHandler(il.getStart(), endTry, hdlr, liftingVeto); + + InstructionHandle nop = il.append(new NOP()); + skipHdlr.setTarget(nop); + + return il; + } + + /** Create the recursice call of this chaining method. + * @param il insert the call into this list. + * @param mg this chaining method + * @param ot_result stack index of the _OT$result variable or NORESULT. + * @param idx_offset TODO + * @param bindIdx_offset TODO + * @param methodName TODO + * @param methodSignature TODO + */ + void createRecursiveCall (InstructionList il, + MethodGen mg, + int ot_result, int idx_offset, int bindIdx_offset, String methodName, String methodSignature, int firstLine) + { + InstructionHandle ih = !mg.isStatic() + ? il.append(InstructionFactory.createThis()) + : il.append(new NOP()); + if (debugging) + mg.addLineNumber(ih, SHOW_RECURSIVE_CALL ? firstLine : STEP_INTO_LINENUMBER); // show recursive call at method header ("dispatching") + + Type[] argTypes = mg.getArgumentTypes(); + Type returnType = mg.getReturnType(); + short invocationKind = getInvocationType(mg); + + // arguments: no adjustment except idx++. + int index = 1; + int indexOffset = mg.isStatic()?-1:0; // argument indizes are decremented for static methods, + // because of the missing 'this' + for (int i=0; i<argTypes.length; i++) { + il.append(InstructionFactory.createLoad(argTypes[i], index+indexOffset)); + if (index == OTConstants.IDX_ARG && idx_offset != 0) { // _OT$idx has to be incremented (adding idx_offset) + il.append(new ICONST(idx_offset)); + il.append(new IADD()); + } else if (index == OTConstants.BIND_IDX_ARG) { // _OT$bindIdx has to be incremented (adding bindIdx_offset) + if (bindIdx_offset == 0) { // bindArg argument has to be set to '0' + il.append(new POP()); // remove loaded _OT$bindIdx + il.append(new ICONST(0)); // replace it by '0' + } else { + il.append(new ICONST(bindIdx_offset)); + il.append(new IADD()); + } + } + index += argTypes[i].getSize(); + } + il.append(factory.createInvoke(mg.getClassName(), mg.getName(), + returnType, argTypes, + invocationKind)); + + il.append(InstructionFactory.createStore(returnType, ot_result)); + + // _OT$result = _OT$<method_name>$chain(_OT$teams, _OT$teamIDs, _OT$idx+1, + // a1, .., aN); + if (debugging) + mg.addLineNumber(il.append(new NOP()), STEP_OVER_LINENUMBER); + } + + + /** + * Create a block for a before or after callin as a case with a surrounding switch. + * @param mg TODO + * @param il InstructionList being assembled. + * @param connectorClassName name of a Team which has a callin to this method. + * @param mbList MethodBindings describing the callins. + * @param ot_result index of local variable <tt>_OT$result</tt>. + * @param ot_team index of local variable <tt>_OT$team</tt>.l + * @param major + * @param firstLine the first real source line of this method + */ + void createBeforeAfterCase(MethodGen mg, + InstructionList il, String connectorClassName, + List<MethodBinding> mbList, int ot_result, int ot_team, + int major, int firstLine) + { + + MethodBinding mb = mbList.get(0); + ConstantPoolGen cpg = mg.getConstantPool(); + + mbList = CallinBindingManager.sortMethodBindings(mbList, connectorClassName); + + Iterator<MethodBinding> it = mbList.iterator(); + while (it.hasNext()) { + /*MethodBinding nextMethodBinding*/mb = it.next(); + + String baseMethodSignature = mb.getBaseMethodSignature(); + Type[] baseArgTypes = Type.getArgumentTypes(baseMethodSignature); + Type baseReturnType = Type.getReturnType(baseMethodSignature); + + Type[] wrapperArgTypes = Type.getArgumentTypes(mb.getWrapperSignature()); + int argsLen = wrapperArgTypes.length - 1; // don't count base arg. + + il.append(InstructionFactory.createLoad(teamType, ot_team)); + + int packedArgPos = 0; + InstructionHandle argArray = null; + if (useReflection) { + il.append(factory.createInvoke("java.lang.Object", "getClass", classType, new Type[0], Constants.INVOKEVIRTUAL)); + il.append(new LDC(cpg.addString(mb.getWrapperName()))); + pushTypeArray(il, wrapperArgTypes, major, cpg); + il.append(factory.createInvoke("java.lang.Class", "getMethod", methodType, OTConstants.getMethodSignature, Constants.INVOKEVIRTUAL)); + + il.append(InstructionFactory.createLoad(teamType, ot_team)); + // generated: + // _OT$team.getClass().getMethod(<wrapperName>, <wrapper sign>) + // _OT$team + argArray = il.append(new ANEWARRAY(cpg.addClass(object))); + + } else { + il.append(factory.createCast(teamType, new ObjectType(connectorClassName))); + // generated: (<TeamClass>)_OT$team + } + + // first argument: base object: + packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg); + { + if (!mg.isStatic()) + il.append(InstructionFactory.createThis()); + else + il.append(InstructionFactory.createNull(Type.OBJECT)); // no base object + } + checkPackValue1(il, useReflection, Type.OBJECT); + + // second argument: base result (if appropriate) + if ( mb.isAfter() && !baseReturnType.equals(Type.VOID)) { // after callin wrapper get the base method result as second argument + packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg); + { + il.append(InstructionFactory.createLoad(object, ot_result)); + adjustValue(il, null, object, baseReturnType); + argsLen --; // the second (result) arg is also not part of the arg list + } + checkPackValue1(il, useReflection, baseReturnType); + } + + // ------- load regular args: -------- + + // stack positions for load instructions (one-based for non-statics, since 0 == this) + int stackIndex = EXTRA_ARGS + (mg.isStatic() ? 0 : 1); + // where within baseArgTypes do source arguments start? + int firstArg = 0; + + if (mb.baseMethodIsCallin()) { + // skip enhancement of this callin method (lower role). + firstArg += EXTRA_ARGS; + stackIndex += EXTRA_ARGS; + argsLen += EXTRA_ARGS; + } + for (int i = firstArg; i < argsLen; i++) { + if(logging) printLogMessage("loading " + baseArgTypes[i].toString()); //$NON-NLS-1$ + + packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg); + { + il.append(InstructionFactory.createLoad(baseArgTypes[i],stackIndex)); + } + checkPackValue1(il, useReflection, baseArgTypes[i]); + + stackIndex += baseArgTypes[i].getSize(); + } + + InstructionHandle callinCall; + if (useReflection) { + // this information was missing above: + il.insert(argArray, createIntegerPush(cpg, packedArgPos)); + + callinCall = il.append(factory.createInvoke("java.lang.reflect.Method", "invoke", object, new Type[]{object, objectArray}, Constants.INVOKEVIRTUAL)); + il.append(new POP()); // before/after wrappers return void + } else { + callinCall = il.append(factory.createInvoke(connectorClassName, + mb.getWrapperName(), + Type.VOID, wrapperArgTypes, + Constants.INVOKEVIRTUAL)); + } + if (debugging) { + mg.addLineNumber(callinCall, SHOW_ROLE_CALL ? firstLine : STEP_INTO_LINENUMBER); // show role call at method header ("dispatching") + mg.addLineNumber(il.append(new NOP()), STEP_OVER_LINENUMBER); + } + } + } + + /* this and the next method serve as a bracket for all pushes that may or may not require + * packing in an array, the array must already exist on the stack. */ + private int checkPackValue0(InstructionList il, boolean doPack, int argCount, ConstantPoolGen cpg) { + if (doPack) { + il.append(new DUP()); + il.append(createIntegerPush(cpg, argCount)); + return argCount+1; + } else { + return argCount; + } + } + private void checkPackValue1(InstructionList il, boolean doPack, Type argType) { + if (doPack) { + if (argType instanceof BasicType) + il.append(createBoxing((BasicType)argType)); + il.append(new AASTORE()); + } + } + /* Push an array of Class representing the signature given by argTypes. */ + private void pushTypeArray(InstructionList il, Type[] argTypes, int major, ConstantPoolGen cpg) { + il.append(createIntegerPush(cpg, argTypes.length)); + il.append(new ANEWARRAY(cpg.addClass(classType))); + for (int i=0; i<argTypes.length; i++) { + Type type = argTypes[i]; + il.append(new DUP()); + il.append(createIntegerPush(cpg, i)); + if (type instanceof BasicType) { + il.append(factory.createFieldAccess(toObjectTypeName((BasicType)type), "TYPE", classType, Constants.GETSTATIC)); + } else if (type instanceof ObjectType) { + appendClassLiteral(il, ((ObjectType)type).getClassName(), major, cpg); + } else if (type instanceof ArrayType) { + String prefix = ""; + while (type instanceof ArrayType) { + prefix += '['; + type = ((ArrayType)type).getElementType(); + } + String elemTypeName = null; + if (type instanceof ObjectType) + elemTypeName = "L"+((ObjectType)type).getClassName()+';'; + else if (type instanceof BasicType) + elemTypeName = ((BasicType)type).getSignature(); + appendClassLiteral(il, prefix+elemTypeName, major, cpg); + } else { + throw new OTREInternalError("unsupported type in signature "+type); + } + il.append(new AASTORE()); + } + } + + /** + * Create a block for a replace callin as a case within a surrounding switch. If there are multiple bindings from the + * same team to this base method, the bindings are sorted according to the precedence list of this team. + * @param mg base method being generated. + * @param il instruction list being assembled. + * @param connectorClassName name of a Team which has a callin to this method. + * @param mbList list of 'MethodBinding's + * @param ot_result index of a local variable <tt>_OT$result</tt>. + * @param ot_team index of local variable <tt>_OT$team</tt>. + * @param major class file version + */ + void createReplaceCase(MethodGen mg, InstructionList il, + String connectorClassName, List<MethodBinding> mbList, + int ot_result, int ot_team, int major, int firstLine) + { + + int indexOffset = mg.isStatic() ? -1 : 0; // argument indizes are decremented for static methods, + // because of the missing 'this' + boolean multipleBindings = mbList.size() > 1; + mbList = CallinBindingManager.sortMethodBindings(mbList, connectorClassName); + + MethodBinding mb = mbList.get(0); // default, if only one binding exists + + LocalVariableGen unused_args_lg = mg.addLocalVariable(UNUSED, objectArray, null, null); + int unused_args = unused_args_lg.getIndex(); + unused_args_lg.setStart(il.append(new NOP())); + if (multipleBindings) { + InstructionList addition = new InstructionList(); + // load value to be switched: + + InstructionHandle switchStart = addition.append(InstructionFactory.createLoad(Type.INT, BIND_IDX_ARG+indexOffset)); + // loaded _OT$bindIdx + + int numberOfCases = mbList.size(); + + // one break for each case clause + GOTO[] breaks = new GOTO[numberOfCases]; + for (int i=0; i<numberOfCases; i++) + breaks[i] = new GOTO(null); + + int[] matches = new int[numberOfCases]; + InstructionHandle[] targets = new InstructionHandle[numberOfCases]; + + int caseCounter = 0; + Iterator<MethodBinding> mbIterator = mbList.iterator(); + while (mbIterator.hasNext()) { + mb = mbIterator.next(); + matches[caseCounter] = caseCounter; + InstructionHandle nextBranch = addition.append(new NOP()); + // ========== create Cases: =========== + addition.append(createSingleReplaceCallin(mg, connectorClassName, mb, ot_result, ot_team, unused_args, multipleBindings, mg.isStatic(), major, firstLine)); + // ============================== + targets[caseCounter] = nextBranch; + /*InstructionHandle break_instr =*/ addition.append(breaks[caseCounter]); + // generated: break; + caseCounter++; + } + // ========== create default: =========== + InstructionHandle defaultBranch = addition.append(new NOP()); + createRecursiveCall(addition, mg, ot_result, 1, 0, mb.getBaseMethodName(), mb.getBaseMethodSignature(), firstLine); + // ============================== + + InstructionHandle afterSwitch = addition.append(new NOP()); // all breaks point here. + + for (int i=0; i<numberOfCases; i++) + breaks[i].setTarget(afterSwitch); + + addition.append(switchStart, new TABLESWITCH(matches, targets, defaultBranch)); + // wrap everything in a try {} catch (LiftingVetoException e) {..} + InstructionHandle endTry = addition.getEnd(); + + GOTO skipHdlr = null; + skipHdlr = new GOTO(null); + addition.append(skipHdlr); + // generated: goto normal exit + + InstructionHandle hdlr = addition.append(new NOP()); + addition.append(DebugUtil.createReportExc(factory)); + + // ========== create catch instructions: =========== + createRecursiveCall(addition, mg, ot_result, 0, 1, mb.getBaseMethodName(), mb.getBaseMethodSignature(), firstLine); + // ===================================== + mg.addExceptionHandler(addition.getStart(), endTry, hdlr, liftingVeto); + + InstructionHandle nop = addition.append(new NOP()); + skipHdlr.setTarget(nop); + il.append(addition); + } else { // only a single replace callin: + il.append(createSingleReplaceCallin(mg, connectorClassName, mb, ot_result, ot_team, unused_args, multipleBindings, mg.isStatic(), major, firstLine)); + } + unused_args_lg.setEnd(il.getEnd()); + } + + /** + * Creates a single replace callin call. + * + * @param mg base method being generated. + * @param connectorClassName name of a Team which has a callin to this method. + * @param mb the 'MethodBinding' for this callin. + * @param ot_result index of a local variable <tt>_OT$result</tt>. + * @param ot_team index of local variable <tt>_OT$team</tt>. + * @param unused_args index of local variable <tt>_OT$unused_args</tt>. + * @param multipleBindings flag indicating if there are multiple bindings in this case + * @param staticBaseMethod TODO + * @param major class file version + * @param firstLine first real source line number of this method + * @return instruction list for the callin call + */ + private InstructionList createSingleReplaceCallin(MethodGen mg, String connectorClassName, MethodBinding mb, int ot_result, int ot_team, int unused_args, boolean multipleBindings, boolean staticBaseMethod, int major, int firstLine) + { + // Sequence of values to load is (letters refer to document parameter-passing.odg): + // (f) _OT$team: call target for invoking the callin-wrapper + // (g) baseObject: this or null + // (h) enhancement: [Team[IIII[Object; + // idxs are being manipulated here, + // unusedArgs[] is allocated and filled with + // (i) - (int,Team) if current method is static role method. + // (j) - enhancement arguments, if current method is callin method + // (k) regular arguments. + + // ------------------------------------------ + // prepare Types: + // ------------------------------------------ + Type[] chainArgTypes = mg.getArgumentTypes(); + Type[] baseArgTypes = Type.getArgumentTypes(mb.getBaseMethodSignature()); + Type[] roleArgTypes = Type.getArgumentTypes(mb.getRoleMethodSignature()); + roleArgTypes = enhanceArgumentTypes(roleArgTypes); + String wrapperName = mb.getWrapperName(); + Type wrapperReturnType = Type.getReturnType(mb.getWrapperSignature()); + + Type[] wrapperArgTypes = Type.getArgumentTypes(mb.getWrapperSignature()); + + // calculate return type: + Type chainReturnType = mg.getReturnType(); + + ConstantPoolGen cpg = mg.getConstantPool(); + InstructionList il = new InstructionList(); + // (f): + il.append(InstructionFactory.createLoad(teamType, ot_team)); + + int packedArgPos = 0; + InstructionHandle argArray = null; + if (useReflection) { + il.append(factory.createInvoke("java.lang.Object", "getClass", classType, new Type[0], Constants.INVOKEVIRTUAL)); + il.append(new LDC(cpg.addString(mb.getWrapperName()))); + pushTypeArray(il, wrapperArgTypes, major, cpg); + il.append(factory.createInvoke("java.lang.Class", "getMethod", methodType, OTConstants.getMethodSignature, Constants.INVOKEVIRTUAL)); + + il.append(InstructionFactory.createLoad(teamType, ot_team)); + // generated: + // _OT$team.getClass().getMethod(<wrapperName>, <wrapper sign>) + // _OT$team + argArray = il.append(new ANEWARRAY(cpg.addClass(object))); + } else { + il.append(factory.createCast(teamType, new ObjectType(connectorClassName))); + // generated: (<TeamClass>)_OT$team + } + + // (g): + packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg); + { + if(!staticBaseMethod) + il.append(InstructionFactory.createThis()); + else + il.append(InstructionFactory.createNull(Type.OBJECT)); // no base object + } + checkPackValue1(il, useReflection, Type.OBJECT); + + // ---------------------------------------- + // (h) Load Extra Arguments: + // ---------------------------------------- + int staticOffset = staticBaseMethod?-1:0; // argument indizes are decremented for static methods, + // because of the missing 'this' + // first 4 extra arguments: _OT$teams, _OT$teamIDs, _OT$idx, _OT$bindIdx + for (int i=0; i<4; i++) { // If this is called only once _OT$idx has to be incremented, else _OT$bindIdx has to be incremented + packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg); + { + il.append(InstructionFactory.createLoad(chainArgTypes[i], i+1+staticOffset)); + if (!multipleBindings && i+1+staticOffset == OTConstants.IDX_ARG+staticOffset) {// _OT$idx++: + il.append(new ICONST(1)); + il.append(new IADD()); + } else if (multipleBindings && i+1+staticOffset == OTConstants.BIND_IDX_ARG+staticOffset) {// _OT$bindIdx: + il.append(new ICONST(1)); + il.append(new IADD()); + } + } + checkPackValue1(il, useReflection, chainArgTypes[i]); + } + // _OT$baseMethTag: + int base_meth_tag = CallinBindingManager.getBaseCallTag(mb.getBaseClassName(), + mb.getBaseMethodName(), + mb.getBaseMethodSignature()); +// //JU: added this if-statement (begin) ---------------------------------------------- +// if(staticBaseMethod && !mg.getClassName().equals(mb.getBaseClassName())){ +// //the method binding is a dummy -> the callin wrapper is performed +// //with an invalid base method tag value -> an exception will be thrown +// base_meth_tag = INVALID_BASE_METHOD_TAG; +// } +// //JU (end) -------------------------------------------------------------------------- + + packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg); + { + il.append(createIntegerPush(cpg, base_meth_tag)); + } + checkPackValue1(il, useReflection, Type.INT); + + // collect regular args first, insert into il later: + InstructionList regularArgs = new InstructionList(); + + // put "new Object[]" on stack, later containing unused arguments: + packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg); + { + il.append(createIntegerPush(cpg, baseArgTypes.length)); + // enough space to hold ALL base arguments + // Note that the callin wrapper may add more elements to this array. + il.append((Instruction)factory.createNewArray(object, (short)1)); + il.append(InstructionFactory.createStore(objectArray, unused_args)); + // generated: _OT$unusedArgs = new Object[<n_base_args>]; + + // ---------------------------------------- + // handle more arguments: load regular ones, store unused arguments into the array: + // ---------------------------------------- + int unusedArgsIdx = 0; // index into Object[]. + + int stackIdx = EXTRA_ARGS + 1 + staticOffset; // one-based, since 0 == this (unless static) + int regularArgsStart = EXTRA_ARGS; + boolean baseIsStaticCallin = mb.baseMethodIsCallin() && staticBaseMethod; + if (baseIsStaticCallin) { + // (i) need these synthetic arguments up-front: "int dummy, Team enclosingTeam" + storeUnusedArg(il, unused_args, 0, // constant + new ICONST(0), + Type.INT, + cpg); + storeUnusedArg(il, unused_args, 1, // constant + new ALOAD(regularArgsStart+1), + OTConstants.teamType, + cpg); + + // consumed first two parameters into unusedArgs: + regularArgsStart+=2; + stackIdx += 2; + unusedArgsIdx += 2; + } + for (int i=regularArgsStart; i<chainArgTypes.length; i++) { + Type argType = chainArgTypes[i]; + Instruction loadingInstruction = InstructionFactory.createLoad(argType, stackIdx); + if (isRegularArg(i, mb.baseMethodIsCallin(), staticBaseMethod)) { + // (k) collect loading instruction, for appending after _OT$unusedArgs. + packedArgPos = checkPackValue0(regularArgs, useReflection, packedArgPos, cpg); + { + regularArgs.append(loadingInstruction); + } + checkPackValue0(regularArgs, useReflection, packedArgPos, cpg); + } else { + // (j) store unused arg + storeUnusedArg(il, unused_args, unusedArgsIdx++, loadingInstruction, argType, cpg); + // generated: _OT$unusedArgs[<unusedArgsIdx>] = maybeBox(a<index>); + } + stackIdx += argType.getSize(); + } + il.append(InstructionFactory.createLoad(objectArray, unused_args)); + } + checkPackValue1(il, useReflection, objectArray); + + // (k) insert previously assembled load-sequence: + il.append(regularArgs); + + // ============= INVOKEVIRTUAL (wrapper) ============= + InstructionHandle callinCall; + if (useReflection) { + // this information was missing above: + il.insert(argArray, createIntegerPush(cpg, packedArgPos)); + + callinCall = il.append(factory.createInvoke("java.lang.reflect.Method", "invoke", object, new Type[]{object, objectArray}, Constants.INVOKEVIRTUAL)); + wrapperReturnType = Type.OBJECT; + } else { + callinCall = il.append(factory.createInvoke(connectorClassName, wrapperName, + wrapperReturnType, + wrapperArgTypes, + Constants.INVOKEVIRTUAL)); + } + if (debugging) { + mg.addLineNumber(callinCall, SHOW_ROLE_CALL ? firstLine : STEP_INTO_LINENUMBER); // show role call at method header ("dispatching") + mg.addLineNumber(il.append(new NOP()), STEP_OVER_LINENUMBER); + } + + adjustValue(il, null, wrapperReturnType, chainReturnType); + il.append(InstructionFactory.createStore(chainReturnType, ot_result)); + + return il; + } + + /** + * Store an unused value (loaded by pushInstruction) into _OT$unusedArgs + */ + private void storeUnusedArg(InstructionList il, + int unused_args, + int arrayIndex, + Instruction pushInstruction, + Type argType, + ConstantPoolGen cpg) + { + il.append(InstructionFactory.createLoad(objectArray, unused_args)); + il.append(createIntegerPush(cpg, arrayIndex)); + il.append(pushInstruction); + if (argType instanceof BasicType) + il.append(createBoxing((BasicType)argType)); + il.append(InstructionFactory.createArrayStore(objectArray)); + } + + /** + * Is the parameter at position idx mapped (by paramPositions or implicitly)? + * Cut off head: + * - (int,Team) if present (static role method) + * - enhancement (possible twice) + * @param idx parameter index of enhanced signature + */ + static boolean isRegularArg (int idx, boolean baseIsCallin, boolean baseIsStatic) { + if (baseIsCallin && baseIsStatic) // FIXME(SH): should be baseIsRole instead of baseIsCallin! + idx -= 2; + int firstVisible = EXTRA_ARGS + (baseIsCallin?EXTRA_ARGS:0); // skip one or two enhancements + return idx >= firstVisible; + } + + /** + * Given an argument of type <tt>actual</tt>, must + * we use type <tt>formal</tt> in signatures, + * because it is a supertype of <tt>actual</tt>? + */ + static Type checkWiden (Type actual, Type formal) { + if (!actual.equals(formal) + && actual instanceof ObjectType + && formal instanceof ObjectType) + { + ObjectType actualObj = (ObjectType)actual; + ObjectType formalObj = (ObjectType)formal; + if (actualObj.subclassOf(formalObj)) + return formalObj; + } + return actual; + } + + /** + * Create an instruction list for initializing the role set field on-demand. + * @param valueRequired should the role set be on the stack after this sequence? + * @param cg the ClassGen of the appropriate class + */ + private InstructionList getInitializedRoleSet(String class_name, boolean valueRequired) {; + InstructionList il = new InstructionList(); + + // try to retrieve existing set: + il.append(new ALOAD(0)); + il.append(factory.createGetField(class_name, OTConstants.ROLE_SET, OTConstants.roleSetType)); + if (valueRequired) + il.append(new DUP()); // a spare value to keep on the stack if successful + + // if (roleSet == null) .. + IFNONNULL branch = new IFNONNULL(null); + il.append(branch); + + // conditionally create the set: + if (valueRequired) + il.append(new POP()); // remove useless "null", replace with DUP_X1 below + il.append(new ALOAD(0)); + il.append(factory.createNew(OTConstants.roleSetType)); + il.append(new DUP()); + il.append(factory.createInvoke(OTConstants.roleSetType.getClassName(), + Constants.CONSTRUCTOR_NAME, + Type.VOID, + Type.NO_ARGS, + Constants.INVOKESPECIAL)); + if (valueRequired) + il.append(new DUP_X1()); // push below pending "this" + + // store in the field: + il.append(factory.createPutField(class_name, OTConstants.ROLE_SET, OTConstants.roleSetType)); + + // endif + branch.setTarget(il.append(new NOP())); + return il; + } + + /** + * Generates the field '_OT$roleSet' which is used to store the added roles. + * @param cpg the ClassGen of the appropriate class + * @param class_name the name of the class + * @return the generated field + */ + private Field generateRoleSet(ConstantPoolGen cpg, String class_name) { + FieldGen fg = new FieldGen(Constants.ACC_PROTECTED, + OTConstants.roleSetType, + OTConstants.ROLE_SET, + cpg); + return fg.getField(); + } + + /** + * Generates the method 'public void _OT$addRole(Object role)' which adds the passed object + * to the role set of this base class. + * @param cpg the ClassGen of the appropriate class + * @param class_name the name of the class + * @return the generated method + */ + private Method generateAddRole(ConstantPoolGen cpg, String class_name) { + + InstructionList il = new InstructionList(); + MethodGen mg = new MethodGen(Constants.ACC_PUBLIC, + Type.VOID, + new Type[] { Type.OBJECT }, + new String[] {"role"}, + OTConstants.ADD_ROLE, class_name, + il, cpg); + + il.append(getInitializedRoleSet(class_name, /*valueRequired*/true)); + + il.append(InstructionFactory.createLoad(Type.OBJECT, 1)); + il.append(factory.createInvoke(OTConstants.roleSetType.getClassName(), + "add", + Type.BOOLEAN, + new Type[] {Type.OBJECT}, + Constants.INVOKEVIRTUAL)); + il.append(new POP()); + il.append(InstructionFactory.createReturn(Type.VOID)); + mg.removeNOPs(); + mg.setMaxStack(); + mg.setMaxLocals(2); + return mg.getMethod(); + } + + /** + * Generates the method 'public void _OT$removeRole(Object role)' which removes the passed object + * from the role set of this base class. + * @param cpg the ClassGen of the appropriate class + * @param class_name the name of the class + * @return the generated method + */ + private Method generateRemoveRole(ConstantPoolGen cpg, String class_name) { + + InstructionList il = new InstructionList(); + MethodGen mg = new MethodGen(Constants.ACC_PUBLIC, + Type.VOID, + new Type[] { Type.OBJECT }, + new String[] {"role"}, + OTConstants.REMOVE_ROLE, class_name, + il, cpg); + il.append(new ALOAD(0)); + il.append(factory.createGetField(class_name, OTConstants.ROLE_SET, OTConstants.roleSetType)); + il.append(InstructionFactory.createLoad(Type.OBJECT, 1)); + il.append(factory.createInvoke(OTConstants.roleSetType.getClassName(), + "remove", + Type.BOOLEAN, + new Type[] {Type.OBJECT}, + Constants.INVOKEVIRTUAL)); + il.append(new POP()); + il.append(InstructionFactory.createReturn(Type.VOID)); + mg.removeNOPs(); + mg.setMaxStack(2); + mg.setMaxLocals(2); + return mg.getMethod(); + } + +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/BaseTagInsertion.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/BaseTagInsertion.java new file mode 100644 index 000000000..ebbf234aa --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/BaseTagInsertion.java @@ -0,0 +1,205 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: BaseTagInsertion.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import de.fub.bytecode.generic.*; +import de.fub.bytecode.*; + + +import java.util.*; + +import org.eclipse.objectteams.otre.util.*; + +/** + * This transformer inserts tag fields for all (topmost) bound base classes. + * Tag fields look like this: + * <pre> + * public short <i>TeamName</i>_OT$Tag; + * </pre> + * where <i>TeamName</i> is any team which has a callin binding for this base class. + * It also inserts initialization of this tag into every constructor. + * + * @version $Id: BaseTagInsertion.java 23408 2010-02-03 18:07:35Z stephan $ + * @author Christine Hundt + * @author Stephan Herrmann + */ +public class BaseTagInsertion +{ + private static boolean logging = false; + final static String tagSuffix = "_OT$Tag"; + + static String getTagFieldName (String teamName) { + return teamName.replace('.', '$') + tagSuffix; + } + + static { + if (System.getProperty("ot.log") != null) + logging = true; + } + + public static class SharedState extends ObjectTeamsTransformation.SharedState { + // Record base tags of interfaces here (String->HashMap). + // Need to be considered in all implementing classes. + // baseName -> HashMap (teamName -> tag (Integer)): + private HashMap<String, HashMap<String, Integer>> baseTagsOfInterfaces = new HashMap<String, HashMap<String, Integer>>(); + } + + SharedState state; + + public BaseTagInsertion(SharedState state) { + this.state = state; + } + + /** + * @param ce + * @param cg + */ + public void doTransformInterface(ClassEnhancer ce, ClassGen cg) { + + String class_name = cg.getClassName(); + ConstantPoolGen cpg = cg.getConstantPool(); +/* + if (interfaceTransformedClasses.contains(class_name)) + continue; // already transformed! +*/ + + HashMap<String, Integer> baseTags = getAllBaseTags(cg); + + if (baseTags == null) return; // not a bound base + + if (cg.isInterface()) + state.baseTagsOfInterfaces.put(class_name, baseTags); + + state.interfaceTransformedClasses.add(class_name); + if (logging) + printLogMessage("Found bound base: " + class_name); + + Iterator<String> teams = baseTags.keySet().iterator(); + while (teams.hasNext()) { + String teamName = teams.next(); + + String fieldName = getTagFieldName(teamName);//team + tagSuffix; + + if (cg.containsMethod("get"+fieldName, "()S") == null) + { + if(logging) + printLogMessage("create base tag access get" + fieldName); + Integer tag = baseTags.get(teamName); + MethodGen mg = genBaseTagGetter(cpg, class_name, fieldName, tag.intValue(), cg.isInterface()); + ce.addMethod(mg.getMethod(), cg); + } + } + // addedBaseTags.addAll(baseTags); + } + + /** + * Collect base tags from this class and its super interfaces. + * @param cg + * @return + */ + private HashMap<String, Integer> getAllBaseTags(ClassGen cg) { + // accumulate here base tags from bound super interfaces and this class: + HashMap<String, Integer> baseTags = null; + + // TODO (SH): consider all transformed super interfaces. + String superIfc = getTransformedSuperIfc(cg); + if (superIfc != null) + baseTags = state.baseTagsOfInterfaces.get(superIfc); + + String class_name = cg.getClassName(); + HashMap<String, Integer> classBaseTags = CallinBindingManager.getBaseTags(class_name); + if (CallinBindingManager.isRole(class_name)) { + // search for base tags inherited from the implicit super (role) class: + HashMap<String, Integer> inheritedBaseTags = CallinBindingManager.getInheritedBaseTags(class_name); + classBaseTags.putAll(inheritedBaseTags); + // search for base tags of the implementing role class: + String implementingRoleName = ObjectTeamsTransformation.genImplementingRoleName(class_name); + HashMap<String, Integer> implementingRolebaseTags = CallinBindingManager.getBaseTags(implementingRoleName); + classBaseTags.putAll(implementingRolebaseTags); + // TODO: check, if the we also need the implicitly inherited tags of the implementing role class! + } + + if (baseTags == null) + baseTags = classBaseTags; + else + baseTags.putAll(classBaseTags); + + return baseTags; + } + + /** + * Search the interfaces implemented by the class `cg' for an interface + * that is a bound base. + * TODO (SH): should return all such interfaces! + * @param cg + * @return + */ + private String getTransformedSuperIfc(ClassGen cg) { + String[] ifcs = cg.getInterfaceNames(); + for (int i=0; i<ifcs.length; i++){ + if (state.interfaceTransformedClasses.contains(ifcs[i])) + return ifcs[i]; + } + return null; + } + + /** + * Generate a getter method for the base tag field. + * @param cpg + * @param class_name this (base-) class shall carry the new method + * @param fieldName name of the tag field. + * @param abstractFlg should the method be generated as abstract (ie., without a body)? + * @return + */ + static MethodGen genBaseTagGetter( + ConstantPoolGen cpg, String class_name, + String fieldName, int tagValue, boolean abstractFlg) + { + int accessFlags = Constants.ACC_PUBLIC; + if (abstractFlg) + accessFlags |= Constants.ACC_ABSTRACT; + + InstructionList il = new InstructionList(); + MethodGen mg = new MethodGen( + accessFlags, + Type.SHORT, Type.NO_ARGS, new String[]{}, + "get"+fieldName, class_name, + il, cpg); + if (!abstractFlg) { + // gen: "return <constant tagValue>" + il.append(new PUSH(cpg, tagValue)); + il.append(InstructionFactory.createReturn(Type.INT)); + } + mg.setMaxStack(); + mg.setMaxLocals(); + return mg; + } + + /** + * Insert tag initializations into each constructor. + */ + public void doTransformCode(ClassGen cg) { + // FIXME(SH): do not declare as CodeTransformer. + } + + /** + * @param message + */ + private static void printLogMessage(String message) { + System.out.println(message); + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/ClassEnhancer.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/ClassEnhancer.java new file mode 100644 index 000000000..aa80c98ab --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/ClassEnhancer.java @@ -0,0 +1,79 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2005-2009 Berlin Institute of Technology, 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: ClassEnhancer.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import de.fub.bytecode.classfile.Field; +import de.fub.bytecode.classfile.Method; +import de.fub.bytecode.generic.ClassGen; +import de.fub.bytecode.generic.ConstantPoolGen; + + + +/** + * This interface is used to abstract from the BCEL external transformations. + * + * @author Christine Hundt + * @author Juergen Widiker + * @author Stephan Herrmann + */ +public interface ClassEnhancer { + + /** + * Adds the interface 'interfaceName' to the implements clause of class 'cg'. + */ + public void addImplements(String interfaceName, ClassGen cg); + + /** + * Adds the method 'm' to the class represented by 'cg'. + * @param m the method to be added + * @param cg the ClassGen of the appropriate class + */ + void addMethod(Method m, ClassGen cg); + + /** + * Adds method 'm' to the class 'cg' or, if a method with the + * same name and signature already exists, replace that method. + * @param method + * @param cg + */ + void addOrReplaceMethod(Method method, ClassGen cg); + + /** + * Adds the field 'f' to the class represented by 'cg'. + * @param f + * @param cg + */ + void addField(Field f, ClassGen cg); + + /** + * Loads the class named 'className'. + * @param className the name of the class to be loaded + * @param client the transformer on behalf of which we are called, can be used to call checkReadClassAttributes. + */ + void loadClass(String className, ObjectTeamsTransformation client); + + /** + * Decapsulation of the method 'm'. This means that the access modifier of this method is set to 'public'. + * @param m the name of the method to be decapsulated + * @param className the name of the belonging class + * @param packageName the name of the belonging package + * @param cpg the ConstantPoolGen of the class + */ + void decapsulateMethod(Method m, ClassGen cg, String packageName, ConstantPoolGen cpg); + + +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/Decapsulation.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/Decapsulation.java new file mode 100644 index 000000000..9f7412371 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/Decapsulation.java @@ -0,0 +1,427 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2003, 2009 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: Decapsulation.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Technical University Berlin - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.objectteams.otre.util.CallinBindingManager; +import org.eclipse.objectteams.otre.util.FieldDescriptor; +import org.eclipse.objectteams.otre.util.SuperMethodDescriptor; + +import de.fub.bytecode.Constants; +import de.fub.bytecode.classfile.Method; +import de.fub.bytecode.generic.ClassGen; +import de.fub.bytecode.generic.ConstantPoolGen; +import de.fub.bytecode.generic.InstructionFactory; +import de.fub.bytecode.generic.InstructionList; +import de.fub.bytecode.generic.MethodGen; +import de.fub.bytecode.generic.ObjectType; +import de.fub.bytecode.generic.Type; + +/** + * For each base method that is bound by callout and has + * insufficient visibility, the visibility is set to public. + * If the corresponding JMangler-patch is installed, check + * whether the affected base class resides in a sealed package. + * In that case dissallow decapsulation by throwing an IllegalAccessError. + * + * @version $Id: Decapsulation.java 23408 2010-02-03 18:07:35Z stephan $ + * @author Stephan Herrmann + */ +public class Decapsulation + extends ObjectTeamsTransformation + implements Constants +{ + +// HashSet modifiedPackages = new HashSet(); + + public static class SharedState extends ObjectTeamsTransformation.SharedState { + private HashMap /* class_name -> HashSet(callout accessed fields) */<String, HashSet<String>> generatedFieldCalloutAccessors + = new HashMap<String, HashSet<String>>(); + private HashMap /* class_name -> HashSet(super-accessed methods (sign))*/<String, HashSet<String>> generatedSuperAccessors + = new HashMap<String, HashSet<String>>(); + } + @Override + SharedState state() { + return (SharedState)this.state; + } + + public Decapsulation(SharedState state) { + this(null, state); + } + public Decapsulation(ClassLoader loader, SharedState state) { + super(loader, state); + // FIXME(SH): can we ever release this transformer and its state? + synchronized(ObjectTeamsTransformation.reentrentTransformations) { + ObjectTeamsTransformation.reentrentTransformations.add(this); + } + } + + /** + * Main entry for this transformer. + */ +// @SuppressWarnings("unchecked") + public void doTransformInterface(ClassEnhancer ce, ClassGen cg) { + String class_name = cg.getClassName(); + ConstantPoolGen cpg = cg.getConstantPool(); + + // if class is already transformed by this transformer + if (state.interfaceTransformedClasses.contains(class_name)) + return; + + checkReadClassAttributes(ce, cg, class_name, cpg); + + // next step starts to transform, so record this class now. + state.interfaceTransformedClasses.add(class_name); + + generateFieldAccessForCallout(ce, cg, class_name, cpg); + + generateSuperAccessors(ce, cg, class_name, cpg); + + HashSet<String> calloutBindings = CallinBindingManager.getCalloutBindings(class_name); + + if (calloutBindings == null) { + if(logging) printLogMessage("\nClass " + class_name + + " requires no callout adjustment."); + return; + } + + if(logging) printLogMessage("\nCallout bindings might be changing class " + + class_name + ":"); + + HashSet<String> oldStyleBinding = new HashSet<String>(); + + // try new style decapsulation first (since 1.2.8): + for (String calloutBinding : calloutBindings) { + DecapsulationDescriptor desc = new DecapsulationDescriptor(); + if (!desc.decode(calloutBinding, cg)) + oldStyleBinding.add(calloutBinding); // old style attribute + else if (!desc.existsAlready) + ce.addMethod(desc.generate(class_name, cpg), cg); + } + + if (oldStyleBinding.isEmpty()) return; + + // --> follows: old style decapsulation for remaining bindings: + int pos = class_name.lastIndexOf('.'); + String package_name = "NO_PACKAGE"; + if (pos != -1) + package_name = class_name.substring(0,pos); + + Method[] methods = cg.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + String method_name = m.getName(); + + boolean requiresAdjustment = CallinBindingManager. + requiresCalloutAdjustment(oldStyleBinding, + method_name, + m.getSignature()); + + if (requiresAdjustment) { + ce.decapsulateMethod(m, cg, package_name, cpg); + } + } + } + class DecapsulationDescriptor { + short invokeKind; + String targetClass; + String methodName; + String methodSign; + Type returnType; + Type[] args; + String accessorName; + + boolean existsAlready; + + /** + * new style encoding is + * targetClassName ('!' | '?') methodName '.' methodSign + */ + boolean decode(String encodedBinding, ClassGen cg) { + int sepPos = encodedBinding.indexOf('!'); + if (sepPos != -1) { // static method: + invokeKind = INVOKESTATIC; + } else { + sepPos = encodedBinding.indexOf('?'); + if (sepPos != -1) { + invokeKind = INVOKEVIRTUAL; + } else { + return false; // old style + } + } + targetClass = encodedBinding.substring(0, sepPos); + int sigPos = encodedBinding.indexOf('(', sepPos); + methodName = encodedBinding.substring(sepPos+1, sigPos); + methodSign = encodedBinding.substring(sigPos); + + returnType = Type.getReturnType(methodSign); + args = Type.getArgumentTypes(methodSign); + + accessorName = "_OT$decaps$"+methodName; + existsAlready = cg.containsMethod(accessorName, methodSign) != null; + if (invokeKind == INVOKEVIRTUAL) { + Method existing = cg.containsMethod(methodName, methodSign); + if (existing != null && existing.isPrivate()) + invokeKind = INVOKESPECIAL; // accessing private + } + + return true; + } + Method generate(String currentClass, ConstantPoolGen cpg) { + InstructionList il = new InstructionList(); + int instanceOffset = 0; + short flags = Constants.ACC_PUBLIC; + if (invokeKind != INVOKESTATIC) { + instanceOffset=1; + il.append(InstructionFactory.createThis()); + } else { + flags |= Constants.ACC_STATIC; + } + int pos= 0; + for (int i = 0; i < args.length; i++) { + il.append(InstructionFactory.createLoad(args[i], pos+instanceOffset)); + pos += args[i].getSize(); + } + il.append(new InstructionFactory(cpg).createInvoke(targetClass, methodName, returnType, args, invokeKind)); + il.append(InstructionFactory.createReturn(returnType)); + MethodGen newMethod = new MethodGen(flags, returnType, args, /*argNames*/null, accessorName, currentClass, il, cpg); + newMethod.setMaxLocals(); + newMethod.setMaxStack(); + return newMethod.getMethod(); + } + } + + /** + * Generates getter and setter methods for all fields of the class 'class_name' which are accessed via callout. + * Informations are received via attributs (CallinBindingManager). + * @param cg the ClassGen of the appropriate class + * @param class_name the name of the class + * @param cpg the ConstantPoolGen of the class + * @param es the ExtensionSet to add the new access methods + */ + private void generateFieldAccessForCallout(ClassEnhancer ce, ClassGen cg, String class_name, ConstantPoolGen cpg) { + InstructionFactory factory = null; + + HashSet<String> addedAccessMethods = state().generatedFieldCalloutAccessors.get(class_name); + + List<FieldDescriptor> getter = CallinBindingManager.getCalloutGetFields(class_name); + if (getter != null) { + factory = new InstructionFactory(cg); + Iterator<FieldDescriptor> it = getter.iterator(); + while (it.hasNext()) { + FieldDescriptor fd = it.next(); + String key = "get_" + fd.getFieldName() + fd.getFieldSignature(); + if (logging) + printLogMessage("Generating getter method "+key); + if (addedAccessMethods == null) + addedAccessMethods = new HashSet<String>(); + if (addedAccessMethods.contains(key)) + continue; // this getter has already been created + ce.addMethod(generateGetter(cpg, class_name, fd, factory), cg); + addedAccessMethods.add(key); + state().generatedFieldCalloutAccessors.put(class_name, addedAccessMethods); + } + } + + List<FieldDescriptor> setter = CallinBindingManager.getCalloutSetFields(class_name); + if (setter != null) { + if (factory == null) + factory = new InstructionFactory(cg); + Iterator<FieldDescriptor> it = setter.iterator(); + while (it.hasNext()) { + FieldDescriptor fd = it.next(); + String key = "set_"+fd.getFieldName()+fd.getFieldSignature(); + if (logging) + printLogMessage("Generating setter method "+key); + if (addedAccessMethods == null) + addedAccessMethods = new HashSet<String>(); + if (addedAccessMethods.contains(key)) + continue; // this setter has already been created + ce.addMethod(generateSetter(cpg, class_name, fd, factory), cg); + addedAccessMethods.add(key); + state().generatedFieldCalloutAccessors.put(class_name, addedAccessMethods); + } + } + } + + + /** + * Generates a getter method for the field described by 'fd' in the class 'class_name'. + * @param cpg the ConstantPoolGen of the class + * @param class_name the name of the class + * @param fd the FieldDescriptor describing the affected field + * @param factory an InstructionFactory for this class + * @return the generated getter method + */ + private Method generateGetter(ConstantPoolGen cpg, String class_name, FieldDescriptor fd, InstructionFactory factory) { + String fieldName = fd.getFieldName(); + Type fieldType = Type.getType(fd.getFieldSignature()); + Type baseType = new ObjectType(class_name); + + InstructionList il = new InstructionList(); + String[] argumentNames; + Type[] argumentTypes; + if (fd.isStaticField()) { + argumentNames = new String[0]; + argumentTypes = new Type[0]; + } else { + argumentNames = new String[] {"base_obj"}; + argumentTypes = new Type[] {baseType}; + } + MethodGen mg = new MethodGen((Constants.ACC_PUBLIC|Constants.ACC_STATIC), + fieldType, + argumentTypes, + argumentNames, + OT_PREFIX+"get$"+fieldName, + class_name, + il, cpg); + if (!fd.isStaticField()) + il.append(InstructionFactory.createLoad(baseType, 0)); // first argument is at slot 0 in static methods + short fieldKind = fd.isStaticField()?Constants.GETSTATIC:Constants.GETFIELD; + il.append(factory.createFieldAccess(class_name, fieldName, fieldType, fieldKind)); + il.append(InstructionFactory.createReturn(fieldType)); + + mg.removeNOPs(); + mg.setMaxStack(); + mg.setMaxLocals(); + return mg.getMethod(); + } + + + /** + * Generates a setter method for the field described by 'fd' in the class 'class_name'. + * @param cpg the ConstantPoolGen of the class + * @param class_name the name of the class + * @param fd the FieldDescriptor describing the affected field + * @param factory an InstructionFactory for this class + * @return the generated getter method + */ + private Method generateSetter(ConstantPoolGen cpg, String class_name, FieldDescriptor fd, InstructionFactory factory ) { + String fieldName = fd.getFieldName(); + Type fieldType = Type.getType(fd.getFieldSignature()); + Type baseType = new ObjectType(class_name); + + Type[] argumentTypes; + String[] argumentNames; + if (fd.isStaticField()) { + argumentTypes = new Type[] { fieldType}; + argumentNames = new String[] {"new_value"}; + } else { + argumentTypes = new Type[] {baseType, fieldType}; + argumentNames = new String[] {"base_obj", "new_value"}; + } + + InstructionList il = new InstructionList(); + MethodGen mg = new MethodGen((Constants.ACC_PUBLIC|Constants.ACC_STATIC), + Type.VOID, + argumentTypes, + argumentNames, + OT_PREFIX+"set$"+fieldName, + class_name, + il, cpg); + + int argumentPosition; // position for the argument holding the new field value. + if (!fd.isStaticField()) { + il.append(InstructionFactory.createLoad(baseType, 0)); // first argument is at slot 0 in static methods + argumentPosition = 1; + } else { + argumentPosition = 0; + } + il.append(InstructionFactory.createLoad(fieldType, argumentPosition)); + short fieldKind = fd.isStaticField()?Constants.PUTSTATIC:Constants.PUTFIELD; + il.append(factory.createFieldAccess(class_name, fieldName, fieldType, fieldKind)); + il.append(InstructionFactory.createReturn(Type.VOID)); + + mg.removeNOPs(); + mg.setMaxStack(); + mg.setMaxLocals(); + return mg.getMethod(); + } + + private void generateSuperAccessors(ClassEnhancer ce, ClassGen cg, String class_name, ConstantPoolGen cpg) { + InstructionFactory factory = null; + + HashSet<String> addedAccessMethods = state().generatedSuperAccessors.get(class_name); + + List<SuperMethodDescriptor> methods = CallinBindingManager.getSuperAccesses(class_name); + if (methods != null) { + factory = new InstructionFactory(cg); + for (SuperMethodDescriptor superMethod : methods) { + String key = superMethod.methodName+'.'+superMethod.signature; + if (logging) + printLogMessage("Generating super access method "+key); + if (addedAccessMethods == null) + addedAccessMethods = new HashSet<String>(); + if (addedAccessMethods.contains(key)) + continue; // this accessor has already been created + ce.addMethod(generateSuperAccessor(cpg, class_name, superMethod, factory), cg); + addedAccessMethods.add(key); + state().generatedSuperAccessors.put(class_name, addedAccessMethods); + } + } + } + + private Method generateSuperAccessor(ConstantPoolGen cpg, String className, SuperMethodDescriptor superMethod, InstructionFactory factory) + { + int endPos = superMethod.signature.indexOf(')'); + String segment = superMethod.signature.substring(1, endPos); + String[] typeNames = (segment.length() > 0) ? segment.split(",") : new String[0]; + Type[] argTypes = new Type[typeNames.length]; + for (int i = 0; i < argTypes.length; i++) + argTypes[i] = Type.getType(typeNames[i]); + + int index = superMethod.signature.lastIndexOf(')') + 1; + Type returnType = Type.getType(superMethod.signature.substring(index)); + + Type baseType = new ObjectType(className); + Type[] wrapperTypes = new Type[argTypes.length+1]; + System.arraycopy(argTypes, 0, wrapperTypes, 1, argTypes.length); + wrapperTypes[0] = baseType; + String[] argNames = new String[wrapperTypes.length]; + for (int i = 0; i < argNames.length; i++) { + argNames[i] = "arg"+i; + } + InstructionList il = new InstructionList(); + MethodGen mg = new MethodGen((Constants.ACC_PUBLIC|Constants.ACC_STATIC), + returnType, + wrapperTypes, + argNames, + OT_PREFIX+superMethod.methodName+"$super", + className, + il, cpg); + il.append(InstructionFactory.createLoad(baseType, 0)); // first argument is base instance + for (int i = 0; i < argTypes.length; i++) + il.append(InstructionFactory.createLoad(argTypes[i], i+1)); + + // if super method is also callin bound directly invoke the orig-version + // (to avoid that BaseMethodTransformation.checkReplaceWickedSuper() has to rewrite this code again): + String methodName = (CallinBindingManager.isBoundBaseMethod(superMethod.superClass, superMethod.methodName, superMethod.signature)) + ? genOrigMethName(superMethod.methodName) + : superMethod.methodName; + + il.append(factory.createInvoke(superMethod.superClass, methodName, returnType, argTypes, INVOKESPECIAL)); + il.append(InstructionFactory.createReturn(returnType)); + mg.setMaxStack(); + mg.setMaxLocals(); + return mg.getMethod(); + } + +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/LiftingParticipantTransformation.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/LiftingParticipantTransformation.java new file mode 100644 index 000000000..48e949d96 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/LiftingParticipantTransformation.java @@ -0,0 +1,162 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2009 Stephan Herrmann + * + * 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: LiftingParticipantTransformation.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Stephan Herrmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + + +import org.objectteams.Team; + +import de.fub.bytecode.Constants; +import de.fub.bytecode.classfile.Method; +import de.fub.bytecode.generic.ALOAD; +import de.fub.bytecode.generic.BranchInstruction; +import de.fub.bytecode.generic.ClassGen; +import de.fub.bytecode.generic.ConstantPoolGen; +import de.fub.bytecode.generic.DUP; +import de.fub.bytecode.generic.GOTO; +import de.fub.bytecode.generic.IFNULL; +import de.fub.bytecode.generic.INVOKESPECIAL; +import de.fub.bytecode.generic.InstructionFactory; +import de.fub.bytecode.generic.InstructionHandle; +import de.fub.bytecode.generic.InstructionList; +import de.fub.bytecode.generic.LDC; +import de.fub.bytecode.generic.MethodGen; +import de.fub.bytecode.generic.NEW; +import de.fub.bytecode.generic.NOP; +import de.fub.bytecode.generic.ObjectType; +import de.fub.bytecode.generic.POP; +import de.fub.bytecode.generic.Type; + +/** + * If the property ot.lifting.participant is set transform all lift methods and insert + * static calls to createRole(Team,Object,String)Object; to the registered lifting participant + * before creating a new role. If createRole returns non-null then that value is taken + * as the new role, otherwise lifting proceeds as normal, i.e., normally creates a new role. + * + * @author stephan + * @since 1.3.1 + */ +public class LiftingParticipantTransformation extends ObjectTeamsTransformation { + + + private static String PARTICIPANT_NAME = System.getProperty("ot.lifting.participant"); + private static boolean checked = false; + + final private static String LIFT_PREFIX = "_OT$liftTo"; + + private static final String WRONG_ROLE_EXCEPTION = "org.objectteams.WrongRoleException"; + private static final String LIFTING_FAILED_EXCEPTION = "org.objectteams.LiftingFailedException"; + private static final String LIFTING_VETO_EXCEPTION = "org.objectteams.LiftingVetoException"; + + private static final ObjectType iLiftingParticipant = new ObjectType("org.objectteams.ILiftingParticipant"); + + private static final String CREATE_ROLE_METHOD = "createRole"; + private static final String LIFTING_PARTICIPANT_FIELD = "_OT$liftingParticipant"; + + public LiftingParticipantTransformation(SharedState state) { this(null, state); } + + public LiftingParticipantTransformation(ClassLoader loader, SharedState state) { super(loader, state); } + + public void doTransformCode(ClassGen cg) + { + if (PARTICIPANT_NAME == null) return; + + if (!classNeedsTeamExtensions(cg)) return; + + synchronized (LiftingParticipantTransformation.class) { + if (!checked) { + try { + // install a shared instance into class Team: + Class<?> participantClass = loader.loadClass(PARTICIPANT_NAME); + Team.class.getField(LIFTING_PARTICIPANT_FIELD).set(null, participantClass.newInstance()); + } catch (Exception e) { + new IllegalArgumentException("Lifting participant "+PARTICIPANT_NAME+" is invalid.", e).printStackTrace(); + PARTICIPANT_NAME = null; + } + checked = true; + } + } + + factory = new InstructionFactory(cg); + ConstantPoolGen cpg = cg.getConstantPool(); + String class_name = cg.getClassName(); + + // FIXME(SH): evaluate inclusion/exclusion filter per className + + Method[] methods = cg.getMethods(); + for (int i=0; i<methods.length; i++) { + Method m = methods[i]; + if (!m.getName().startsWith(LIFT_PREFIX)) + continue; + + cg.replaceMethod(m, m = weaveLiftingParticipant(m, class_name, cpg)); + } + } + + private Method weaveLiftingParticipant(Method m, String className, ConstantPoolGen cpg) { + MethodGen mg = new MethodGen(m, className, cpg); + InstructionList il = mg.getInstructionList(); + InstructionHandle[] ihs = il.getInstructionHandles(); + for (int i=0; i<ihs.length; i++) { + InstructionHandle ih = ihs[i]; + if (ih.getInstruction() instanceof NEW) + { + NEW newInstr = (NEW) ih.getInstruction(); + Type newType = newInstr.getType(cpg); + String newTypeName = newType.toString(); + + // don't transform creation of these exceptions: + if (newTypeName.equals(LIFTING_FAILED_EXCEPTION)) continue; + if (newTypeName.equals(LIFTING_VETO_EXCEPTION)) continue; + if (newTypeName.equals(WRONG_ROLE_EXCEPTION)) continue; + + ih.setInstruction(new NOP()); // keep this handle for the enclosing switch + + InstructionList inset = new InstructionList(); + // fetch instance of lifting participant from Team._OT$liftingParticipant + inset.append(factory.createFieldAccess(teamClassType.getClassName(), + LIFTING_PARTICIPANT_FIELD, + iLiftingParticipant, + Constants.GETSTATIC)); + inset.append(new ALOAD(0)); // load the team + inset.append(new ALOAD(1)); // load the base + inset.append(new LDC(cpg.addString(newTypeName))); // load the role class name + inset.append(factory.createInvoke(iLiftingParticipant.getClassName(), // receiver type + CREATE_ROLE_METHOD, // method + object, // return type + new Type[] {teamType, object, string},// arg types + Constants.INVOKEINTERFACE)); + inset.append(new DUP()); // keep value after null-check + BranchInstruction isNull = new IFNULL(null); + inset.append(isNull); + inset.append(factory.createCast(object, newType)); + // let goto skip: 0: new R, 1: dup, 2: aload_0, 3: aload_1, (4: cast if needed), 4: o. 5: invokespecial<init> + int invokeOffset = 4; + if (!(ihs[i+invokeOffset].getInstruction() instanceof INVOKESPECIAL)) + invokeOffset++; + inset.append(new GOTO(ihs[i+invokeOffset+1])); // one past above sequence + + // continue here if null, i.e., perform the original new-instruction + InstructionHandle goOn = inset.append(new POP()); // discard dup'ed value from above + isNull.setTarget(goOn); + inset.append(newInstr); // re-insert deleted first instruction + + il.append(ih, inset); + } + } + return mg.getMethod(); + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/LowerableTransformation.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/LowerableTransformation.java new file mode 100644 index 000000000..1db8d9fb0 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/LowerableTransformation.java @@ -0,0 +1,72 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2009 Stephan Herrmann + * + * 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: LowerableTransformation.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Stephan Herrmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import java.util.HashSet; +import java.util.Set; + +import de.fub.bytecode.Constants; +import de.fub.bytecode.generic.ClassGen; +import de.fub.bytecode.generic.MethodGen; +import de.fub.bytecode.generic.Type; + +/** + * This transformer helps legacy class files pre 1.3.2 to cope with changes re ILowerable.lower(). + * + * @author stephan + * @since 1.3.2 + */ +public class LowerableTransformation extends ObjectTeamsTransformation { + + // static because checking is performed without instance context (from scanClassOTAttribrutes) + // using ClassGen rather than names should, however, avoid conflicts between different class loaders etc. + static Set<ClassGen> transformationRequests = new HashSet<ClassGen>(); + + public LowerableTransformation(SharedState state) { this(null, state); } + + public LowerableTransformation(ClassLoader loader, SharedState state) { + super(loader, state); + } + + public void doTransformInterface(ClassEnhancer ce, ClassGen cg) { + synchronized (transformationRequests) { + if (!transformationRequests.remove(cg)) + return; + } + // yes, a change was requested, add method "public abstract Object lower();" + MethodGen lower = new MethodGen(Constants.ACC_PUBLIC|Constants.ACC_ABSTRACT, object, new Type[0], new String[0], "lower", cg.getClassName(), null, cg.getConstantPool()); + ce.addMethod(lower.getMethod(), cg); + } + + /** After reading the compiler version of a class file, check if this class is affected by the change. */ + public static void checkRequiresAdaptation(int major, int minor, int revision, ClassGen cg) { + // only 1.3.1 and below: + if (major > 1) return; + if (major == 1 && minor > 3) return; + if (major == 1 && minor == 3 && revision > 1) return; + // only interfaces ... + if (!cg.isInterface()) return; + // ... implementing ILowerabel: + for (String superInterface : cg.getInterfaceNames()) { + if ("org.objectteams.Team$ILowerable".equals(superInterface)) + synchronized(transformationRequests) { + transformationRequests.add(cg); + return; + } + } + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/OTConstants.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/OTConstants.java new file mode 100644 index 000000000..d207abd65 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/OTConstants.java @@ -0,0 +1,178 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: OTConstants.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import de.fub.bytecode.generic.*; + + +/** + * Constants for the Object Teams Runtime Environment + * @author Christine Hundt + * @author Stephan Herrmann + */ +public interface OTConstants { + // ------------------------------------------ + // ---------- Types: ------------------------ + // ------------------------------------------ + /** Type <tt>java.lang.Object</tt> */ + ObjectType object = new ObjectType("java.lang.Object"); + /** Type <tt>java.lang.Object</tt> */ + ObjectType string = new ObjectType("java.lang.String"); + /** Type <tt>java.lang.Class</tt> */ + ObjectType classType = new ObjectType("java.lang.Class"); + /** Signature of java.lang.Class#getMethod(String, Class...) */ + public static final Type[] getMethodSignature = new Type[]{string, new ArrayType(classType, 1)}; + /** Type <tt>java.lang.reflect.Method</tt> */ + ObjectType methodType = new ObjectType("java.lang.reflect.Method"); + /** Type <tt>org.objectteams.Team</tt> */ + String teamName = "org.objectteams.ITeam"; + ObjectType teamType = new ObjectType(teamName); + String teamClassName = "org.objectteams.Team"; + ObjectType teamClassType = new ObjectType(teamClassName); + /** Type <tt>org.objectteams.LiftingVetoException</tt> */ + ObjectType liftingVeto = new ObjectType("org.objectteams.LiftingVetoException"); + /** Type <tt>org.eclipse.objectteams.otre.OTREInternalError</tt> */ + ObjectType internalError = new ObjectType("org.eclipse.objectteams.otre.OTREInternalError"); + /** Type <tt>org.eclipse.objectteams.otre.OTREInternalError</tt> */ + ObjectType notProvidedError = new ObjectType("org.objectteams.ResultNotProvidedError"); + /** Type <tt>org.eclipse.objectteams.otre.UnsupportedFeatureException</tt> */ + ObjectType unsupportedFeature = new ObjectType("org.objectteams.UnsupportedFeatureException"); + + ObjectType threadType = new ObjectType("java.lang.Thread"); + + /** Type <tt>org.objectteams.Team[]</tt> */ + ArrayType teamArray = new ArrayType(teamType, 1); + /** Type <tt>int[]</tt> */ + ArrayType intArray = new ArrayType(Type.INT, 1); + /** Type <tt>java.lang.Object[]</tt> */ + ArrayType objectArray = new ArrayType(object, 1); + + ObjectType roleSetType = new ObjectType("java.util.HashSet"); + + ObjectType nullPointerException = new ObjectType("java.lang.NullPointerException"); + + String STRING_BUFFER_NAME = "java.lang.StringBuffer"; + + // ============ VERSION: ============== + public static final int OT_VERSION_MAJOR = 0; + public static final int OT_VERSION_MINOR = 8; + public static final int OT_REVISION = 18; + + // required compiler revision in the 0.9 stream: + public static final int OT09_REVISION = 26; + + // required compiler revision in the 1.0 stream: + public static final int OT10_REVISION = 0; + + // required compiler revision in the 1.1 stream: + public static final int OT11_REVISION = 0; + + // required compiler revision in the 1.2 stream: + public static final int OT12_REVISION = 0; + + // required compiler revision in the 1.3 stream: + public static final int OT13_REVISION = 0; + + // required compiler revision in the 1.4 stream: + public static final int OT14_REVISION = 1; + + // ------------------------------------------ + // ---------- Flags and Modifiers: ---------- + // ------------------------------------------ + /** Bytecode encoding of modifier <tt>team</tt> */ + final static int TEAM = 0x8000; + + // 'CallinFlags': + final static int OVERRIDING =1; // this role method is inherited from the super role + final static int WRAPPER =2; // this is a role method wrapper (in a team) + + // ------------------------------------------ + // ---------- Names: ------------------------ + // ------------------------------------------ + /** General prefix to mark all generated names. */ + final static String OT_PREFIX = "_OT$"; + /** Name of the base reference of roles. */ + final static String BASE = "_OT$base"; + /** Name of the getBase method of roles (ifc and class). */ + final static String GET_BASE = "_OT$getBase"; + /** Prefix for otdt. */ + final static String OTDT_PREFIX = "__OT__"; + /** Tsuper marker interface prefix. */ + final static String TSUPER_PREFIX = "TSuper__OT__"; + /** field for storing the class object in JVM < 5 */ + final static String SELF_CLASS = "_OT$self_class$"; + + // ----------------------------------------- + // ---------- Signature enhancement -------- + // ----------------------------------------- + + /** Name of synthetic parameter. */ + final static String TEAMS = "_OT$teams"; + /** Name of synthetic parameter. */ + final static String TEAMIDS = "_OT$teamIDs"; + /** Name of synthetic parameter. */ + final static String IDX = "_OT$idx"; + /** Name of synthetic parameter. */ + final static String BIND_IDX = "_OT$bindIdx"; + /** Name of synthetic parameter. */ + final static String UNUSED = "_OT$unusedArgs"; + /** Name of synthetic parameter. */ + final static String BASE_METH_TAG = "_OT$baseMethTag"; + + /** Number of extra arguments in enhanced signatures. */ + static final int EXTRA_ARGS = 6; + /** Position of generated argument. */ + static final int TEAMS_ARG = 1; + /** Position of generated argument. */ + static final int TEAMIDS_ARG = 2; + /** Position of generated argument. */ + static final int IDX_ARG = 3; + /** Position of generated argument. */ + static final int BIND_IDX_ARG = 4; + /** Position of generated argument. */ + static final int BASE_METH_ARG = 5; // ## really const? also UNUSED? + /** Position of generated argument. */ + static final int UNUSED_ARG = 6; + + // ---------- Features to prevent/aid garbage collection: ---------- + String ROLE_SET = OT_PREFIX + "roleSet"; // field HashSet _OT$roleSet; + String ADD_ROLE = OT_PREFIX + "addRole"; // method void _OT$addRole(Object) + String REMOVE_ROLE = OT_PREFIX + "removeRole"; // method void _OT$removeRole(Object) + + String IBOUND_BASE = "org.objectteams.IBoundBase"; // interface comprising the above methods. + + + // ----------------------------------------- + // ---------- Other constants -------- + // ----------------------------------------- + /** Marker for comment lines in the team config file. */ + static final String COMMENT_MARKER = "#"; + /** Constant for invalid base method tags (a method can not be relocated from a base call). */ + static final int INVALID_BASE_METHOD_TAG = -2; + + // -------------------------------------------------------------- + // ---------- Separator for static replace binding keys --------- + // -------------------------------------------------------------- + static final String STATIC_REPLACE_BINDING_SEPARATOR = ".."; + + // ------------------------------------------------------------------------------------ + // ---------- Linenumbers with more information. ---------------------- + // ---------- (semantic linenumber) For debugging purpose. ------ + // ------------------------------------------------------------------------------------ + static final int STEP_OVER_LINENUMBER = Short.MAX_VALUE *2; + static final int STEP_INTO_LINENUMBER = STEP_OVER_LINENUMBER - 1; +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/OTREInternalError.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/OTREInternalError.java new file mode 100644 index 000000000..04110e261 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/OTREInternalError.java @@ -0,0 +1,62 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2003-2009 Berlin Institute of Technology, 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$ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +/** + * @author stephan + */ +public class OTREInternalError extends Error { + + /** + * + */ + private static final long serialVersionUID = 1L; + private static final String _bugmsg = + "An error occurred in the Object Teams runtime environment.\n"+ + "We would appreciate if you send a bug report to bugs@ObjectTeams.org.\n"+ + "Please include your program (if possible) and the following diagnostic\n"+ + "in your report. -- Thank you. The OT/J developers\n"; + /** + * + */ + public OTREInternalError() { + super(_bugmsg); + } + + /** + * @param message + */ + public OTREInternalError(String message) { + super(_bugmsg+message); + } + + /** + * @param cause + */ + public OTREInternalError(Throwable cause) { + super(_bugmsg+cause.toString()); + } + + /** + * @param message + * @param cause + */ + public OTREInternalError(String message, Throwable cause) { + super(_bugmsg+message+cause.toString()); + } + +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/ObjectTeamsTransformation.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/ObjectTeamsTransformation.java new file mode 100644 index 000000000..7158659f8 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/ObjectTeamsTransformation.java @@ -0,0 +1,2259 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: ObjectTeamsTransformation.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.objectteams.otre.util.AnnotationHelper; +import org.eclipse.objectteams.otre.util.AttributeReadingGuard; +import org.eclipse.objectteams.otre.util.CallinBindingManager; +import org.eclipse.objectteams.otre.util.RoleBaseBinding; + +import de.fub.bytecode.Constants; +import de.fub.bytecode.Repository; +import de.fub.bytecode.classfile.Attribute; +import de.fub.bytecode.classfile.Constant; +import de.fub.bytecode.classfile.ConstantUtf8; +import de.fub.bytecode.classfile.InnerClass; +import de.fub.bytecode.classfile.InnerClasses; +import de.fub.bytecode.classfile.JavaClass; +import de.fub.bytecode.classfile.LineNumberTable; +import de.fub.bytecode.classfile.Method; +import de.fub.bytecode.classfile.Unknown; +import de.fub.bytecode.generic.*; + +/** + * Superclass for all transformations in this package. + * This class and its subclasses depends on neither JMangler nor JPLIS. + * + * Contains common fields and methods. + * + * @author Christine Hundt + * @author Stephan Herrmann + */ +public abstract class ObjectTeamsTransformation + implements OTConstants +{ + + // ------------------------------------------ + // ---------- Flags: ------------------------ + // ------------------------------------------ + + /** Check whether <tt>flags</tt> denote a generated callin wrapper. */ + static boolean isCallinWrapper(Method m, ClassGen cg) { + return methodHasCallinFlags(m, cg, WRAPPER); + } + + /** Check whether <tt>flags</tt> denote a callin method from the source code. */ + static boolean isCallin(Method m, ClassGen cg) { + return (methodHasCallinFlags(m, cg, 0) && !isCallinWrapper(m, cg)); + } + + + // ------------------------------------------ + // ---------- Names: ------------------------ + // ------------------------------------------ + + /** Generate the name for a backup of a method. */ + static String genOrigMethName(String methName) { + return "_OT$" + methName + "$orig"; + } + + /** Generate the name for a chaining wrapper. */ + static String genChainMethName(String methName) { + return "_OT$" + methName + "$chain"; + } + + static ArrayList<ObjectTeamsTransformation> reentrentTransformations = new ArrayList<ObjectTeamsTransformation>(); + + /** Common factory for all tranformers. + * To be initialized once we get a class for transformation. */ + InstructionFactory factory; + + /** State shared among all instances that work for the same class loader. */ + public static class SharedState { + /** ArrayList of classes whose interfaces have already been transformed by this transformer/classloader combo. */ + ArrayList<String> interfaceTransformedClasses = new ArrayList<String>(); + } + /** Reference to the shared state of transformers. */ + final SharedState state; + + SharedState state() { + return state; + } + + /** Which class loader are we working for? */ + protected ClassLoader loader; + + public ObjectTeamsTransformation(SharedState state) { this(null, state); } + + public ObjectTeamsTransformation(ClassLoader loader, SharedState state) { + this.loader = loader; + this.state = state; + } + + // ------------------------------------------ + // ---------- Logging: ---------------------- + // ------------------------------------------ + /** Initialized from property <tt>ot.log</tt>. */ + static boolean logging = false; + + static { + if(System.getProperty("ot.log") != null) + logging = true; + } + + /** Print <tt>message</tt> only if <tt>logging</tt> is true. */ + public static void printLogMessage(String message) { + System.out.println(message); + } + + // ------------------------------------------ + // ---------- use the following file as config file for additional active teams: -------- + // ------------------------------------------ + /** Initialized from property <tt>ot.teamconfig</tt>. */ + + static String TEAM_CONFIG_FILE = null; + + static { + TEAM_CONFIG_FILE = System.getProperty("ot.teamconfig"); + } + + // ------------------------------------------ + // ---------- Compatibility with different compiler versions: -------- + // ------------------------------------------ + + // compiler 1.2.4 introduces isSuperAccess flag for basecall surrogate: + protected static boolean IS_COMPILER_GREATER_123 = false; + + protected static boolean IS_COMPILER_13X_PLUS = false; + + protected static boolean IS_COMPILER_14X_PLUS = false; + + // ------------------------------------------ + // ---------- This flag must currently be true for OT/Equinox: ---------------------- + // ------------------------------------------ + /** Initialized from property <tt>ot.equinox</tt>. */ + public static boolean WORKAROUND_REPOSITORY = false; + + static { + if(System.getProperty("ot.equinox") != null) + WORKAROUND_REPOSITORY = true; + } + + // ------------------------------------------ + // ---------- Debugging: ---------------------- + // ------------------------------------------ + /** Initialized from property <tt>ot.debug</tt>. */ + static boolean debugging = false; + + static { + if(System.getProperty("ot.debug") != null) + debugging = true; + } + + // ------------------------------------------------------- + // ---------- Modes for implicit team activateion -------- + // ------------------------------------------------------- + enum ImplicitActivationMode { NEVER, ANNOTATED, ALWAYS } + static ImplicitActivationMode implicitActivationMode = ImplicitActivationMode.ANNOTATED; + static { + String prop = System.getProperty("ot.implicit.team.activation"); + for (ImplicitActivationMode mode : ImplicitActivationMode.values()) { + if (mode.name().equals(prop)) { + implicitActivationMode = mode; + break; + } + } + } + + // ----------------------------------------- + // ---------- Signature enhancement -------- + // ----------------------------------------- + + /** + * Prepend hidden arguments to the signature. + * This methods only treats argument names. + * @see #enhanceArgumentTypes + * The arguments are: + * <dl> + * <dt><tt>Team[] _OT$teams</tt></dt> + * <dd>array of active Teams affecting the current base method. + * <dt><tt>int[] _OT$teamIDs</tt></dt> + * <dd>array of IDs of the above Teams. + * <dt><tt>int _OT$idx</tt></dt> + * <dd>index into above arrays: the Team currently being processed. + * <dt><tt>Object[] _OT$unusedArgs</tt></dt> + * <dd>array of arguments which are unused by the current role method. + * </dl> + * @param argumentNames array of original argument names. + * @return augmented array of argument names. + */ + static String[] enhanceArgumentNames(String[] argumentNames) { + return enhanceArgumentNames(argumentNames, 0); + } + + /** + * Prepend hidden arguments to the signature. + * This methods only treats argument names. + * @see #enhanceArgumentTypes + * The arguments are: + * <dl> + * <dt><tt>Team[] _OT$teams</tt></dt> + * <dd>array of active Teams affecting the current base method. + * <dt><tt>int[] _OT$teamIDs</tt></dt> + * <dd>array of IDs of the above Teams. + * <dt><tt>int _OT$idx</tt></dt> + * <dd>index into above arrays: the Team currently being processed. + * <dt><tt>Object[] _OT$unusedArgs</tt></dt> + * <dd>array of arguments which are unused by the current role method. + * </dl> + * @param argumentNames array of original argument names. + * @param idx position where new arguments should be inserted into + * <tt>originalArgumentTypes</tt>. + * @return augmented array of argument names. + */ + static String[] enhanceArgumentNames(String[] argumentNames, int idx) { + + String[] enhancedArgumentNames = + new String[argumentNames.length + EXTRA_ARGS]; + + for (int j=0; j<idx; j++) + enhancedArgumentNames[j] = argumentNames[j]; + + enhancedArgumentNames [idx + TEAMS_ARG - 1] = TEAMS; + enhancedArgumentNames [idx + TEAMIDS_ARG - 1] = TEAMIDS; + enhancedArgumentNames [idx + IDX_ARG - 1] = IDX; + enhancedArgumentNames [idx + BIND_IDX_ARG - 1] = BIND_IDX; + enhancedArgumentNames [idx + BASE_METH_ARG - 1] = BASE_METH_TAG; + enhancedArgumentNames [idx + UNUSED_ARG - 1] = UNUSED; + + for (int j = idx; j < argumentNames.length; j++) + enhancedArgumentNames[j + EXTRA_ARGS] = argumentNames[j]; + + return enhancedArgumentNames; + } + + /** + * @see #enhanceArgumentTypes(Type[]) + * @param signature String from which to extract the argument types. + * @return + */ + static Type[] enhanceArgumentTypes(String signature) { + Type[] types = Type.getArgumentTypes(signature); + return enhanceArgumentTypes(types); + } + + /** + * Prepend hidden arguments to the signature. + * This methods only treats argument types. + * @see #enhanceArgumentNames + * + * @param originalArgumentTypes array of original argument types. + * @return augmented array of argument names. + */ + static Type[] enhanceArgumentTypes(Type[] originalArgumentTypes) { + return enhanceArgumentTypes(originalArgumentTypes, 0, true); + } + + /** + * Prepend hidden arguments to the signature. + * This methods only treats argument types. + * @see #enhanceArgumentNames + * + * @param originalArgumentTypes array of original argument types. + * @param idx position where new arguments should be inserted into + * <tt>originalArgumentTypes</tt>. + * @param createUnused should the <tt>unusedArgs</tt> argument be created? + * @return augmented array of argument types. + */ + static Type[] enhanceArgumentTypes(Type[] originalArgumentTypes, + int idx, + boolean createUnused) + { + // creates enhanced argument type array: + // ..(a1,.., aN) -> ..(Team[], int[], int, Object[], a1, .., aN) + int offset = createUnused ? EXTRA_ARGS : EXTRA_ARGS-1; + + Type[] enhancedArgumentTypes = + new Type[originalArgumentTypes.length+offset]; + + for (int j=0; j<idx; j++) + enhancedArgumentTypes[j] = originalArgumentTypes[j]; + + enhancedArgumentTypes [idx+TEAMS_ARG - 1] = teamArray; + enhancedArgumentTypes [idx+TEAMIDS_ARG - 1] = intArray; + enhancedArgumentTypes [idx+IDX_ARG - 1] = Type.INT; + enhancedArgumentTypes [idx+BIND_IDX_ARG - 1] = Type.INT; + enhancedArgumentTypes [idx+BASE_METH_ARG - 1] = Type.INT; + if (createUnused) { + enhancedArgumentTypes [idx+UNUSED_ARG - 1] = objectArray; + } + + for (int j = idx; j < originalArgumentTypes.length; j++) + enhancedArgumentTypes[j + EXTRA_ARGS] = originalArgumentTypes[j]; + + return enhancedArgumentTypes; + } + + /** + * Remove the arguments previously added by + * {@link #enhanceArgumentTypes enhanceArgumentTypes}. + * + * @param enhancedArgumentTypes + * @param staticFlag + * @return + */ + // FIXME(SH): obsolete + public static Type[] _retrenchArgumentTypes(Type[] enhancedArgumentTypes, boolean staticFlag) { + // create retrenched argument type array: + // ..(Team[], int[], int, Object[], a1, .., aN) -> ..(a1,.., aN) + + int offset = staticFlag? -1 : 0; + + Type[] retrenchedArgumentTypes = + new Type[enhancedArgumentTypes.length - EXTRA_ARGS + offset]; + + for (int j = EXTRA_ARGS; j < enhancedArgumentTypes.length + offset; j++) + retrenchedArgumentTypes[j - EXTRA_ARGS] = enhancedArgumentTypes[j]; + + return retrenchedArgumentTypes; + } + + // --------------------------------------------------- + // ---------- further type and value conversions ----- + // --------------------------------------------------- + + /** + * @see #generalizeReturnType(Type) + * @param signature String from which to extract the return type. + */ + static Type generalizeReturnType (String signature) { + Type type = Type.getReturnType(signature); + return generalizeReturnType(type); + } + + /** + * Given a return type, determine a reference type to which this type can be + * converted. "Object" if type is VOID. + * + * @param type + * @return + */ + static Type generalizeReturnType (Type type) { + if (type instanceof ReferenceType) return type; + return object; + } + + /** + * Get the generalized return type from <tt>sign1</tt>, unless + * <tt>sing2</tt> has void return type. In the latter case return + * <tt>Object</tt>. + * + * @see #generalizeReturnType(Type) + * @param sign1 + * @param sign2 + * @return + */ + static Type generalizeReturnType (String sign1, String sign2) { + Type type = Type.getReturnType(sign2); + if (type == Type.VOID) return object; + return generalizeReturnType(sign1); + } + + /** + * Assuming a value of type <tt>oldType</tt> on the stack, convert it to a + * value of type <tt>newType</tt>. Changes instruction list <tt>il</tt> + * after position <tt>ih</tt> or at its end if <tt>ih</tt> is null. + * + * @param il + * @param ih + * @param oldType + * @param newType + * @return the first inserted instruction or null if no adjustment needed + */ + InstructionHandle adjustValue (InstructionList il, InstructionHandle ih, + Type oldType, Type newType) { + if (ih == null) + ih = il.getEnd(); + if (oldType.equals(newType)) + return null; + + if (newType == Type.VOID) + return il.append(ih, new POP()); + else if (oldType == Type.VOID) + return il.append(ih, InstructionFactory.ACONST_NULL); + else if (oldType instanceof BasicType) + return il.append(ih, createBoxing((BasicType)oldType)); + else if (newType instanceof BasicType) + return il.append(ih, createUnboxing((BasicType)newType)); + else + return il.append(ih, factory.createCast(oldType, newType)); + } + + // ------------------------------------------ + // ---------- (Un-)Boxing: ------------------ + // ------------------------------------------ + + /** + * Get the name of the class suitable for boxing <tt>basicType</tt>. + * + * @param basicType + * @return + */ + static String toObjectTypeName(BasicType basicType) { + String result = ""; + switch (basicType.getType()) { + case Constants.T_BOOLEAN : result = "java.lang.Boolean"; break; + case Constants.T_INT : result = "java.lang.Integer"; break; + case Constants.T_FLOAT : result = "java.lang.Float"; break; + case Constants.T_DOUBLE : result = "java.lang.Double"; break; + case Constants.T_SHORT : result = "java.lang.Short"; break; + case Constants.T_BYTE : result = "java.lang.Byte"; break; + case Constants.T_CHAR : result = "java.lang.Character"; break; + case Constants.T_LONG : result = "java.lang.Long"; break; + default: throw new Error("OTRE failure: Basic Type not supported!!"+basicType); + } + return result; + } + + /** + * Create the instructions needed for boxing a basic type value. The value + * is expected on the stack an will be replaced by the boxed value. + * + * @param basicType type of the value on the stack. + * @return an InstructionList containing the conversion instructions. + */ + InstructionList createBoxing(BasicType basicType) { + InstructionList il = new InstructionList(); + String boxedTypeName = toObjectTypeName(basicType); + // .., result + il.append(factory.createNew(boxedTypeName)); // .., result, box, + + if (basicType.equals(Type.DOUBLE) || basicType.equals(Type.LONG)) { + // 'double' and 'long' are category 2 computational type: + il.append(new DUP_X2()); // .., box, result, box + il.append(new DUP_X2()); // .., box, box, result, box + } else { + il.append(new DUP_X1()); // .., box, result, box + il.append(new DUP_X1()); // .., box, box, result, box + } + il.append(new POP()); // .., box, box, result + il.append(factory.createInvoke(boxedTypeName, + Constants.CONSTRUCTOR_NAME, + Type.VOID, + new Type[] { basicType }, + Constants.INVOKESPECIAL)); + return il; + } + + /** + * Create the instructions needed for unboxing a basic type value. The value + * is expected on the stack an will be replaced by the unboxed value. + * + * @param basicType expected type after unboxing. + * @return an InstructionList containing the conversion instructions. + */ + InstructionList createUnboxing(BasicType basicType) { + InstructionList il = new InstructionList(); + String boxedTypeName = toObjectTypeName(basicType); + il.append(factory.createCast(object, + new ObjectType(boxedTypeName))); + il.append(factory.createInvoke(boxedTypeName, + basicType.toString() + "Value", + basicType, + Type.NO_ARGS, + Constants.INVOKEVIRTUAL)); + return il; + } + + /** Push an integer constant using the most appropriate/compact instruction. */ + Instruction createIntegerPush(ConstantPoolGen cpg, int val) { + if (val <= 5) + return new ICONST(val); + if (val <= Byte.MAX_VALUE) + return new BIPUSH((byte)val); + if (val <= Short.MAX_VALUE) + return new SIPUSH((short)val); + return new LDC(cpg.addInteger(val)); + } + + /** + * Create a throwing instruction for an OTREInternalError. + * + * @param cpg + * @param il instruction list to generate into + * @param messagePush push sequence producing the exception message. + * @return handle to the first generated instruction + */ + InstructionHandle createThrowInternalError(ConstantPoolGen cpg, InstructionList il, InstructionList messagePush) { + InstructionHandle start = il.append(factory.createNew(OTConstants.internalError)); + il.append(new DUP()); + il.append(messagePush); + il.append(factory.createInvoke(OTConstants.internalError.getClassName(), + Constants.CONSTRUCTOR_NAME, + Type.VOID, + new Type[] { Type.STRING }, + Constants.INVOKESPECIAL)); + il.append(new ATHROW()); + return start; + } + + /** + * Create a lookswitch from its constituents. Since JVM 1.4 this requires + * sorting of matches. + * + * @param matches + * @param targets + * @param breaks an array of breaks (GOTOs) whose target will + * be updated to point to <tt>afterSwitch</tt> + * @param defaultBranch + * @param afterSwitch + * @return the generated instruction. + */ + static BranchInstruction createLookupSwitch (int[] matches, + InstructionHandle[] targets, + GOTO[] breaks, + InstructionHandle defaultBranch, + InstructionHandle afterSwitch) { + + int numberOfCases = matches.length; + for (int i = 0; i < numberOfCases; i++) + breaks[i].setTarget(afterSwitch); + + HashMap<Integer, InstructionHandle> match_target_mapping = new HashMap<Integer, InstructionHandle>(); + for (int i = 0; i < numberOfCases; i++) + match_target_mapping.put(Integer.valueOf(matches[i]), targets[i]); + + Arrays.sort(matches); + for (int i = 0; i < numberOfCases; i++) + targets[i] = match_target_mapping.get(Integer.valueOf(matches[i])); + + BranchInstruction inst = new LOOKUPSWITCH(matches, targets, defaultBranch); + return inst; + } + + + /** + * Read all byte code attributes for a given class. Side-Effect: depending + * on the Referenced-Team attribute, additional classes may be scheduled for + * loading. + * + * @param ce + * @param cg + * @param class_name + * @param cpg + */ + public void checkReadClassAttributes(ClassEnhancer ce, + ClassGen cg, + String class_name, + ConstantPoolGen cpg) + { + AttributeReadingGuard guard = AttributeReadingGuard.getInstanceForLoader(this.loader); + boolean addTeamInitializations = false; + List<String> classesToLoad; + synchronized (guard) { + if (!guard.iAmTheFirst(class_name)) + return; + if (AttributeReadingGuard.isFirstLoadedClass()) + addTeamInitializations = true; + // scan for attributes here, because this transformer is applied first: + Attribute[] attrsClass = cg.getAttributes(); + classesToLoad = scanClassOTAttributes(attrsClass, class_name, cpg, cg); + + guard.workDone(class_name); + } + if (addTeamInitializations) + addTeamInitializations(cg, ce); + + Iterator<String> it = classesToLoad.iterator(); + while (it.hasNext()) { + String next = it.next(); + if(logging) printLogMessage("Loading of class " + next + " will be forced now!"); + ce.loadClass(next, this); + } + + // scan for parameter bindings: + Method[] possibleRoleMethods = cg.getMethods(); + for (int i=0; i<possibleRoleMethods.length; i++) { + Method meth = possibleRoleMethods[i]; + Attribute[] attrsMethod = meth.getAttributes(); + scanMethodOTAttributes(attrsMethod, class_name, meth.getName(), cpg); + } + if(logging) printLogMessage(this.getClass().getName() + + " picked up the attributes for class " + class_name ); + } + + // --- helpers for adding line numbers at the front of a method --- + // unfortunately BCEL does not sort line numbers, but the debugger expects them sorted. + class Pair<F,S> { + F first; + S second; + Pair(F f, S s) { + this.first = f; + this.second = s; + } + } + @SuppressWarnings("unchecked") // can't declare array of generics + Pair<InstructionHandle, Integer>[] saveLineNumbers(MethodGen method, ConstantPoolGen cpg) { + LineNumberTable lnt = method.getLineNumberTable(cpg); + InstructionHandle[] ihs = method.getInstructionList().getInstructionHandles(); + Pair<InstructionHandle, Integer>[] oldLines = new Pair[lnt.getTableLength()]; + { + int cur = -1; + int n = 0; + for (int i=0; i<ihs.length; i++) { + int next = lnt.getSourceLine(ihs[i].getPosition()); + if (next > cur) // reached a new source line + oldLines[n++] = new Pair<InstructionHandle, Integer>(ihs[i], new Integer(next)); + cur = next; + } + } + return oldLines; + } + /** Append the saved line numbers to the end of the method's line number table. */ + void restoreLineNumbers(MethodGen method, Pair<InstructionHandle, Integer>[] oldLines) { + for (int i=0; i<oldLines.length; i++) { + if (oldLines[i] == null) + continue; + InstructionHandle ih = oldLines[i].first; + int line = oldLines[i].second.intValue(); + method.addLineNumber(ih, line); + } + } + + /** + * Adds team initialization for all teams in the config file. + * + * @param cg + * @param ce + */ + private void addTeamInitializations(ClassGen cg, ClassEnhancer ce) { + String main_class_name = cg.getClassName(); + ConstantPoolGen cpg = cg.getConstantPool(); + InstructionFactory factory = new InstructionFactory(cpg); + Method main = cg.containsMethod("main", "([Ljava/lang/String;)V"); + if (main == null) { + // JPLIS launching may intercept system classes before the custom main. + // reset the guard in order to retry with subsequent classes. + AttributeReadingGuard.reset(); + return; // no main method in the first loaded class... + } + + MethodGen mainMethod = new MethodGen(main, main_class_name, cpg); + InstructionList il = mainMethod.getInstructionList(); + + int startLine = -1; + Pair<InstructionHandle,Integer>[] oldLines = null; + if (debugging) { + LineNumberTable lnt = mainMethod.getLineNumberTable(cpg); + if (lnt != null) { + startLine = lnt.getSourceLine(0); + oldLines = saveLineNumbers(mainMethod, cpg); + } + } + + if (TEAM_CONFIG_FILE != null) { + + InstructionList teamInitializations = new InstructionList(); + List<String> teamsToInitialize = getTeamsFromConfigFile(); + Iterator<String> teamIt = teamsToInitialize.iterator(); + while (teamIt.hasNext()) { + String nextTeam = teamIt.next(); + JavaClass teamClass = Repository.lookupClass(nextTeam); + if (teamClass == null) { + System.err.println("Config error: Team class '"+nextTeam+ "' in config file '"+ TEAM_CONFIG_FILE+"' can not be found!"); + System.err.println("Main class = "+main_class_name+ + ", class loader = "+(this.loader!=null?this.loader.getClass().getName():"null")+ + ", transformer = "+this.getClass().getName()); + continue; + } + ClassGen teamClassGen = new ClassGen(teamClass); + if (teamClassGen.containsMethod(Constants.CONSTRUCTOR_NAME, "()V") == null) { + System.err.println("Activation failed: Team class '"+nextTeam+ "' has no default constuctor!"); + continue; + } + ce.loadClass(nextTeam, this); + if (logging) + printLogMessage("Adding initialization of team " + nextTeam + + " to main method of class " + main_class_name); + teamInitializations.append(factory.createNew(nextTeam)); + teamInitializations.append(new DUP()); + teamInitializations.append(factory.createInvoke(nextTeam, + Constants.CONSTRUCTOR_NAME, + Type.VOID, + Type.NO_ARGS, + Constants.INVOKESPECIAL)); + teamInitializations.append(factory.createGetStatic(OTConstants.teamClassName, + "ALL_THREADS", + OTConstants.threadType)); + teamInitializations.append(factory.createInvoke(nextTeam, + "activate", + Type.VOID, + new Type[] {OTConstants.threadType}, + Constants.INVOKEVIRTUAL)); + } + il.insert(teamInitializations); + } + // register main thread with TeamThreadManager: + InstructionHandle cursor; + cursor = il.insert(new ICONST(1)); // isMain=true + cursor = il.append(cursor, new ACONST_NULL()); // parent=null + cursor = il.append(cursor, factory.createInvoke("org.objectteams.TeamThreadManager", + "newThreadStarted", + Type.BOOLEAN, + new Type[]{Type.BOOLEAN, OTConstants.threadType}, + Constants.INVOKESTATIC)); + cursor = il.append(cursor, new POP()); // don't use boolean return + + il.setPositions(); + if (debugging && startLine > 0) { + mainMethod.removeLineNumbers(); // fresh start, to ensure correct order + mainMethod.addLineNumber(il.getStart(), startLine-1); // new number to the front + restoreLineNumbers(mainMethod, oldLines); // append old numbers + } + mainMethod.setInstructionList(il); + mainMethod.setMaxStack(); + mainMethod.setMaxLocals(); + + cg.replaceMethod(main, mainMethod.getMethod()); + } + + /** + * @return a list of teams in the team initialization config file + */ + private static List<String> getTeamsFromConfigFile() { + List<String> result = new LinkedList<String>(); + try { + FileInputStream fstream = new FileInputStream(TEAM_CONFIG_FILE); + BufferedReader in = new BufferedReader(new InputStreamReader(fstream)); + while (in.ready()) { + String nextLine = in.readLine(); + String nextTeam = nextLine.trim(); + if (nextTeam.startsWith(COMMENT_MARKER)) + continue; // this is a comment line + if (!nextTeam.equals("")) { + result.add(nextTeam.trim()); + } + } + in.close(); + } catch (Exception e) { + System.err.println("File input error: config file '" + TEAM_CONFIG_FILE + "' can not be found!"); + } + return result; + } + + // ------------------------------------------------------------------------------------------------- + // -------- store and return adapted bases for OT/Equinox -------------- + // this data is collected by scanClassOTAttributes and must be collected by the caller + // before processing the next class. + // ------------------------------------------------------------------------------------------------- + public HashSet<String> adaptedBases = new HashSet<String>(); + + /** Internal API for {@link org.eclipse.objectteams.otre.jplis.ObjectTeamsTransformer} */ + public Collection<String> fetchAdaptedBases() { + HashSet<String> result; + result = new HashSet<String>(adaptedBases); + adaptedBases.clear(); + return result; + } + + // ------------------------------------------------------------------------------------------------- + /** + * Container for base method properties + */ + public static class BaseMethodInfo { + private String baseClassName; + private String baseMethodName; + private String baseMethodSignature; + boolean isCallin; + private boolean isRoleMethod; + boolean isStatic; + private int[] parameterPositions; + int translationFlags; + + BaseMethodInfo(String base_class_name, String base_method_name, + String base_method_signature, boolean isCallin, + boolean isRoleMethod, boolean isStatic, + int[] parameter_positions, int translationFlags) + { + this.baseClassName = base_class_name; + this.baseMethodName = base_method_name; + this.baseMethodSignature = base_method_signature; + this.isCallin = isCallin; + this.isRoleMethod = isRoleMethod; + this.isStatic = isStatic; + this.parameterPositions = parameter_positions; + this.translationFlags = translationFlags; + } + /** Minimal version to pass some info into #translateLoads(): */ + BaseMethodInfo(boolean isCallin, boolean isStatic, int translationFlags) + { + this.isCallin = isCallin; + this.isStatic = isStatic; + this.translationFlags = translationFlags; + } + + public boolean isStaticRoleMethod() { + return this.isRoleMethod && this.isStatic; + } + + String getBaseClassName() { + return baseClassName; + } + + String getBaseMethodName() { + return baseMethodName; + } + + String getBaseMethodSignature() { + return baseMethodSignature; + } + + int[] getParameterPositions() { + return parameterPositions; + } + } + + /** + * Scan the Attributes found in the class class_name for binding attributes + * and registers them in the CallinBindingManager. + * + * @param attributes the Attributes to be examined + * @param class_name the name of the class where the Attributes were found + * @param cpg the classes ConstantPoolGen + * @return an ArrayList containing the names of classes which have + * to be loaded immediately + */ + ArrayList<String> scanClassOTAttributes(Attribute[] attributes, + String class_name, + ConstantPoolGen cpg, + ClassGen cg) + { + if(logging) printLogMessage("Inspecting " + class_name); + ArrayList<String> classesToLoad = new ArrayList<String>(); + String base_class_name = null; + for (int k=0; k<attributes.length; k++) { + Attribute actAttr = attributes[k]; + Unknown attr = isOTAttribute(actAttr); + + if (attr != null) { //this is a callin attribute + String attrName = attr.getName(); + if(logging) printLogMessage("CallinBindingAttribute: " + attrName); + byte[] indizes = attr.getBytes(); + int count = combineTwoBytes(indizes, 0); + int numberOfEntries=0; + String [] names; + if (attrName.equals("OTClassFlags")) { + int classFlags = combineTwoBytes(indizes, 0); + String flagsString = ""; + if ((classFlags & 1) != 0) { + flagsString = "team "; + // TODO: use this instead of team modifier + } + if ((classFlags & 2) != 0) { + flagsString += "role"; + CallinBindingManager.addRole(class_name); + } + if (logging) { + printLogMessage("OTClassFlags:"); + printLogMessage("\t" + flagsString); + } + } else if (attrName.equals("CallinRoleBaseBindings")) { + numberOfEntries = 2; + int i = 2; + int n = 2 * count * numberOfEntries; // n = count << 2; + names = new String[numberOfEntries]; + while (i <= n) { + i = scanStrings(names, indizes, i, cpg); + String role_name = names[0]; + String base_name = names[1]; + if(logging) printLogMessage("**** Binding: " + role_name + + " playedBy " + base_name); + + //set binding: + CallinBindingManager.addRoleBaseBinding(role_name, base_name, class_name); + CallinBindingManager.addTeamBaseRelation(class_name, base_name); + + // [OT/Equinox] store adapted bases: + adaptedBases.add(base_name); + + // super roles have to be loaded first for binding inheritance purpose: + // not necessary anymore? + //classesToLoad.addAll(getSuperRoles(role_name, attributes, cpg)); + // roles themselve have to be loaded too: + classesToLoad.add(role_name); + } + } else if (attrName.equals("BoundClassesHierarchy")) { + numberOfEntries = 2; + int i = 2; + int n = 2 * count * numberOfEntries; // n = count << 2; + names = new String[numberOfEntries]; + while (i <= n) { + i = scanStrings(names, indizes, i, cpg); + String sub_name = names[0]; + String super_name = names[1]; + + CallinBindingManager.addBoundSuperclassLink(sub_name, super_name); + if(logging)printLogMessage("**** super-class link: "+sub_name + +" -> "+super_name); + } + } else if (attrName.equals("CallinMethodMappings")) { + //numberOfEntries = 6; + int i = 2; + for (int n=0; n<count;n++) { + // JSR-045 support: + names = new String[1]; + i = scanStrings(names, indizes, i, cpg); + String binding_file_name = names[0]; + int binding_line_number = combineTwoBytes(indizes, i); + i += 2; + int binding_line_offset = combineTwoBytes(indizes, i); + i += 2; + boolean is_static_role_method = false; + boolean covariant_base_return= false; + // regular stuff: + numberOfEntries = 3; + names = new String[numberOfEntries]; + i = scanStrings(names, indizes, i, cpg); + String wrapper_name = null; + String wrapper_signature = null; + // first 3 names: + int index = 0; + + String binding_label = names[index++]; + String role_method_name = names[index++]; + String role_method_signature = names[index++]; + + { // a flag: + int flags = combineTwoBytes(indizes, i); + is_static_role_method = (flags & 1) != 0; + // flag value 4 is "inherited" + covariant_base_return = (flags & 8) != 0; + i+=2; + } + // 3 more names + numberOfEntries = 3; + names = new String[numberOfEntries]; + i = scanStrings(names, indizes, i, cpg); + index = 0; + //if (NEW_COMPILER_VERSION) + // static_role_method = see attribute + + String lift_method_name = names[index++]; + String lift_method_signature = names[index++]; + String binding_modifier = names[index++]; + + int base_len = combineTwoBytes(indizes, i); + i += 2; + + names = new String[4]; + for (int n_base = 0; n_base < base_len; n_base++) { + i = scanStrings(names, indizes, i, cpg); + String base_method_name = names[0]; + String base_method_signature = names[1]; + wrapper_name = names[2]; + wrapper_signature = names[3]; + + byte baseFlags = indizes[i++]; + boolean baseIsCallin = (baseFlags & 1) != 0; + boolean baseIsStatic = (baseFlags & 2) != 0; + int translationFlags = (combineTwoBytes(indizes, i)<<16) + combineTwoBytes(indizes, i+2); + i += 4; + + if(logging) { + printLogMessage("**** Binding: " + binding_label + ":" + + role_method_name + role_method_signature + + " <- " + binding_modifier + " " + + base_method_name + base_method_signature); + printLogMessage("**** Wrapper: " + wrapper_name + + wrapper_signature); + } + //set binding: + CallinBindingManager.addMethodBinding(class_name, + base_class_name, // previously read from PlayedBy attribute + binding_file_name, + binding_line_number, + binding_line_offset, + binding_label, + role_method_name, + role_method_signature, + is_static_role_method, + wrapper_name, + wrapper_signature, + binding_modifier, + base_method_name, + base_method_signature, + baseIsStatic, + baseIsCallin, + covariant_base_return, + translationFlags, + lift_method_name, + lift_method_signature); + + } + } + } else if (attrName.equals("OTSpecialAccess")) { + numberOfEntries = 3; + int i = 2; + for (int j = 0; j < count; j++) { + short kind = indizes[i++]; + switch (kind) { + case 1: // DecapsulatedMethodAccess + names = new String[numberOfEntries]; + i = scanStrings(names, indizes, i, cpg); + if(logging) printLogMessage("**** Callout: " + names[0] + "." + + names[1]+ " " + names[2]); + CallinBindingManager.addCalloutBinding(names[0], names[1], names[2]); + break; + case 2: // CalloutFieldAccess + short flags = indizes[i++]; + String accessMode = (flags & 1) == 1 ? "set" : "get"; + boolean isStaticField = (flags & 2) != 0; + names = new String[numberOfEntries]; + i = scanStrings(names, indizes, i, cpg); + if (logging) + printLogMessage("**** Callout bound field: " + accessMode+(isStaticField?" static ":" ") + + names[2]+ " " + names[1]); + CallinBindingManager.addCalloutBoundFileds(names[0], names[1], names[2], accessMode, isStaticField); + + synchronized(reentrentTransformations) { + for (ObjectTeamsTransformation transformation : reentrentTransformations) + transformation.state.interfaceTransformedClasses.remove(names[0]); + } + + break; + case 3: // SuperMethodAccess + numberOfEntries = 4; + names = new String[numberOfEntries]; + i = scanStrings(names, indizes, i, cpg); + if(logging) printLogMessage("**** SuperAccess: " + names[0] + "." + + names[2]+ names[3]+" superclass "+names[1]); + CallinBindingManager.addSuperAccess(names[0], names[1], names[2], names[3]); + break; + } + } + // adapted baseclasses (w/ or w/o decapsulation): + count = combineTwoBytes(indizes, i); + i += 2; + names = new String[1]; + for (int j=0; j<count; j++) { + i = scanStrings(names, indizes, i, cpg); + byte flag = indizes[i++]; + if (flag == 1) { + CallinBindingManager.addBaseClassForModifierChange(names[0]); + } else { + CallinBindingManager.addExtraReferencedBase(class_name, names[0]); + // [OT/Equinox]: store adapted bases: + adaptedBases.add(names[0]); + } + } + } else if (attrName.equals("ReferencedTeams")) { + numberOfEntries = 1; + int i = 2; + int n = 2 * count * numberOfEntries; // n = count << 1; + names = new String[numberOfEntries]; + while (i <= n) { + i = scanStrings(names, indizes, i, cpg); + String referenced_team = names[0]; + if(logging) printLogMessage("**** found ReferencedTeams: " + referenced_team); + classesToLoad.add(referenced_team); + + } + } else if (attrName.equals("BaseClassTags")) { + numberOfEntries = 2; + String baseClass = ""; + int tag = 0; + HashMap<String, Integer> tagMap = new HashMap<String, Integer>(); + + int i = 2; + int n = 2 * count * numberOfEntries; //count << 2; + while (i <= n) { + for (int j = 0; j < numberOfEntries; j++) { + int nextIndex = combineTwoBytes(indizes, i); + if (j == 0) { + ConstantUtf8 cons = (ConstantUtf8) cpg + .getConstant(nextIndex); + baseClass = cons.getBytes(); + } else if (j == 1) { + tag = nextIndex; + } + i += 2; + } + tagMap.put(baseClass, Integer.valueOf(tag)); + if(logging) printLogMessage("**** found Base tag: " + class_name + "." + + baseClass + "->" + tag); + } + CallinBindingManager.addBaseTags(class_name, tagMap); + } else if (attrName.equals("PlayedBy")) { + names = new String[1]; + scanStrings(names, indizes, 0, cpg); + base_class_name = names[0]; + int langle = base_class_name.indexOf('<'); + if (langle > -1) // it's an anchored type p.T$R<@C.o.f>, cut off everything after and including '<'. + base_class_name = base_class_name.substring(0, langle-1); + if(logging) printLogMessage("**** found PlayedBy: " + base_class_name); + // base_class_name is stored for later use, when method bindings are found. + CallinBindingManager.addSuperRoleLink(cg.getClassName(), cg.getSuperclassName()); + // this information is NOT NEEDED at moment! + } else if (attrName.equals("OTCompilerVersion")) { + int encodedVersion = combineTwoBytes(indizes, 0); + int major = encodedVersion >>> 9; + int minor = (encodedVersion >>> 5) & 0xF; + int revision = encodedVersion & 0x1F; + if(logging) printLogMessage("**** class file was produced by compiler version " + + major + "." + minor + "." + revision + " ****"); + IS_COMPILER_GREATER_123 = false; // reset, may be updated below + LowerableTransformation.checkRequiresAdaptation(major, minor, revision, cg); + // 1.4 stream: + if (major == 1 && minor == 4) { + if (revision < OT14_REVISION) { + if (class_name.startsWith(OTConstants.teamClassName)) + continue; // no specific byte codes in ooTeam and its inner classes. + throw new InternalError("OTRE: Class " + class_name + " has unsupported revision " + revision); + } + IS_COMPILER_GREATER_123 = true; + IS_COMPILER_13X_PLUS = true; + IS_COMPILER_14X_PLUS = true; + // 1.3 stream: + } else if (major == 1 && minor == 3) { + if (revision < OT13_REVISION) { + if (class_name.startsWith(OTConstants.teamClassName)) + continue; // no specific byte codes in ooTeam and its inner classes. + throw new InternalError("OTRE: Class " + class_name + " has unsupported revision " + revision); + } + IS_COMPILER_GREATER_123 = true; + IS_COMPILER_13X_PLUS = true; + // 1.2 stream: + } else if (major == 1 && minor == 2) { + if (revision < OT12_REVISION) { + if (class_name.startsWith(OTConstants.teamClassName)) + continue; // no specific byte codes in ooTeam and its inner classes. + throw new InternalError("OTRE: Class " + class_name + " has unsupported revision " + revision); + } + if (revision > 3) + IS_COMPILER_GREATER_123 = true; + // 1.1 stream: + } else if (major == 1 && minor == 1) { + if (revision < OT11_REVISION) { + if (class_name.startsWith(OTConstants.teamClassName)) + continue; // no specific byte codes in ooTeam and its inner classes. + throw new InternalError("OTRE: Class " + class_name + " has unsupported revision " + revision); + } + // 1.0 stream: + } else if (major == 1 && minor == 0) { + if (revision < OT10_REVISION) { + if (class_name.startsWith(OTConstants.teamClassName)) + continue; // no specific byte codes in ooTeam and its inner classes. + throw new InternalError("OTRE: Class " + class_name + " has unsupported revision " + revision); + } + // 0.9 stream: + } else if (major == 0 && minor == 9) { + if (revision < OT09_REVISION) { + if (class_name.startsWith(OTConstants.teamClassName)) + continue; // no specific byte codes in ooTeam and its inner classes. + throw new InternalError("OTRE: Class " + class_name + " has unsupported revision " + revision); + } + // 0.8 stream (OBSOLETE!) + } else { + if (major != OT_VERSION_MAJOR) + throw new InternalError("OTRE: Class " + class_name + " has unsupported major version " + major); + if (minor != OT_VERSION_MINOR) + throw new InternalError("OTRE: Class " + class_name + " has unsupported minor version " + minor); + if (revision < OT_REVISION) + throw new InternalError("OTRE: Class " + class_name + " has unsupported revision " + revision); + } + } else if (attrName.equals("CallinPrecedence")) { + List<String> precedenceList = new LinkedList<String>(); + numberOfEntries = 1; + int i = 2; + int n = 2 * count * numberOfEntries; // n = count << 1; + names = new String[numberOfEntries]; + while (i <= n) { + i = scanStrings(names, indizes, i, cpg); + String binding_label = names[0]; + precedenceList.add(binding_label); + } + if(logging) printLogMessage("**** found precedence list for " + class_name + ": " + + precedenceList + " ****"); + CallinBindingManager.addPrecedenceList(precedenceList, class_name); + } + } + } + return classesToLoad; + } + + /** + * Read some strings from a byte array. + * @param entries Result array to be provided by caller. + * @param indizes buffer of read bytes to be provided by caller, + * consists if indizes into the constant pool + * @param i current index into indizes + * @param cpg the pool. + * @result updated value of <tt>i</tt>. + */ + public static int scanStrings(String[] entries, + byte[] indizes, + int i, + ConstantPoolGen cpg) + { + for (int j = 0; j < entries.length; j++) { + int nextIndex = combineTwoBytes(indizes, i); + ConstantUtf8 cons = (ConstantUtf8)cpg.getConstant(nextIndex); + String content = cons.getBytes(); + entries[j] = content; + i += 2; + } + return i; + } + + /** + * Scan the Attributes found in the method <tt>method_name</tt> for binding attributes + * and registers them in the {@link CallinBindingManager CallinBindingManager}. + * + * @param attributes the Attributes to be examined + * @param class_name the name of the class where the Attributes were found + * @param method_name the name of the method where the Attributes were found + * @param cpg + */ + static void scanMethodOTAttributes(Attribute[] attributes, + String class_name, + String method_name, + ConstantPoolGen cpg) + { + for (int k = 0; k < attributes.length; k++) { + Attribute actAttr = attributes[k]; + Unknown attr = isOTAttribute(actAttr); + if (attr != null) { //this is an OT attribute + String attrName = attr.getName(); + if(logging) printLogMessage("CallinBindingAttribute(" + method_name + ") :" + + attrName); + if (attrName.equals("CallinParamMappings")) { + byte[] indizes = attr.getBytes(); + int [] positions = null; + if (indizes == null) throw new RuntimeException("Unexpected null attr"); + int count = combineTwoBytes(indizes, 0); + positions = new int[count]; + int p = 2; + for (int i = 0; i < count; i++, p += 2) { + positions[i] = combineTwoBytes(indizes, p); + // System.out.println(" "+i+"<-"+positions[i]); + } + CallinBindingManager.addParameterBinding(class_name, + method_name, + positions); + // Here it is correct to use the (wrapper) method name without a signature, + // because for overloaded methods there are separate wrappers with unique (mangled) names. + } + } + } + } + + + /** + * Determines, if the given Attribute is a callin-attribute. + * + * @param attr the Attriute to be checked + * @return the Attribute casted to Unknown, for later use, if true + * null, if it is not a callin-attribute + */ + static Unknown isOTAttribute(Attribute attr) { + if (attr instanceof Unknown) { + Unknown unknown = (Unknown)attr; + String attrName = unknown.getName(); + if (attrName.equals("CallinRoleBaseBindings") || + attrName.equals("BoundClassesHierarchy") || + attrName.equals("CallinMethodMappings") || + attrName.equals("CallinParamMappings") || + attrName.equals("CallinFlags") || + attrName.equals("OTSpecialAccess") || + attrName.equals("WrappedRoleSignature") || + attrName.equals("WrappedBaseSignature") || + attrName.equals("ReferencedTeams") || + attrName.equals("BaseClassTags") || + attrName.equals("PlayedBy") || + attrName.equals("Modifiers") || + attrName.equals("OTCompilerVersion") || + attrName.equals("OTClassFlags") || + attrName.equals("OTJoinPoints") || + attrName.equals("CallinPrecedence") || + attrName.equals("StaticReplaceBindings")) + { + return unknown; + } + } + return null; + } + + /** + * Combines two int's representing the higher and the lower part + * of a two byte number. + * + * @param first the first (higer?) byte + * @param second the second (lower?) byte + * @return the combined number + */ + public static int combineTwoBytes(byte [] indizes, int start) { + int first = indizes[start]; + int second = indizes[start + 1]; + int twoBytes = 0; + + twoBytes = twoBytes | (first & 0xff); + twoBytes = twoBytes << 8; + twoBytes = twoBytes | (second & 0xff); + return twoBytes; + } + + /** + * Returns a list of names of all super classes of class 'role_name' which are inner + * classes of the enclosing team, what are all super roles of the + * role 'role_name'. + * + * @param role_name the name of the role class + * @param attributes the attributes of the enclosing team class + * @param cpg the constant pool of the enclosing team class + * @return the list of super role names + */ + /* + private List getSuperRoles(String role_name, Attribute[] attributes, ConstantPoolGen cpg) { + LinkedList superRoleNames = new LinkedList(); + JavaClass[] super_classes = Repository.getSuperClasses(role_name); + if (super_classes.length < 2) {// extends only Object, or none? + return superRoleNames; + } + LinkedList innerClassNames = new LinkedList(); + for (int i=0; i<attributes.length; i++) { + Attribute actAttr = attributes[i]; + if (actAttr instanceof InnerClasses) { + InnerClass[] inners = ((InnerClasses)actAttr).getInnerClasses(); + for (int j=0; j<inners.length; j++) { + int name_index = inners[j].getInnerNameIndex(); + Constant name_c = cpg.getConstant(name_index); + String name = ((ConstantUtf8)name_c).getBytes(); + int outer_class_index = inners[j].getOuterClassIndex(); + Constant outer_c = cpg.getConstant(outer_class_index); + String outerName = ((ConstantClass)outer_c).getBytes(cpg.getConstantPool()); + outerName = outerName.replace('/', '.'); + innerClassNames.add(outerName + "$" +name); + } + } + } + for (int i=0; i<super_classes.length; i++) { + String superClassName = super_classes[i].getClassName(); + if (innerClassNames.contains(superClassName)) { + superRoleNames.addFirst(superClassName); + } + } + return superRoleNames; + }*/ + + /** + * Remove all contents of a method as preparation for adding a new implementation + * + * @param m The original method + * @param class_name The class containing the method + * @param cpg The class' constant pool + * @return An empty method generator with the same declaration as m + * and no implementation. + */ + protected static MethodGen wipeMethod(Method m, String class_name, ConstantPoolGen cpg) { + MethodGen mg; + mg = new MethodGen(m, class_name, cpg); + mg.getInstructionList().dispose(); //throw away the old implementation + mg.removeLineNumbers(); + mg.removeLocalVariables(); + mg.removeLocalVariableTypes(); + mg.removeExceptionHandlers(); + mg.removeAttributes(); + return mg; + } + + /** + * Add instructions of InstructionList il after the super constructor call of this constuctor. + * + * @param m the constructor method + * @param addedCode the InstructionList containing the instructions to be added + * @param cg the ClassGen + * @param cpg the constant pool + */ + static void addToConstructor(Method m, InstructionList addedCode, ClassGen cg, ConstantPoolGen cpg) { + String class_name = cg.getClassName(); + MethodGen mg = new MethodGen(m, class_name, cpg); + InstructionList il = mg.getInstructionList().copy(); + InstructionHandle[] ihs = il.getInstructionHandles(); + + MethodGen newConstructor = new MethodGen(mg.getAccessFlags(), + mg.getReturnType(), + mg.getArgumentTypes(), + mg.getArgumentNames(), + mg.getName(), + class_name, + il, + cpg ); + +//[SH:] + updateCopiedMethod(mg, il, ihs, newConstructor); +//[:HS] + + int stackDepth = 0; + int actInstrIndex = 0; + + boolean pauseStackCounting = false; + InstructionHandle gotoTarget = null; + + // skip everything up to and including super() or this() call + while (!((ihs[actInstrIndex].getInstruction() instanceof INVOKESPECIAL) + && (stackDepth - (ihs[actInstrIndex].getInstruction().consumeStack(cpg))) == 0)) { + + Instruction actInstruction = ihs[actInstrIndex].getInstruction(); + + if (gotoTarget != null && actInstruction.equals(gotoTarget.getInstruction())) { + pauseStackCounting = false; + gotoTarget = null; + } + + if (actInstruction instanceof GotoInstruction) { + GotoInstruction gotoInsruction = (GotoInstruction)actInstruction; + gotoTarget = gotoInsruction.getTarget(); + pauseStackCounting = true; + } + if (!pauseStackCounting) { + stackDepth -= actInstruction.consumeStack(cpg); + stackDepth += actInstruction.produceStack(cpg); + } + + actInstrIndex++; + } + + InstructionHandle ih = ihs[actInstrIndex]; + INVOKESPECIAL invoke = (INVOKESPECIAL)ih.getInstruction(); + String specialName = invoke.getName(cpg); + if (!specialName.equals(Constants.CONSTRUCTOR_NAME)) { + System.err.println("###ALERT: " + specialName); + return; + } + + if (logging) printLogMessage("Adding code to " + class_name + "." + m.getName()); + + // calculate the length of the added code BEFORE it is inserted into the instruction list: +//[SH:] extracted for adding further manipulation of addedCode: + int addedCodeLength = padCodeToAdd(addedCode); +//[:HS] + + InstructionHandle startOfAddedCode = il.append(ih, addedCode); + + il.setPositions(); + + newConstructor.setInstructionList(il); + newConstructor.setMaxStack(); + newConstructor.setMaxLocals(); +//[SH:] if an unused local variable was copied in updateCopiedMethod() +// we have to adjust max_locals, +// because unused locals are not found by setMaxLocals(). + newConstructor.setMaxLocals(Math.max(newConstructor.getMaxLocals(), mg.getMaxLocals())); +//[:HS] + + copyAndAdjustLineNumbers(mg, newConstructor, addedCodeLength, startOfAddedCode); + + cg.replaceMethod(m, newConstructor.getMethod()); + } + + /** + * Pads a given instruction list to multiples of 4 returning the padded length. + * Note that clients of this function must not call removeNOPs()! + * + * Rationale: + * switch instructions pad the lists of jmp targets to start at an offset % 4 == 0. + * In order to ensure that this padding will not change when inserting new bytes + * the added bytes have to be a multiple of 4. + */ + static int padCodeToAdd(InstructionList addedCode) { +//[orig:] + int addedCodeLength = 0; + Instruction[] instr = addedCode.getInstructions(); + for (int i = 0; i < instr.length; i++) { + addedCodeLength += instr[i].getLength(); + } +//[:giro] + while (addedCodeLength % 4 > 0) { + addedCode.append(new NOP()); + addedCodeLength++; + } + return addedCodeLength; + } + +//[SH:] helper methods for more complete method copying: + + /** After a method (incl. its instruction list) has been copied, + * we need to manually copy some more properties: + * + local variables + * + declared exceptions + * + exception handlers. + */ + static void updateCopiedMethod(MethodGen methodOrig, + InstructionList ilCopy, + InstructionHandle[] ihsCopy, + MethodGen methodCopy) + { + // local variables: + LocalVariableGen[] oldLocals = methodOrig.getLocalVariables(); + int argLen = methodOrig.getArgumentTypes().length; + if (!methodOrig.isStatic()) + argLen++; // this + if (oldLocals.length > argLen) { + InstructionList oldIL = methodOrig.getInstructionList(); + int maxLocals = methodOrig.getMaxLocals(); + for (int i = argLen; i<oldLocals.length; i++) { + LocalVariableGen var = oldLocals[i]; + LocalVariableGen newVar = + methodCopy.addLocalVariable(var.getName(), var.getType(), + mapIH(var.getStart(), oldIL, ihsCopy), + mapIH(var.getEnd(), oldIL, ihsCopy)); + newVar.setIndex(var.getIndex()); + // reset, addLocalVariable might have changed this. + methodCopy.setMaxLocals(maxLocals); + } + } + // declared exceptions: + BaseCallRedirection.copyExceptionHandlers(methodOrig, methodCopy, ilCopy); + // exception handlers: + for(String excName : methodOrig.getExceptions()) + methodCopy.addException(excName); + } + + static InstructionHandle mapIH(InstructionHandle alienIH, InstructionList oldIL, InstructionHandle[] newIHs) + { + int position = alienIH.getPosition(); + int[] newPositions = oldIL.getInstructionPositions(); + for (int i=0; i<newPositions.length; i++) { + if (newPositions[i] == position) + return newIHs[i]; + } + return null; + } +//[:HS] + + /** + * Copy all line numbers from <tt>src</tt> to <tt>dest</tt>. + * For bytecode instructions after the added code area an offset has to be added to the + * bytecode positions of the line numbers. + * If in debug modus (flag 'debugging') add 'STEP_OVER_LINENUMBER' for the added code. + * + * @param src The source method. + * @param dest The destination method. + * @param offset The offest to be added (the length of the added code list). + * @param startOfAddedCode InstructionHandle to the beginning of the added code. + */ + static void copyAndAdjustLineNumbers(MethodGen src, MethodGen dest, int offset, InstructionHandle startOfAddedCode) { + InstructionList il_dest = dest.getInstructionList(); + LineNumberGen[] src_lng = src.getLineNumbers(); + boolean addedCodeHasLineNumber = false; + + for (int i = 0; i < src_lng.length; i++) { + int position = src_lng[i].getInstruction().getPosition(); + if (position == startOfAddedCode.getPosition()) { // add the line number for added code here: + dest.addLineNumber(startOfAddedCode, STEP_OVER_LINENUMBER); + addedCodeHasLineNumber = true; + continue; + } + if (position >= startOfAddedCode.getPosition()) // move line numbers, because of inserted code (add offset) + position += offset; + InstructionHandle ih = il_dest.findHandle(position); + if (ih == null) { + System.err.println("Handle not found!"); + } else { + dest.addLineNumber(ih, src_lng[i].getSourceLine()); + } + } + if (!addedCodeHasLineNumber) { // no custom code after added code: add line number now: + dest.addLineNumber(startOfAddedCode, STEP_OVER_LINENUMBER); + } + } + + /** + * Test if a method has the given 'callin_flag'. + * + * @param m the method to search + * @param cg the ClassGen + * @param callin_flags the flags to check for; 0 means any callin flags + * @return true, if the method has the callin flag + */ + public static boolean methodHasCallinFlags(Method m, ClassGen cg, int callin_flags) { + boolean found = false; + Attribute[] attributes = m.getAttributes(); + for (int i = 0; i < attributes.length; i++) { + Attribute actAttr = attributes[i]; + if (!(actAttr instanceof Unknown)) + continue; + Unknown attr = (Unknown)actAttr; + if (!(attr.getName().equals("CallinFlags"))) + continue; + if (callin_flags == 0) + return true; + byte [] bytes = attr.getBytes(); + int flags = combineTwoBytes(bytes, 0); + if ((flags & callin_flags) != 0) { + if(logging) printLogMessage("Found CallinFlag " + callin_flags + " for " + + cg.getClassName() + "." + m.getName() + "."); + found = true; + } + } + return found; + } + + /** + * A team needs to get added the team specific part, if it is a non-abstract team and + * not the class org.objectteams.Team itself. + * + * @param cg the class to be tested + * @return true if this is a team which has to be extended + */ + protected static boolean classNeedsTeamExtensions(ClassGen cg) { + return !(( (cg.getAccessFlags() & OTConstants.TEAM) == 0) + || (cg.getClassName().equals(OTConstants.teamClassName)) + || ((cg.getAccessFlags() & Constants.ACC_ABSTRACT) != 0)); + } + + /** + * Generates the name of the implementing role for the given role interface name. + * (Inserts the '__OT__' marker.) + * + * @param roleName the role interface name + * @return the implementing role class name + */ + protected static String genImplementingRoleName(String roleName) { + int lastDollar = roleName.lastIndexOf('$'); + StringBuilder sb = new StringBuilder(roleName); + sb.insert(lastDollar + 1, OTDT_PREFIX); + return sb.toString(); + } + + /** + * Generates the name of the role interface the given role class is implementing. + *(Removes the '__OT__' marker.) + * + * @param roleName the role class name + * @return the implemented role interface + */ + public static String genRoleInterfaceName(String roleName) { + int lastDollar = roleName.lastIndexOf('$'); + StringBuilder sb = new StringBuilder(roleName); + sb.delete(lastDollar + 1, lastDollar + 1 + OTDT_PREFIX.length()); + return sb.toString(); + } + + protected static boolean isReflectiveOTMethod(String methodName, String methodSignature) { + if ((methodName.equals("hasRole") && methodSignature.equals("(Ljava/lang/Object;)Z")) + || (methodName.equals("hasRole") && methodSignature.equals("(Ljava/lang/Object;Ljava/lang/Class;)Z")) + || (methodName.equals("getRole") && methodSignature.equals("(Ljava/lang/Object;)Ljava/lang/Object;")) + || (methodName.equals("getRole") && methodSignature.equals("(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;")) + || (methodName.equals("getAllRoles") && methodSignature.equals("()[Ljava/lang/Object;")) + || (methodName.equals("getAllRoles") && methodSignature.equals("(Ljava/lang/Class;)[Ljava/lang/Object;")) + || (methodName.equals("unregisterRole") && methodSignature.equals("(Ljava/lang/Object;)V")) + || (methodName.equals("unregisterRole") && methodSignature.equals("(Ljava/lang/Object;Ljava/lang/Class;)V")) + ) + return true; + return false; + } + + /** + * Calculates the name of the outer class. + * + * @param className the full name of the inner class + * @return the name of the outer class + */ + public static String getOuterClassName(String className) { + int dollarIndex = className.lastIndexOf('$'); + if (dollarIndex == -1) // no outer class exists! + return null; + String outerClassName = className.substring(0, dollarIndex); + return outerClassName; + } + + /** + * @param m + * @param cg + * @return + */ + static boolean candidateForImplicitActivation(Method m, ClassGen cg, ConstantPoolGen cpg) { + if (!IS_COMPILER_14X_PLUS) + implicitActivationMode = ImplicitActivationMode.ALWAYS; + switch (implicitActivationMode) { + case NEVER: + return false; + case ANNOTATED : + if ( AnnotationHelper.containsImplicitActivationAttribute(m.getAttributes(), cpg) + || AnnotationHelper.containsImplicitActivationAttribute(cg.getAttributes(), cpg)) + return canImplicitlyActivate(m); + return false; + case ALWAYS: + if (!canImplicitlyActivate(m)) + return false; + + Attribute[] attributes = m.getAttributes(); + for(Attribute a : attributes) { + if (a instanceof Unknown) { + Unknown attr = (Unknown) a; + String attrName = attr.getName(); + if ("RoleClassMethodModifiers".equals(attrName)) { + byte[] bytes = attr.getBytes(); + int flags = combineTwoBytes(bytes, 0); + if (flags == 0 ||/* flags == 2 || */flags == 4) { + // 0: default, 2: private, 4: protected + return false; + } + } + } + } + if (!m.isPublic()) + return false; // m originally wasn't public + + String className = cg.getClassName(); + return !(CallinBindingManager.isRole(className) && cg.isProtected()); + default: + return false; // cannot happen switch is exhaustive. + } + } + + private static boolean canImplicitlyActivate(Method m) { + String methodName = m.getName(); + String methodSignature = m.getSignature(); + boolean isCandidate = + (!m.isAbstract()) && + (!m.isStatic()) && + (!methodName.startsWith(OT_PREFIX)) && + (!methodName.equals(Constants.CONSTRUCTOR_NAME)) && + (!(methodName.equals("activate") && methodSignature.equals("()V"))) && + (!(methodName.equals("deactivate") && methodSignature.equals("()V"))) && + (!isReflectiveOTMethod(m.getName(), methodSignature)); + return isCandidate; + } + + /** + * @param s + * @param c + * @return + */ + static int countOccurrences(String s, char c) { + int count = 0; + int idx = s.indexOf(c); + while (idx != -1) { + idx = s.indexOf(c, idx + 1); + count++; + } + return count; + } + + /** + * This method performs the changes for implicit Team activation. + * @param m a method for which implicit activation will be enabled. + * @param className the class name for 'm'. + * @param cpg the constant pool of the class 'className'. + * @param activateOuter true, if the surrounding team has to be activated + * @return + */ + Method genImplicitActivation(Method m, String className, ConstantPoolGen cpg, boolean activateOuter) { + String targetName = className; + int nestingDepth = 0; + ObjectType outerClass = null; + if (activateOuter) { + outerClass = new ObjectType(getOuterClassName(className)); + nestingDepth = countOccurrences(className, '$') - 1; + targetName = outerClass.getClassName(); + } + MethodGen mg = new MethodGen(m, className, cpg); + InstructionList il = mg.getInstructionList(); + InstructionList prefix = new InstructionList(); + InstructionHandle try_start = il.getStart(); + // ---> new implicit activation + prefix.append(InstructionFactory.createThis()); + if (activateOuter) {// this is for a role method: activate the outer team + // FIXME(SH): check replacing this$n with _OT$getTeam() + prefix.append(factory.createGetField(className, "this$" + nestingDepth, outerClass)); + } + prefix.append(factory.createInvoke(targetName, "_OT$implicitlyActivate", + Type.VOID, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + // <-- new implicit activation + if (debugging) + mg.addLineNumber(prefix.getStart() , STEP_OVER_LINENUMBER); + il.insert(prefix); + + InstructionList postfix = new InstructionList(); + // ---> new implicit deactivation + postfix.append(InstructionFactory.createThis()); + if (activateOuter) {// this is for a role method: deactivate the outer team + postfix.append(factory.createGetField(className, "this$"+nestingDepth, outerClass)); + } + postfix.append(factory.createInvoke(targetName, + "_OT$implicitlyDeactivate", Type.VOID, Type.NO_ARGS, + Constants.INVOKEVIRTUAL)); + // <-- new implicit deactivation + + FindPattern findPattern = new FindPattern(il); + String pat = "`ReturnInstruction'"; + InstructionHandle ih = findPattern.search(pat); + while (ih != null) { + // insert deactivate-call before return instruction in ih: + InstructionList postfixCopy = postfix.copy(); + if (debugging) + mg.addLineNumber(postfixCopy.getStart(), STEP_OVER_LINENUMBER); + InstructionHandle inserted = il.insert(ih, postfixCopy); // instruction lists can not be reused + il.redirectBranches(ih, inserted); // SH: retarget all jumps that targeted at the return instruction + if (ih.getNext() == null) + break; // end of instruction list reached + ih = findPattern.search(pat, ih.getNext()); + } + + /** + * **** add an exception handler which calls deactivate before throwing + * the exception (finaly-simulation): *** + */ + ObjectType throwable = new ObjectType("java.lang.Throwable"); + LocalVariableGen exception = mg.addLocalVariable( + "_OT$thrown_exception", throwable, null, null); + InstructionHandle try_end = il.getEnd(); + InstructionList postfix_ex = postfix.copy(); + if (debugging) + mg.addLineNumber(postfix_ex.getStart(), STEP_OVER_LINENUMBER); + postfix_ex.insert(InstructionFactory.createStore(throwable, exception + .getIndex())); + postfix_ex.append(InstructionFactory.createLoad(throwable, exception + .getIndex())); + postfix_ex.append(new ATHROW()); + InstructionHandle deactivation_handler = il.append(il.getEnd(), + postfix_ex); + mg.addExceptionHandler(try_start, try_end, deactivation_handler, + throwable); + /** ******************************************************************** */ + + mg.setInstructionList(il); + mg.setMaxLocals(); + mg.setMaxStack(); + + if (!debugging) + return mg.getMethod(); + + /* sorting the line numbers per start pc: */ + Method newMethod = mg.getMethod(); + MethodGen newMethodGen = new MethodGen(newMethod, className, cpg); + + LineNumberGen[] lineNumbers = newMethodGen.getLineNumbers(); + + newMethodGen.removeLineNumbers(); + + Arrays.sort(lineNumbers, new Comparator<LineNumberGen>() { + public int compare(LineNumberGen ln1, LineNumberGen ln2) { + int firstLineNumberPC = ln1.getLineNumber().getStartPC(); + int secondLineNumberPC = ln2.getLineNumber().getStartPC(); + + if (firstLineNumberPC < secondLineNumberPC) + return -1; + if (firstLineNumberPC > secondLineNumberPC) + return 1; + return 0; + } + }); + + for (int i = 0; i<lineNumbers.length; i++) { + newMethodGen.addLineNumber(lineNumbers[i].getInstruction(), lineNumbers[i].getSourceLine()); + } + + return newMethodGen.getMethod(); + } + + /** + * How many bytes does an instruction produce on the stack? + */ + static protected int stackDiff(Instruction instr, ConstantPoolGen cpg) { + return instr.produceStack(cpg) - instr.consumeStack(cpg); + } + + /** + * Split an instruction list that loads method arguments into + * one list for each argument. + * @param cpg + * @param src the original loading sequence (will be destroyed by this method). + * @param argumentTypes source-level argument types of the callin method. + * @return array of load sequences, same order as before but split into individual arguments. + */ + protected InstructionList[] splitLoading(ConstantPoolGen cpg, InstructionList src, Type[] argumentTypes) { + int len = argumentTypes.length; + InstructionList[] res = new InstructionList[len]; + // starting right _before_ an invoke instruction we loop backwards + // to find those values on the stack that would be passed to the method. + for (int idx = len-1; idx>=0; idx--) { + // new sub-list: + res[idx] = new InstructionList(); + // how many bytes to expect on the stack: + int expectedArgSize = argumentTypes[idx].getSize(); + // loop until we have a sequence that produces the expected number of bytes: + while (expectedArgSize > 0) { + InstructionHandle loadH = src.getEnd(); + Instruction load = loadH.getInstruction(); + expectedArgSize -= stackDiff(load, cpg); + try { + // transfer instruction from src to res[roleIdx]: + res[idx].insert(load); + src.delete(loadH); + } catch (TargetLostException e) { + throw new OTREInternalError(e); + } + } + } + return res; + } + + /** + * Translate the parameter loading of a base call to the correct sequence + * expected by the base chaining wrapper. + * Treats tunneled arguments but not current enhancement args. + * Tasks performed: + * <ul> + * <li>pick load sequences from <tt>splitLoad</tt> + * <li>extract unused args from Object[] + * <li>insert casting or lowering if needed. + * </ul> + * More documentation is found in document parameter-passing.odg. + * + * @param splitLoad one list of load instructions for each argument, ordered as found in the bytecode. + * @param enhancedRoleArgumentTypes enhanced role arguments + * @param enhancedBaseArgumentTypes arguments of the base chaining wrapper. + * @param parameterPosition encoded parameter mappings. + * @param teamName The name of the team containing the role class. + * @param enclosingRoleName enclosing role class or null for static replace/base-call. + * @param baseIsCallin is the base method a callin method? + * @param baseIsStaticRoleMethod is the base method a static role method? + * @param one byte for each parameter, signalling if lowering is required + * @return a complete loading sequence for all source-level and tunneled arguments + */ + protected InstructionList translateLoads(InstructionList[] splitLoad, + Type[] enhancedRoleArgumentTypes, + Type[] enhancedBaseArgumentTypes, + int[] parameterPositions, + String teamName, + String enclosingRoleName, + BaseMethodInfo baseMethod, + int start, + ConstantPoolGen cpg) + { + boolean isStatic = baseMethod.isStatic; + boolean baseIsStaticRoleMethod = baseMethod.isStaticRoleMethod(); + boolean baseIsCallin = baseMethod.isCallin; + int translationFlags = baseMethod.translationFlags; + + InstructionList il = new InstructionList(); + + // index into "Object[] _OT$unusedArgs" (points to first arg that has not yet been consumed): + int nextUnusedArg = 0; + // index into enhancedBaseArgumentTypes: + int baseIdx; + // source-level version of baseIdx, i.e., ignoring any enhanced arguments: + int baseSrcIdx; + // index into roleArgumentTypes: + int roleIdx; + // source-level version of roleIdx, i.e., ignoring any enhanced arguments: + int roleSrcIdx; + + // == Note: letters (u-x) below refer to document parameter-passing.odg. == + // Skip enhancements that are already loaded (given in start). + for (baseIdx = start; baseIdx < enhancedBaseArgumentTypes.length; baseIdx++) { + baseSrcIdx = baseIdx-start; // (u) skip first enhancement (already loaded) + if (baseIsStaticRoleMethod) + baseSrcIdx -= 2; // (v) not mapped but extracted from unusedArgs + if (baseIsCallin) + baseSrcIdx -= EXTRA_ARGS; // (w) not mapped but extracted from unusedArgs + + // value-dispatching: where to find this parameters? + if (baseSrcIdx < 0) { + // (v) or (w) + roleSrcIdx = -1; + } else { + // (x) merge arguments from unused and real: + int numAvailableRoleArgs = enhancedRoleArgumentTypes.length-EXTRA_ARGS; + if (!isStatic && IS_COMPILER_GREATER_123) + numAvailableRoleArgs--; // don't count isSuperAccess + roleSrcIdx = getMappedRolePosition(baseSrcIdx, parameterPositions, numAvailableRoleArgs); + } + + // got all information, fetch it now: + Type baseArgumentType = enhancedBaseArgumentTypes[baseIdx]; + if (roleSrcIdx == -1) { + // not mapped, retrieve from _OT$unusedArgs: + retrieveFromUnusedArg(il, nextUnusedArg++, baseArgumentType, cpg); + } else { + // mapped, fetch from splitLoad: + roleIdx = roleSrcIdx+EXTRA_ARGS; // role always has an additional set of enhancements + if (IS_COMPILER_GREATER_123 && !isStatic) roleIdx++; // (+ isSuperAccess) + Type roleArgumentType = enhancedRoleArgumentTypes[roleIdx]; + il.append(splitLoad[roleSrcIdx]); + if (!roleArgumentType.equals(baseArgumentType)) { + convertParamToBase(il, teamName, enclosingRoleName, roleArgumentType, baseArgumentType, + (translationFlags & (2<<baseSrcIdx)) != 0); + } + } + } + if (il.isEmpty()) + il.append(new NOP()); // ensure caller receives at least one instruction handle (for line number) + return il; + } + + /* Is the base arg identified by 'baseIdx' mapped to a role parameter? + * If so: which role position is it mapped to? + * If not: return -1 + */ + private int getMappedRolePosition(int baseSrcIdx, + int[] parameterPositions, + int numAvailableRoleArgs) + { + if (parameterPositions == null) { + if (baseSrcIdx < numAvailableRoleArgs) // as many as available ... + return baseSrcIdx; // in original order. + } else { + // search parameter mapping: + for (int i = 0; i < parameterPositions.length; i++) { + if (parameterPositions[i] == baseSrcIdx+1) // positions are one-based (base side) + return i; + } + } + return -1; + } + + /* Retrieve an unused (tunneled) argument from position 'unusedIdx' of the _OT$unusedArgs array. */ + private void retrieveFromUnusedArg(InstructionList il, + int unusedIdx, + Type baseArgumentType, + ConstantPoolGen cpg) + { + il.append(InstructionFactory.createLoad(objectArray, /*this*/1 + (UNUSED_ARG-1))); // 'UNUSED_ARG' is one-based + il.append(createIntegerPush(cpg, unusedIdx)); + il.append(InstructionFactory.createArrayLoad(objectArray)); + if (baseArgumentType instanceof BasicType) + il.append(createUnboxing((BasicType) baseArgumentType)); + else // ObjectTypes just have to be re-casted + il.append(factory.createCast(object, baseArgumentType)); + } + + /* May perform any of these conversions: casting, lowering, array-lowering. + * Conversion is generated into 'il'. + */ + private void convertParamToBase(InstructionList il, + String teamName, + String enclosingRoleName, + Type roleArgumentType, + Type baseArgumentType, + boolean loweringFlag) + { + if (roleArgumentType instanceof ObjectType + && baseArgumentType instanceof ObjectType) + { + if (loweringFlag) { + lowerObject(il, teamName, roleArgumentType, baseArgumentType); + } else { + if(logging) printLogMessage("Try to cast " + roleArgumentType + + " to " + baseArgumentType); + il.append(factory.createCast(roleArgumentType, + baseArgumentType)); + } + } else + if ( roleArgumentType instanceof BasicType + && baseArgumentType instanceof ObjectType) + { + il.append(createBoxing((BasicType)roleArgumentType)); + } else + if ( roleArgumentType instanceof ObjectType + && baseArgumentType instanceof BasicType) + { + il.append(createUnboxing((BasicType)baseArgumentType)); + } else + if ( roleArgumentType instanceof ArrayType + && baseArgumentType instanceof ArrayType) + { + lowerArray(il, teamName, enclosingRoleName, roleArgumentType, baseArgumentType); + } else { + throw new OTREInternalError("OTRE internal error:"+ + "No way to make types conform\n"+ + "role type "+roleArgumentType+ + " -> "+baseArgumentType); + } + } + + /* Lower one object from roleArgumentType to baseArgumentType. */ + private void lowerObject(InstructionList il, + String teamName, + Type roleArgumentType, + Type baseArgumentType) + { + String roleIfcName = ((ObjectType)roleArgumentType).getClassName(); + // use interface implementing role type for base-field access! + String roleClassName = ObjectTeamsTransformation.genImplementingRoleName(roleIfcName); + + int dollarIdx = roleClassName.lastIndexOf('$'); + if (dollarIdx == -1) { + throw new OTREInternalError("OTRE internal error:" + + "No way to make types conform\n" + + "role type " + roleArgumentType + + " -> " + baseArgumentType); + } + boolean isNested = dollarIdx != roleClassName.indexOf('$'); // is last == first? + String strengthenedRoleName = teamName + roleClassName.substring(dollarIdx); + + // baseArgumentType may be imprecise due to cast in param mapping, + // try a RBB instead to find the exact base type + RoleBaseBinding rbb = CallinBindingManager.getRoleBaseBinding(strengthenedRoleName); + if (rbb != null) + baseArgumentType = new ObjectType(rbb.getBaseClassName()); + + if (isNested) { + // cannot use field access here, because role strengthening would require resolved information + // so use the _OT$getBase() method instead: + short kind = (roleIfcName.lastIndexOf('$') == roleIfcName.lastIndexOf("$__OT__")) // last segment is roleclass? + ? Constants.INVOKEVIRTUAL + : Constants.INVOKEINTERFACE; + il.append(factory.createInvoke(roleIfcName, GET_BASE, baseArgumentType, new Type[0], kind)); + } else { + // access field via the role class: + il.append(factory.createCast(roleArgumentType, new ObjectType(strengthenedRoleName))); + + // optimized version directly accessing the field: + il.append(factory.createGetField(strengthenedRoleName, + BASE, + baseArgumentType)); + } + } + + /* Lower from roleArgumentType[] to baseArgumentType[]. */ + private void lowerArray(InstructionList il, String teamName, String enclosingRoleName, Type roleArgumentType, Type baseArgumentType) { + ArrayType array = (ArrayType)roleArgumentType; + String roleName =((ObjectType)array.getElementType()).getClassName(); + int dollarIdx = roleName.lastIndexOf('$'); + String pureRoleName = roleName.substring(dollarIdx + 1); + String transformMethodName = getArrayLoweringMethodName(pureRoleName, array.getDimensions()); + if (enclosingRoleName != null) { + // fetch team from enclosing role: + il.append(InstructionFactory.createThis()); + // FIXME(SH): is this$0 always correct (nesting!)?? + il.append(factory.createGetField(enclosingRoleName, "this$0", new ObjectType(teamName))); + } else { + // "this" is the team: + il.append(InstructionFactory.createThis()); + } + il.append(new SWAP()); // 'push' team instance below the role array + il.append(factory.createInvoke(teamName, + transformMethodName, + baseArgumentType, + new Type[]{roleArgumentType}, + Constants.INVOKEVIRTUAL)); + } + + private String getArrayLoweringMethodName(String roleName, int dimensions) { + return "_OT$transformArray" + roleName + "_OT$" + dimensions; + } + + public List<String> getInnerClassNames(ClassGen cg, ConstantPoolGen cpg) { + Attribute[] attributes = cg.getAttributes(); + LinkedList<String> innerClassNames = new LinkedList<String>(); + for (int i = 0; i < attributes.length; i++) { + Attribute actAttr = attributes[i]; + if (actAttr instanceof InnerClasses) { + InnerClass[] inners = ((InnerClasses)actAttr).getInnerClasses(); + for (int j=0; j<inners.length; j++) { + int name_index = inners[j].getInnerNameIndex(); + Constant name_c = cpg.getConstant(name_index); + String name = ((ConstantUtf8)name_c).getBytes(); + innerClassNames.add(name); + } + } + } + return innerClassNames; + } + + /** + * Create a monitor enter using a class literal + * @param mg target method to which a local variable is added which holds the monitor object + * @param il instruction list to append to + * @param class_name name of the class literal to use as monitor + * @param major major class file version, used to determine how to translate class literals + * @param cpg constant pool gen + * @return the slot index of the monitor local variable and the handle of the first generated instruction. + */ + protected Pair<Integer,InstructionHandle> addClassMonitorEnter(MethodGen mg, + InstructionList il, + String class_name, + int major, + ConstantPoolGen cpg) + { + int monitor; + InstructionHandle ih= + appendClassLiteral(il, class_name, major, cpg); + il.append(new DUP()); // for store and monitorenter + LocalVariableGen lg2= + mg.addLocalVariable("monitor", Type.OBJECT, il.getStart(), null); //$NON-NLS-1$ + monitor= lg2.getIndex(); + il.append(InstructionFactory.createStore(Type.OBJECT, monitor)); // for use by monitorexit + il.append(new MONITORENTER()); + return new Pair<Integer, InstructionHandle>(monitor, ih); + } + + /** + * Append an instruction sequence for loading the class literal for `class_name'. + * @param il instruction list to append to + * @param class_name class name of the class literal + * @param major java version as stored in the byte code. + * @param cpg for generating bytes + * @return the handle of the first instruction of the sequence. + */ + protected InstructionHandle appendClassLiteral(InstructionList il, + String class_name, + int major, + ConstantPoolGen cpg) + { + if (major >= 49) // java 5 + return il.append(new LDC(cpg.addClass(new ObjectType(class_name)))); + // pre java 5, do it the hard way: + // if (_OT$self_class$ != null) + InstructionHandle start= + il.append(factory.createFieldAccess(class_name, OTConstants.SELF_CLASS, classType, Constants.GETSTATIC)); + il.append(new DUP()); // keep a copy as a potential result + BranchInstruction checkLoaded= + InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); + il.append(checkLoaded); + il.append(new POP()); // discard null from above + // _OT$self_class$= Class.forName(<class_name>); // never fails, it is THIS class + il.append(new LDC(cpg.addString(class_name))); + il.append(factory.createInvoke("java.lang.Class", + "forName", + classType, + new Type[]{new ObjectType("java.lang.String")}, + Constants.INVOKESTATIC)); + il.append(new DUP()); // keep a copy as the result + il.append(factory.createFieldAccess(class_name, OTConstants.SELF_CLASS, classType, Constants.PUTSTATIC)); + + checkLoaded.setTarget(il.append(new NOP())); + return start; + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/StaticSliceBaseTransformation.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/StaticSliceBaseTransformation.java new file mode 100644 index 000000000..35259125c --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/StaticSliceBaseTransformation.java @@ -0,0 +1,757 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: StaticSliceBaseTransformation.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import de.fub.bytecode.classfile.*; +import de.fub.bytecode.generic.*; +import de.fub.bytecode.*; + +import org.eclipse.objectteams.otre.util.*; + + +/** + * Adds to base classes with callin bindings what can be literally pasted in: + * + * Fields: + * protected static Team[] _OT$activeTeams; + * protected static int[] _OT$activeTeamIDs; + * Methods: + * public static void _OT$addTeam(Team team, in team_id) + * public static void _OT$removeTeam(Team team) + * Static initialization (adds clinit method, if not yet presented): + * _OT$activeTeams = new Team[0]; + * _OT$activeTeamIDs = new int[0]; + * + * + * @version $Id: StaticSliceBaseTransformation.java 23408 2010-02-03 18:07:35Z stephan $ + * @author Christine Hundt + * @author Stephan Herrmann + */ +public class StaticSliceBaseTransformation + extends ObjectTeamsTransformation +{ + static final String _OT_ACTIVE_TEAMS= "_OT$activeTeams"; //$NON-NLS-1$ + static final String _OT_ACTIVE_TEAM_IDS= "_OT$activeTeamIDs"; //$NON-NLS-1$ + + public StaticSliceBaseTransformation(SharedState state) { this(null, state); } + public StaticSliceBaseTransformation(ClassLoader loader, SharedState state) { super(loader, state); } + + public void doTransformCode(ClassGen cg) { + if (cg.isInterface()) + return; // can't add implementation + // IMPLICIT_INHERITANCE + + String class_name = cg.getClassName(); + if (!CallinBindingManager.isBoundBaseClass(class_name) + /*&& !CallinBindingManager.containsBoundBaseInterface(cg.getInterfaceNames())*/) + return; //only (base-)classes with callin bindings need this addition + /*// this only works if teams are only added at the root bound base class + if (CallinBindingManager.isBoundBaseClass(cg.getSuperclassName())) + continue; // team infrastructur already added by super class + */ + if (CallinBindingManager.hasBoundBaseParent(class_name)) + return; // team infrastructure already has been added to a super class + + ConstantPoolGen cpg = cg.getConstantPool(); + factory = new InstructionFactory(cpg); + addStaticInitializations(cg, cpg); + } + + /** + * @param ce + * @param cg + */ + public void doTransformInterface(ClassEnhancer ce, ClassGen cg) { + String class_name = cg.getClassName(); + ConstantPoolGen cpg = cg.getConstantPool(); + + checkReadClassAttributes(ce, cg, class_name, cpg); + + if (cg.isInterface()) + return; // can't add implementation + if (state.interfaceTransformedClasses.contains(class_name)) + return; // class has already been transformed by this transformer + // IMPLICIT_INHERITANCE + if (!CallinBindingManager.isBoundBaseClass(class_name) + /*&& !CallinBindingManager.containsBoundBaseInterface(cg.getInterfaceNames())*/) + return; //only (base-)classes with callin bindings need this addition + /*// this only works if teams are only added at the root bound base class + if (CallinBindingManager.isBoundBaseClass(cg.getSuperclassName())) + continue; // team infrastructur already added by super class + */ + if (CallinBindingManager.hasBoundBaseParent(class_name)) + return; // team infrastructure already has been added to a super class + if(logging) printLogMessage("StaticSliceBaseTransformer transforms "+ class_name); + + if(CallinBindingManager.isRole(class_name)) { + addImplicitSubclassNotificationInfrastructure(ce, cg); + } + + // addition of the fields '_OT$activeTeams' and '_OT$activeTeamIDs': + int accessFlags = Constants.ACC_PROTECTED | Constants.ACC_STATIC; + + FieldGen activeTeamsField = new FieldGen(accessFlags, teamArray, _OT_ACTIVE_TEAMS, cpg); + ce.addField(activeTeamsField.getField(), cg); + // generated global variable: protected static Team[] _OT$activeTeams; + + FieldGen activeTeamIDsField = new FieldGen(accessFlags, intArray, _OT_ACTIVE_TEAM_IDS, cpg); + ce.addField(activeTeamIDsField.getField(), cg); + // generated global variable: protected static int[] _OT$activeTeamIDs; + + factory = new InstructionFactory(cpg); + + // implementation of method '_OT$addTeam' + ce.addMethod(genAddTeam(class_name, cg.getMajor(), cpg).getMethod(), cg); + + // implementation of method '_OT$removeTeam': + ce.addMethod(genRemoveTeam(class_name, cg.getMajor(), cpg).getMethod(), cg); + + // implementation of the static class initialization method '<clinit>': + /* Adding static initializations requires the addition of the clinit method, if not yet presented. + * This requires synchronization with other transformers (TeamInterfaceImplementer) + * which may do the same this. This is done via 'TeamIdDispenser.clinitAdded(class_name)'. + */ + Method clinit = cg.containsMethod(Constants.STATIC_INITIALIZER_NAME, "()V"); + + if (clinit != null || TeamIdDispenser.clinitAdded(class_name, loader)) { + // the clinit-Method only has to be extended by the code transformation of this transformer + state.interfaceTransformedClasses.add(class_name); + return; + } + + InstructionList il = new InstructionList(); + MethodGen clinitMethod = new MethodGen(Constants.ACC_STATIC, + Type.VOID, + Type.NO_ARGS, + new String[] { }, + Constants.STATIC_INITIALIZER_NAME, class_name, + il, cpg); + + il.append(InstructionFactory.createReturn(Type.VOID)); + + clinitMethod.setMaxStack(); + clinitMethod.setMaxLocals(); + + ce.addMethod(clinitMethod.getMethod(), cg); + +/***********************************************************************************************************/ + il.dispose(); + state.interfaceTransformedClasses.add(class_name); + } + + /* Code to be generated: + public static void _OT$addTeam(Team team, int teamID) + { + int l; + // Second part of the "if" below: Avoid duplicate entry of the same team. + // Assumption (see r17473): this strategy assumes that a team instance + // can only be duplicated directly after the first registration. + // Reason: registerAtBases() calling addTeam() for multiple sub-bases + // which share the same _OT$activateTeams et al fields from a common super-base. + if((l = _OT$activeTeams.length) != 0 && _OT$activeTeams[0] == team) + { + return; + } else + { + Team newTeams[] = new Team[l + 1]; + int newTeamIDs[] = new int[l + 1]; + System.arraycopy(_OT$activeTeams, 0, newTeams, 1, l); + System.arraycopy(_OT$activeTeamIDs, 0, newTeamIDs, 1, l); + _OT$activeTeams = newTeams; + _OT$activeTeamIDs = newTeamIDs; + _OT$activeTeams[0] = team; + _OT$activeTeamIDs[0] = teamID; + return; + } + } + */ + private MethodGen genAddTeam(String class_name, int major, ConstantPoolGen cpg) { + LocalVariableGen lg; + InstructionList il; + il = new InstructionList(); + MethodGen addTeamMethod = new MethodGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNCHRONIZED, + Type.VOID, + new Type[] { teamType, Type.INT }, + new String[] { "team", "teamID" }, + "_OT$addTeam", class_name, + il, cpg); + // synchronized (BaseClass.class) { + int monitor = addClassMonitorEnter(addTeamMethod, il, class_name, major, cpg).first; + + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(new ARRAYLENGTH()); + lg = addTeamMethod.addLocalVariable("l", Type.INT, null, null); + int l = lg.getIndex(); + il.append(new DUP()); + lg.setStart(il.append(InstructionFactory.createStore(Type.INT, l))); + // generated: int l = _OT$activeTeams.length; + + // add duplication check: + BranchHandle emptyArray = + il.append(new IFEQ(null)); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(new ICONST(0)); + il.append(InstructionFactory.createArrayLoad(teamType)); + il.append(new ALOAD(0)); + BranchHandle noDuplication = + il.append(new IF_ACMPNE(null)); + GOTO earlyExit = new GOTO(null); + il.append(earlyExit); + InstructionHandle skipReturn = il.append(new NOP()); + emptyArray.setTarget(skipReturn); + noDuplication.setTarget(skipReturn); + // generated: if (l > 0 && _OT$activeTeams[0] == team ) return; + + lg = addTeamMethod.addLocalVariable("newTeams", teamArray, null, null); + int newTeams = lg.getIndex(); + il.append(InstructionFactory.createLoad(Type.INT, l)); + il.append(new ICONST(1)); + il.append(new IADD()); + il.append((Instruction)factory.createNewArray(teamType, (short)1)); + //this are very strange (but necessary!?) casts... + lg.setStart(il.append(InstructionFactory.createStore(teamArray, newTeams))); + // generated: Team[] newTeams = new Team[l+1]; + + lg = addTeamMethod.addLocalVariable("newTeamIDs", intArray, null, null); + int newTeamIDs = lg.getIndex(); + il.append(InstructionFactory.createLoad(Type.INT, l)); + il.append(new ICONST(1)); + il.append(new IADD()); + il.append((Instruction)factory.createNewArray(Type.INT, (short)1)); + //this are very strange (but necessary!?) casts... + lg.setStart(il.append(InstructionFactory.createStore(intArray, newTeamIDs))); + // generated: int[] newTeamIDs = new int[l+1]; + + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(new ICONST(0)); + il.append(InstructionFactory.createLoad(teamArray, newTeams)); + il.append(new ICONST(1)); + il.append(InstructionFactory.createLoad(Type.INT, l)); + ObjectType object = new ObjectType("java.lang.Object"); + il.append(factory.createInvoke("java.lang.System", "arraycopy", + Type.VOID, + new Type[] {object, Type.INT, object, Type.INT, Type.INT }, + Constants.INVOKESTATIC)); + // generated: System.arraycopy(_OT$activeTeams, 0, newTeams, 1, l); + + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAM_IDS, intArray, Constants.GETSTATIC)); + il.append(new ICONST(0)); + il.append(InstructionFactory.createLoad(intArray, newTeamIDs)); + il.append(new ICONST(1)); + il.append(InstructionFactory.createLoad(Type.INT, l)); + il.append(factory.createInvoke("java.lang.System", "arraycopy", + Type.VOID, + new Type[] {object, Type.INT, object, Type.INT, Type.INT }, + Constants.INVOKESTATIC)); + // generated: System.arraycopy(_OT$activeTeamIDs, 0, newTeamIDs, 1, l); + + il.append(InstructionFactory.createLoad(teamArray, newTeams)); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.PUTSTATIC)); + // generated: _OT$activeTeams = newTeams; + + il.append(InstructionFactory.createLoad(intArray, newTeamIDs)); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAM_IDS, intArray, Constants.PUTSTATIC)); + // generated: _OT$activeTeamIDs = newTeamIDs; + + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(new ICONST(0)); + il.append(new ALOAD(0)); + il.append(InstructionFactory.createArrayStore(teamType)); + // generated: _OT$activeTeams[0] = team; + + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAM_IDS, intArray, Constants.GETSTATIC)); + il.append(new ICONST(0)); + il.append(new ILOAD(1)); + il.append(InstructionFactory.createArrayStore(Type.INT)); + // generated: _OT$activeTeamIDs[0] = teamID; + + if (CallinBindingManager.isRole(class_name)) { + il.append(new ALOAD(0)); + il.append(new ILOAD(1)); + il.append(factory.createInvoke(class_name, "_OT$activateNotify", Type.VOID, new Type[] { teamType, Type.INT }, Constants.INVOKESTATIC)); + // generated: _OT$activateNotify(team, teamID); + } + + // No more access to array fields, release monitor: + InstructionHandle exitSequence = + il.append(InstructionFactory.createLoad(Type.OBJECT, monitor)); + il.append(new MONITOREXIT()); + earlyExit.setTarget(exitSequence); + + il.append(InstructionFactory.createReturn(Type.VOID)); + + addTeamMethod.setMaxStack(); + addTeamMethod.setMaxLocals(); + addTeamMethod.removeNOPs(); + return addTeamMethod; + } + + /* Code to be generated: + public static void _OT$removeTeam(Team team) + { + int l; + if((l = _OT$activeTeams.length) == 0) + return; + boolean found = false; + int newLen= l-1; + Team newTeams[] = new Team[newLen]; + int newTeamIDs[] = new int[newLen]; + for(int i = 0; i < l; i++) + if(!found) + { + if(_OT$activeTeams[i] == team) + { + found = true; + } else if (i<newLen) { // coded as: jump if (newLen<=i) + { + newTeams[i] = _OT$activeTeams[i]; + newTeamIDs[i] = _OT$activeTeamIDs[i]; + } + } else + { + newTeams[i - 1] = _OT$activeTeams[i]; + newTeamIDs[i - 1] = _OT$activeTeamIDs[i]; + } + + if(found) + { + _OT$activeTeams = newTeams; + _OT$activeTeamIDs = newTeamIDs; + } + } + */ + private MethodGen genRemoveTeam(String class_name, int major, ConstantPoolGen cpg) + { + LocalVariableGen lg; + InstructionList il; + int l; + BranchHandle emptyArray; + int newTeams; + int newTeamIDs; + il = new InstructionList(); + MethodGen removeTeamMethod = new MethodGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNCHRONIZED, + Type.VOID, + new Type[] { teamType }, + new String[] { "team" }, + "_OT$removeTeam", class_name, + il, cpg); + + // synchronized (BaseClass.class) { + int monitor = addClassMonitorEnter(removeTeamMethod, il, class_name, major, cpg).first; + + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(new ARRAYLENGTH()); + lg = removeTeamMethod.addLocalVariable("l", Type.INT, null, null); + //int l = lg.getIndex(); + l = lg.getIndex(); + il.append(new DUP()); + lg.setStart(il.append(InstructionFactory.createStore(Type.INT, l))); + // generated: int l = _OT$activeTeams.length; + + emptyArray = il.append(new IFNE(null)); + GOTO earlyExit = new GOTO(null); + il.append(earlyExit); + emptyArray.setTarget(il.append(new NOP())); + // generated: if (l == 0) return; + + lg = removeTeamMethod.addLocalVariable("found", Type.BOOLEAN, null, null); + int found = lg.getIndex(); + il.append(new ICONST(0)); + lg.setStart(il.append(InstructionFactory.createStore(Type.BOOLEAN, found))); + // generated: boolean found = false; + + lg = removeTeamMethod.addLocalVariable("newTeams", teamArray, null, null); + newTeams = lg.getIndex(); + il.append(InstructionFactory.createLoad(Type.INT, l)); + // [SH]: variable newLen + LocalVariableGen lgNewLen= removeTeamMethod.addLocalVariable("newLen", Type.INT, il.getEnd(), null); + il.append(new ICONST(1)); + il.append(new ISUB()); + // [SH] store for later use: + il.append(new DUP()); + il.append(InstructionFactory.createStore(Type.INT, lgNewLen.getIndex())); + // [HS] generated: "newLen= l-1;" + il.append((Instruction)factory.createNewArray(teamType, (short)1)); + lg.setStart(il.append(InstructionFactory.createStore(teamArray, newTeams))); + // generated: Team[] newTeams = new Team[newLen]; + + lg = removeTeamMethod.addLocalVariable("newTeamIDs", intArray, null, null); + //int newTeamIDs = lg.getIndex(); + newTeamIDs = lg.getIndex(); + //[SH]: + il.append(InstructionFactory.createLoad(Type.INT, lgNewLen.getIndex())); + il.append((Instruction)factory.createNewArray(Type.INT, (short)1)); + lg.setStart(il.append(InstructionFactory.createStore(intArray, newTeamIDs))); + // generated: int[] newTeamIDs = new int[newLen]; + + // start for-loop + lg = removeTeamMethod.addLocalVariable("i", Type.INT, null, null); + int loopCounter = lg.getIndex(); + il.append(new ICONST(0)); + lg.setStart(il.append(InstructionFactory.createStore(Type.INT, loopCounter))); + GOTO try_leave_loop = new GOTO(null); + il.append(try_leave_loop); + InstructionHandle i_lower_l = il.append(new NOP()); + // loop body: + il.append(InstructionFactory.createLoad(Type.BOOLEAN, found)); + IFNE already_found = new IFNE(null); + il.append(already_found); + + // outer if part: + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(new ILOAD(loopCounter)); + il.append(InstructionFactory.createArrayLoad(teamType)); + il.append(new ALOAD(0)); //first parameter + IF_ACMPNE teams_not_equal = new IF_ACMPNE(null); + il.append(teams_not_equal); + + // inner if part: + il.append(new ICONST(1)); + il.append(InstructionFactory.createStore(Type.BOOLEAN, found)); + GOTO skip_outer_else_part = new GOTO(null); + il.append(skip_outer_else_part); + + // inner else part: + teams_not_equal.setTarget(il.append(new NOP())); + + // [SH] sanity check: + il.append(InstructionFactory.createLoad(Type.INT, lgNewLen.getIndex())); + il.append(new ILOAD(loopCounter)); + IF_ICMPLE if_len_le_i= new IF_ICMPLE(null); + il.append(if_len_le_i); + // [HS] generated: else if (!(newLen <= i)) { + il.append(new ALOAD(newTeams)); + il.append(new ILOAD(loopCounter)); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(new ILOAD(loopCounter)); + il.append(InstructionFactory.createArrayLoad(teamType)); + il.append(new AASTORE()); + // generated: newTeams[i] = _OT$activeTeams[i]; + + il.append(new ALOAD(newTeamIDs)); + il.append(new ILOAD(loopCounter)); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAM_IDS, intArray, Constants.GETSTATIC)); + il.append(new ILOAD(loopCounter)); + il.append(InstructionFactory.createArrayLoad(Type.INT)); + il.append(new IASTORE()); + // generated: newTeamIDs[i] = _OT$activeTeamIDs[i]; + + GOTO end_of_loop = new GOTO(null); + il.append(end_of_loop); + + // outer else part: + already_found.setTarget(il.append(new NOP())); + il.append(new ALOAD(newTeams)); + il.append(new ILOAD(loopCounter)); + il.append(new ICONST(1)); + il.append(new ISUB()); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC)); + il.append(new ILOAD(loopCounter)); + il.append(InstructionFactory.createArrayLoad(teamType)); + il.append(new AASTORE()); + // generated: newTeams[i-1] = _OT$activeTeams[i]; + + il.append(new ALOAD(newTeamIDs)); + il.append(new ILOAD(loopCounter)); + il.append(new ICONST(1)); + il.append(new ISUB()); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAM_IDS, intArray, Constants.GETSTATIC)); + il.append(new ILOAD(loopCounter)); + il.append(InstructionFactory.createArrayLoad(Type.INT)); + il.append(new IASTORE()); + // generated: newTeamIDs[i-1] = _OT$activeTeamIDs[i]; + + skip_outer_else_part.setTarget(il.append(new NOP())); + // [SH] connect "else if" from above: + if_len_le_i.setTarget(il.getEnd()); + end_of_loop.setTarget(il.append(new IINC(loopCounter, 1))); + try_leave_loop.setTarget(il.append(InstructionFactory.createLoad(Type.INT, loopCounter))); + il.append(new ILOAD(l)); + il.append(new IF_ICMPLT(i_lower_l)); + // end for-loop + + il.append(InstructionFactory.createLoad(Type.BOOLEAN, found)); + BranchHandle notFound = il.append(new IFEQ(null)); + // generated: if (found) { + + il.append(InstructionFactory.createLoad(teamArray, newTeams)); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.PUTSTATIC)); + // generated: _OT$activeTeams = newTeams; + + il.append(InstructionFactory.createLoad(intArray, newTeamIDs)); + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAM_IDS, intArray, Constants.PUTSTATIC)); + // generated: _OT$activeTeamIDs = newTeamIDs; + + if (CallinBindingManager.isRole(class_name)) { + il.append(new ALOAD(0)); + il.append(factory.createInvoke(class_name, "_OT$deactivateNotify", Type.VOID, new Type[] { teamType }, Constants.INVOKESTATIC)); + // generated: _OT$deactivateNotify(team); + } + + // No more access to array fields, release monitor: + InstructionHandle exitSequence = + il.append(InstructionFactory.createLoad(Type.OBJECT, monitor)); + il.append(new MONITOREXIT()); + earlyExit.setTarget(exitSequence); + notFound.setTarget(exitSequence); + + il.append(InstructionFactory.createReturn(Type.VOID)); + + removeTeamMethod.setMaxStack(); + removeTeamMethod.setMaxLocals(); + removeTeamMethod.removeNOPs(); + return removeTeamMethod; + } + + /** + * Add infrastructure for implicit subclasses to register and be notified. This 'observer' mechanism + * is necessary, because an implicit subclass does not inherit the addition/removal + * of a team at the implicit superclass. + * @param ce The ClassEnhancer object to add methods and fields. + * @param cg The ClassGen object representing the current class. + */ + private void addImplicitSubclassNotificationInfrastructure(ClassEnhancer ce, ClassGen cg) { + + ConstantPoolGen cpg = cg.getConstantPool(); + factory = new InstructionFactory(cpg); + String class_name = cg.getClassName(); + ObjectType linkedListType = new ObjectType("java.util.LinkedList"); + int accessFlags = Constants.ACC_PROTECTED | Constants.ACC_STATIC; + FieldGen teamRegistrationObserversField = new FieldGen(accessFlags, linkedListType, "_OT$teamRegistrationObservers", cpg); + ce.addField(teamRegistrationObserversField.getField(), cg); + +/***********************************************************************************************************/ + + InstructionList il = new InstructionList(); + + MethodGen observerRegistrationMethod = new MethodGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC, + Type.VOID, + new Type[] { classType }, + new String[] { "implicitSubClass" }, + "_OT$registerObserver", class_name, + il, cpg); + + il.append(factory.createFieldAccess(class_name, "_OT$teamRegistrationObservers", linkedListType, Constants.GETSTATIC)); + il.append(InstructionFactory.createLoad(classType, 0)); + il.append(factory.createInvoke("java.util.LinkedList", "add", Type.BOOLEAN, new Type[] { Type.OBJECT }, Constants.INVOKEVIRTUAL)); + il.append(new POP()); + il.append(new RETURN()); + + observerRegistrationMethod.setMaxStack(2); + observerRegistrationMethod.setMaxLocals(); + + ce.addMethod(observerRegistrationMethod.getMethod(), cg); +/***********************************************************************************************************/ + il = new InstructionList(); + + MethodGen activateNotifyMethod = new MethodGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC, + Type.VOID, + new Type[] { teamType, Type.INT }, + new String[] { "team", "teamID" }, + "_OT$activateNotify", class_name, + il, cpg); + + createNotifyMethodImplementation(activateNotifyMethod, cpg, class_name); + + ce.addMethod(activateNotifyMethod.getMethod(), cg); +/***********************************************************************************************************/ + il = new InstructionList(); + + MethodGen deactivateNotifyMethod = new MethodGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC, + Type.VOID, + new Type[] { teamType }, + new String[] { "team" }, + "_OT$deactivateNotify", class_name, + il, cpg); + createNotifyMethodImplementation(deactivateNotifyMethod, cpg, class_name); + + ce.addMethod(deactivateNotifyMethod.getMethod(), cg); + } + + + /** + * Create implementation of methods '_OT$activateNotify' and _OT$deactivateNotify'. + * @param notifyMethod Method which has to be implemented. + * @param cpg Corresponding constant pool. + * @param class_name Name of the class for wich to implement the method. + */ + private void createNotifyMethodImplementation(MethodGen notifyMethod, ConstantPoolGen cpg, String class_name) { + boolean isActivateMethod = notifyMethod.getName().equals("_OT$activateNotify"); +// int methodArgs = isActivateMethod ? 2 : 1; + + int iteratorIdx = isActivateMethod ? 2 : 1; + int curClassIdx = isActivateMethod ? 3 : 2; + int anotherIdx1 = isActivateMethod ? 4 : 3; + int anotherIdx2 = isActivateMethod ? 5 : 4; + String methodToInvoke = isActivateMethod ? "_OT$addTeam" : "_OT$removeTeam"; + + InstructionList il = notifyMethod.getInstructionList(); + il.append(factory.createFieldAccess(class_name, "_OT$teamRegistrationObservers", new ObjectType("java.util.LinkedList"), Constants.GETSTATIC)); + BranchInstruction ifnonnull_3 = InstructionFactory.createBranchInstruction(Constants.IFNONNULL, null); + il.append(ifnonnull_3); + il.append(InstructionFactory.createReturn(Type.VOID)); + InstructionHandle ih_7 = il.append(factory.createFieldAccess(class_name, "_OT$teamRegistrationObservers", new ObjectType("java.util.LinkedList"), Constants.GETSTATIC)); + il.append(factory.createInvoke("java.util.LinkedList", "iterator", new ObjectType("java.util.Iterator"), Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + il.append(InstructionFactory.createStore(Type.OBJECT,iteratorIdx)); + InstructionHandle ih_14 = il.append(InstructionFactory.createLoad(Type.OBJECT,iteratorIdx)); + il.append(factory.createInvoke("java.util.Iterator", "hasNext", Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEINTERFACE)); + BranchInstruction ifeq_20 = InstructionFactory.createBranchInstruction(Constants.IFEQ, null); + il.append(ifeq_20); + il.append(InstructionFactory.createLoad(Type.OBJECT,iteratorIdx)); + il.append(factory.createInvoke("java.util.Iterator", "next", Type.OBJECT, Type.NO_ARGS, Constants.INVOKEINTERFACE)); + il.append(factory.createCheckCast(classType)); + il.append(InstructionFactory.createStore(Type.OBJECT,curClassIdx)); + il.append(InstructionConstants.ACONST_NULL); + il.append(InstructionFactory.createStore(Type.OBJECT,anotherIdx1)); + InstructionHandle ih_36 = il.append(InstructionFactory.createLoad(Type.OBJECT,curClassIdx)); + il.append(new PUSH(cpg, methodToInvoke)); + il.append(new PUSH(cpg,iteratorIdx)); + il.append((Instruction)factory.createNewArray(classType, (short) 1)); + il.append(InstructionConstants.DUP); + + il.append(new PUSH(cpg, 0)); + il.append(new PUSH(cpg, OTConstants.teamName)); + il.append(factory.createInvoke("java.lang.Class", "forName", + classType, new Type[] { Type.STRING }, + Constants.INVOKESTATIC)); + + il.append(InstructionConstants.AASTORE); + if (isActivateMethod){ // add the integer argument: + il.append(InstructionConstants.DUP); + il.append(new PUSH(cpg, 1)); + il.append(factory.createFieldAccess("java.lang.Integer", "TYPE", classType, Constants.GETSTATIC)); + il.append(InstructionConstants.AASTORE); + } + il.append(factory.createInvoke("java.lang.Class", "getMethod", new ObjectType("java.lang.reflect.Method"), new Type[] { Type.STRING, new ArrayType(classType, 1) }, Constants.INVOKEVIRTUAL)); + InstructionHandle ih_76 = il.append(InstructionFactory.createStore(Type.OBJECT,anotherIdx1)); + BranchInstruction goto_78 = InstructionFactory.createBranchInstruction(Constants.GOTO, null); + il.append(goto_78); + InstructionHandle ih_81 = il.append(InstructionFactory.createStore(Type.OBJECT,anotherIdx2)); + il.append(factory.createFieldAccess("java.lang.System", "err", new ObjectType("java.io.PrintStream"), Constants.GETSTATIC)); + il.append(new PUSH(cpg, "activateNotifyMethod not found!")); + il.append(factory.createInvoke("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL)); + InstructionHandle ih_91 = il.append(InstructionFactory.createLoad(Type.OBJECT,anotherIdx1)); + BranchInstruction ifnull_93 = InstructionFactory.createBranchInstruction(Constants.IFNULL, null); + il.append(ifnull_93); + il.append(InstructionFactory.createLoad(Type.OBJECT,anotherIdx1)); + il.append(InstructionConstants.ACONST_NULL); + il.append(new PUSH(cpg,iteratorIdx)); + il.append((Instruction)factory.createNewArray(Type.OBJECT, (short) 1)); + il.append(InstructionConstants.DUP); + il.append(new PUSH(cpg, 0)); + il.append(InstructionFactory.createLoad(Type.OBJECT, 0)); + il.append(InstructionConstants.AASTORE); + + if (isActivateMethod) { // load the integer argument: + il.append(InstructionConstants.DUP); + il.append(new PUSH(cpg, 1)); + il.append(factory.createNew("java.lang.Integer")); + il.append(InstructionConstants.DUP); + il.append(InstructionFactory.createLoad(Type.INT, 1)); + il.append(factory.createInvoke("java.lang.Integer", Constants.CONSTRUCTOR_NAME, Type.VOID, new Type[] { Type.INT }, Constants.INVOKESPECIAL)); + il.append(InstructionConstants.AASTORE); + } + il.append(factory.createInvoke("java.lang.reflect.Method", "invoke", Type.OBJECT, new Type[] { Type.OBJECT, new ArrayType(Type.OBJECT, 1) }, Constants.INVOKEVIRTUAL)); + InstructionHandle ih_121 = il.append(InstructionConstants.POP); + InstructionHandle ih_122; + BranchInstruction goto_122 = InstructionFactory.createBranchInstruction(Constants.GOTO, ih_14); + ih_122 = il.append(goto_122); + InstructionHandle ih_125 = il.append(InstructionFactory.createStore(Type.OBJECT,anotherIdx2)); + il.append(factory.createFieldAccess("java.lang.System", "err", new ObjectType("java.io.PrintStream"), Constants.GETSTATIC)); + il.append(new PUSH(cpg, "Can not call activateNotifyMethod!")); + il.append(factory.createInvoke("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL)); + BranchInstruction goto_135 = InstructionFactory.createBranchInstruction(Constants.GOTO, ih_14); + il.append(goto_135); + InstructionHandle ih_138 = il.append(InstructionFactory.createStore(Type.OBJECT,anotherIdx2)); + il.append(factory.createFieldAccess("java.lang.System", "err", new ObjectType("java.io.PrintStream"), Constants.GETSTATIC)); + il.append(new PUSH(cpg, "InvocationTargetException")); + il.append(factory.createInvoke("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL)); + BranchInstruction goto_148 = InstructionFactory.createBranchInstruction(Constants.GOTO, ih_14); + il.append(goto_148); + InstructionHandle ih_151 = il.append(InstructionFactory.createReturn(Type.VOID)); + ifnonnull_3.setTarget(ih_7); + ifeq_20.setTarget(ih_151); + goto_78.setTarget(ih_91); + ifnull_93.setTarget(ih_122); + notifyMethod.addExceptionHandler(ih_36, ih_76, ih_81, new ObjectType("java.lang.NoSuchMethodException")); + notifyMethod.addExceptionHandler(ih_91, ih_121, ih_125, new ObjectType("java.lang.IllegalAccessException")); + notifyMethod.addExceptionHandler(ih_91, ih_121, ih_138, new ObjectType("java.lang.reflect.InvocationTargetException")); + + notifyMethod.setMaxStack(); + notifyMethod.setMaxLocals(); + } + + /** + * Adds initialization of the team array and the team index array to the static initializer of this class. + * If this base class is a role at the same time, then also add initialization of the observer list. + * @param cg The representation of the class. + * @param cpg The constant pool of the class. + */ + private void addStaticInitializations(ClassGen cg, ConstantPoolGen cpg) { + Method clinitMethod = cg.containsMethod(Constants.STATIC_INITIALIZER_NAME, "()V"); + /* The clinit method always exists at this moment, because it has been added + * by the interface transformer part of this transformer if necessary. + */ + MethodGen mg = new MethodGen(clinitMethod, cg.getClassName(), cpg); + InstructionList il = mg.getInstructionList(); + // add static initialization for added static fields at start of the <clinit> method: + il.insert(inizializeStaticFields(cg.getClassName())); + mg.setMaxStack(); + mg.setMaxLocals(); + Method newClinit = mg.getMethod(); + cg.replaceMethod(clinitMethod, newClinit); + il.dispose(); + // Reuse instruction handles + } + + /** + * @param class_name + * @return + */ + private InstructionList inizializeStaticFields(String class_name) { + // STATIC_PARTS_TODO : in base: static initialization of team fields + InstructionList il = new InstructionList(); + il.append(new ICONST(0)); + il.append((Instruction)factory.createNewArray(teamType, (short)1)); + //this are very strange (but necessary!?) casts... + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.PUTSTATIC)); + //generated: _OT$activeTeams = new Team[0]; + + il.append(new ICONST(0)); + il.append((Instruction)factory.createNewArray(Type.INT, (short)1)); + //this are very strange (but necessary!?) casts... + il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAM_IDS, intArray, Constants.PUTSTATIC)); + //generated: _OT$activeTeamIDs = new int[0]; + + if (CallinBindingManager.isRole(class_name)) { + ObjectType linkedListType = new ObjectType("java.util.LinkedList"); + il.append(factory.createNew(linkedListType)); + il.append(new DUP()); + il.append(factory.createInvoke("java.util.LinkedList", + Constants.CONSTRUCTOR_NAME, + Type.VOID, Type.NO_ARGS, + Constants.INVOKESPECIAL)); + + il.append(factory.createFieldAccess(class_name, + "_OT$teamRegistrationObservers", + linkedListType, Constants.PUTSTATIC )); + + //generated: _OT$teamRegistrationObservers = new LinkedList(); + } + return il; + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/SubBoundBaseMethodRedefinition.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/SubBoundBaseMethodRedefinition.java new file mode 100644 index 000000000..104374d73 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/SubBoundBaseMethodRedefinition.java @@ -0,0 +1,175 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: SubBoundBaseMethodRedefinition.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import de.fub.bytecode.classfile.*; +import de.fub.bytecode.generic.*; +import de.fub.bytecode.*; + +import java.util.*; + +import org.eclipse.objectteams.otre.util.*; + +/** + * Redefines inherited (and not redefined) base methods in subclasses with a + * call to the super method if they are bound for the subclass only. This is + * done in order to provide a place for the BaseMethodTransformer to weave in + * the callin code. + * + * @version $Id: SubBoundBaseMethodRedefinition.java 23408 2010-02-03 18:07:35Z stephan $ + * @author Christine Hundt + * @author Stephan Herrmann + */ + +public class SubBoundBaseMethodRedefinition + extends ObjectTeamsTransformation { + + public SubBoundBaseMethodRedefinition(SharedState state) { this(null, state); } + public SubBoundBaseMethodRedefinition(ClassLoader loader, SharedState state) { super(loader, state); } + + /** + * Main entry for this transformer. + */ + public void doTransformInterface(ClassEnhancer ce, ClassGen cg) { + String class_name = cg.getClassName(); + ConstantPoolGen cpg = cg.getConstantPool(); + + factory = new InstructionFactory(cg); + + checkReadClassAttributes(ce, cg, class_name, cpg); + + // if class is already transformed by this transformer + if (state.interfaceTransformedClasses.contains(class_name)) { + return; + } + + List<MethodBinding> mbsForClass = CallinBindingManager + .getMethodBindingsForBaseClass(class_name); + if (mbsForClass.isEmpty()) { + return; // no bindings for this base class + } + + List<MethodBinding> inheritedBoundMethods = getInheritedBoundMethods(mbsForClass, cg); + + addSubBoundMethodRedefinitions(inheritedBoundMethods, ce, cg); + state.interfaceTransformedClasses.add(class_name); + } + + /** + * Adds redefinitions with calls to the super method to all methods + * contained in the 'inheritedBoundMethods' list. + * + * @param inheritedBoundMethods + * The list of method bindings for inherited methods. + * @param ce + * ClassEnhancer with the extension set of this class. + * @param cg + * The ClassGen object for the transformed class. + */ + private void addSubBoundMethodRedefinitions(List<MethodBinding> inheritedBoundMethods, + ClassEnhancer ce, ClassGen cg) { + List<String> alreadyAddedRedefinitions = new LinkedList<String>(); + + Iterator<MethodBinding> it = inheritedBoundMethods.iterator(); + while (it.hasNext()) { + MethodBinding mb = it.next(); + String baseMethodKey = mb.getBaseMethodName() + "." + + mb.getBaseMethodSignature(); + + if (alreadyAddedRedefinitions.contains(baseMethodKey)) { + continue; + } + Method m = genMethodRedefinition(mb, cg); + + ce.addMethod(m, cg); + if(logging) printLogMessage("Added " + baseMethodKey + " to " + cg.getClassName()); + alreadyAddedRedefinitions.add(baseMethodKey); + } + } + + /** + * Generates a (redefining) method, which just calls its super version. + * + * @param mb + * A method binding containing the method to be redefined. + * @param cg + * The ClassGen object for the transformed class. + * @return The generated method. + */ + private Method genMethodRedefinition(MethodBinding mb, ClassGen cg) { + boolean staticMethod = mb.hasStaticBaseMethod(); + short invocationKind = staticMethod ? Constants.INVOKESTATIC : Constants.INVOKESPECIAL; + + String methodName = mb.getBaseMethodName(); + String methodSignature = mb.getBaseMethodSignature(); + String className = mb.getBaseClassName(); + Type returnType = Type.getReturnType(methodSignature); + Type[] argTypes = Type.getArgumentTypes(methodSignature); + InstructionList il = new InstructionList(); + + int accFlags = Constants.ACC_PUBLIC; + if (staticMethod) { + accFlags = accFlags | Constants.ACC_STATIC; + } + MethodGen redefinition = new MethodGen(accFlags, returnType, argTypes, + null, methodName, className, il, + cg.getConstantPool()); + + if(!staticMethod){ + il.append(InstructionFactory.createThis()); + } + // load all arguments: + int index = 1; + for (int i = 0; i < argTypes.length; i++) { + il.append(InstructionFactory.createLoad(argTypes[i], index)); + index += argTypes[i].getSize(); + } + il.append(factory.createInvoke(cg.getSuperclassName(), methodName, + returnType, argTypes, invocationKind)); + il.append(InstructionFactory.createReturn(returnType)); + + redefinition.removeNOPs(); + il.setPositions(); + redefinition.setMaxStack(); + redefinition.setMaxLocals(); + return redefinition.getMethod(); + } + + /** + * Selects all method bindings from the 'mbsForClass' list which methods are + * inherited only (not defined in the class itself). + * + * @param mbsForClass + * The method bindings of the transformed class. + * @param cg + * The ClassGen object for the transformed class. + * @return A sublist of 'mbsForClass' containing all bindings for methods + * which are inherited only. + * + */ + private static List<MethodBinding> getInheritedBoundMethods(List<MethodBinding> mbsForClass, ClassGen cg) { + List<MethodBinding> inheritedBoundMethod = new LinkedList<MethodBinding>(); + Iterator<MethodBinding> it = mbsForClass.iterator(); + while (it.hasNext()) { + MethodBinding mb = it.next(); + if ((cg.containsMethod(mb.getBaseMethodName(), mb + .getBaseMethodSignature())) == null) + inheritedBoundMethod.add(mb); + } + return inheritedBoundMethod; + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/TeamInterfaceImplementation.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/TeamInterfaceImplementation.java new file mode 100644 index 000000000..faa7cd177 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/TeamInterfaceImplementation.java @@ -0,0 +1,1011 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: TeamInterfaceImplementation.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import de.fub.bytecode.classfile.*; +import de.fub.bytecode.generic.*; +import de.fub.bytecode.*; + +import java.util.*; + +import org.eclipse.objectteams.otre.util.*; + +/** + * Adds the general Team infrastructure to team classes. + * + * Fields: public (static) final_OT$ID <- static if 'ot.no_static' ist set + * Methods: public int activate(int level) public int deactivate(int level) + * public int _OT$getID() Static initialization (adds clinit method, if not yet + * presented) OR to every constructor: <- if 'ot.no_static' is set _OT$ID = + * TeamIdDispenser.getTeamId(class_name) + * + * Enables implicit team activation for normal (user defined) public team-level + * methods. A call to 'activate' is woven at the beginning of the method. A call + * to 'deactivate' is woven before every return and at every thrown exception. + * + * @version $Id: TeamInterfaceImplementation.java 23408 2010-02-03 18:07:35Z stephan $ + * @author Christine Hundt + * @author Stephan Herrmann + */ +public class TeamInterfaceImplementation + extends ObjectTeamsTransformation { + + private boolean isJPLIS; + + + public TeamInterfaceImplementation(boolean isJPLIS, SharedState state) { + this(isJPLIS, null, state); + } + + public TeamInterfaceImplementation(boolean isJPLIS, ClassLoader loader, SharedState state) { + super(loader, state); + this.isJPLIS = isJPLIS; + } + + /** + * @param cg + */ + public void doTransformCode(ClassGen cg) { + factory = new InstructionFactory(cg); + + if (!classNeedsTeamExtensions(cg)) { + return; + } + + ConstantPoolGen cpg = cg.getConstantPool(); + String class_name = cg.getClassName(); + genImplicitActivation(cg, cpg); + + InstructionList implicitSuperRoleRegistrations = genImplicitSuperRegistration(cg, cpg); + if (!implicitSuperRoleRegistrations.isEmpty()) { + // add implicitSuperRoleRegistrations to the static initializer: + Method clinitMethod = cg.containsMethod(Constants.STATIC_INITIALIZER_NAME, "()V"); + addToMethodStart(implicitSuperRoleRegistrations, clinitMethod, cg, cpg); + } + + if (CallinBindingManager.getBasesPerTeam(class_name) == null) { + return; // this team does not adapt any base class + } + + /** + * ******************* add initialization of the field '_OT$ID' to the static initializer : ****** + */ + int nextTeamId = TeamIdDispenser.getTeamId(class_name); + + /* + * The clinit method always exists at this moment, because it has + * been added by the interface transformer part of this transformer + * if necessary. + */ + addStaticInitializations(nextTeamId, cg, cpg); + } + + /** + * Add initialization of "_OT$ID" with 'nextTeamID' to the static + * initializer of this team class. + * + * @param nextTeamId + * the id which will be associated with the currently transformed + * team + * @param cg + * the ClassGen for the given team class + * @param cpg + * the constant pool ot the team + */ + private void addStaticInitializations(int nextTeamId, ClassGen cg, + ConstantPoolGen cpg) { + Method clinitMethod = cg.containsMethod( + Constants.STATIC_INITIALIZER_NAME, "()V"); + MethodGen mg = new MethodGen(clinitMethod, cg.getClassName(), cpg); + InstructionList il = mg.getInstructionList(); + + InstructionList addedInitialization = new InstructionList(); + addedInitialization.append(createIntegerPush(cpg, nextTeamId)); + addedInitialization.append(factory.createFieldAccess(cg.getClassName(), + "_OT$ID", Type.INT, Constants.PUTSTATIC)); + // generated: _OT$ID = <id dispensed by TeamIdDispenser>; + + // add static initialization for added static field at the beginning of + // the <clinit> method: + il.insert(addedInitialization); + mg.setMaxStack(); + mg.setMaxLocals(); + + Method newClinit = mg.getMethod(); + cg.replaceMethod(clinitMethod, newClinit); + + il.dispose(); // Reuse instruction handles + } + + /** + * For all roles in the current team which have tsuper versions: + * Generate instruction list containig calls to registration methods + * the implicit super roles. + * + * @param cg The ClassGen of the current team. + * @param cpg The constant pool of the current team. + * @return The instruction list containing the registration calls. + */ + private InstructionList genImplicitSuperRegistration(ClassGen cg, ConstantPoolGen cpg) { + InstructionList il = new InstructionList(); + List<String> inheritedRoleNames = getInheritedRoleNames(cg, cpg); + if (inheritedRoleNames.isEmpty()) + return il; // nothing to do + Iterator<String> iter = inheritedRoleNames.iterator(); + while (iter.hasNext()) { + String roleName = iter.next(); + String unqualifiedRoleName = roleName.substring(roleName.lastIndexOf('$') + 1); + if (CallinBindingManager.isBoundBaseAndRoleClass(roleName)) { + String potientialImplicitSuperRoleName = cg.getSuperclassName() + '$' + unqualifiedRoleName; + + if (!CallinBindingManager.isBoundBaseAndRoleClass(potientialImplicitSuperRoleName)) { + // Only bound base classes have the infrastructure for notifying + // implicit subclasses about team activation! + continue; + } + il.append(new PUSH(cpg, roleName)); + il.append(factory.createInvoke("java.lang.Class", "forName", + classType, new Type[] { Type.STRING }, + Constants.INVOKESTATIC)); + il.append(factory.createInvoke(potientialImplicitSuperRoleName, "_OT$registerObserver", + Type.VOID, new Type [] { classType }, + Constants.INVOKESTATIC)); + } + } + return il; + } + + public void doTransformInterface(ClassEnhancer ce, ClassGen cg) { + String class_name = cg.getClassName(); + ConstantPoolGen cpg = cg.getConstantPool(); + + checkReadClassAttributes(ce, cg, class_name, cpg); + + if (state.interfaceTransformedClasses.contains(class_name)) { + return; // class has already been transformed by this transformer + } + + if (!classNeedsTeamExtensions(cg)) { + return; + } + + factory = new InstructionFactory(cg); + + /** + * ********** empty implementation of the static class initialization method '<clinit>' *** + * NOTE: this is unnecessary in some cases, but checking is too complicated + */ + addStaticInitializer(cg, cpg, class_name, ce); + + List<String> handledBases = CallinBindingManager.getBasesPerTeam(class_name); + // TeamInterfaceImplementer only registers teams at bases, wich are part + // of a 'CallinRoleBaseBinding'-attribute of the team. + if (handledBases == null) { + return; // this team does not adapt any base class + } + + if(logging) printLogMessage("Adding the general Team infrastructure to " + + class_name); + + /** + * ******************* addition of the field '_OT$ID' + * ********************************** + */ + + int accessFlags = Constants.ACC_PUBLIC | Constants.ACC_FINAL | Constants.ACC_STATIC; + + FieldGen IDField = new FieldGen(accessFlags, Type.INT, "_OT$ID", cpg); + ce.addField(IDField.getField(), cg); + // generated global variable: public final static int _OT$ID; + + factory = new InstructionFactory(cpg); + InstructionList il; + + /** + * ******************* implementation of method '_OT$getID' + * ************************ + */ + + il = new InstructionList(); + MethodGen getIDMethod = new MethodGen(Constants.ACC_PUBLIC, Type.INT, + Type.NO_ARGS, new String[] {}, "_OT$getID", class_name, il, cpg); + + il.append(factory.createFieldAccess(class_name, "_OT$ID", Type.INT, + Constants.GETSTATIC)); + il.append(InstructionFactory.createReturn(Type.INT)); + + getIDMethod.setMaxStack(); + getIDMethod.setMaxLocals(); + + ce.addMethod(getIDMethod.getMethod(), cg); + + /** + * ***************** implementation of team (un)registration methods + * ********************** + */ + + ce.addMethod(generateTeamRegistrationMethod(cpg, class_name, + handledBases), cg); + + ce.addMethod(generateTeamUnregistrationMethod(cpg, class_name, + handledBases), cg); + + /** + * ***************** implementation of base call surrogtes for static methods + * ********************** + */ + Method [] base_call_surrogates = generateStaticBaseCallSurrogates(class_name, cpg, cg); + for(int i=0; i<base_call_surrogates.length;i++) { + // perhaps the compiler already generated an empty surrogate for an unbound super-role? + // (see X.1.5-otjld-callin-from-static-base-method-12a) + ce.addOrReplaceMethod(base_call_surrogates[i], cg); + } + + /** *************************************************************************** */ + + il.dispose(); + state.interfaceTransformedClasses.add(class_name); + } + + /** + * Add the given instruction list to the start of the givern method. + * @param additionalInstructions + * @param method + * @param cg + * @param cpg + */ + private void addToMethodStart(InstructionList additionalInstructions, Method method, ClassGen cg, ConstantPoolGen cpg) { + MethodGen mg = new MethodGen(method, cg.getClassName(), cpg); + InstructionList il = mg.getInstructionList(); + il.insert(additionalInstructions); + mg.setMaxStack(); + mg.setMaxLocals(); + + Method newMethod = mg.getMethod(); + cg.replaceMethod(method, newMethod); + il.dispose(); // Reuse instruction handles + } + + /** + * Generate the static initializer method 'clinit'. + * @param cpg The constant pool + * @param class_name The name of the class + * @param cg The ClassGen for the class + * @return The static initialier for this class + */ + void addStaticInitializer(ClassGen cg, ConstantPoolGen cpg, String class_name, ClassEnhancer ce) { + /* + * Adding static initializations requires the addition of the clinit + * method, if not yet presented. This requires synchronization with + * other transformers (TeamInterfaceImplementer) which may do the + * same this. This is done via 'TeamIdDispenser.clinitAdded(class_name)'. + */ + Method existingClinit = cg.containsMethod(Constants.STATIC_INITIALIZER_NAME, "()V"); + if (existingClinit == null && !TeamIdDispenser.clinitAdded(class_name, loader)) { + // otherwise the clinit-Method already exists and only has to be extended + // by the code transformation of this transformer + InstructionList il = new InstructionList(); + MethodGen clinitMethodGen = new MethodGen(Constants.ACC_STATIC, + Type.VOID, Type.NO_ARGS, new String[] {}, + Constants.STATIC_INITIALIZER_NAME, class_name, il, cpg); + + il.append(InstructionFactory.createReturn(Type.VOID)); + + clinitMethodGen.setMaxStack(); + clinitMethodGen.setMaxLocals(); + Method clinitMethod = clinitMethodGen.getMethod(); + ce.addMethod(clinitMethod, cg); + } + } + + /** + * Generates the ' _OT$registerAtBases()' method. This method registers the + * team at every adapted base class by calling the respective 'addTeam' + * method. + * + * @param cpg + * The ConstantPoolGen of the team class. + * @param class_name + * The name of the team class. + * @param handledBases + * The list of teams adapted by (roles of) this team. + * @return The generated 'activate' method. + */ + Method generateTeamRegistrationMethod(ConstantPoolGen cpg, + String class_name, List<String> handledBases) + { + InstructionList il = new InstructionList(); + MethodGen mg = new MethodGen(Constants.ACC_PUBLIC, Type.VOID, + Type.NO_ARGS, null, "_OT$registerAtBases", class_name, il, cpg); + + Iterator<String> it = handledBases.iterator(); + while (it.hasNext()) { + String actBase = it.next(); + //if (CallinBindingManager.hasBoundBaseParent(actBase)) + // continue; // team was already added to a super base class + // problem: bound base parent could be bound to another team class!!! + + //String boundBase = CallinBindingManager.getBoundBaseParent(actBase); + //if (boundBase != null && handledBases.contains(boundBase)) + // continue; + if (CallinBindingManager.teamAdaptsSuperBase(class_name, actBase)) + continue; + + InstructionHandle startTry = il.append(new ALOAD(0)); + + il.append(factory.createFieldAccess(class_name, "_OT$ID", Type.INT, + Constants.GETSTATIC)); + il.append(factory.createInvoke(actBase, "_OT$addTeam", Type.VOID, + new Type[] { teamType, Type.INT }, Constants.INVOKESTATIC)); + // generated: <actBase>._OT$addTeam(this, _OT$ID); + + addNoSuchMethodErrorHandling(startTry, il.getEnd(), getErrorMessage(class_name, actBase, "Activation"), il, mg, cpg); + } + il.append(new RETURN()); + + mg.setMaxStack(); + mg.setMaxLocals(); + return mg.getMethod(); + } + + /** + * Direct inverse of generateTeamRegistrationMethod(..). Generates the ' + * _OT$unregisterFromBases' method. This method deregisters the team at + * every adapted base class by calling the respective 'removeTeam' method. + * + * @param cpg + * The ConstantPoolGen of the team class. + * @param class_name + * The name of the team class. + * @param handledBases + * The list of teams adapted by (roles of) this team. + * @return The generated 'deactivate' method. + */ + Method generateTeamUnregistrationMethod(ConstantPoolGen cpg, + String class_name, List<String> handledBases) { + + InstructionList il = new InstructionList(); + MethodGen mg = new MethodGen(Constants.ACC_PUBLIC, Type.VOID, + Type.NO_ARGS, null, "_OT$unregisterFromBases", class_name, il, + cpg); + + Iterator<String> it = handledBases.iterator(); + while (it.hasNext()) { + String actBase = it.next(); + //if (CallinBindingManager.hasBoundBaseParent(actBase)) + // continue; // team was only added to a super base class + // problem: bound base parent could be bound to another team class!!! + + // String boundBase = CallinBindingManager.getBoundBaseParent(actBase); + //if (boundBase != null && handledBases.contains(boundBase)) + // continue; + if (CallinBindingManager.teamAdaptsSuperBase(class_name, actBase)) + continue; + + InstructionHandle startTry = il.append(new ALOAD(0)); + + il.append(factory.createInvoke(actBase, "_OT$removeTeam", + Type.VOID, new Type[] { teamType }, Constants.INVOKESTATIC)); + // generated: <actBase>._OT$removeTeam(this, _OT$ID); + + addNoSuchMethodErrorHandling(startTry, il.getEnd(), getErrorMessage(class_name, actBase, "Deactivation"), il, mg, cpg); + } + il.append(new RETURN()); + + mg.setMaxStack(); + mg.setMaxLocals(); + return mg.getMethod(); + } + + private String getErrorMessage(String teamName, String baseName, String action) { + String errorMessage = action+" of team '" + teamName + "' failed! Callins of this team have NOT been WOVEN into base class '" + baseName + "'!\n" + + "This is probably caused by a loading order problem."; + if (!isJPLIS) + errorMessage += "\nIf "+baseName+" is loaded from the bootstrap classpath launching the program in JPLIS mode may perhaps avoid this problem."; + return errorMessage; + } + + /** + * Adds an exception handler to the given method which catches 'java.lang.NoSuchMethodError's + * caused by missing team registration methods in base classes. Causes the throwing of an + * 'org.objectteams.UnsupportedFeatureException'. + * + * @param startTry handle to the start of the try block + * @param endTry handle to the end of the try block + * @param errorMessage the error message to be printed when throwing the exception + * @param il instruction list of the method + * @param mg MethodGen of the method + * @param cpg corresponding ConstantPoolGen + */ + private void addNoSuchMethodErrorHandling(InstructionHandle startTry, InstructionHandle endTry, + String errorMessage, InstructionList il, MethodGen mg, + ConstantPoolGen cpg) { + GOTO skipHdlr = null; + skipHdlr = new GOTO(null); + il.append(skipHdlr); + // generated: goto normal exit + + // throw away the expection reference: + InstructionHandle hdlr = il.append(new POP()); + + il.append(factory.createNew(OTConstants.unsupportedFeature)); + il.append(new DUP()); + il.append(new PUSH(cpg, errorMessage)); + il.append(factory.createInvoke(OTConstants.unsupportedFeature.getClassName(), + Constants.CONSTRUCTOR_NAME, + Type.VOID, + new Type[] { Type.STRING }, + Constants.INVOKESPECIAL)); + il.append(new ATHROW()); + + InstructionHandle nop = il.append(new NOP()); + skipHdlr.setTarget(nop); + mg.addExceptionHandler(startTry, endTry, hdlr, new ObjectType("java.lang.NoSuchMethodError")); + } + + /** + * Generate implicit Team activation for each "normal" public method. + * + * @param cg + * class for which methods are to be augmented. + * @param cpg + * the constant pool of the class 'cg'. + */ + void genImplicitActivation(ClassGen cg, ConstantPoolGen cpg) { + Method[] methods = cg.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (candidateForImplicitActivation(m, cg, cpg)) { + if(logging) printLogMessage("Adding implicit activation to " + m.getName()); + cg.replaceMethod(m, genImplicitActivation(m, cg.getClassName(), cpg, false)); + } + } + } + + /** + * Scans the team attribute StaticReplaceBinding and generates an array of base call surrogates + * + * @param class_name + * @param cpg + * @param cg + * @return + */ + private Method [] generateStaticBaseCallSurrogates(String class_name, ConstantPoolGen cpg, ClassGen cg){ + + Set<String> roleMethodKeys = new HashSet<String>(); + + //------------------------------------------------------------------------------------------ + // scan static replace bindings attributes + //------------------------------------------------------------------------------------------ + Attribute [] attributes = cg.getAttributes(); + for(int k=0; k<attributes.length; k++){ + + Unknown attr = isOTAttribute(attributes[k]); + if(attr == null) continue; + + if(attr.getName().equals("StaticReplaceBindings")) { + + byte[] indizes = attr.getBytes(); + int count = combineTwoBytes(indizes, 0); + int numberOfEntries=0; + String [] names; + numberOfEntries = 5; + int i = 2; + + for (int n=0; n<count;n++) { + names = new String[numberOfEntries]; + i = scanStrings(names, indizes, i, cpg); + int index = 0; + String role_name = names[index++]; + String role_method_name = names[index++]; + String role_method_signature = names[index++]; + String lift_method_name = names[index++]; + String lift_method_signature = names[index++]; + + String roleMethodKey = genRoleMethodKey(class_name, role_name, role_method_name, role_method_signature, lift_method_name, lift_method_signature); + roleMethodKeys.add(roleMethodKey); + + int base_len = combineTwoBytes(indizes, i); + BaseMethodInfo baseMethod; + i += 2; + names = new String[3]; + for (int n_base = 0; n_base < base_len; n_base++) { + int [] positions = null; + + i = scanStrings(names, indizes, i, cpg); + + int flags = indizes[i++]; + boolean baseIsCallin = (flags & 1) != 0; + boolean baseIsRoleMethod = (flags & 2) != 0; + boolean baseIsStatic = (flags & 4) != 0; + //parameter positions scanning + int pos_len = combineTwoBytes(indizes, i); + i+=2; + + if(pos_len > 0) { + positions = new int[pos_len]; + } + + for(int pos = 0; pos < pos_len; pos++){ + positions[pos] = combineTwoBytes(indizes,i); + i += 2; + } + int translationFlags = (combineTwoBytes(indizes, i)<<16) + combineTwoBytes(indizes, i+2); + i+=4; + baseMethod = new BaseMethodInfo(names[0], names[1], names[2], + baseIsCallin, baseIsRoleMethod, baseIsStatic, + positions, translationFlags); + + CallinBindingManager.assignBaseCallTag(names[0],names[1],names[2]); + CallinBindingManager.addStaticReplaceBindingForRoleMethod(roleMethodKey, baseMethod); + } + } + break; + } + } + + //------------------------------------------------------------------------------------------ + // generate base call surrogates + //------------------------------------------------------------------------------------------ + Iterator<String> roleMethodIter = roleMethodKeys.iterator(); + int count = roleMethodKeys.size(); + Method [] generatedSurrogates = new Method[count]; + + int j = 0; + + while(roleMethodIter.hasNext()) { + String roleMethodKey = roleMethodIter.next(); + LinkedList<BaseMethodInfo> baseMethods = CallinBindingManager.getStaticReplaceBindingsForRoleMethod(roleMethodKey); + + //split the key into team class name, role class name, role method name and role method signature + int firstPointIndex = roleMethodKey.indexOf(STATIC_REPLACE_BINDING_SEPARATOR); + int secondPointIndex = roleMethodKey.indexOf(STATIC_REPLACE_BINDING_SEPARATOR, firstPointIndex+1); + int thirdPointIndex = roleMethodKey.indexOf(STATIC_REPLACE_BINDING_SEPARATOR, secondPointIndex+1); + int fourthPointIndex = roleMethodKey.indexOf(STATIC_REPLACE_BINDING_SEPARATOR, thirdPointIndex+1); + int fifthPointIndex = roleMethodKey.indexOf(STATIC_REPLACE_BINDING_SEPARATOR, fourthPointIndex+1); + + String role_name = roleMethodKey.substring(firstPointIndex+2, secondPointIndex); + String role_method_name = roleMethodKey.substring(secondPointIndex+2, thirdPointIndex); + String role_method_signature = roleMethodKey.substring(thirdPointIndex+2, fourthPointIndex); + String lift_method_name = null; + String lift_method_signature = null; + // SH: without this check we get + // StringIndexOutOfBoundsException: String index out of range: -1 + // I hope this patch is correct.. + if ( fourthPointIndex + 2 <= fifthPointIndex + && fifthPointIndex + 2 < roleMethodKey.length()) + { + lift_method_name = roleMethodKey.substring(fourthPointIndex+2, fifthPointIndex); + lift_method_signature = roleMethodKey.substring(fifthPointIndex+2, roleMethodKey.length()); + } + + generatedSurrogates[j] = genBaseCallSurrogate(cg, role_name, role_method_name, + role_method_signature, + lift_method_name, lift_method_signature, baseMethods); + j++; + } + + return generatedSurrogates; + } + + /** + * Generates a base call surrogate for a given static role method + * @param cg + * @param role_name + * @param role_method_name + * @param role_method_signature + * @param lift_method_name + * @param lift_method_signature + * @param base_methods + * @return + */ + private Method genBaseCallSurrogate(ClassGen cg, + String role_name, + String role_method_name, + String role_method_signature, + String lift_method_name, + String lift_method_signature, + LinkedList<BaseMethodInfo> base_methods) + { + ConstantPoolGen cpg = cg.getConstantPool(); + String class_name = cg.getClassName(); + + if (base_methods.isEmpty()) { + return null; + } + Type[] enhancedArgumentTypes = enhanceArgumentTypes(Type.getArgumentTypes(role_method_signature)); + Type enhancedReturnType = generalizeReturnType(Type.getReturnType(role_method_signature)); + String[] enhancedArgumentNames = null; + InstructionList il = new InstructionList(); + int accessFlags = Constants.ACC_PROTECTED; + + MethodGen baseCallSurrogate = new MethodGen(accessFlags, + enhancedReturnType, + enhancedArgumentTypes, + enhancedArgumentNames, + getBaseCallSurrogateName(role_name, role_method_name), + class_name, + il, cpg); + + + LocalVariableGen otResult = null; + + /* + int slot = enhancedArgumentTypes.length+1; + otResult = baseCallSurrogate.addLocalVariable("_OT$result", + enhancedReturnType, + slot, null, null); + */ + otResult = baseCallSurrogate.addLocalVariable("_OT$result", + enhancedReturnType, null, null); + + il.insert(InstructionFactory.createStore(enhancedReturnType, + otResult.getIndex())); + il.insert(new ACONST_NULL()); + il.setPositions(); // about to retrieve instruction handles. + + if(logging) printLogMessage("base-call switch has to be inserted!"); + InstructionList loading = new InstructionList(); + loading.append(InstructionFactory.createThis()); + int index = 1; + for (int i = 0; i < enhancedArgumentTypes.length; i++) { + loading.append(InstructionFactory.createLoad(enhancedArgumentTypes[i],index)); + index += enhancedArgumentTypes[i].getSize(); + } + + Type[] argumentTypes = Type.getArgumentTypes(role_method_signature); + Type returnType = Type.getReturnType(role_method_signature); + + if (debugging) { + baseCallSurrogate.addLineNumber(il.getStart(), STEP_OVER_LINENUMBER); + } + + il.append(genBaseCallSwitch(cpg, base_methods, baseCallSurrogate, + argumentTypes, + returnType, + lift_method_name, + lift_method_signature, + otResult, loading, cg.getClassName())); + + il.append(InstructionFactory.createLoad(enhancedReturnType, otResult.getIndex())); + il.append(InstructionFactory.createReturn(enhancedReturnType)); + + il.setPositions(); + baseCallSurrogate.removeNOPs(); + baseCallSurrogate.setMaxStack(); + baseCallSurrogate.setMaxLocals(); + return baseCallSurrogate.getMethod(); + } + + private static String getBaseCallSurrogateName(String class_name, String method_name){ + // base call surrogate for static callin methods: + // name contains role class name and role method name, + // because its generated into the team. + return OT_PREFIX + class_name + "$" + method_name + "$base"; + } + + /** + * Generate a dispatching switch statement which calls the proper base method. + * @param cpg + * @param base_methods list of BaseMethodInfo that applies to this callin method + * @param enhancedMethod the enhanced callin method + * @param argumentTypes arg types of the callin method + * @param returnType the return type of the original callin method + * @param liftMethodSignature + * @param otResult the local variable storing the base call result + * @param loading an instruction list holding the original instructions for + * loading parameters + * @param teamName + * @param lift_method_name + * @return InstructionList the complete replacement implementing the base call. + */ + InstructionList genBaseCallSwitch (ConstantPoolGen cpg, + LinkedList<BaseMethodInfo> base_methods, MethodGen enhancedMethod, + Type[] argumentTypes, + Type returnType, String liftMethodName, String liftMethodSignature, + LocalVariableGen otResult, InstructionList loading, String teamName) + { + + short invocationKind = Constants.INVOKESTATIC; + + String className = enhancedMethod.getClassName(); + Type enhancedMethodReturnType = enhancedMethod.getReturnType(); + boolean callinHasReturnValue = returnType != Type.VOID; + + InstructionList il = new InstructionList(); + + // Setup a variable which holds the result of this base call. + // This variabel is local to this segment of code and used only + // to transport this result out off the switch statement. + int localResult = -1; + LocalVariableGen lg = null; + if (callinHasReturnValue) { + lg = enhancedMethod.addLocalVariable("_OT$tmpResult", returnType, + null, null); + localResult = lg.getIndex(); + il.append(InstructionFactory.createNull (returnType)); + il.append(InstructionFactory.createStore(returnType, localResult)); + } + + // ---- Prepare the switch: ---- + InstructionHandle switchStart = il.append + (InstructionFactory.createLoad(Type.INT, BASE_METH_ARG)); + // generated: _OT$baseMethTag + + //base_methods may contain duplicates! + //baseMethodTags is used to determine the number of cases without duplicates + HashSet<Integer> baseMethodTags = new HashSet<Integer>(); + Iterator<BaseMethodInfo> iter = base_methods.iterator(); + while(iter.hasNext()) { + BaseMethodInfo baseMethod = iter.next(); + //baseMethodTags.add(CallinBindingManager.getBaseCallTag( baseMethod.getBaseClassName(), + // baseMethod.getBaseMethodName(), + // baseMethod.getBaseMethodSignature())); + int baseMethodTag = CallinBindingManager.getBaseCallTag(baseMethod.getBaseClassName(), + baseMethod.getBaseMethodName(), + baseMethod.getBaseMethodSignature()); + baseMethodTags.add(Integer.valueOf(baseMethodTag)); + } + + // one break for each case clause + int numberOfCases = baseMethodTags.size(); + + GOTO[] breaks = new GOTO[numberOfCases]; + for (int i=0; i<numberOfCases; i++) + breaks[i] = new GOTO(null); + + int[] matches = new int[numberOfCases]; + InstructionHandle[] targets = new InstructionHandle[numberOfCases]; + int caseCounter = 0; + + //now baseMethodTags is used to store the handled tags + baseMethodTags.clear(); + + //JU: + Type[] enhancedMethodArguments = enhancedMethod.getArgumentTypes(); + Type[] enhancedArgumentsForBaseCall = new Type[enhancedMethodArguments.length - 1]; + System.arraycopy(enhancedMethodArguments, 0, + enhancedArgumentsForBaseCall, 0, + enhancedArgumentsForBaseCall.length); + + Iterator<BaseMethodInfo> it = base_methods.iterator(); + while (it.hasNext()) { + + BaseMethodInfo baseMethod = it.next(); + String baseClassName = baseMethod.getBaseClassName(); + String baseMethodName = baseMethod.getBaseMethodName(); + String baseMethodSignature = baseMethod.getBaseMethodSignature(); + int base_method_tag = CallinBindingManager.getBaseCallTag(baseClassName, baseMethodName, baseMethodSignature); + + //if the current baseMethod is a dulpicate: + // workaround for jdk 1.4: + Integer bmt = Integer.valueOf(base_method_tag); + //if(baseMethodTags.contains(base_method_tag)){ + if (baseMethodTags.contains(bmt)) { + continue; + } + + //baseMethodTags.add(base_method_tag); + baseMethodTags.add(bmt); + + int [] parameterPositions = baseMethod.getParameterPositions(); + int len = Type.getArgumentTypes(baseMethodSignature).length; + + // if the base method is a callin method as well, further enhance the signature: + if (baseMethod.isCallin) + len += EXTRA_ARGS; + + + matches[caseCounter] = CallinBindingManager.getBaseCallTag(baseClassName, baseMethodName, baseMethodSignature); + InstructionHandle nextBranch = il.append(new NOP()); + + Type[] baseMethodArgumentTypes = Type.getArgumentTypes(baseMethodSignature); + Type baseMethodReturnType = Type.getReturnType (baseMethodSignature); + String baseChainMethodName = genChainMethName(baseMethodName); + Type baseChainReturnType = object; // ALWAYS + Type[] enhancedBaseArgumentTypes = enhanceArgumentTypes(baseMethodArgumentTypes); + + boolean resultLiftingNecessary = false; + + // TODO (SH): if both types are ObjectType we should probably use subclassOf() ?? + // (don't lift if simple polymorphism suffices!) + //TODO: if the base method return type is a subtype of the role method return type no lifting has to take place!! is this allowed?? + //if (!returnTypeCompatible(baseMethodReturnType, returnType) && callinHasReturnValue) + if (/*!baseMethodReturnType.equals(object) &&*/ + !baseMethodReturnType.equals(returnType) + && !(baseMethodReturnType instanceof BasicType) // requires boxing not lifting + && !(returnType instanceof BasicType) // requires unboxing not lifting + && callinHasReturnValue) + { + resultLiftingNecessary = true; + } + + // --- load arguments of the new method: --- + // (letters refer to document parameter-passing.odg) + + // (u) generate extra arguments (indices are equal at role and base): + for (int idx = 0; idx < EXTRA_ARGS; idx++) + il.append(InstructionFactory.createLoad(enhancedMethodArguments[idx], + idx+1)); // first arg is "this" (enclosing team) + + // (v)(w)(x) split loading sequence and transfer source-level arguments + // (includes reverse-application of parameter mappings): + InstructionHandle baseCallLine = il.append(translateLoads(splitLoading(cpg, + loading.copy(), + argumentTypes), + enhancedMethodArguments, + enhancedBaseArgumentTypes, + parameterPositions, + teamName, + null, + baseMethod, + EXTRA_ARGS/*start*/, + cpg)); + // --- done loading --- + + // invoke the chaining method of the base class (base-call!): + il.append(factory.createInvoke(baseClassName, + baseChainMethodName, + baseChainReturnType, + enhancedBaseArgumentTypes, + invocationKind)); + + // FIXME(SH): if this assert holds, remove computing of resultLiftingNecessary above. + assert resultLiftingNecessary == ((baseMethod.translationFlags & 1) != 0); + + if (resultLiftingNecessary) { // call the lift-method: + Type[] liftMethodArgs = Type.getArgumentTypes(liftMethodSignature); + Type liftMethodReturnType = Type.getReturnType(liftMethodSignature); + + // cast result of base call: + il.append(factory.createCast(baseChainReturnType, baseMethodReturnType)); + + // load the team instance at which to call the lift method: + il.append(InstructionFactory.createThis()); + il.append(factory.createCast(object, new ObjectType(teamName))); + + // put them in correct order: + il.append(new SWAP()); // -> .., this$0, (BaseType)result + + il.append(factory.createInvoke(teamName, + liftMethodName, + liftMethodReturnType, + liftMethodArgs, + Constants.INVOKEVIRTUAL)); + } + + // adjust the return value to the type expected by the WRAPPER: + il.append(new DUP()); // keep for adjustment below + if (!resultLiftingNecessary) + adjustValue(il, null, baseChainReturnType, enhancedMethodReturnType); + il.append(InstructionFactory.createStore(enhancedMethodReturnType, + otResult.getIndex())); // store "globally" + // this store is needed to tunnel unused results through the callin. + + InstructionHandle afterBaseCallLine = il.append(new NOP()); + + // adjust the return value to the type expected by the ORIGINAL CALLIN: + adjustValue(il, null, baseChainReturnType, returnType); + if (callinHasReturnValue) { + il.append(InstructionFactory.createStore(returnType, localResult)); // store "locally" + } + // this store is useful for callins which make use of the result. + + targets[caseCounter] = nextBranch; + il.append(breaks[caseCounter]); + // generated: break; + + caseCounter++; + + if (debugging) { + enhancedMethod.addLineNumber(baseCallLine, STEP_INTO_LINENUMBER); + enhancedMethod.addLineNumber(afterBaseCallLine, STEP_OVER_LINENUMBER); + } + } + + //JU: added the follwing part (begin) ----------------------------------------------------- + InstructionHandle defaultBranch = il.append(new NOP()); + + if (logging) + printLogMessage("Exeption has to be thrown! Base-Call is impossible."); + + il.append(factory.createNew(OTConstants.unsupportedFeature)); + il.append(new DUP()); + // ## FIXME: fix otld$ + il.append(new PUSH(cpg, "Binding-Error: base-call from " + className + "." + enhancedMethod.getName() + + "impossible! This problem is documented in OTLD $XY.")); + il.append(factory.createInvoke(OTConstants.unsupportedFeature.getClassName(), + Constants.CONSTRUCTOR_NAME, + Type.VOID, + new Type[] { Type.STRING }, + Constants.INVOKESPECIAL)); + il.append(new ATHROW()); + //JU: (end) -------------------------------------------------------------------------------- + + InstructionHandle afterSwitch = il.append(new NOP()); // all breaks point here. + + il.append(switchStart, createLookupSwitch(matches, targets, breaks, + defaultBranch, afterSwitch)); + + // retrieve locally stored result: + if (callinHasReturnValue) { + il.append(InstructionFactory.createLoad(returnType, localResult)); + lg.setStart(il.getStart()); // restrict local variable to this segment. + lg.setEnd(il.getEnd()); + } + return il; + } + + /** + * Generates a key for the given role method parameters + * + * @param teamClassName + * @param roleClassName + * @param roleMethodName + * @param roleMethodSignature + * @param liftMethodSignature + * @return + */ + private static String genRoleMethodKey(String teamClassName, + String roleClassName, String roleMethodName, + String roleMethodSignature, String liftMethodName, + String liftMethodSignature) + { + StringBuilder roleMethodKey = new StringBuilder(64); + roleMethodKey.append(teamClassName); + roleMethodKey.append(STATIC_REPLACE_BINDING_SEPARATOR); + roleMethodKey.append(roleClassName); + roleMethodKey.append(STATIC_REPLACE_BINDING_SEPARATOR); + roleMethodKey.append(roleMethodName); + roleMethodKey.append(STATIC_REPLACE_BINDING_SEPARATOR); + roleMethodKey.append(roleMethodSignature); + roleMethodKey.append(STATIC_REPLACE_BINDING_SEPARATOR); + roleMethodKey.append(liftMethodName); + roleMethodKey.append(STATIC_REPLACE_BINDING_SEPARATOR); + roleMethodKey.append(liftMethodSignature); + return roleMethodKey.toString(); + } + + /** + * Read the InheritedRoles attribute and return the list of inherited roles. + * @param cg The ClassGen of the inspected class. + * @param cpg The constant pool of the instpected class. + * @return A list of inherited role names. + */ + private List<String> getInheritedRoleNames(ClassGen cg, ConstantPoolGen cpg) { + Attribute[] attributes = cg.getAttributes(); + LinkedList<String> inheritedRoleNames = new LinkedList<String>(); + for (int i = 0; i < attributes.length; i++) { + Attribute actAttr = attributes[i]; + if (actAttr instanceof Unknown) { + Unknown attr = (Unknown)actAttr; + byte[] indizes = attr.getBytes(); + int count = combineTwoBytes(indizes, 0); + if (attr.getName().equals("InheritedRoles")) { + int j = 2; + while (j<=2*count) { + String[] names = new String[1]; + j = scanStrings(names, indizes, j, cpg); + String inherited_role = names[0]; + inheritedRoleNames.add(inherited_role); + } + } + } + } + return inheritedRoleNames; + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/ThreadActivation.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/ThreadActivation.java new file mode 100644 index 000000000..7ac2e1eb0 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/ThreadActivation.java @@ -0,0 +1,190 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: ThreadActivation.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre; + +import java.util.HashSet; + +import de.fub.bytecode.classfile.*; +import de.fub.bytecode.generic.*; +import de.fub.bytecode.*; + + +/** + * This transformer inserts a notification call to the TeamThreadManager at the + * beginning of every run()-Method in Subtypes of java.lang.Thread or java.lang.Runnable. + * Thre TeamThreadManager then ensures the thread activation of the current thread + * for every global active team instance. + * + * @author Christine Hundt + * @author Stephan Herrmann + */ +public class ThreadActivation +{ + // name of a generated field that stores the thread which created a given runnable: + private static final String CREATION_THREAD = "_OT$creationThread"; + HashSet<String> transformableClasses = new HashSet<String>(); + + private boolean shouldTransform(ClassGen cg) { + Method runMethode = cg.containsMethod("run", "()V"); + if (runMethode == null || runMethode.isAbstract()) { + // this class contains no concrete run() method + return false; + } + String class_name = cg.getClassName(); + // check if this class is a subtype of Thread or Runnable: + try { + return Repository.implementationOf(class_name, "java.lang.Runnable") || Repository.instanceOf(class_name, "java.lang.Thread"); + } catch (NullPointerException npe) { + if (ObjectTeamsTransformation.WORKAROUND_REPOSITORY) { + return false; + } + else + throw npe; // rethrow + } + } + public void doTransformInterface(ClassEnhancer enhancer, ClassGen cg) { + if (!shouldTransform(cg)) + return; + + // if class is already transformed by this transformer + if (this.transformableClasses.contains(cg.getClassName())) + return; + + this.transformableClasses.add(cg.getClassName()); + FieldGen field = new FieldGen(Constants.ACC_PRIVATE, OTConstants.threadType, CREATION_THREAD, cg.getConstantPool()); + enhancer.addField(field.getField(), cg); + } + /** + * + */ + public void doTransformCode(ClassGen cg) { + if (!this.transformableClasses.contains(cg.getClassName())) + return; + + this.transformableClasses.remove(cg.getClassName()); + + InstructionFactory factory = new InstructionFactory(cg); + + for (Method method : cg.getMethods()) { + MethodGen mg = isRootCtor(method, cg); + if (mg != null) + enhanceConstructor(cg, factory, method, mg); + } + + enhanceRunMethod(cg, factory); + } + private void enhanceRunMethod(ClassGen cg, InstructionFactory factory) { + String class_name = cg.getClassName(); + ConstantPoolGen cpg = cg.getConstantPool(); + + Method runMethode = cg.containsMethod("run", "()V"); // existence checked in transformInterface + + MethodGen mg = new MethodGen(runMethode, class_name, cpg); + InstructionList il = mg.getInstructionList(); + InstructionHandle try_start = il.getStart(); + + /** *** Insert a call to TeamThreadManager.newThreadStarted() at the beginning of the run() method: ****** */ + InstructionList threadActivation = new InstructionList(); + threadActivation.append(new ICONST(0)); // isMain = false + threadActivation.append(InstructionConstants.ALOAD_0); // parent=this._OT$creationThread; + threadActivation.append(factory.createFieldAccess(class_name, CREATION_THREAD, OTConstants.threadType, Constants.GETFIELD)); + threadActivation.append(factory.createInvoke("org.objectteams.TeamThreadManager", + "newThreadStarted", + Type.BOOLEAN, + new Type[]{Type.BOOLEAN, OTConstants.threadType}, + Constants.INVOKESTATIC)); + LocalVariableGen flag = mg.addLocalVariable("_OT$isThreadStart", Type.BOOLEAN, il.getStart(), il.getEnd()); + threadActivation.append(new ISTORE(flag.getIndex())); + il.insert(threadActivation); + + /** *** Insert a call to TeamThreadManager.threadEnded() before every return of the run() method: ******** */ + InstructionList threadDeactivation = new InstructionList(); + threadDeactivation.append(new ILOAD(flag.getIndex())); + BranchInstruction ifIsThreadStarted = new IFEQ(null); + threadDeactivation.append(ifIsThreadStarted); + threadDeactivation.append(factory.createInvoke("org.objectteams.TeamThreadManager", + "threadEnded", + Type.VOID, + Type.NO_ARGS, + Constants.INVOKESTATIC)); + ifIsThreadStarted.setTarget(threadDeactivation.append(new NOP())); + + FindPattern findPattern = new FindPattern(il); + String pat = "`ReturnInstruction'"; + InstructionHandle ih = findPattern.search(pat); + while (ih != null) { + // insert deactivate-call before return instruction in ih: + InstructionList deactivationCopy = threadDeactivation.copy(); + InstructionHandle inserted = il.insert(ih, deactivationCopy); // instruction lists can not be reused + il.redirectBranches(ih, inserted);// SH: retarget all jumps that targeted at the return instruction + if (ih.getNext() == null) + break; // end of instruction list reached + ih = findPattern.search(pat, ih.getNext()); + } + + /** **** Add an exception handler which calls TeamThreadManager.threadEnded() ***** + * ***** before throwing the exception (finaly-simulation): */ + ObjectType throwable = new ObjectType("java.lang.Throwable"); + LocalVariableGen exception = mg.addLocalVariable( + "_OT$thrown_exception", throwable, null, null); + InstructionHandle try_end = il.getEnd(); + InstructionList deactivation_ex = threadDeactivation.copy(); + deactivation_ex.insert(InstructionFactory.createStore(throwable, exception.getIndex())); + deactivation_ex.append(InstructionFactory.createLoad(throwable, exception.getIndex())); + deactivation_ex.append(new ATHROW()); + InstructionHandle deactivation_handler = il.append(il.getEnd(), deactivation_ex); + mg.addExceptionHandler(try_start, try_end, deactivation_handler, throwable); + /** ******************************************************************** */ + + mg.setMaxStack(); + mg.setMaxLocals(); + Method generatedMethod = mg.getMethod(); + cg.replaceMethod(runMethode, generatedMethod); + threadActivation.dispose(); + il.dispose(); + } + // is method a constructor that does not invoke another this()-ctor? + private MethodGen isRootCtor(Method method, ClassGen cg) { + if (!method.getName().equals("<init>")) + return null; + String className = cg.getClassName(); + ConstantPoolGen cpg = cg.getConstantPool(); + MethodGen mg = new MethodGen(method, className, cpg); + InstructionList il = mg.getInstructionList(); + InstructionHandle ih = il.getStart(); + while (ih != null && ih.getInstruction().getOpcode() != Constants.INVOKESPECIAL) { + ih = ih.getNext(); + } + if (ih == null) + return null; + if (((InvokeInstruction)ih.getInstruction()).getClassName(cpg).equals(className)) + return null; // this-call + return mg; + } + // add statements to store the thread that created this runnable + private void enhanceConstructor(ClassGen cg, InstructionFactory factory, Method initMethod, MethodGen mg) { + String class_name = cg.getClassName(); + InstructionList il = mg.getInstructionList(); + il.insert(il.getEnd(), InstructionConstants.ALOAD_0); + il.insert(il.getEnd(), factory.createInvoke("java.lang.Thread", "currentThread", OTConstants.threadType, new Type[0], Constants.INVOKESTATIC)); + il.insert(il.getEnd(), factory.createFieldAccess(class_name, CREATION_THREAD, OTConstants.threadType, Constants.PUTFIELD)); + mg.setMaxStack(); + mg.setMaxLocals(); + cg.replaceMethod(initMethod, mg.getMethod()); + il.dispose(); + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/jplis/JPLISEnhancer.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/jplis/JPLISEnhancer.java new file mode 100644 index 000000000..e390663ee --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/jplis/JPLISEnhancer.java @@ -0,0 +1,143 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2005-2008 Berlin Institute of Technology, 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: JPLISEnhancer.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.jplis; + + +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.objectteams.otre.ClassEnhancer; +import org.eclipse.objectteams.otre.OTREInternalError; +import org.eclipse.objectteams.otre.ObjectTeamsTransformation; + +import de.fub.bytecode.Constants; +import de.fub.bytecode.classfile.ClassParser; +import de.fub.bytecode.classfile.Field; +import de.fub.bytecode.classfile.Method; +import de.fub.bytecode.classfile.Utility; +import de.fub.bytecode.generic.ClassGen; +import de.fub.bytecode.generic.ConstantPoolGen; +import de.fub.bytecode.generic.MethodGen; + + +/** +* This class implements the ClassEnhancer interface with the JPLIS (Java5) specific behavior. +* +* @author Christine Hundt +* @author Juergen Widiker +* @author Stephan Herrmann +*/ + +public class JPLISEnhancer implements ClassEnhancer { + + private ClassLoader loader; + + public JPLISEnhancer(ClassGen cg, ClassLoader loader) { + this.loader = loader; + } + + /* (non-Javadoc) + * @see org.eclipse.objectteams.otre.ClassEnhancer#addImplements(java.lang.String, de.fub.bytecode.generic.ClassGen) + */ + public void addImplements(String interfaceName, ClassGen cg) { + cg.addInterface(interfaceName); + } + + /* (non-Javadoc) + * @see org.eclipse.objectteams.otre.common.ClassEnhancer#addMethod(de.fub.bytecode.classfile.Method, de.fub.bytecode.generic.ClassGen) + */ + public void addMethod(Method m, ClassGen cg) { + if (cg.containsMethod(m.getName(), m.getSignature()) != null) + new OTREInternalError("Warning: repetive adding of method " + + m.getName() + m.getSignature() + + " to class " + cg.getClassName()) + .printStackTrace(); + cg.addMethod(m); + } + + public void addOrReplaceMethod(Method method, ClassGen cg) { + Method existingMethod = cg.containsMethod(method.getName(), method.getSignature()); + if (existingMethod == null) + addMethod(method, cg); + else + cg.replaceMethod(existingMethod, method); + } + + /* (non-Javadoc) + * @see org.eclipse.objectteams.otre.common.ClassEnhancer#addField(de.fub.bytecode.classfile.Field, de.fub.bytecode.generic.ClassGen) + */ + public void addField(Field f, ClassGen cg) { + if (cg.containsField(f.getName()) != null) + new OTREInternalError("Warning: repetitive adding of field " + + f.getName() + f.getSignature() + + " to class " + cg.getClassName()) + .printStackTrace(); + cg.addField(f); + } + + /* (non-Javadoc) + * @see org.eclipse.objectteams.otre.common.ClassEnhancer#loadClass(java.lang.String) + */ + public void loadClass(String className, ObjectTeamsTransformation client) { + // SH: no forced class loading within OT/Equinox + if(System.getProperty("ot.equinox") != null) + return; + try { + String binaryName = className.replace('.', '/'); + InputStream is = loader.getResourceAsStream(binaryName+".class"); + if (is != null) { + ClassGen cg = new ClassGen(new ClassParser(is, className).parse()); + client.checkReadClassAttributes(this, cg, className, cg.getConstantPool()); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + /* (non-Javadoc) + * @see org.eclipse.objectteams.otre.common.ClassEnhancer#decapsulateMethod(de.fub.bytecode.classfile.Method, java.lang.String, de.fub.bytecode.generic.ConstantPoolGen) + */ + public void decapsulateMethod(Method m, ClassGen cg, String packageName, ConstantPoolGen cpg) { + String className = cg.getClassName(); + int flags = m.getAccessFlags(); + MethodGen mg = new MethodGen(m, className, cpg); + + if ((flags & Constants.ACC_PUBLIC) == 0) { + int newFlags = flags; + newFlags &= ~(Constants.ACC_PROTECTED|Constants.ACC_PRIVATE); // clear old visibility + newFlags |= Constants.ACC_PUBLIC; // set new visibility + mg.setAccessFlags(newFlags); + if(System.getProperty("ot.log") != null) + ObjectTeamsTransformation.printLogMessage("Adjusting from " + + Utility.accessToString(flags) + + " to public:\n\t" + + className + + "." + m); + if (!packageName.equals("NO_PACKAGE")) + checkSeal(packageName, className); + } + cg.replaceMethod(m, mg.getMethod()); + } + + private static void checkSeal(String package_name, String class_name) { + Package pckg = Package.getPackage(package_name); + if ( (pckg != null) && pckg.isSealed()) + throw new IllegalAccessError( + "OT/J callout binding:\n" + +"Trying to break sealed "+pckg); + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/jplis/ObjectTeamsTransformer.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/jplis/ObjectTeamsTransformer.java new file mode 100644 index 000000000..df7488bb6 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/jplis/ObjectTeamsTransformer.java @@ -0,0 +1,287 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2005-2009 Berlin Institute of Technology, 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: ObjectTeamsTransformer.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.jplis; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.security.ProtectionDomain; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.objectteams.otre.BaseCallRedirection; +import org.eclipse.objectteams.otre.BaseMethodTransformation; +import org.eclipse.objectteams.otre.BaseTagInsertion; +import org.eclipse.objectteams.otre.Decapsulation; +import org.eclipse.objectteams.otre.LiftingParticipantTransformation; +import org.eclipse.objectteams.otre.LowerableTransformation; +import org.eclipse.objectteams.otre.OTConstants; +import org.eclipse.objectteams.otre.ObjectTeamsTransformation; +import org.eclipse.objectteams.otre.StaticSliceBaseTransformation; +import org.eclipse.objectteams.otre.SubBoundBaseMethodRedefinition; +import org.eclipse.objectteams.otre.TeamInterfaceImplementation; +import org.eclipse.objectteams.otre.ThreadActivation; +import org.eclipse.objectteams.otre.util.AttributeReadingGuard; +import org.eclipse.objectteams.otre.util.CallinBindingManager; + +import de.fub.bytecode.Repository; +import de.fub.bytecode.classfile.ClassParser; +import de.fub.bytecode.classfile.JavaClass; +import de.fub.bytecode.generic.ClassGen; + + +/** + * Main entry into the OTRE when using JPLIS + * + * @author Christine Hundt + * @author Stephan Herrmann + */ +public class ObjectTeamsTransformer implements ClassFileTransformer { + + // force loading all transformer classes to reduce risk of deadlock in class loading. + static Class<?>[] transformerClasses = new Class<?>[] { + BaseCallRedirection.class, + BaseMethodTransformation.class, + BaseTagInsertion.class, + Decapsulation.class, + LiftingParticipantTransformation.class, + LowerableTransformation.class, + StaticSliceBaseTransformation.class, + SubBoundBaseMethodRedefinition.class, + TeamInterfaceImplementation.class, + ThreadActivation.class + }; + + /** + * One instance of this class is used per class loader to ensure disjoint scopes. + */ + static class StateGroup { + ObjectTeamsTransformation.SharedState bcrState = new ObjectTeamsTransformation.SharedState(); + ObjectTeamsTransformation.SharedState bmtState = new ObjectTeamsTransformation.SharedState(); + BaseTagInsertion.SharedState btiState = new BaseTagInsertion.SharedState(); + Decapsulation.SharedState decState = new Decapsulation.SharedState(); + ObjectTeamsTransformation.SharedState lptState = new ObjectTeamsTransformation.SharedState(); + ObjectTeamsTransformation.SharedState lowState = new ObjectTeamsTransformation.SharedState(); + ObjectTeamsTransformation.SharedState ssbtState = new ObjectTeamsTransformation.SharedState(); + ObjectTeamsTransformation.SharedState sbbmrState = new ObjectTeamsTransformation.SharedState(); + ObjectTeamsTransformation.SharedState tiiState = new ObjectTeamsTransformation.SharedState(); + } + static Map<ClassLoader, StateGroup> states = new HashMap<ClassLoader, StateGroup>(); + + static boolean warmedUp = false; + /* + * (non-Javadoc) + * + * @see java.lang.instrument.ClassFileTransformer#transform(java.lang.ClassLoader, + * java.lang.String, java.lang.Class, java.security.ProtectionDomain, + * byte[]) + */ + public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, + ProtectionDomain protectionDomain, byte[] classfileBuffer) + throws IllegalClassFormatException + { + if (warmedUp) + return internalTransform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); + synchronized (loader) { + try { + return internalTransform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); + } finally { + warmedUp = true; + } + } + } + public byte[] internalTransform(ClassLoader loader, String className, Class<?> classBeingRedefined, + ProtectionDomain protectionDomain, byte[] classfileBuffer) + throws IllegalClassFormatException + { + if (className.startsWith("org/objectteams/transformer") + || className.startsWith("org/cs3/jmangler") + || className.startsWith("de/fub/bytecode")) + { + // skip OTRE, BCEL and JMangler classes + return null; + } +// if (!(className.startsWith("java") || className.startsWith("sun"))) + // System.err.println("ObjectTeamsTransformer transforming: " + className); + if (classBeingRedefined != null) { + System.out.println("Redefinition!"); + return null; + } + + // state sharing among transformers: + StateGroup states = ObjectTeamsTransformer.states.get(loader); + if (states == null) + ObjectTeamsTransformer.states.put(loader, states = new StateGroup()); + // + // One fresh instance of each transformer for a given class: + // + BaseCallRedirection baseCallRedirection + = new BaseCallRedirection( loader, states.bcrState); + BaseMethodTransformation baseMethodTransformation + = new BaseMethodTransformation( loader, states.bmtState); + BaseTagInsertion baseTagInsertion + = new BaseTagInsertion( states.btiState); + Decapsulation decapsulation + = new Decapsulation( loader, states.decState); + LiftingParticipantTransformation liftingParticipantTransformation + = new LiftingParticipantTransformation( loader, states.lptState); + LowerableTransformation lowerableTransformation + = new LowerableTransformation( loader, states.lowState); + StaticSliceBaseTransformation staticSliceBaseTransformation + = new StaticSliceBaseTransformation( loader, states.ssbtState); + SubBoundBaseMethodRedefinition subBoundBaseMethodRedefinition + = new SubBoundBaseMethodRedefinition( loader, states.sbbmrState); + TeamInterfaceImplementation teamInterfaceImplementation + = new TeamInterfaceImplementation(true, loader, states.tiiState); + ThreadActivation threadActivation + = new ThreadActivation(); + + // tell Repository about the class loader for improved lookupClass() + ClassLoader prevLoader= Repository.classLoaders.get(); + Repository.classLoaders.set(loader); + + InputStream is = new ByteArrayInputStream(classfileBuffer); + try { + JavaClass java_class = new ClassParser(is, className).parse(); + //Repository.addClass(java_class); + ClassGen cg = new ClassGen(java_class); + + JPLISEnhancer jpe = new JPLISEnhancer(cg, loader); + + Collection<String> adaptedBases; // [OT/Equinox] + // [OT/Equinox] remember the first transformation which holds adaptedBases + adaptedBases= setFirstTransformation(subBoundBaseMethodRedefinition); + // [OT/Equinox] if class has previously been transformed fetch the list of + // adapted bases from the CallinBindingManager instead of reading it now. + if ( (cg.getAccessFlags() & OTConstants.TEAM) != 0 + && !AttributeReadingGuard.getInstanceForLoader(loader).iAmTheFirst(cg.getClassName())) + { + List<String> basesOfTeam = CallinBindingManager.getBasesPerTeam(cg.getClassName()); + if (basesOfTeam != null) + adaptedBases.addAll(basesOfTeam); + } + subBoundBaseMethodRedefinition.doTransformInterface(jpe, cg); + baseCallRedirection.doTransformInterface(jpe, cg); + decapsulation.doTransformInterface(jpe, cg); + try { + baseMethodTransformation.useReflection = (loader == null); // bootstrap classes cannot be called directly + baseMethodTransformation.doTransformInterface(jpe, cg); + } catch (Throwable t) { + System.err.println("Error transforming class: "+cg.getClassName()); + t.printStackTrace(); + } + baseTagInsertion.doTransformInterface(jpe, cg); + lowerableTransformation.doTransformInterface(jpe, cg); + staticSliceBaseTransformation.doTransformInterface(jpe, cg); + teamInterfaceImplementation.doTransformInterface(jpe, cg); + +// subBoundBaseMethodRedefinition.doTransformInterface(jpe, cg); +// baseCallRedirection.doTransformInterface(jpe, cg); +// decapsulation.doTransformInterface(jpe, cg); +// baseMethodTransformation.doTransformInterface(jpe, cg); +// baseTagInsertion.doTransformInterface(jpe, cg); +// staticSliceBaseTransformation.doTransformInterface(jpe, cg); +// teamInterfaceImplementation.doTransformInterface(jpe, cg); + threadActivation.doTransformInterface(jpe, cg); + + +// baseCallRedirection.doTransformCode(cg); // empty method + baseMethodTransformation.doTransformCode(cg); + baseTagInsertion.doTransformCode(cg); + liftingParticipantTransformation.doTransformCode(cg); + staticSliceBaseTransformation.doTransformCode(cg); + teamInterfaceImplementation.doTransformCode(cg); + threadActivation.doTransformCode(cg); + + JavaClass new_java_class = cg.getJavaClass(); + if (dumping) { + new_java_class.dump("jplis_dump/" + className + ".class"); + } + return new_java_class.getBytes(); + } catch (IOException e) { + System.err.println("ClassFileTransformer could not parse class file buffer to JavaClass"); + e.printStackTrace(); + } finally { + // uninstall class loader: + Repository.classLoaders.set(prevLoader); + } + return null; + } + + /** + * External API (for OT/Equinox): + * Destructively fetch the set of adapted base classes + * recorded since the last call to this method. + * + * @return + */ + public Collection<String> fetchAdaptedBases() { + if (this.firstTransformation == null) + return null; + Collection<String>result= this.firstTransformation.fetchAdaptedBases(); + this.firstTransformation= null; + return result; + } + + /** + * External API (for OT/Equinox): + * Read the OT-Attributes of a class without loading the class. + * @throws IOException + * @throws ClassFormatError + */ + public void readOTAttributes(InputStream file, String fileName, ClassLoader loader) + throws ClassFormatError, IOException + { + ClassParser cp = new ClassParser(file, fileName); + ClassGen cg = new ClassGen(cp.parse()); + JPLISEnhancer jpe = new JPLISEnhancer(cg, /*loader (unused)*/null); + ClassLoader prevLoader= Repository.classLoaders.get(); + Repository.classLoaders.set(loader); + try { + setFirstTransformation(new ObjectTeamsTransformation(loader, null) {}); + firstTransformation.checkReadClassAttributes(jpe, cg, cg.getClassName(), cg.getConstantPool()); + } finally { + Repository.classLoaders.set(prevLoader); + } + } + + // helper structure for above: + /* The first transformation performed holds the list of adapted bases. */ + private ObjectTeamsTransformation firstTransformation; + + /* @return the collection of adapted bases currently in use. */ + private Collection<String> setFirstTransformation(ObjectTeamsTransformation t) { + if (this.firstTransformation != null) + t.adaptedBases= this.firstTransformation.adaptedBases; // collect into existing + this.firstTransformation= t; + return t.adaptedBases; + } + + // ------------------------------------------ + // ---------- Class file dumping: ---------------------- + // ------------------------------------------ + /** Initialized from property <tt>ot.dump</tt>. */ + static boolean dumping = false; + static { + if(System.getProperty("ot.dump")!=null) + dumping = true; + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/jplis/otreAgent.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/jplis/otreAgent.java new file mode 100644 index 000000000..ee6077f11 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/jplis/otreAgent.java @@ -0,0 +1,48 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2005-2009 Berlin Institute of Technology, 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: otreAgent.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.jplis; + +import java.lang.instrument.Instrumentation; + +/** +* +* @version $Id: otreAgent.java 23408 2010-02-03 18:07:35Z stephan $ +* @author Christine Hundt +*/ +public class otreAgent { + + private static Instrumentation instCopy; +// private static String optionsCopy; + + private static ObjectTeamsTransformer otTransformer; + + public static void premain(String options, Instrumentation inst) { + instCopy = inst; +// optionsCopy = options; + + // add all necessary transformers: + otTransformer = new ObjectTeamsTransformer(); + instCopy.addTransformer(otTransformer); + + /* All future class definitions will be seen by the transformer, + except definitions of classes upon which any registered transformer is dependent. */ + } + + public static Instrumentation getInstrumentation() { + return instCopy; + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/AnnotationHelper.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/AnnotationHelper.java new file mode 100644 index 000000000..9f014123f --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/AnnotationHelper.java @@ -0,0 +1,102 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2009 Stephan Herrmann + * + * 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$ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Stephan Herrmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; + +import org.eclipse.objectteams.otre.ObjectTeamsTransformation; +import org.objectteams.ImplicitTeamActivation; + +import de.fub.bytecode.classfile.Attribute; +import de.fub.bytecode.classfile.Unknown; +import de.fub.bytecode.generic.ConstantPoolGen; + +/** + * Helper class for parsing / skipping runtime visible annotations. + * + * @author stephan + * @since 1.4.0 + */ +public class AnnotationHelper { + + /** + * Does any of the given attributes contain an {@link ImplicitTeamActivation} annotation? + * @param attrs attributes to inspect + * @param cpg constant pool for string lookup + * @return true if an {@link ImplicitTeamActivation} annotation was found. + */ + public static boolean containsImplicitActivationAttribute(Attribute[] attrs, ConstantPoolGen cpg) { + if (attrs != null) { + for (Attribute attr : attrs) { + if (attr instanceof Unknown && ((Unknown)attr).getName().equals("RuntimeVisibleAnnotations")) { + Unknown unknown = (Unknown) attr; + byte[] bytes = unknown.getBytes(); + int len = ObjectTeamsTransformation.combineTwoBytes(bytes, 0); + int i = 2; + String[] names = new String[1]; + for (int n=0; n<len; n++) { + i = ObjectTeamsTransformation.scanStrings(names, bytes, i, cpg); + if ("Lorg/objectteams/ImplicitTeamActivation;".equals(names[0])) + return true; + i = skipNameValuePairs(bytes, i, names[0], cpg); + } + } + } + } + return false; + } + + private static int skipNameValuePairs(byte[] bytes, int i, String typeName, ConstantPoolGen cpg) { + int numPairs = ObjectTeamsTransformation.combineTwoBytes(bytes, i); + i+=2; + for (int p=0; p<numPairs; p++) + i = skipElementValue(bytes, i+2 /*skip name*/, typeName, cpg); + return i; + } + + private static int skipElementValue(byte[] bytes, int i, String typeName, ConstantPoolGen cpg) { + short tag = bytes[i++]; + switch (tag) { + case 'B': // byte + case 'C': // char + case 'D': // double + case 'F': // float + case 'I': // int + case 'J': // long + case 'S': // short + case 'Z': // boolean + case 's': // String + case 'c': // Class + i+=2; break; + case 'e': // Enum constant + i+=4; break; + case '@': // Annotation + String[] typeName2 = new String[1]; + i = ObjectTeamsTransformation.scanStrings(typeName2, bytes, i, cpg); // nested annotation type + i = skipNameValuePairs(bytes, i, typeName2[0], cpg); + break; + case '[': // Array + int numArrayVals = ObjectTeamsTransformation.combineTwoBytes(bytes, i); + i+=2; + for (int j = 0; j < numArrayVals; j++) + i = skipElementValue(bytes, i, typeName, cpg); + break; + default: + throw new RuntimeException("Unexpected element value kind in annotation: " + typeName); + } + return i; + } + +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/AttributeReadingGuard.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/AttributeReadingGuard.java new file mode 100644 index 000000000..4d72e02d8 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/AttributeReadingGuard.java @@ -0,0 +1,80 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: AttributeReadingGuard.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Christine Hundt + * @author Stephan Herrmann + */ +public class AttributeReadingGuard { + + private static Map<ClassLoader, AttributeReadingGuard> instances = new HashMap<ClassLoader, AttributeReadingGuard>(); + private static AttributeReadingGuard defaultInstance = new AttributeReadingGuard(); + + private ArrayList<String> servedClasses = new ArrayList<String>(); + + // this one flag is really global (concerns the one main class of the application): + private static boolean firstLoaded = true; + + /** + * @param className + * @return + */ + public boolean iAmTheFirst(String className) { + return !this.servedClasses.contains(className); + } + + /** + * Processing the given class is done. + * @param className + */ + public void workDone(String className) { + this.servedClasses.add(className); + } + + /** + * @return whether this class is the first being loaded => possibly the main class. + */ + public static synchronized boolean isFirstLoadedClass() { + if (!firstLoaded) + return false; + firstLoaded = false; + return true; + } + + /** First loaded class has no main => it was a false alarm. */ + public static void reset() { + firstLoaded = true; + } + + /** + * Since actual data are stored in an instance, static methods need to retrieve the appropriate + * instance regarding the given class loader. + */ + public static AttributeReadingGuard getInstanceForLoader(ClassLoader loader) { + if (loader == null) + return defaultInstance; + + AttributeReadingGuard instance = instances.get(loader); + if (instance == null) + instances.put(loader, instance = new AttributeReadingGuard()); + return instance; + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/BoundClass.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/BoundClass.java new file mode 100644 index 000000000..1746be56e --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/BoundClass.java @@ -0,0 +1,80 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2004-2009 Berlin Institute of Technology, 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$ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; + +import java.util.HashSet; + +import de.fub.bytecode.generic.ObjectType; + +/** + * @version $Id: BoundClass.java,v 1.8 2006-12-19 21:31:30 stephan Exp $ + * @author Christine Hundt + */ +public class BoundClass { + private String name; + + private BoundClass _super; + //private BoundClass _tsuper; + private HashSet<String> adaptingTeams = new HashSet<String>(); + + public BoundClass(String className, String teamName) { + name = className; + this.adaptingTeams.add(teamName); + _super = null; + } + + public String getName() { + return name; + } + + public boolean isAdaptedByTeam(String teamName) { + return this.adaptingTeams.contains(teamName); + } + + public void addAdaptingTeam(String teamName) { + this.adaptingTeams.add(teamName); + } + + public void setSuper(BoundClass superClass) { + _super = superClass; + } + + public BoundClass getSuper() { + return _super; + } + + public boolean isSubClassOf(String anotherClass) { + BoundClass superClass = _super; + while (superClass!=null) { + if (superClass.getName().equals(anotherClass)) { + return true; + } + superClass = superClass.getSuper(); + } + return false; + } + + public void updateSuper(BoundClass newSuperBaseClass, ObjectType newSuperBaseType) { + // FIXME(SH): implement ;-) + // test if newSuperBaseType is above or below _super. + // also check if tsupers (i.e., more than one super) must be treated as well. + } + +// public String toString() { +// return name; +// } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/BoundMethod.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/BoundMethod.java new file mode 100644 index 000000000..4a4a8fbdc --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/BoundMethod.java @@ -0,0 +1,48 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2004-2009 Berlin Institute of Technology, 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: BoundMethod.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; + +/** + * @author Christine Hundt + * @version $Id: BoundMethod.java 23408 2010-02-03 18:07:35Z stephan $ + */ +public class BoundMethod { + private String name; + private String signature; + private boolean isCallin; + +// private MethodBinding binding; + + public BoundMethod(String methodName, String methodSignature, boolean isCallin, MethodBinding methodBinding) { + name = methodName; + signature = methodSignature; + this.isCallin = isCallin; +// binding = methodBinding; + } + + public String getName() { + return name; + } + + public String getSignature() { + return signature; + } + + public boolean getIsCallin() { + return this.isCallin; + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/CallinBindingManager.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/CallinBindingManager.java new file mode 100644 index 000000000..ba2330a10 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/CallinBindingManager.java @@ -0,0 +1,1429 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: CallinBindingManager.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; + +//import de.fub.bytecode.generic.Type; // just for javadoc. +import de.fub.bytecode.Repository; +import de.fub.bytecode.classfile.JavaClass; +import de.fub.bytecode.generic.ObjectType; + +import org.eclipse.objectteams.otre.OTREInternalError; +import org.eclipse.objectteams.otre.ObjectTeamsTransformation; +import org.eclipse.objectteams.otre.ObjectTeamsTransformation.BaseMethodInfo; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Map.Entry; + +/** + * @version $Id: CallinBindingManager.java 23408 2010-02-03 18:07:35Z stephan $ + * @author Christine Hundt + */ +@SuppressWarnings("nls") +public class CallinBindingManager { + // map of bindings for role classes: roleClassName -> RoleBaseBinding + private static HashMap<String, RoleBaseBinding> roleBindings = new HashMap<String, RoleBaseBinding>(); + + // map of bindings for base classes: baseClassName -> List[RoleBaseBinding] + private static ListValueHashMap<RoleBaseBinding> baseBindings = new ListValueHashMap<RoleBaseBinding>(); + + // maps a team to its handled bases: teamClassName -> List[BaseClassName] + private static ListValueHashMap<String> basesPerTeam = new ListValueHashMap<String>(); + + // maps a team to its contained roles: teamClassName -> List[RoleClassName] + private static ListValueHashMap<String> rolesPerTeam = new ListValueHashMap<String>(); + + // store sets of bases indexed by a team name, + // where the team adapts (decapsulates) the given bases without a direct playedBy relation + private static HashMap<String, HashSet<String>> extraReferencedBases = new HashMap<String, HashSet<String>>(); + + /** + * Add super-link of the BoundRole object for the RoleBaseBinding of 'className' to 'superClassName'. + * @param className the name of the class for which to add the super-link + * @param superClassName the name of the super class to be linked + */ + public static void addSuperRoleLink(String className, String superClassName) { + if (!roleBindings.containsKey(superClassName)) + return; // no super role class stored! + RoleBaseBinding rbbSuper = roleBindings.get(superClassName); + RoleBaseBinding rbb = roleBindings.get(className); + // establish link from the role to its super role class: + rbb.getRoleClass().setSuper(rbbSuper.getRoleClass()); + } + + /** + * @param baseClassName + */ + private static void addSuperBaseLink(String teamClassName, String baseClassName, RoleBaseBinding rbb) + { + BoundClass baseClass = rbb.getBaseClass(); + ObjectType baseClassType = new ObjectType(baseClassName); + if (!checkLookup(baseClassType.getClassName())) // TODO: workaround for classes loaded from special locations + return; + // clone the set for re-entrance: + Iterator<Entry<String, LinkedList<RoleBaseBinding>>> it = getBaseBindingsCloneIterator(); + while (it.hasNext()) { + Entry<String, LinkedList<RoleBaseBinding>> entry = it.next(); + String currentBaseClassName = entry.getKey(); + if (currentBaseClassName.equals(baseClassName)) continue; + ObjectType currentType = new ObjectType(currentBaseClassName); + if (!checkLookup(currentType.getClassName())) // TODO: workaround for classes loaded from special locations + continue; + // BoundClass objects are unique per base object, so just take the first binding: + BoundClass currentBaseClass = entry.getValue().getFirst().getBaseClass(); + if(baseClassType.subclassOf(currentType)) { + BoundClass rbbSuper = baseClass.getSuper(); + if (rbbSuper != null) { + if (rbbSuper.getName().equals(currentBaseClassName)) + continue; // no need for action: already set. + baseClass.updateSuper(currentBaseClass, currentType); + } + baseClass.setSuper(currentBaseClass); + } + else if (currentType.subclassOf(baseClassType)) { + BoundClass currentSuper = currentBaseClass.getSuper(); + // if sub base classes may be registered before super base class, + // this case has to be considered too: + if (currentSuper != null) { + if (currentSuper.getName().equals(baseClassName)) + continue; // no need for action: already set. + currentBaseClass.updateSuper(baseClass, baseClassType); + } + currentBaseClass.setSuper(baseClass); + } + } + } + + /** + * Declare role playedBy base. + * + * @param roleClassName the name of the played role class + * @param baseClassName the name of the playing base class + */ + public static void addRoleBaseBinding(String roleClassName, String baseClassName, String teamClassName) { + RoleBaseBinding rbb = roleBindings.get(roleClassName); + if (rbb == null) { + rbb = new RoleBaseBinding(roleClassName, baseClassName, teamClassName); + roleBindings.put(roleClassName, rbb); + } + addSuperBaseLink(teamClassName, baseClassName, rbb); + synchronized (baseBindings) { + baseBindings.put(baseClassName, rbb); + } + addTeamRoleRelation(teamClassName, roleClassName); + } + + /** + * @param teamClassName + * @param baseClassName + */ + public static void addTeamBaseRelation(String teamClassName, String baseClassName) { + JavaClass baseClass = Repository.lookupClass(baseClassName); + if (checkLookup(baseClassName) && baseClass.isInterface()) { // TODO: workaround for classes loaded from special locations + // TODO (SH): need to register with all implementing classes! + if (logging) { + ObjectTeamsTransformation.printLogMessage("*** Skipping base " + baseClassName + ": is an interface"); + ObjectTeamsTransformation.printLogMessage("Classses implementing the interface " + baseClassName + " have to be transformed!"); + } + addBoundBaseInterface(baseClassName); + } else { + List<String> bases = basesPerTeam.get(teamClassName); + if (bases==null || !bases.contains(baseClassName)) + basesPerTeam.put(teamClassName, baseClassName); + } + } + + /** + * @param teamClassName + * @return + */ + public static List<String> getBasesPerTeam(String teamClassName) { + // (PH): return empty list instead of null when team has no bases? + return basesPerTeam.get(teamClassName); + } + + /** + * @param teamClassName + * @param roleClassName + */ + public static void addTeamRoleRelation(String teamClassName, String roleClassName) { + List<String> roles = rolesPerTeam.get(teamClassName); + if (roles == null || !roles.contains(roleClassName)) + rolesPerTeam.put(teamClassName, roleClassName); + } + + /** + * @param teamClassName + * @return + */ + public static List<String> getRolePerTeam(String teamClassName) { + return rolesPerTeam.get(teamClassName); + } + + private static LinkedList<String> allRoles = new LinkedList<String>(); + + /** + * @param roleName + */ + public static void addRole(String roleName) { + allRoles.add(roleName); + } + + /** + * @param roleName + * @return + */ + public static boolean isRole(String roleName) { + return allRoles.contains(roleName); + } + + public static void addExtraReferencedBase(String teamName, String baseName) { + HashSet<String> bases = extraReferencedBases.get(teamName); + if (bases == null) { + bases = new HashSet<String>(); + extraReferencedBases.put(teamName, bases); + } + bases.add(baseName); + } + + /** + * Get all bases adapted by a team without a direct playedBy relation. + * @param teamName + * @return non-null Set (may be empty). + */ + public static Set<String> getExtraReferencedBases(String teamName) { + Set<String> bases = extraReferencedBases.get(teamName); + if (bases != null) + return bases; + return new HashSet<String>(); + } + + /** + * Declare binding of a pair of methods. + * @param roleClassName + * @param bindingFileName TODO + * @param bindingLineNumber TODO + * @param bindingLineOffset TODO + * @param roleMethodName + * @param roleMethodSignature signature ready for interpretation by + * {@link Type de.fub.bytecode.generic.Type} + * @param isStaticRoleMethod TODO + * @param modifier "before", "after" or "replace" + * @param baseMethodName + * @param baseMethodSignature + * @param isStaticBaseMethod is the base method static? + * @param baseIsCallin is the base method a callin method? + * @param translationFlags one bit for the result and for each argument indicating whether lifting/lowering is needed + * @param liftMethodName + * @param liftMethodSignature + */ + public static void addMethodBinding( + String roleClassName, String baseClassName, + String bindingFileName, int bindingLineNumber, int bindingLineOffset, + String bindingLabel, String roleMethodName, String roleMethodSignature, boolean isStaticRoleMethod, + String wrapperName, String wrapperSignature, String modifier, + String baseMethodName, String baseMethodSignature, + boolean isStaticBaseMethod, boolean baseIsCallin, boolean covariantBaseReturn, + int translationFlags, String liftMethodName, String liftMethodSignature) + { + RoleBaseBinding rbb = roleBindings.get(roleClassName); + if (rbb == null) { + // no binding found, create it now: + if (baseClassName == null) + throw new OTREInternalError("PlayedBy attribute must be read before method bindings."); + int lastDollar = roleClassName.lastIndexOf('$'); + String teamClassName = roleClassName.substring(0, lastDollar); + rbb = new RoleBaseBinding(roleClassName, baseClassName, teamClassName); + roleBindings.put(roleClassName, rbb); + } + //System.err.println(rbb.getRoleClass().getName()+"<-*->"+rbb.getBaseClass().getName()); + rbb.addMethodBinding(bindingFileName, bindingLineNumber, bindingLineOffset, + bindingLabel, roleMethodName, roleMethodSignature, isStaticRoleMethod, + wrapperName, wrapperSignature, modifier, + baseMethodName, baseMethodSignature, + isStaticBaseMethod, baseIsCallin, covariantBaseReturn, + translationFlags, + liftMethodName, liftMethodSignature); + + if (modifier.equals("replace")) + assignBaseCallTag(rbb.getBaseClassName(), baseMethodName, baseMethodSignature); + } + + /** + * Get all callin bindings for a given base method. + * @param className the base class + * @param methodName the base method + * @return Collection of <pre>MethodBinding</pre> + */ + public static Collection<MethodBinding> getBindingForBaseMethod(String baseClassName, String baseMethodName, String baseMethodSignature) + { + LinkedList<RoleBaseBinding> rbbList = baseBindings.get(baseClassName); + LinkedList<MethodBinding> resultList = new LinkedList<MethodBinding>(); + if (rbbList != null) { + Iterator<RoleBaseBinding> it = rbbList.iterator(); + while (it.hasNext()) { + RoleBaseBinding rbb = it.next(); + List<MethodBinding> mbs = rbb.getBaseMethodBindings(baseMethodName, baseMethodSignature); + if (mbs != null) + addFiltered(resultList, mbs, baseMethodName, baseMethodSignature); + } + } + // IMPLICIT_INHERITANCE + addFiltered(resultList, + getImplicitlyInheritedBaseMethodBindings(baseClassName, baseMethodName, baseMethodSignature), + baseMethodName, + baseMethodSignature); + if (resultList.isEmpty()) + return null; + return resultList; + } + /** variant of the above, optimized for computing only a boolean result instead of a complete list. */ + public static boolean isBoundBaseMethod(String baseClassName, String baseMethodName, String baseMethodSignature) + { + LinkedList<RoleBaseBinding> rbbList = baseBindings.get(baseClassName); + if (rbbList != null) { + for (RoleBaseBinding rbb : rbbList) { + List<MethodBinding> mbs = rbb.getBaseMethodBindings(baseMethodName, baseMethodSignature); + if (mbs != null) + for (MethodBinding mb : mbs) + if (mb.matchesMethod(baseMethodName, baseMethodSignature, /* strict */ true)) + return true; + } + } + // IMPLICIT_INHERITANCE + for (MethodBinding mb : getImplicitlyInheritedBaseMethodBindings(baseClassName, baseMethodName, baseMethodSignature)) + if (mb.matchesMethod(baseMethodName, baseMethodSignature, /* strict */ true)) + return true; + return false; + } + // add bindings from candidates to resultList, perhaps checking + // full signatures (i.e., if not covariantBaseReturn). + private static void addFiltered(Collection<MethodBinding> resultList, + Collection<MethodBinding> candidates, + String name, + String fullSignature) + { + for (MethodBinding methodBinding : candidates) + if (methodBinding.matchesMethod(name, fullSignature, /*strict*/false)) + resultList.add(methodBinding); + } + + /** + * Gets all implicitly inherited method bindings for the given base method. + * Note: the result can only contain elements for base classes which are + * roles at the same time! + * @param baseClassName the name of the base class + * @param baseMethodName the name of the base mehtod + * @param baseMethodSignature the descriptor or the base method signature + * @return a Collection with all implicitly inherited method bindings + */ + private static Collection<MethodBinding> getImplicitlyInheritedBaseMethodBindings(String baseClassName, String baseMethodName, String baseMethodSignature) { + List<MethodBinding> resultList = new LinkedList<MethodBinding>(); + + if (!isRole(baseClassName)) + return resultList; // only roles can have implicit super types + + Iterator<Entry<String, LinkedList<RoleBaseBinding>>> it = getBaseBindingsCloneIterator(); + + while (it.hasNext()) { + Entry<String, LinkedList<RoleBaseBinding>> entry = it.next(); + String have = entry.getKey(); + // look for true superClass (not same): + if (have.equals(baseClassName)) + continue; + if (isImplicitSubtype(baseClassName, have)) { + // now we have an implicit superClass: + if (logging) + ObjectTeamsTransformation.printLogMessage(baseClassName + + " implicitly inherits callin bindings from " + have); + // collect the signatures of all bound base methods: + List<RoleBaseBinding> rbbList = entry.getValue(); + if (rbbList != null) { + Iterator<RoleBaseBinding> rbb_it = rbbList.iterator(); + while (rbb_it.hasNext()) { + RoleBaseBinding rbb = rbb_it.next(); + List<MethodBinding> mbs = rbb.getBaseMethodBindings(baseMethodName, baseMethodSignature); + if (mbs != null) + resultList.addAll(mbs); + } + } + } + } + //if (!resultList.isEmpty()) + // System.err.println(resultList); + return resultList; + } + + /** + * Get all callin bindings that a given base class may inherit from its superclass. + * @param className + * @result list of MethodBinding + */ + public static Collection<MethodBinding> getInheritedCallinBindings (String className) { +/* + List classBindings = baseBindings.get(className); + if (classBindings!=null) { + // any RoleBaseBinding contains the corresponding BoundClass object for the super base: + RoleBaseBinding anyRBB = (RoleBaseBinding)classBindings.get(0); + // problem: if a sub base class is not explicitly bound to a role class no corresponding + // BoundClass object exists. How can we access the super bindings via the + // super-link?? + BoundClass bc = anyRBB.getBaseClass(); + //System.out.println("-----------"); + while (bc.getSuper()!=null) { + System.out.println(bc.getName()); + bc = bc.getSuper(); + } + //System.out.println("-----------"); + } +*/ + + List<MethodBinding> result = new LinkedList<MethodBinding>(); + ObjectType current = new ObjectType(className); + if (!checkLookup(current.getClassName())) // TODO: workaround for classes loaded from special locations + return result; + // clone for re-entrance: + Iterator<Entry<String, LinkedList<RoleBaseBinding>>> it= getBaseBindingsCloneIterator(); + while (it.hasNext()) { + Entry<String, LinkedList<RoleBaseBinding>> entry = it.next(); + String have = entry.getKey(); + // look for true superClass (not same): + if (have.equals(className)) continue; + ObjectType haveType = new ObjectType(have); + //System.err.println(current + " : "+haveType); + //System.err.println(de.fub.bytecode.Repository.lookupClass(have)); + if (!checkLookup(haveType.getClassName())) // TODO: workaround for classes loaded from special locations + continue; + if (current.subclassOf(haveType)) { + // now we have a true superClass: + if (logging) + ObjectTeamsTransformation.printLogMessage(className + + " inherits callin bindings from " + have); + // collect the signatures of all bound base methods: + List<RoleBaseBinding> rbbList = entry.getValue(); + if (rbbList != null) { + Iterator<RoleBaseBinding> rbb_it = rbbList.iterator(); + while (rbb_it.hasNext()) { + RoleBaseBinding rbb = rbb_it.next(); + result.addAll(rbb.getBaseMethodBindings()); + } + } + } + } + return result; + } + + /** + * Get all super classes defining callin bindings that a given static base + * method may inherit. + * + * @author juerwid + * @param className + * @param methodName + * @param methodSignaturte + * @result list of <String[] {class_name}> + */ + public static Collection<String> getInheritedCallinBindingsForStaticMethods( + String className, String methodName, String methodSignature) + { + List<String> result = new LinkedList<String>(); + ObjectType current = new ObjectType(className); + if (!checkLookup(current.getClassName())) // TODO: workaround for classes loaded from special locations + return result; + // clone for re-entrance: + Iterator<Entry<String, LinkedList<RoleBaseBinding>>> it = getBaseBindingsCloneIterator(); + boolean getNextClass = false; + while (it.hasNext()) { + Entry<String,LinkedList<RoleBaseBinding>> entry = it.next(); + String have = entry.getKey(); + // look for true superClass (not same): + if (have.equals(className)) continue; + ObjectType haveType = new ObjectType(have); + if (!checkLookup(haveType.getClassName())) // TODO: workaround for classes loaded from special locations + continue; + if (current.subclassOf(haveType)) { + // now we have a true superClass: + if (logging) + ObjectTeamsTransformation.printLogMessage(className + + " inherits callin bindings from " + have); + // collect the signatures of all bound base methods: + LinkedList<RoleBaseBinding> rbbList = entry.getValue(); + if (rbbList != null) { + Iterator<RoleBaseBinding> rbb_it = rbbList.iterator(); + while (rbb_it.hasNext() && !getNextClass) { + RoleBaseBinding rbb = rbb_it.next(); + List<String[]> methods = rbb.getBaseSignatures(); + Iterator<String[]> methodsIter = methods.iterator(); + while(methodsIter.hasNext() && !getNextClass){ + String[] aMethod = methodsIter.next(); + if(methodName.equals(aMethod[0]) && methodSignature.equals(aMethod[1])){ + result.add(have); + getNextClass = true; + } + } + } + if(getNextClass){ + getNextClass = false; + continue; + } + } + } + } + return result; + } + + /** + * Checks if 'subType' is an implicit subtype of 'superType'. + * To be true the role name parts have to be equal and the + * team name parts have to be in a 'real' subtype relationship. + * + * @param subType the name of the potential subtype. + * @param superType the name of the potential supertype. + * @return true, if subType implicitly inherits from superType. + */ + private static boolean isImplicitSubtype(String subType, String superType) { + //System.err.println(subType +" -?-> " + superType); + int dollarIdxSub = subType.lastIndexOf('$'); + int dollarIdxSuper = superType.lastIndexOf('$'); + if (dollarIdxSub==-1 || dollarIdxSuper==-1) + return false; // no roles + String pureSubType = subType.substring(dollarIdxSub+1, subType.length()); + String pureSuperType = superType.substring(dollarIdxSuper+1, superType.length()); + if (!pureSubType.equals(pureSuperType)) + return false;// no identical role names + + String subTeamName = subType.substring(0, dollarIdxSub); + String superTeamName = superType.substring(0,dollarIdxSuper); + + if (!(checkLookup(subTeamName) && checkLookup(superTeamName))) + return false; // Repository can not lookup types + ObjectType subTeamType = new ObjectType(subTeamName); + ObjectType superTeamType = new ObjectType(superTeamName); + if (subTeamType.subclassOf(superTeamType)) { + if (logging) + ObjectTeamsTransformation.printLogMessage(subType + + " implicitly inherits method bindings from " + superType); + //System.err.println(subType + " implicitly inherits method bindings from " + superType); + return true; + } + return false; // no inheritance relation between teams + } + + /** + * Check to avoid NullPointerExceptions caused by the Repository when classes can not be looked up (because they loaded by + * classloaders with special a classpath) + * + * @param current The name of the class to look up in the Repository. + * @return False, if the class can not be found, true else. + */ + private static boolean checkLookup(String className) { + if (Repository.lookupClass(className) == null) { + if (OTEQUINOX_WARN) + System.err.println("Warning: Repository could not lookup class " + className + "!"); + return false; + } + return true; + } + + /** + * Get all inherited method bindings for a given base method, which are + * bindings declared for a super base class. + * + * the actual base class has additional bindings for this method! + * @param baseClassName the name of the role class + * @param baseMethodName the name of the role method + * @param baseMethodSignature the signature of the role method + * @result a List containing all inherited bindings for the role method + */ + public static List<MethodBinding> getInheritedBaseMethodBindings(String baseClassName, String baseMethodName, String baseMethodSignature) { + + List<MethodBinding> inheritedMethodBindings = new LinkedList<MethodBinding>(); + if (!isBoundBaseClass(baseClassName)) { + return inheritedMethodBindings; + } + LinkedList<RoleBaseBinding> baseBindingsForBase = baseBindings.get(baseClassName); + if (baseBindingsForBase == null) + return inheritedMethodBindings; + + Iterator<RoleBaseBinding> it = baseBindingsForBase.iterator(); + while (it.hasNext()) { + RoleBaseBinding rbb = it.next(); + BoundClass bc = rbb.getBaseClass(); + while (bc.getSuper() != null) { + bc = bc.getSuper(); + String superRoleName = bc.getName(); + Collection<MethodBinding> superRoleMethodBindings = CallinBindingManager.getBindingForBaseMethod( + superRoleName, + baseMethodName, + baseMethodSignature); + if (superRoleMethodBindings!=null) + inheritedMethodBindings.addAll(superRoleMethodBindings); + } + } + return inheritedMethodBindings; + } + + /** + * Get all inherited method bindings for a given role method, which are + * bindings declared in a super role class. The actual role class has + * additional bindings for this method! + * + * @param roleClassName the name of the role class + * @param roleMethodName the name of the role method + * @param roleMethodSignature the signature of the role method + * @result a List containing all inherited bindings for the role method + */ + public static List<MethodBinding> getInheritedRoleMethodBindings(String roleClassName, String roleMethodName, String roleMethodSignature) { + List<MethodBinding> inheritedMethodBindings = new LinkedList<MethodBinding>(); + if (!isBoundRoleClass(roleClassName)) { + return inheritedMethodBindings; + } + RoleBaseBinding rbb = roleBindings.get(roleClassName); + BoundClass bc = rbb.getRoleClass(); + while (bc.getSuper() != null) { + bc = bc.getSuper(); + String superRoleName = bc.getName(); + List<MethodBinding> superRoleMethodBindings = CallinBindingManager.getBindingsForRoleMethod( + superRoleName, + roleMethodName, + roleMethodSignature); + inheritedMethodBindings.addAll(superRoleMethodBindings); + } + return inheritedMethodBindings; + } + + /** + * All bindings for a given role method concerning its base class. + * + * @return List of MethodBinding + */ + public static List<MethodBinding> getBindingsForRoleMethod(String roleClassName, + String roleMethodName, String roleMethodSignature) + { + RoleBaseBinding rbb = roleBindings.get(roleClassName); + if (rbb==null) { + return new LinkedList<MethodBinding>(); + } + List<MethodBinding> roleMethodBindings = rbb.getRoleMethodBindings(roleMethodName, roleMethodSignature); + if (roleMethodBindings == null) { + return new LinkedList<MethodBinding>(); + } + return roleMethodBindings; + } + + /** + * @param className + * @return + */ + public static boolean isBoundBaseClass(String className) { + // and has at least one method binding? + boolean result = baseBindings.containsKey(className); + if (result || !isRole(className)) { + return result; + } + else {// bound implicit super class? + Iterator<String> it = getBaseBindingsKeyIterator(); + while (it.hasNext()) { + String have = it.next(); + // look for true superClass (not same): + if (have.equals(className)) + continue; + if (isImplicitSubtype(className, have)) + return true; + } + return result; + } + } + + /** + * @param className + * @return + */ + public static boolean isBoundBaseAndRoleClass(String className) { + // and has at least one method binding? + if (baseBindings.containsKey(className)) + return true; + // bound implicit super class? + Iterator<String> it = getBaseBindingsKeyIterator(); + while (it.hasNext()) { + String have = it.next(); + // look for true superClass (not same): + if (have.equals(className)) + continue; + if (isImplicitSubtype(className, have)) + return true; + } + return false; + } + + /** + * @param baseClassName + * @return + */ + public static boolean hasBoundBaseParent(String baseClassName) { + LinkedList<RoleBaseBinding> rbbList = baseBindings.get(baseClassName); + if (rbbList != null) { + Iterator<RoleBaseBinding> it = rbbList.iterator(); + while (it.hasNext()) { + RoleBaseBinding rbb = it.next(); + if (rbb.getBaseClass().getSuper()!=null) + return true; + } + } + // IMPLICIT_INHERITANCE + //if (isUnboundSubBase(baseClassName)) + // return true; + return false; + } + + + /** + * Returns the name of the topmost bound base class. + * + * @param baseClassName + * @return + */ + public static String getTopmostBoundBase(String baseClassName) { + LinkedList<RoleBaseBinding> rbbList = baseBindings.get(baseClassName); + if (rbbList != null) { + // assumption: EVERY BoundClass object has been connected to its bound super classes. + RoleBaseBinding anyRBB = rbbList.get(0); + BoundClass bc = anyRBB.getBaseClass(); + while (bc.getSuper() != null) { + bc = bc.getSuper(); + } + return bc.getName(); + } + return baseClassName; + } + + /** + * @param baseClassName + * @return + */ + public static String getBoundBaseParent(String baseClassName) { + LinkedList<RoleBaseBinding> rbbList = baseBindings.get(baseClassName); + if (rbbList != null) { + Iterator<RoleBaseBinding> it = rbbList.iterator(); + while (it.hasNext()) { + RoleBaseBinding rbb = it.next(); + BoundClass bc = rbb.getBaseClass().getSuper(); + if (bc!=null) + return bc.getName(); + } + } + // IMPLICIT_INHERITANCE + //if (isUnboundSubBase(baseClassName)) + // return true; + return null; + } + + /** + * @param teamClassName + * @param baseClassName + * @return + */ + public static boolean teamAdaptsSuperBase(String teamClassName, String baseClassName) { + LinkedList<RoleBaseBinding> rbbList = baseBindings.get(baseClassName); + if (rbbList != null && !rbbList.isEmpty()) { + RoleBaseBinding rbb = rbbList.getFirst(); // all entries refer to the same base class. + BoundClass bc = rbb.getBaseClass().getSuper(); + while (bc!=null) { + if (bc.isAdaptedByTeam(teamClassName)) + return true; + bc = bc.getSuper(); + } + } + return false; + } + + /** + * @param baseClassName + * @return + */ +// private static boolean isUnboundSubBase(String baseClassName) { +// ObjectType current = new ObjectType(baseClassName); +// if (!checkLookup(current.getClassName())) // TODO: workaround for classes loaded from special locations +// return false; +// Iterator /* String */ it = baseBindings.keySet().iterator(); +// while (it.hasNext()) { +// String have = (String)it.next(); +// // look for true superClass (not same): +// if (have.equals(baseClassName)) continue; +// ObjectType haveType = new ObjectType(have); +// //System.err.println(current + " : "+haveType); +// //System.err.println(de.fub.bytecode.Repository.lookupClass(have)); +// if (!checkLookup(haveType.getClassName())) // TODO: workaround for classes loaded from special locations +// continue; +// if (current.subclassOf(haveType)) { +// return true; +// } +// } +// return false; +// } + + /** + * @param className + * @return + */ + public static boolean isBoundRoleClass(String className) { + return roleBindings.containsKey(className); + } + + /** + * @param className + * @return + */ + public static RoleBaseBinding getRoleBaseBinding(String roleClassName) { + return roleBindings.get(roleClassName); + } + + /** + * @param roleClassName + * @return + */ + public static List<MethodBinding> getMethodBindingsForRoleClass(String roleClassName) + { + RoleBaseBinding rbb = roleBindings.get(roleClassName); + return rbb.getRoleMethodBindings(); + } + + /** + * @param roleClassName + * @return + */ + public static Set<String> getBoundRoleMethods(String roleClassName) { + Set<String> roleMethodKeySet = new HashSet<String>(); + if (roleBindings.get(roleClassName) != null) { + RoleBaseBinding rbb = roleBindings.get(roleClassName); + roleMethodKeySet = rbb.getRoleMethodSignatures(); + } + return roleMethodKeySet; + } + + /** + * @param baseClassName + * @return + */ + public static List<MethodBinding> getMethodBindingsForBaseClass(String baseClassName) + { + LinkedList<RoleBaseBinding> rbbList = baseBindings.get(baseClassName); + LinkedList<MethodBinding> resultList = new LinkedList<MethodBinding>(); + if (rbbList != null) { + Iterator<RoleBaseBinding> it = rbbList.iterator(); + while (it.hasNext()) { + RoleBaseBinding rbb = it.next(); + resultList.addAll(rbb.getRoleMethodBindings()); + } + } + return resultList; + } + + // -------------------------------------------------- + // discriminate base methods by tag per callin + // -------------------------------------------------- + // baseClass.baseMeth.baseMethodSignature -> tag + private static HashMap<String, Integer> baseCallTags = new HashMap<String, Integer>(); + // -- follow access methods for baseCallTags: + + /** + * @param baseClass + * @param baseMeth + * @param baseMethodSignature + */ + public static void assignBaseCallTag (String baseClass, String baseMeth, + String baseMethodSignature) + { + // every base method bound by a replace callin gets an unique tag: + String key = baseClass+"."+baseMeth+"."+baseMethodSignature; + int tag = baseCallTags.size(); + if (!baseCallTags.containsKey(key)) + baseCallTags.put(key, Integer.valueOf(tag)); + } + + public static int getBaseCallTag (String base_class_name, + String base_method_name, + String base_method_signature) { + String key = base_class_name + "." + base_method_name + + "." + base_method_signature; + return baseCallTags.get(key).intValue(); + } + + // ---------------------------------------------- + // Map argument positions: + // ---------------------------------------------- + // teamName -> callinWrapperName -> int[] + private static HashMap<String, HashMap<String, int[]>> paramMappings = new HashMap<String, HashMap<String, int[]>>(); + // -- follow access methods for paramMappings: + + /** + * @param teamName + * @param methodWrapper + * @param positions + */ + public static void addParameterBinding (String teamName, + String methodWrapper, + int[] positions) { + HashMap<String, int[]> teamMap = paramMappings.get(teamName); + if (teamMap == null) { + teamMap = new HashMap<String, int[]>(); + paramMappings.put(teamName, teamMap); + } + teamMap.put(methodWrapper, positions); + } + + public static boolean hasParamMappings (String teamName, String methodWrapper) + { + HashMap<String,int[]> teamMap = paramMappings.get(teamName); + if (teamMap == null) return false; + return teamMap.containsKey(methodWrapper); + } + + /** Get parameter positions for the given team or any super team. */ + public static int[] getParamPositions (String teamName, String methodWrapper) + { + int[] positions = getExactParamPositions(teamName, methodWrapper); + if (positions != null) + return positions; + // inherit parameter mappings from super teams: -- >> + Iterator<String> superTeams = getSuperTeamsWithParamMappigs(teamName).iterator(); + while (superTeams.hasNext()) { + positions = getExactParamPositions(superTeams.next(), methodWrapper); + if (positions != null) // found (the!) parameter mapping + return positions; + } + return null; + } + /** Get parameter positions for exactly the given team (no super teams). */ + private static int[] getExactParamPositions(String teamName, String methodWrapper) { + HashMap<String,int[]>teamMap = paramMappings.get(teamName); + if (teamMap == null) + return null; + return teamMap.get(methodWrapper); + } + + private static List<String> getSuperTeamsWithParamMappigs(String teamName) { + LinkedList<String> result = new LinkedList<String>(); + if (!checkLookup(teamName)) // TODO: workaround for classes loaded from special locations + return result; + for (JavaClass superTeam : Repository.getSuperClasses(teamName)) + if (paramMappings.get(superTeam.getClassName()) != null) + result.add(superTeam.getClassName()); + return result; + } + + + // ----------------------------------------------- + // tags for teams per base class: + // ----------------------------------------------- + // baseName -> HashMap (teamName -> tag (Integer)) + private static HashMap<String, HashMap<String, Integer>> baseTags = new HashMap<String, HashMap<String, Integer>>(); + + /** + * @param teamName + * @param baseToTag + */ + public static void addBaseTags (String teamName, HashMap<String, Integer> baseToTag) { + Iterator<Entry<String, Integer>> iter = baseToTag.entrySet().iterator(); + while (iter.hasNext()) { + Entry<String,Integer> entry = iter.next(); + String baseName = entry.getKey(); + Integer tag = entry.getValue(); + HashMap<String, Integer> thisBaseTags = baseTags.get(baseName); + if (thisBaseTags == null) { + thisBaseTags = new HashMap<String, Integer>(); + baseTags.put(baseName, thisBaseTags); + } + thisBaseTags.put(teamName, tag); + } + } + + /** + * Get all tags of teams which have callins for a given base class. + * @param baseClassName base class name. + * @return HashMap: team name -> tag (Integer) + */ + public static HashMap<String, Integer> getBaseTags (String baseClassName) { + HashMap<String, Integer> baseClassTags = baseTags.get(baseClassName); + if (baseClassTags == null) + return new HashMap<String, Integer>(); + return baseTags.get(baseClassName); + } + + /** + * Get all tags of teams which have callins for implicit super types + * of the given base class. + * Note: this is only possible if the base class is a role at the same time! + * @param baseClassName base class name. + * @return HashMap: team name -> tag (Integer) + */ + public static HashMap<String, Integer> getInheritedBaseTags(String baseClassName) { + HashMap<String, Integer> result = new HashMap<String, Integer>(); + // clone for re-entrance: + Iterator<String> it = getBaseBindingsKeyIterator(); + while (it.hasNext()) { + String have = it.next(); + // look for true superClass (not same): + if (have.equals(baseClassName)) + continue; + if (isImplicitSubtype(baseClassName, have)) + result.putAll(getBaseTags(have)); + } + return result; + } + + // ----------------------------------------------- + // callout bindings that require adjustment: + // ----------------------------------------------- + // baseClass -> HashSet (method_name+signature) + private static HashMap<String, HashSet<String>> calloutBindings = new HashMap<String, HashSet<String>>(); + + /** + * @param clazz + * @param meth + * @param sign + */ + public static void addCalloutBinding(String clazz, String meth, String sign) { + HashSet<String> bindings = calloutBindings.get(clazz); + if (bindings == null) { + bindings = new HashSet<String>(); + calloutBindings.put(clazz, bindings); + } + bindings.add(meth + sign); + } + + /** + * @param clazz + * @return + */ + public static HashSet<String> getCalloutBindings (String clazz) { + return calloutBindings.get(clazz); + } + + /** + * @param bindings + * @param method_name + * @param signature + * @return + */ + public static boolean requiresCalloutAdjustment(HashSet<String> bindings, + String method_name, + String signature) + { + return bindings.contains(method_name+signature); + } + + // ----------------------------------------------- + // callouts to base class fields are stored here for furher reading and get/set method generation: + // ----------------------------------------------- + private static ListValueHashMap<FieldDescriptor> calloutSetFields = new ListValueHashMap<FieldDescriptor>(); + private static ListValueHashMap<FieldDescriptor> calloutGetFields = new ListValueHashMap<FieldDescriptor>(); + + + /** + * @param roleClassName + * @param fieldName + * @param fieldSignature + * @param accessMode + * @param isStaticField + */ + public static void addCalloutBoundFileds(String baseClassName, String fieldName, + String fieldSignature, String accessMode, boolean isStaticField) + { + FieldDescriptor fd = new FieldDescriptor(fieldName, fieldSignature, isStaticField); + + if (accessMode.equals("get")) { + calloutGetFields.put(baseClassName, fd); + } else if (accessMode.equals("set")) { + calloutSetFields.put(baseClassName, fd); + } else { + throw new OTREInternalError("CalloutFieldAccess attribute contains wrong access mode: "+accessMode); + } + } + + /** + * @param baseClassName + * @return + */ + public static List<FieldDescriptor> getCalloutGetFields(String baseClassName) { + return calloutGetFields.get(baseClassName); + } + + /** + * @param baseClassName + * @return + */ + public static List<FieldDescriptor> getCalloutSetFields(String baseClassName) { + return calloutSetFields.get(baseClassName); + } + + // ----------------------------------------------- + // base calls to super methods via special accessor + // ----------------------------------------------- + private static ListValueHashMap<SuperMethodDescriptor> superMethods = new ListValueHashMap<SuperMethodDescriptor>(); + + public static void addSuperAccess(String baseClassName, String superClassName, String methodName, String signature) { + SuperMethodDescriptor superMethod = new SuperMethodDescriptor(methodName, baseClassName, superClassName, signature); + superMethods.put(baseClassName, superMethod); + } + public static List<SuperMethodDescriptor> getSuperAccesses(String class_name) { + return superMethods.get(class_name); + } + + // ----------------------------------------------- + // bound interfaces have to be registered, for implementing classes to inherit callin bindings: + // ----------------------------------------------- + private static List<String> boundBaseInterfaces = new LinkedList<String>(); + + /** + * @param baseInterfaceName The name of the bound base interface. + */ + public static void addBoundBaseInterface(String baseInterfaceName) { + if (!boundBaseInterfaces.contains(baseInterfaceName)) + boundBaseInterfaces.add(baseInterfaceName); + } + + /** + * @param interfaceName + * @return + */ + public static Collection<String[]> getInterfaceInheritedCallinBindings(String interfaceName) { + List<String[]> result = new LinkedList<String[]>(); + //System.err.println(interfaceName); + //System.err.println(boundBaseInterfaces); + if (boundBaseInterfaces.contains(interfaceName)) { + if (logging) + ObjectTeamsTransformation.printLogMessage(interfaceName + + " bequests callin bindings to implementing class"); + // collect the signatures of all bound base methods: + LinkedList<RoleBaseBinding> rbbList = baseBindings.get(interfaceName); + if (rbbList != null) { + Iterator<RoleBaseBinding> rbb_it = rbbList.iterator(); + while (rbb_it.hasNext()) { + RoleBaseBinding rbb = rbb_it.next(); + result.addAll(rbb.getBaseSignatures()); + + // System.err.println("inheriting binding: "+rbb); + } + } + } + return result; + } + + /** + * @param methodName + * @param methodSignature + * @param interfaceName + * @return + */ + public static Collection<MethodBinding> getInterfaceInheritedMethodBindings( + String methodName, String methodSignature, String interfaceName) { + List<MethodBinding> result = new LinkedList<MethodBinding>(); + if (boundBaseInterfaces.contains(interfaceName)) { + LinkedList<RoleBaseBinding> rbbList = baseBindings.get(interfaceName); + if (rbbList != null) { + Iterator<RoleBaseBinding> rbb_it = rbbList.iterator(); + while (rbb_it.hasNext()) { + RoleBaseBinding rbb = rbb_it.next(); + List<MethodBinding> mbs = rbb.getBaseMethodBindings(methodName, methodSignature); + if (mbs != null) + result.addAll(mbs); + } + } + } + return result; + } + + /** + * @param interfaceNames + * @return + */ + public static boolean containsBoundBaseInterface(String[] interfaceNames) { + for (int i = 0; i < interfaceNames.length; i++) { + if (boundBaseInterfaces.contains(interfaceNames[i])) + return true; + } + return false; + } + + // ----------------------------------------------- + // precedence can be set for a list of method bindings: + // ----------------------------------------------- + + private static ListValueHashMap<List<String>> precedencePerTeam = new ListValueHashMap<List<String>>(); + + /** + * Add a precedence list for the given team. + * @param precedenceList the list of binding labels defining their precedence + * @param teamName the name of the team for which the precedence list was defined + */ + public static void addPrecedenceList(List<String> precedenceList, String teamName) { + precedencePerTeam.put(teamName, precedenceList); + } + + /** + * Sorts the method binding list according to the given precedence for the team + * @param mbList the list of 'MethodBinding's to be sorted + * @param teamName the name of the corresponding team + * @return the sorted 'MethodBindig' list + */ + public static List<MethodBinding> sortMethodBindings(List<MethodBinding> mbList, String teamName) { + if (mbList.size() < 2) + return mbList; // nothing to sort + String outermostTeamName; + int dollarIdx = teamName.indexOf('$'); + if (dollarIdx > 0) + outermostTeamName = teamName.substring(0, dollarIdx); + else outermostTeamName = teamName; + + List<List<String>> precedenceList = precedencePerTeam.get(outermostTeamName); + if (precedenceList==null) { + // mbList has to be reduced by removing overridden bindings: + return removeOverridden(mbList); + } + + LinkedList<MethodBinding> sortedMethodBindings = new LinkedList<MethodBinding>(); + Iterator<List<String>> predIt = precedenceList.iterator(); + + while (predIt.hasNext()) { + List<String> plainList = predIt.next(); + Iterator<String> plainIt = plainList.iterator(); + + while (plainIt.hasNext()) { + boolean foundOne = false; + String label = plainIt.next(); + Iterator<MethodBinding> mbIter = mbList.iterator(); + List<MethodBinding> alreadySorted = new LinkedList<MethodBinding>(); + + while (mbIter.hasNext()) { + MethodBinding mb = mbIter.next(); + + if (mb.getQualifiedBindingLabel().equals(label)) { // mb exactly fits binding label: + alreadySorted.add(mb); + if (!foundOne) { + sortedMethodBindings.add(mb); + foundOne = true; + } else checkInheritance(sortedMethodBindings.getLast(), mb); + + } else if (mb.inheritsBindingLabel(label, teamName)) { // mb inherits binding label: + alreadySorted.add(mb); + if (!foundOne) { + sortedMethodBindings.add(mb); + foundOne = true; + } else {// maybe it is a subtype of the already added? + MethodBinding lastAdded = sortedMethodBindings.getLast(); + if (mb.overridesMethodBinding(lastAdded)) { + sortedMethodBindings.set(sortedMethodBindings.size()-1, mb); + foundOne = true; + } else checkInheritance(sortedMethodBindings.getLast(), mb); + } + } + } + if (foundOne) { + Iterator<MethodBinding> sortedIter = alreadySorted.iterator(); + while (sortedIter.hasNext()) { + MethodBinding mb = sortedIter.next(); + mbList.remove(mb); + } + } + } + } +// if (mbList.size() > 0) { +// System.err.println("ERROR: Unsortable method bindings: " + mbList +"!"); +// // assumption: all remaining method bindings are overridden! TODO: check assumption! +// } + return sortedMethodBindings; + } + + /** + * Removes overridden method bindings from the given list. Assumes that + * all bindings in the list are in a sub/super type relationship. + * @param mbList the method binding list + * @return the most specific method binding overriding all others + */ + private static List<MethodBinding> removeOverridden(List<MethodBinding> mbList) { + MethodBinding mostSpecificMB = mbList.get(0); + Iterator<MethodBinding> mbIter = mbList.iterator(); + while (mbIter.hasNext()) { + MethodBinding mb = mbIter.next(); + if (mb.overridesMethodBinding(mostSpecificMB)) { + mostSpecificMB = mb; + } else checkInheritance(mostSpecificMB, mb); + } + List<MethodBinding> resultList = new LinkedList<MethodBinding>(); + resultList.add(mostSpecificMB); + return resultList; + } + + /** + * Checks if 'subMB' 'inherits' from 'superMB'. Used to check if assumptions + * about the precedence lists are fulfilled. + * + * @param subMB the overriding method binding + * @param superMB the overridden method binding + */ + private static void checkInheritance(MethodBinding subMB, MethodBinding superMB) { + if (! (subMB.equals(superMB) || subMB.overridesMethodBinding(superMB))) { + //System.err.println("sub: " + subMB + "\n super: " + superMB); + throw new OTREInternalError("Wrong assumption! Broken precedence list possible."); + } + } + + // ------------------------------------------ + // ---------- Logging: ---------------------- + // ------------------------------------------ + /** Initialized from property <tt>ot.log</tt>. */ + static boolean logging = false; + /** Initialized from property <tt>otequinox.debug</tt> */ + static boolean OTEQUINOX_WARN = false; + static { + if(System.getProperty("ot.log") != null) + logging = true; + String warnlevel = System.getProperty("otequinox.debug"); + if (warnlevel != null) { + warnlevel = warnlevel.toUpperCase(); + if (warnlevel.startsWith("INFO") || warnlevel.equals("OK")) + OTEQUINOX_WARN = true; + } + } + + // ----------------------------------------------- + // static replace callin bindings have to be stored in the team, special treatment: + // ----------------------------------------------- + + // maps an implemented role method to its base methods: roleMethod -> List[baseMethod] // added by JU + private static ListValueHashMap<BaseMethodInfo> staticReplaceBindings = new ListValueHashMap<BaseMethodInfo>(); + + /** + * Adds a base method to its implemented role method (static replace bindings). + * + * @param roleMethodKey a string structured according team_class_ name.role_class_name.role_method_name.role_method_signature + * @param baseMethodInfo an Object contained base class name, base method name and base method signature + */ + public static void addStaticReplaceBindingForRoleMethod(String roleMethodKey, BaseMethodInfo baseMethodInfo) { + staticReplaceBindings.put(roleMethodKey, baseMethodInfo); + } + + /** + * Returns static method bindings for the given role method + * @param roleMethodKey + * @return + */ + public static LinkedList<BaseMethodInfo> getStaticReplaceBindingsForRoleMethod(String roleMethodKey) { + return staticReplaceBindings.get(roleMethodKey); + } + + /** + * @param roleClassName + * @param roleMethodName + * @param roleMethodSignature + * @return + */ + public static boolean roleMethodHasBinding(String roleClassName, String roleMethodName, String roleMethodSignature) { + RoleBaseBinding rbb = roleBindings.get(roleClassName); + if (rbb == null) { + return false; + } + return rbb.hasRoleMethodBinding(roleMethodName, roleMethodSignature); + } + + // ----------------------------------------------- + // access modifiers of some base classes have to be changed to 'public' (decapsulation) + // the names of these classes are stored in baseClassesForModifierChange + // ----------------------------------------------- + private static LinkedList<String> baseClassesForModifierChange = new LinkedList<String>(); + + /** + * Adds the given class name to the list of class names intended for decapsulation + * @param className + */ + public static void addBaseClassForModifierChange(String className){ + baseClassesForModifierChange.add(className); + } + + /** + * Checks whether a class name is contained in the list of classes intended for decapsulation + * @param className + * @return + */ + public static boolean checkBaseClassModifierChange(String className) { + Iterator<String> iter = baseClassesForModifierChange.iterator(); + while(iter.hasNext()) { + if(className.equals(iter.next())){ + return true; + } + } + return false; + } + + public static void addBoundSuperclassLink(String sub_name, String super_name) { + synchronized (baseBindings) { + bases: + for (RoleBaseBinding subBinding : baseBindings.getFlattenValues()) { + if (subBinding.getBaseClassName().equals(sub_name)) { + for (RoleBaseBinding superBinding : baseBindings.getFlattenValues()) { + if (subBinding == superBinding) continue; + if (superBinding.getBaseClassName().equals(super_name)) { + subBinding.getBaseClass().setSuper(superBinding.getBaseClass()); + break bases; + } + } + } + } + } + } + + /** Either retrieve an existing BoundClass for `className' are create a new one. + * If an existing one is used it is updated for the new adapting team. + */ + public static BoundClass getBoundBaseClass(String className, String teamClassName) { + for (RoleBaseBinding subBinding : baseBindings.getFlattenValues()) { + if (subBinding.getBaseClassName().equals(className)) { + BoundClass baseClass = subBinding.getBaseClass(); + baseClass.addAdaptingTeam(teamClassName); + return baseClass; + } + } + return new BoundClass(className, teamClassName); + } + + // ==== HELPERS FOR RE-ENTRANCE SAFETY: ==== + + private static Iterator<String> getBaseBindingsKeyIterator() { + synchronized (baseBindings) { + ArrayList<String> list = new ArrayList<String>(); + list.addAll(baseBindings.keySet()); + Iterator<String> it = list.iterator(); + return it; + } + } + + private static Iterator<Entry<String, LinkedList<RoleBaseBinding>>> getBaseBindingsCloneIterator() { + synchronized (baseBindings) { + ArrayList<Entry<String, LinkedList<RoleBaseBinding>>> list = new ArrayList<Entry<String,LinkedList<RoleBaseBinding>>>(); + list.addAll(baseBindings.entrySet()); + Iterator<Entry<String, LinkedList<RoleBaseBinding>>> it = list.iterator(); + return it; + } + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/DebugUtil.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/DebugUtil.java new file mode 100644 index 000000000..7282185c5 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/DebugUtil.java @@ -0,0 +1,155 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2003-2009 Berlin Institute of Technology, 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: DebugUtil.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; + +import de.fub.bytecode.generic.*; +import de.fub.bytecode.Constants; +import java.util.Enumeration; + +import org.eclipse.objectteams.otre.OTConstants; + +/** + * @author Stephan Herrmann + */ +@SuppressWarnings("nls") +public class DebugUtil { + + // ----------------------------------------- + // -------- Development utilities --------- + // ----------------------------------------- + + /** + * Creates an the instructions necessary to "System.out.println" + * the given string. + * + * @param cpg the constant pool of the class where this instructions + * will be inserted + * @param string the String to be printed + * @return an InstructionList containing the necessary instructions + */ + public static InstructionList createPrintln(ConstantPoolGen cpg, + InstructionFactory factory, + String string) + { + InstructionList il = new InstructionList(); + ObjectType p_stream = new ObjectType("java.io.PrintStream"); + + il.append(factory.createFieldAccess("java.lang.System", "out", + p_stream, + Constants.GETSTATIC)); + il.append(new PUSH(cpg, string + "\n")); + il.append(factory.createInvoke("java.io.PrintStream", "print", + Type.VOID, + new Type[] { Type.STRING }, + Constants.INVOKEVIRTUAL)); + return il; + } + + /** + * Create a <tt>System.out.println</tt> call for an <tt>Object</tt> + * argument. The argument is assumed to be on the stack and will not + * be consumed. + */ + public static InstructionList createPrintlnObj(InstructionFactory factory) { + InstructionList il = new InstructionList(); + ObjectType p_stream = new ObjectType("java.io.PrintStream"); + + il.append(new DUP()); + il.append(factory.createFieldAccess("java.lang.System", "out", + p_stream, + Constants.GETSTATIC)); + il.append(new SWAP()); + il.append(factory.createInvoke("java.io.PrintStream", "print", + Type.VOID, + new Type[] { OTConstants.object }, + Constants.INVOKEVIRTUAL)); + return il; + } + + /** + * Create a <tt>System.out.println</tt> call for an <tt>Exception</tt> + * argument. The argument is assumed to be on the stack and _will_ + * be consumed. This effect is however only performed, if + * the property ot.log.lift is set. + */ + public static InstructionList createReportExc(InstructionFactory factory) { + InstructionList il = new InstructionList(); + if (System.getProperty("ot.log.lift") == null) { + il.append(new POP()); + } else { + ObjectType p_stream = new ObjectType("java.io.PrintStream"); + + il.append(factory.createFieldAccess("java.lang.System", "out", + p_stream, + Constants.GETSTATIC)); + il.append(new SWAP()); + il.append(factory.createInvoke("java.io.PrintStream", "println", + Type.VOID, + new Type[] { OTConstants.object }, + Constants.INVOKEVIRTUAL)); + } + return il; + } + + /** + * Create a <tt>System.out.println</tt> call for an <tt>int</tt> + * argument. The argument is assumed to be on the stack and will not + * be consumed. + */ + public static InstructionList createPrintlnInt(InstructionFactory factory) { + InstructionList il = new InstructionList(); + ObjectType p_stream = new ObjectType("java.io.PrintStream"); + + il.append(new DUP()); + il.append(factory.createFieldAccess("java.lang.System", "out", + p_stream, + Constants.GETSTATIC)); + il.append(new SWAP()); + il.append(factory.createInvoke("java.io.PrintStream", "print", + Type.VOID, + new Type[] { Type.INT }, + Constants.INVOKEVIRTUAL)); + return il; + } + + public static InstructionList createPrintlnBool(ConstantPoolGen cp) { + InstructionList il= new InstructionList(); + int out = cp.addFieldref("java.lang.System", "out", + "Ljava/io/PrintStream;"); + int println = cp.addMethodref("java.io.PrintStream", "println", + "(Z)V"); + il.append(new DUP()); + il.append(new GETSTATIC(out)); + il.append(new SWAP()); + il.append(new INVOKEVIRTUAL(println)); + return il; + } + + @SuppressWarnings("unchecked") + public static void printIL (InstructionList il, ConstantPoolGen cpg) { + int off = 0; + Enumeration en = il.elements(); + while(en.hasMoreElements()) { + Instruction i = ((InstructionHandle)en.nextElement()).getInstruction(); + off += i.produceStack(cpg); + off -= i.consumeStack(cpg); + System.out.print(off); + System.out.println(" = "+i); + } + } + +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/FieldDescriptor.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/FieldDescriptor.java new file mode 100644 index 000000000..30a3dd026 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/FieldDescriptor.java @@ -0,0 +1,53 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2004-2009 Berlin Institute of Technology, 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: FieldDescriptor.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; + +/** + * @author resix + */ +public class FieldDescriptor { + private String fieldName; + private String fieldSignature; + private boolean isStaticField; + + public FieldDescriptor(String name, String signature, boolean is_static) { + fieldName = name; + fieldSignature = signature; + isStaticField = is_static; + } + + /** + * @return Returns the fieldName. + */ + public String getFieldName() { + return fieldName; + } + + /** + * @return Returns the fieldSignature. + */ + public String getFieldSignature() { + return fieldSignature; + } + + /** + * @return Returns the isStaticField. + */ + public boolean isStaticField() { + return isStaticField; + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/ListValueHashMap.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/ListValueHashMap.java new file mode 100644 index 000000000..665ee5f03 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/ListValueHashMap.java @@ -0,0 +1,103 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2004-2009 Berlin Institute of Technology, 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: ListValueHashMap.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Map.Entry; + +/** + * @author resix + */ +public class ListValueHashMap<ValueType> { + private HashMap<String, LinkedList<ValueType>> hashMap = new HashMap<String, LinkedList<ValueType>>(); + //redundant structure for faster access: + private LinkedList<ValueType> flattenValues = new LinkedList<ValueType>(); + + /** + * @param key + * @param value + */ + public void put(String key, ValueType value) { + LinkedList<ValueType> list; + if (!hashMap.containsKey(key)) { + list = new LinkedList<ValueType>(); + } else { + list = hashMap.get(key); + } + list.add(value); + hashMap.put(key, list); + flattenValues.add(value); + } + + /** + * @return + */ + public List<ValueType> getFlattenValues() { + return flattenValues; + } + + /** + * @param key + * @return + */ + public LinkedList<ValueType> get(String key) { + if (!hashMap.containsKey(key)) { + return null; + } + return hashMap.get(key); + } + + public boolean containsKey(Object o) { + return hashMap.containsKey(o); + } + + public Set<String> keySet() { + return hashMap.keySet(); + } + + public Set<Entry<String, LinkedList<ValueType>>> entrySet() { + return hashMap.entrySet(); + } + + public int size() { + return hashMap.size(); + } + + public String toString() { + StringBuilder result = new StringBuilder(32); + Iterator it = hashMap.entrySet().iterator(); + while (it.hasNext()) { + Entry entry = (Entry) it.next(); + result.append(entry.getKey()); + result.append(": "); + result.append(entry.getValue().toString()); + result.append("\n"); + } + if (result.length() == 0) + return super.toString(); + return result.toString(); + } + + public Collection<LinkedList<ValueType>> valueSet() { + return hashMap.values(); + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/MethodBinding.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/MethodBinding.java new file mode 100644 index 000000000..ecc543659 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/MethodBinding.java @@ -0,0 +1,395 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: MethodBinding.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; + +import org.eclipse.objectteams.otre.OTConstants; + +/** + * @version $Id: MethodBinding.java 23408 2010-02-03 18:07:35Z stephan $ + * @author Christine Hundt + */ +public class MethodBinding { + + private String bindingFileName; + private int bindingLineNumber; + private int bindingLineOffset; + + private String bindingLabel; + + private BoundMethod baseMethod; + private BoundMethod roleMethod; + + private boolean isStaticBaseMethod; + private boolean isStaticRoleMethod; + private boolean covariantBaseReturn; + + private int translationFlags; + + private String wrapperName; + private String wrapperSignature; + + private String modifier; + private String liftMethodName; + private String liftMethodSignature; + + private String roleClassName; + + // back reference for the class binding containing this method binding. + private RoleBaseBinding classBinding; + + /** + * + */ + public MethodBinding() {} + + /** + * @param bindingFileName + * @param bindingLineNumber + * @param bindingLineOffest + * @param bindingLabel + * @param roleMethodName + * @param roleMethodSignature + * @param isStaticRoleMethod + * @param wrapperName + * @param wrapperSignature + * @param modifier + * @param baseMethodName + * @param baseMethodSignature + * @param isStaticBaseMethod + * @param baseIsCallin + * @param liftMethodName + * @param liftMethodSignature + * @param classBinding + */ + public MethodBinding( + String bindingFileName, int bindingLineNumber, int bindingLineOffest, + String bindingLabel, String roleMethodName, String roleMethodSignature, boolean isStaticRoleMethod, + String wrapperName, String wrapperSignature, String modifier, + String baseMethodName, String baseMethodSignature, + boolean isStaticBaseMethod, boolean baseIsCallin, boolean covariantBaseReturn, + int translationFlags, + String liftMethodName, String liftMethodSignature, RoleBaseBinding classBinding) + { + this.bindingFileName = bindingFileName; + this.bindingLineNumber = bindingLineNumber; + this.bindingLineOffset = bindingLineOffest; + + this.bindingLabel = bindingLabel; + roleMethod = new BoundMethod(roleMethodName, roleMethodSignature, false, this); + baseMethod = new BoundMethod(baseMethodName, baseMethodSignature, baseIsCallin, this); + // BoundMethod object for one method is not unique! + // but if it would be unique the 'binding' link would not be unique + this.isStaticRoleMethod = isStaticRoleMethod; + this.isStaticBaseMethod = isStaticBaseMethod; + this.covariantBaseReturn = covariantBaseReturn; + this.translationFlags = translationFlags; + this.wrapperName = wrapperName; + this.wrapperSignature = wrapperSignature; + this.modifier = modifier; + this.liftMethodName = liftMethodName; + this.liftMethodSignature = liftMethodSignature; + this.classBinding = classBinding; + this.roleClassName = classBinding.getRoleClassName(); + } + + /** + * @return + */ + public String getRoleClassName() { + return roleClassName; + } + + /** + * @return + */ + public String getBindingFileName() { + return bindingFileName; + } + + /** + * @return + */ + public int getBindingLineNumber() { + return bindingLineNumber; + } + + /** + * @return + */ + public int getBindingLineOffset() { + return bindingLineOffset; + } + + /** + * @return + */ + public String getBindingLabel() { + return bindingLabel; + } + + /** + * @return + */ + public String getQualifiedBindingLabel() { + String result = roleClassName; + // remove the outermost team (use the '$' to distinguish pack1.Team1$Role from Team1$__OT__Team2$Role): + result = result.substring(result.indexOf('$') + 1); + // replace "$" by "." in the role class name: + result = result.replace('$', '.'); + // remove "__OT__" prefixes in role class names: + result = result.replace(OTConstants.OTDT_PREFIX, ""); //$NON-NLS-1$ + // add the binding label and return: + return result + '.' + bindingLabel; + } + + /** + * @return + */ + public String getRoleMethodName() { + return roleMethod.getName(); + } + + /** + * @return + */ + public String getRoleMethodSignature() { + return roleMethod.getSignature(); + } + + /** + * @return + */ + public String getWrapperName() { + return wrapperName; + } + + /** + * @return + */ + public String getWrapperSignature() { + return wrapperSignature; + } + + /** + * @return + */ + public String getBaseClassName() { + return classBinding.getBaseClassName(); + } + + /** + * @return + */ + public String getBaseMethodName() { + return baseMethod.getName(); + } + + /** + * @return + */ + public String getBaseMethodSignature() { + return baseMethod.getSignature(); + } + + /** + * @return + */ + public boolean baseMethodIsCallin() { + return baseMethod.getIsCallin(); + } + + /** + * @return + */ + public String getModifier() { + return modifier; + } + + /** + * @return + */ + public boolean isReplace() { + return modifier.equals("replace"); + } + + /** + * @return + */ + public boolean isAfter() { + return modifier.equals("after"); + } + + /** + * @return + */ + public boolean isBefore() { + return modifier.equals("before"); + } + + /** + * @return Returns the liftMethodName. + */ + public String getLiftMethodName() { + return liftMethodName; + } + + /** + * @return Returns the liftMethodSignature. + */ + public String getLiftMethodSignature() { + return liftMethodSignature; + } + + /** + * @return Returns the corresponding class binding. + */ + public RoleBaseBinding getClassBinding() { + return classBinding; + } + + /** + * Returns the most super bound base class of the base class belonging to + * this MethodBinding. Note: only consider base classes bound by roles of + * the corresponding team! + * + * @return the most super bound base class + */ + public String getRootBoundBase() { + BoundClass bc = classBinding.getRoleClass(); + while (bc.getSuper() != null && CallinBindingManager.isBoundRoleClass(bc.getSuper().getName()) ) { + bc = bc.getSuper(); + } + RoleBaseBinding rbb = CallinBindingManager.getRoleBaseBinding(bc.getName()); + return rbb.getBaseClassName(); + } + + /** + * Returns the name of the team surrounding the role class of this binding. + * + * @return the name of the corresponding team + */ + public String getTeamClassName() { + int dollarIndex = roleClassName.lastIndexOf('$'); + // return everything before the last '$', because for nested teams there are more than one: + return roleClassName.substring(0, dollarIndex); + } + + /** + * @param anotherMB + * @return + */ + public boolean overridesMethodBinding(MethodBinding anotherMB) { + if (anotherMB == null) + return false; + if (!bindingLabel.equals(anotherMB.getBindingLabel())) + return false; + return classBinding.getRoleClass().isSubClassOf(anotherMB.getClassBinding().getRoleClassName()); + } + + /** + * @param bindingLabel + * @param teamName + * @return + */ + public boolean inheritsBindingLabel(String bindingLabel, String teamName) { + String prefix = teamName + "$__OT__"; + int dotIndex = bindingLabel.lastIndexOf('.'); + String classOfLabel = prefix + bindingLabel.substring(0, dotIndex); + if (!bindingLabel.substring(dotIndex + 1).equals(this.bindingLabel)) + return false; + + if (classBinding.getRoleClass().isSubClassOf(classOfLabel)) + return true; + return false; + } + + /** + * @param mb + * @return + */ + public boolean equals(MethodBinding mb) { + return roleMethod.getName().equals(mb.getRoleMethodName()) + && roleMethod.getSignature().equals(mb.getRoleMethodSignature()) + && classBinding.getBaseClassName().equals(mb.getBaseClassName()) + && baseMethod.getName().equals(mb.getBaseMethodName()) + && baseMethod.getSignature().equals(mb.getBaseMethodSignature()) + && modifier.equals(mb.getModifier()); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuilder result = new StringBuilder(32); + result.append("\t"); + result.append(getQualifiedBindingLabel()); + result.append(": "); + result.append(roleMethod.getName()); + result.append(roleMethod.getSignature()); + result.append(" <-> "); + result.append(modifier); + result.append(" "); + result.append(baseMethod.getName()); + result.append(baseMethod.getSignature()); + if (this.covariantBaseReturn) + result.append('+'); + return result.toString(); + } + + /** + * @return + */ + public boolean hasStaticRoleMethod() { + return isStaticRoleMethod; + } + + /** + * @return + */ + public boolean hasStaticBaseMethod() { + return isStaticBaseMethod; + } + + public int getTranslationFlags() { + return this.translationFlags; + } + + /** For base methods provide a key without the trailing return type to cater for covariance. */ + static String getBaseMethodKey(String baseMethodName, String baseMethodSignature) { + int pos= baseMethodSignature.lastIndexOf(')'); + String baseMethodKey = baseMethodName + '.' + baseMethodSignature.substring(0, pos+1); + return baseMethodKey; + } + + /** + * Is the method specified by mName and mSig a match for this method binding? + * @param mName method name + * @param mSig full method signature + * @param strict if true covariance is not supported + */ + public boolean matchesMethod(String mName, String mSig, boolean strict) { + String baseMethodName = getBaseMethodName(); + String baseMethodSignature = getBaseMethodSignature(); + if (this.covariantBaseReturn && !strict) { + return getBaseMethodKey(mName, mSig).equals( + getBaseMethodKey(baseMethodName, baseMethodSignature)); + } else { + return mName.equals(baseMethodName) + && mSig.equals(baseMethodSignature); + } + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/RoleBaseBinding.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/RoleBaseBinding.java new file mode 100644 index 000000000..172c0aeb8 --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/RoleBaseBinding.java @@ -0,0 +1,228 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: RoleBaseBinding.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * @version $Id: RoleBaseBinding.java 23408 2010-02-03 18:07:35Z stephan $ + * @author Christine Hundt + */ +public class RoleBaseBinding { + + private BoundClass roleClass; + private BoundClass baseClass; + + private ListValueHashMap<MethodBinding> roleMethodBindings = new ListValueHashMap<MethodBinding>(); + private ListValueHashMap<MethodBinding> baseMethodBindings = new ListValueHashMap<MethodBinding>(); + + /** + * + */ + public RoleBaseBinding() { + } + + /** + * @param _roleClassName + * @param _baseClassName + * @param teamClassName + */ + public RoleBaseBinding(String _roleClassName, String _baseClassName, String teamClassName) { + roleClass = new BoundClass(_roleClassName, teamClassName); + baseClass = CallinBindingManager.getBoundBaseClass(_baseClassName, teamClassName); + } + + /** + * @param bindingFileName + * @param bindingLineNumber + * @param bindingLineOffset + * @param bindingLabel + * @param roleMethodName + * @param roleMethodSignature + * @param isStaticRoleMethod + * @param wrapperName + * @param wrapperSignature + * @param modifier + * @param baseMethodName + * @param baseMethodSignature + * @param isStaticBaseMethod + * @param baseIsCallin + * @param translationFlags + * @param liftMethodName + * @param liftMethodSignature + */ + public void addMethodBinding( + String bindingFileName, int bindingLineNumber, int bindingLineOffset, + String bindingLabel, String roleMethodName, String roleMethodSignature, + boolean isStaticRoleMethod, String wrapperName, String wrapperSignature, String modifier, + String baseMethodName, String baseMethodSignature, + boolean isStaticBaseMethod, boolean baseIsCallin, boolean covariantBaseReturn, + int translationFlags, String liftMethodName, String liftMethodSignature) + { + MethodBinding mb = new MethodBinding(bindingFileName, bindingLineNumber, bindingLineOffset, + bindingLabel, roleMethodName, roleMethodSignature, isStaticRoleMethod, + wrapperName, wrapperSignature, modifier, + baseMethodName, baseMethodSignature, + isStaticBaseMethod, baseIsCallin, covariantBaseReturn, + translationFlags, + liftMethodName, + liftMethodSignature, this); + // TODO: check, if the key has to include the 'binding_label' + String baseMethodKey = MethodBinding.getBaseMethodKey(baseMethodName, baseMethodSignature); + String roleMethodKey = roleMethodName + '.' + roleMethodSignature; + roleMethodBindings.put(roleMethodKey, mb); + baseMethodBindings.put(baseMethodKey, mb); + } + + /** + * @return + */ + public List<MethodBinding> getBaseMethodBindings() { + return baseMethodBindings.getFlattenValues(); + } + + /** + * @param baseMethodName + * @param baseMethodSignature + * @return + */ + public List<MethodBinding> getBaseMethodBindings(String baseMethodName, String baseMethodSignature) { + String baseMethodKey = MethodBinding.getBaseMethodKey(baseMethodName, baseMethodSignature); + return baseMethodBindings.get(baseMethodKey); + } + + /** + * @return + */ + public List<MethodBinding> getRoleMethodBindings() { + return roleMethodBindings.getFlattenValues(); + } + + /** + * @param roleMethodName + * @param roleMethodSignature + * @return + */ + public List<MethodBinding> getRoleMethodBindings(String roleMethodName, String roleMethodSignature) { + String roleMethodKey = roleMethodName + '.' + roleMethodSignature; + return roleMethodBindings.get(roleMethodKey); + } + + /** + * @param roleMethodName + * @param roleMethodSignature + * @return + */ + public boolean hasRoleMethodBinding(String roleMethodName, String roleMethodSignature) { + String signatureWithoutReturnType = roleMethodSignature.substring(0, roleMethodSignature.lastIndexOf(')') + 1); + Set<String> bindingKeys = roleMethodBindings.keySet(); + Iterator<String> it = bindingKeys.iterator(); + while (it.hasNext()) { + String key = it.next(); + String keyWithoutReturnType = key.substring(0, key.lastIndexOf(')') + 1); + if (keyWithoutReturnType.equals(roleMethodName + '.' + + signatureWithoutReturnType)) + return true; + } + return false; + } + + public Set<String> getRoleMethodSignatures() { + return roleMethodBindings.keySet(); + } + + /** + * @return + */ + public BoundClass getRoleClass() { + return roleClass; + } + + /** + * @return + */ + public BoundClass getBaseClass() { + return baseClass; + } + + /** + * @return + */ + public String getRoleClassName() { + return roleClass.getName(); + } + + /** + * @return + */ + public String getBaseClassName() { + return baseClass.getName(); + } + + /** + * Collect all base method signatures for this role-base pair. + * @result List <String[] {name, signature}> + */ + public List<String[]> getBaseSignatures () { + List<String[]> result = new LinkedList<String[]>(); + List<MethodBinding> baseMethodBindingList = getBaseMethodBindings(); + Iterator<MethodBinding> it = baseMethodBindingList.iterator(); + while (it.hasNext()) { + MethodBinding mb = it.next(); + result.add(new String [] { + mb.getBaseMethodName(), + mb.getBaseMethodSignature() + }); + } + return result; + } + + /** + * @param rbb + * @return + */ + public boolean equals(RoleBaseBinding rbb) { + return roleClass.getName().equals(rbb.getRoleClassName()) + && baseClass.getName().equals(rbb.getBaseClassName()); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuilder out = new StringBuilder(64); + out.append(roleClass.getName()); + out.append(" <-> "); + out.append(baseClass.getName()); + out.append("\nmethod bindings:\n"); + List mbsList = getBaseMethodBindings(); + Iterator it = mbsList.iterator(); + while (it.hasNext()) { + MethodBinding mb = (MethodBinding)it.next(); + out.append("\n"); + out.append(mb.getBaseMethodName()); + out.append("."); + out.append(mb.getBaseMethodSignature()); + out.append(":"); + out.append(mb.toString()); + } + return out.toString(); + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/SuperMethodDescriptor.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/SuperMethodDescriptor.java new file mode 100644 index 000000000..02b9c04de --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/SuperMethodDescriptor.java @@ -0,0 +1,33 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2008 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: SuperMethodDescriptor.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Technical University Berlin - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; + +/** Representation of a base-super method call, requiring a special access method. */ +public class SuperMethodDescriptor { + public String methodName; + public String declaringClass; + public String superClass; + public String signature; + public SuperMethodDescriptor(String methodName, String declaringClass, + String superClass, String signature) { + super(); + this.methodName = methodName; + this.declaringClass = declaringClass; + this.superClass = superClass; + this.signature = signature; + } +} diff --git a/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/TeamIdDispenser.java b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/TeamIdDispenser.java new file mode 100644 index 000000000..100a0a1da --- /dev/null +++ b/othersrc/OTRE/src/org/eclipse/objectteams/otre/util/TeamIdDispenser.java @@ -0,0 +1,69 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2009 Berlin Institute of Technology, 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: TeamIdDispenser.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otre.util; +import java.util.*; + +public class TeamIdDispenser { + + private static Map<ClassLoader, TeamIdDispenser> instances = new HashMap<ClassLoader, TeamIdDispenser>(); + private static TeamIdDispenser defaultInstance = new TeamIdDispenser(); + + static int lastDispensedId = 0; + private static HashMap<String, Integer> teamIDs = new HashMap<String, Integer>(); + +// @SuppressWarnings("unchecked") + private static int produceNextTeamId(String team_name) { + lastDispensedId++; + Integer teamId = Integer.valueOf(lastDispensedId); + teamIDs.put(team_name, teamId); + return lastDispensedId; + } + + public static int getTeamId(String class_name) { + Integer teamId = teamIDs.get(class_name); + if (teamId != null) + // the team <class_name> already has a team-id assigned + return teamId.intValue(); + else return produceNextTeamId(class_name); + } + + // Data shared among different transformers of the same class loader: + // REFACTOR: move the following to a better place: + private ArrayList<String> clinitAddedClasses = new ArrayList<String>(); + public static boolean clinitAdded(String class_name, ClassLoader loader) { + TeamIdDispenser instance = getInstanceForLoader(loader); + if (instance.clinitAddedClasses.contains(class_name)) + return true; + + instance.clinitAddedClasses.add(class_name); + return false; + } + + /** + * Since actual data are stored in an instance, static methods need to retrieve the appropriate + * instance regarding the given class loader. + */ + private static TeamIdDispenser getInstanceForLoader(ClassLoader loader) { + if (loader == null) + return defaultInstance; + + TeamIdDispenser instance = instances.get(loader); + if (instance == null) + instances.put(loader, instance = new TeamIdDispenser()); + return instance; + } +} diff --git a/othersrc/OTRE/src/org/objectteams/DoublyWeakHashMap.java b/othersrc/OTRE/src/org/objectteams/DoublyWeakHashMap.java new file mode 100644 index 000000000..4b72f1e1c --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/DoublyWeakHashMap.java @@ -0,0 +1,96 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2010 Stephan Herrmann. + * + * 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$ + * + * Please visit http://www.eclipse.org/objectteams for updates and contact. + * + * Contributors: + * Stephan Herrmann - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * This class defines hash maps where both key and value are weak references. + * It is implemented by delegating to a WeakHashMap and additionally + * wrapping the value in a WeakReference. + * + * @author stephan + * @since 0.7.0 + * @param <K> + * @param <V> + */ +public class DoublyWeakHashMap<K,V> implements Map<K,V> { + + private WeakHashMap<K, WeakReference<V>> map; + + public DoublyWeakHashMap() { + this.map = new WeakHashMap<K, WeakReference<V>>(); + } + public int size() { + return this.map.size(); + } + + public boolean isEmpty() { + return this.map.isEmpty(); + } + + public boolean containsKey(Object key) { + return this.map.containsKey(key); + } + + public boolean containsValue(Object value) { + return this.map.containsValue(value); + } + + public V get(Object key) { + return this.map.get(key).get(); + } + + public V put(K key, V value) { + this.map.put(key, new WeakReference<V>(value)); + return value; + } + + public V remove(Object key) { + WeakReference<V> value = this.map.remove(key); + return (value == null) ? null : value.get(); + } + + public void putAll(Map<? extends K, ? extends V> t) { + for (Entry<? extends K, ? extends V> entry : t.entrySet()) + this.map.put(entry.getKey(), new WeakReference<V>(entry.getValue())); + } + + public void clear() { + this.map.clear(); + } + + public Set<K> keySet() { + return this.map.keySet(); + } + + public Collection<V> values() { + ArrayList<V> result = new ArrayList<V>(this.map.size()); + for (WeakReference<V> valRef : this.map.values()) + result.add(valRef.get()); + return result; + } + + public Set<java.util.Map.Entry<K, V>> entrySet() { + throw new UnsupportedFeatureException("Method entrySet is not implemented for DoublyWeakHashMap"); + } +} diff --git a/othersrc/OTRE/src/org/objectteams/DuplicateRoleException.java b/othersrc/OTRE/src/org/objectteams/DuplicateRoleException.java new file mode 100644 index 000000000..ae3e851a9 --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/DuplicateRoleException.java @@ -0,0 +1,48 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2004-2009 Berlin Institute of Technology, 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: DuplicateRoleException.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * Signal a violation of OTJLD 2.4.1(c). + * Also Team.getRole(Object) may throw a DuplicateRoleException if + * more than one role is found for the given base object + * (in that case those roles are found in different role-caches). + * + * + * @author stephan + * @version $Id: DuplicateRoleException.java 23408 2010-02-03 18:07:35Z stephan $ + */ +public class DuplicateRoleException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * @param message + */ + public DuplicateRoleException(String roleClassName) { + super("Failed to create a role instance of type "+roleClassName+"\n"+ + "A role for the given base object already exists (OTJLD 2.4.1(c))."); + } + + public DuplicateRoleException(String roleName1, String roleName2) { + super("Ambiguous role instances: found a role in hierarchies "+ + roleName1+" and "+roleName2); + } +} diff --git a/othersrc/OTRE/src/org/objectteams/IBaseMigratable.java b/othersrc/OTRE/src/org/objectteams/IBaseMigratable.java new file mode 100644 index 000000000..edc9202ba --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/IBaseMigratable.java @@ -0,0 +1,35 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2008 Berlin Institute of Technology, 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: IBaseMigratable.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * Marker interface: if a role declares to implement this interface + * the compiler will generate the method defined herein, and prepare + * the role so that the migration will indeed be possible. + * + * @author stephan + * @since 1.2.5 + */ +public interface IBaseMigratable { + /** + * Migrate the current role to the otherBase. + * + * @param otherBase new base that this role should adapt, must + * be of a valid base type for the current role. + */ + <B> void migrateToBase(B otherBase); +} diff --git a/othersrc/OTRE/src/org/objectteams/IBoundBase.java b/othersrc/OTRE/src/org/objectteams/IBoundBase.java new file mode 100644 index 000000000..9f196bf92 --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/IBoundBase.java @@ -0,0 +1,28 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2007-2009 Berlin Institute of Technology, 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: IBoundBase.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * Super type for all bound base classes. Purely internal class, not intended for client use. + * @author Stephan Herrmann + */ +public interface IBoundBase { + /** Method to be used by generated code, only (lifting constructor). */ + void _OT$addRole(Object aRole); + /** Method to be used by generated code, only (unregisterRole()). */ + void _OT$removeRole(Object aRole); +}
\ No newline at end of file diff --git a/othersrc/OTRE/src/org/objectteams/IConfined.java b/othersrc/OTRE/src/org/objectteams/IConfined.java new file mode 100644 index 000000000..8ead4b473 --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/IConfined.java @@ -0,0 +1,24 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2004 Berlin Institute of Technology, 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: IConfined.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * Special interface that does not extend Object + * + * @author stephan + */ +public interface IConfined {}
\ No newline at end of file diff --git a/othersrc/OTRE/src/org/objectteams/ILiftingParticipant.java b/othersrc/OTRE/src/org/objectteams/ILiftingParticipant.java new file mode 100644 index 000000000..408d0873e --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/ILiftingParticipant.java @@ -0,0 +1,40 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2009 Stephan Herrmann + * + * 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: ILiftingParticipant.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Stephan Herrmann - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * A lifting participant hooks into the lifting process. + * + * @author stephan + * @since 1.3.1 + */ +public interface ILiftingParticipant { + /** + * This method is called when lifting does not find a suitable role within the + * team's internal role cache. If this method returns a non-null value, + * this value is considered by the runtime as being the desired role + * (i.e., it must be castable to that role type), and no new role is created. + * If this method returns null, lifting proceeds as normal, i.e., + * a fresh role is created using the default lifting constructor. + * + * @param teamInstance + * @param baseInstance + * @param roleClassName + * @return either null or an instance of the class specified by roleClassName + */ + Object createRole(ITeam teamInstance, Object baseInstance, String roleClassName); +} diff --git a/othersrc/OTRE/src/org/objectteams/ITeam.java b/othersrc/OTRE/src/org/objectteams/ITeam.java new file mode 100644 index 000000000..84b1cd0c2 --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/ITeam.java @@ -0,0 +1,204 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2010 Stephan Herrmann. + * + * 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: ITeam.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Stephan Herrmann - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * Public interface of all team classes. + */ +public interface ITeam { + + /** + * Interface for all role classes that should allow explicit lowering. + * The interface provides a phantom method <pre><B> B lower()</pre> + * where B is the bound base class of the implementing role class. + * There is no need to implement the method lower, since this is done by the compiler. + */ + public interface ILowerable { + // internal method needed for cast and instanceof + ITeam _OT$getTeam(); + } + + /** + * This role interface has no properties not even those of java.lang.Object. + */ + public interface IConfined extends org.objectteams.IConfined { + // internal method needed for cast and instanceof + ITeam _OT$getTeam(); + } + + + /** + * Activates the team and therefore all of its callin bindings. + * This activation applies to the current thread only. + */ + public abstract void activate(); + + /** + * Deactivates the team and therefore all of its callin bindings. + * This deactivation applies to the current thread only. + */ + public abstract void deactivate(); + + /** + * Activates the team and therefore all of its callin bindings for passed thread. + * If the constant 'Team.ALL_THREADS' is passed, this activation globally applies to all threads. + */ + public abstract void activate(Thread thread); + + /** + * Deactivates the team and therefore all of its callin bindings for passed thread. + * If the constant 'Team.ALL_THREADS' is passed, this deactivation globally applies to all threads. + */ + public abstract void deactivate(Thread thread); + + /** + * Checks, if the team instance is active for the current thread. + * @return true, if the team is active, false else. + */ + public abstract boolean isActive(); + + /** + * Checks, if the team instance is active for the 'thread'. + * @param thread The thread for which to check activity. + * @return true, if the team is active for 'thread', false else. + */ + public abstract boolean isActive(Thread thread); + + /** + * Does given base object have a role in this team? + * This method will consider roles of any type. + * + * @param aBase any object, i.e., no checks are performed whether the base object's + * class is bound by any role class in this team. + * @return + */ + public abstract boolean hasRole(Object aBase); + + /** + * Does given base object have a role in this team? + * The role must be an instance of the specified role type. + * + * @param aBase any object, i.e., no checks are performed whether the base object's + * class is bound by any role class in this team. + * @param roleClass Class instance specifying the required role type. + * If this does not specify an existing role class an IllegalArgumentException will be thrown. + * TODO (SH): is it legal to pass an unbound role class? + * @return + */ + public abstract boolean hasRole(Object aBase, Class<?> roleType); + + /** + * Retrieve a role for a given base object. + * If more than one role exists, a DuplicateRoleException is thrown. + * + * @param aBase + * @return + */ + public abstract Object getRole(Object aBase); + + /** + * Retrieve a role for a given base object. + * The role must be an instance of the specified role type. + * + * @param aBase any object, i.e., no checks are performed whether the base object's + * class is bound by any role class in this team. + * @param roleClass Class instance specifying the required role type. + * If this does not specify an existing role class an IllegalArgumentException will be thrown. + * @return + */ + public abstract <T> T getRole(Object aBase, Class<T> roleType); + + /** + * Retrieve all bound roles registered in the current team. + * + * This method uses internal structures of weak references. + * For that reason it may return role instances which were about to be reclaimed + * by the garbage collector. + * If performance permits, it is thus advisable to always call System.gc() + * prior to calling getAllRoles() in order to achieve deterministic results + * + * @return a non-null array. + */ + public abstract Object[] getAllRoles(); + + /** + * Retrieve all bound roles registered in the current team that + * are instance of roleType or a subtype thereof. + * + * This method uses internal structures of weak references. + * For that reason it may return role instances which were about to be reclaimed + * by the garbage collector. + * If performance permits, it is thus advisable to always call System.gc() + * prior to calling getAllRoles() in order to achieve deterministic results + * + * @param roleType must be a top-most bound role of this team. + * @return a non-null array. + */ + public abstract <T> T[] getAllRoles(Class<T> roleType); + + /** + * Query whether any role instance of this team instance is currently executing a + * method due to a callin binding. + * @return + */ + public abstract boolean isExecutingCallin(); + + /** + * Remove a role from the internal registry, which means that the role will no longer be + * considered during lifting. + * + * @param aRole + */ + public abstract void unregisterRole(Object aRole); + + /** + * Remove a role from the internal registry, which means that the role will no longer be + * considered during lifting. + * + * @param aRole + * @param roleType + */ + public abstract void unregisterRole(Object aRole, Class<?> roleType); + + /** + * Not API. + * This method saves the activation state of the team for the current thread. + * If active, it also saves, if the activation was explicit or implicit. + * This method has to be called by the generated code when entering a within block, + * before the activation. + */ + public int _OT$saveActivationState(); + + /** + * Not API. + * This method restores the former saved activation state of the team for the current thread. + * If active, it also restores, if the activation was explicit or implicit. + * This method has to be called by the generated code when leaving a within block + * (in the finally block). + */ + public void _OT$restoreActivationState(int old_state); + + /** + * Not API, for use by TeamThreadManager, only. + */ + public boolean internalIsActiveSpecificallyFor(Thread t); + + /** + * Not API, for use by TeamThreadManager, only. + */ + public void deactivateForEndedThread(Thread thread); +}
\ No newline at end of file diff --git a/othersrc/OTRE/src/org/objectteams/ITeamMigratable.java b/othersrc/OTRE/src/org/objectteams/ITeamMigratable.java new file mode 100644 index 000000000..39be5661f --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/ITeamMigratable.java @@ -0,0 +1,36 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2008 Berlin Institute of Technology, 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: ITeamMigratable.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * Marker interface: if a role declares to implement this interface + * the compiler will generate the method defined herein, and prepare + * the role so that the migration will indeed be possible. + * Note, that a migratable role does not obey the family guarantee. + * + * @author stephan + * @since 1.2.5 + */ +public interface ITeamMigratable { + /** + * Migrate the current role to the otherTeam. + * + * @param otherTeam new team that should adopt this role + * @return the migrated (and re-typed) role (actually of type R<@otherTeam>). FIXME(SH) + */ + <R> R migrateToTeam(final ITeam otherTeam); +} diff --git a/othersrc/OTRE/src/org/objectteams/IllegalRoleCreationException.java b/othersrc/OTRE/src/org/objectteams/IllegalRoleCreationException.java new file mode 100644 index 000000000..f745d739b --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/IllegalRoleCreationException.java @@ -0,0 +1,35 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2007-2009 Berlin Institute of Technology, 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: IllegalRoleCreationException.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * Exception to be thrown when a bound role is being instantiated but + * the constructor does not assign a base object. + * + * @author stephan + */ +@SuppressWarnings("serial") +public class IllegalRoleCreationException extends RuntimeException { + public IllegalRoleCreationException() { + super(); + } + + @Override + public String getMessage() { + return "Cannot instantiate a bound role using a default constructor of its tsuper class"; + } +} diff --git a/othersrc/OTRE/src/org/objectteams/ImplicitTeamActivation.java b/othersrc/OTRE/src/org/objectteams/ImplicitTeamActivation.java new file mode 100644 index 000000000..09eda9679 --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/ImplicitTeamActivation.java @@ -0,0 +1,45 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2009 Stephan Herrmann + * + * 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$ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Stephan Herrmann - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This marker annotation enables implicit team activation for the annotated element: + * <ul> + * <li>If attached to a method the effect is that each call to this method implicitly + * activates the enclosing team.</li> + * <li>If attached to a class it has the same effect as annotating all contained methods.</li> + * </ul> + * See <a href="http://www.objectteams.org/def/1.3/s5.html#s5.3">OTJLD ยง 5.3</a>. + * <p> + * This annotation is only evaluated if the property <code>ot.implicit.team.activation</code> + * is set to the string <code>ANNOTATED</code>. + * </p> + * @author stephan + * @since 1.4.0 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface ImplicitTeamActivation { + /* no members, pure marker annotation. */ +} diff --git a/othersrc/OTRE/src/org/objectteams/LiftingFailedException.java b/othersrc/OTRE/src/org/objectteams/LiftingFailedException.java new file mode 100644 index 000000000..99f2dd279 --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/LiftingFailedException.java @@ -0,0 +1,46 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2003-2009 Berlin Institute of Technology, 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: LiftingFailedException.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * This exception signals that lifting failed due to unresolved + * binding ambiguity. + */ +public class LiftingFailedException extends RuntimeException { + /** + * + */ + private static final long serialVersionUID = 1L; + private Object base; + private String roleType; + + /** + * @param base the object that should be lifted + * @param roleType the name of the role type for which + * lifting was attempted. + */ + public LiftingFailedException(Object base, String roleType) { + this.base = base; + this.roleType = roleType; + } + + public String getMessage() { + return "\nFailed to lift '" + base + "' of " + base.getClass() + + " to type '" + roleType + + "'\n(See OT/J definition para. 2.3.4(c))."; + } +} diff --git a/othersrc/OTRE/src/org/objectteams/LiftingVetoException.java b/othersrc/OTRE/src/org/objectteams/LiftingVetoException.java new file mode 100644 index 000000000..3a9b83aea --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/LiftingVetoException.java @@ -0,0 +1,45 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2003-2009 Berlin Institute of Technology, 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: LiftingVetoException.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * This exception is used by the language implementation + * to signal a failed lifting due to a guard predicate that evaluated to false. + * @author Stephan Herrmann + */ +public class LiftingVetoException extends RuntimeException { + /** + * + */ + private static final long serialVersionUID = 1L; + ITeam aTeam = null; + Object base = null; + + public LiftingVetoException(ITeam aTeam, Object base) { + this.aTeam = aTeam; + this.base = base; + } + + public LiftingVetoException() { + super(""); + } + + public String toString() { + return "Team " + aTeam + " refuses to lift " + base + + "\n(this exception should not be seen in applications)."; + } +} diff --git a/othersrc/OTRE/src/org/objectteams/ResultNotProvidedException.java b/othersrc/OTRE/src/org/objectteams/ResultNotProvidedException.java new file mode 100644 index 000000000..b6baccbc5 --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/ResultNotProvidedException.java @@ -0,0 +1,61 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2004-2009 Berlin Institute of Technology, 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: ResultNotProvidedException.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * @author resix + */ +public class ResultNotProvidedException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 1L; + private static final String _bugmsg = + "\nNo base call executed! Result value was uninitialized!\n(see OT/J language definition para. 4.3(e))."; + + /** + * + */ + public ResultNotProvidedException() { + super(_bugmsg); + } + + /** + * @param message + */ + public ResultNotProvidedException(String message) { + super(_bugmsg + "\n" + message); + StackTraceElement[] ste = new StackTraceElement[0]; + setStackTrace(ste); + } + + /** + * @param cause + */ + public ResultNotProvidedException(Throwable cause) { + super(_bugmsg + cause.toString()); + } + + /** + * @param message + * @param cause + */ + public ResultNotProvidedException(String message, Throwable cause) { + super(_bugmsg + message/* +cause.toString() */); + } +} diff --git a/othersrc/OTRE/src/org/objectteams/RoleCastException.java b/othersrc/OTRE/src/org/objectteams/RoleCastException.java new file mode 100644 index 000000000..f6608c944 --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/RoleCastException.java @@ -0,0 +1,39 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2004-2009 Berlin Institute of Technology, 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: RoleCastException.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * This exception is thrown if a cast to a role class fails due to + * different enclosing team instances. + * @author Stephan Herrmann + */ +public class RoleCastException extends ClassCastException { + + /** + * + */ + private static final long serialVersionUID = 1L; + private static final String MSG = + "Different enclosing team instances (see OT/J language definition para. 1.2.4(b))."; + + /* (non-Javadoc) + * @see java.lang.Throwable#getMessage() + */ + public String getMessage() { + return MSG; + } +}
\ No newline at end of file diff --git a/othersrc/OTRE/src/org/objectteams/Team.java b/othersrc/OTRE/src/org/objectteams/Team.java new file mode 100644 index 000000000..c6c5f3309 --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/Team.java @@ -0,0 +1,490 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2002-2007 Berlin Institute of Technology, 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: Team.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +import java.awt.EventQueue; +import java.util.HashSet; +import java.util.Iterator; +import java.util.WeakHashMap; + +/** + * This is the root class of all team definitions. + * Any class with the <tt>team</tt> modifier implicitly + * inherits from this class. + * + */ +public /* team */ class Team implements ITeam { + // Technical note: comments starting with //$Debug are intended + // for a standalone tool that generates an interface + // which the debugger needs in order to know about line numbers in this class. + + /* + * Synchronization: This class supports two levels of synchronization: + * <ul> + * <li>Fields of <code>Team</code> are synchronized + * using <code>this</code> as the monitor. + * <li>Those calls that (un)register a team at its base classes are synchronized + * via <code>registrationLock</code> + * </ul> + * This allows releasing the lock on <code>this</code> before calling to the base class. + * Note, that the synchronized portion of each initial-wrapper is also synchronized + * (against <code>_OT$addTeam/_OT$removeTeam</code>) and that it calls back to + * the registered teams (method <code>isActive()</code>). + * Without <code>registrationLock</code> this situation could easily deadlock: + * Thread1: <pre>t.activate() -> Base._OT$addTeam()</pre>: owns t, waits for Base. + * Thread2: <pre>b.bm() (initial wrapper) -> t.isActive()</pre>: owns Base, waits for t. + */ + + /** + * Internal field used by the runtime to install a lifting participant if one is configured. + */ + public static ILiftingParticipant _OT$liftingParticipant = null; + + /** + * Default constructor for debugging purpose. + */ + public Team() {} //$Debug(TeamConstructor) + + /* + * manual copy-inheritance of a role interface from ITeam. + */ + public interface ILowerable extends ITeam.ILowerable {} + + /* + * manual copy-inheritance of a role interface from ITeam. + */ + public interface IConfined extends ITeam.IConfined {} + + /** + * Special role type that<ul> + * <li> does not extend java.lang.Object + * <li> may not leak references outside the team. + * </ul> + */ + protected interface Confined { + /* internal method needed for cast and instanceof + * (this method will be generated for role classes) */ + ITeam _OT$getTeam(); + } + + /** + * This class would have been generated by the OT-compiler. + * Don't explicitly use it in client code! + */ + protected class __OT__Confined implements Confined { + // internal method needed for cast and instanceof + public ITeam _OT$getTeam() { + return Team.this; //$Debug(ConfinedGetTeam) + } + } + + /** + * Internal function for identifying a Team. + * Should not be called by client code. + */ + public int _OT$getID () {return -1;} + + /** + * The constant <code>ALL_THREADS</code> is used for global team (de-)activation. + */ + public static final Thread ALL_THREADS = new Thread(); + + private static final int _OT$UNREGISTERED = 0; + private static final int _OT$REGISTERED = 1; + private int _OT$registrationState = _OT$UNREGISTERED; + + private boolean _OT$globalActive = false; + + private ThreadLocal<Integer> _OT$implicitActivationsPerThread = new ThreadLocal<Integer>() { + protected synchronized Integer initialValue() { + return Integer.valueOf(0); + } + }; + + private boolean _OT$lazyGlobalActiveFlag = false; + + /** + * <code>_OT$activatedThreads</code> contains all threads for which this team instance is active. + * key = activated thread + * value = Boolean(true) for explicit activation | Boolean(false) for implicit activation. + */ + private WeakHashMap<Thread, Boolean> _OT$activatedThreads = new WeakHashMap<Thread, Boolean>(); + + /** This lock is used to protect activate/deactivate methods <strong>including</strong> + * the calls to doRegistration/doUnregistration. + */ + private Object _OT$registrationLock= new Object(); + + /** + * {@inheritDoc} + */ + public void activate() { + activate(Thread.currentThread()); + } + + /** + * {@inheritDoc} + */ + public void deactivate() { + deactivate(Thread.currentThread()); + } + + /** + * {@inheritDoc} + */ + public void activate(Thread thread) { + // acquire both locks to avoid incomplete execution: + synchronized (this._OT$registrationLock) { + synchronized (this) { + if (thread.equals(ALL_THREADS)) { + _OT$globalActive = true; + _OT$lazyGlobalActiveFlag = true; + TeamThreadManager.addGlobalActiveTeam(this); + } else { // activation only for 'thread': + // register 'thread' as active: + _OT$activatedThreads.put(thread, Boolean.TRUE); + } + } // release this before calling synchronized base class methods + doRegistration(); //$Debug(ActivateMethod) + } + } + + /** + * {@inheritDoc} + */ + public void deactivate(Thread thread) { + // acquire both locks to avoid incomplete execution: + synchronized (this._OT$registrationLock) { + boolean shouldUnregister= false; + synchronized(this) { + if (_OT$globalActive && EventQueue.isDispatchThread()) { + System.err.println("Warning: Deactivation for the AWT-Event-Thread is not effective right now!"); + } + if (thread.equals(ALL_THREADS)) { + _OT$globalActive = false; + TeamThreadManager.removeGlobalActiveTeam(this); + // unregister all threads: + _OT$activatedThreads.clear(); + shouldUnregister= true; + } else { // deactivation only for 'thread': + if (_OT$lazyGlobalActiveFlag) { + // be eager now: activate for all (other) threads: + _OT$activateForAllThreads(); + } + // deactivate for 'thread', no longer active: + _OT$activatedThreads.remove(thread); + if (!_OT$lazyGlobalActiveFlag && _OT$activatedThreads.isEmpty()) { + shouldUnregister= true; + } + } + _OT$lazyGlobalActiveFlag = false; + } // release this before calling synchronized base class methods + if (shouldUnregister) //$Debug(DeactivateMethod) + doUnregistration(); + } + } + + public void deactivateForEndedThread(Thread thread) { + synchronized (_OT$registrationLock) { + boolean shouldUnregister= false; + synchronized (this) { + _OT$activatedThreads.remove(thread); + if (!_OT$lazyGlobalActiveFlag && _OT$activatedThreads.isEmpty()) + shouldUnregister= true; + } + if (shouldUnregister) + doUnregistration(); + } + } + + private void _OT$activateForAllThreads() { + HashSet threads = TeamThreadManager.getExistingThreads(); + Iterator it = threads.iterator(); + while (it.hasNext()) { + Thread a_thread = (Thread) it.next(); + activate(a_thread); // use smaller activate version (no ALL_THREADS, no registerAtBases,... + } + } + + /** + * This method is used for implicit activation in team-level methods. + * Implicit activation only applies to the current thread. + * Don't call it from client code. + */ + public void _OT$implicitlyActivate() { + synchronized (this._OT$registrationLock) { + boolean shouldRegister= false; + synchronized (this) { + // this method is used for debugging purpose (team monitor) + Thread currentThread = Thread.currentThread(); + if (!_OT$activatedThreads.containsKey(currentThread)) { + // register 'thread' as active: + _OT$activatedThreads.put(currentThread, Boolean.FALSE); + shouldRegister= true; + } + // increment thread local implicit activaion counter: + int implActCount = (_OT$implicitActivationsPerThread.get()).intValue(); + _OT$implicitActivationsPerThread.set(Integer.valueOf(implActCount + 1 )); + } + if (shouldRegister) //$Debug(ImplicitActivateMethod) + doRegistration(); + } + } + + /** + * This method is used for implicitly deactivation in team-level methods. + * It respects explicit activation changes and nested calls to team-level methods. + * Implicit deactivation only applies to the current thread. + * Don't call it from client code. + */ + public void _OT$implicitlyDeactivate() { + synchronized (this._OT$registrationLock) { + boolean shouldUnregister= false; + synchronized(this) { + // this method is used for debugging purpose (team monitor) + Thread currentThread = Thread.currentThread(); + boolean explicitlyActivated = false; + if (_OT$activatedThreads.containsKey(currentThread)) { + explicitlyActivated = ((Boolean) _OT$activatedThreads.get(currentThread)).booleanValue(); + } + if (!explicitlyActivated + && !_OT$lazyGlobalActiveFlag // no explicit activation overriding the implicit one + && ((_OT$implicitActivationsPerThread.get()).intValue() == 1)) // this is the last implicit activation + { + _OT$activatedThreads.remove(currentThread); + if (_OT$activatedThreads.isEmpty()) // there are not other threads for which this theam is active + { + shouldUnregister= true; + } + } + // decrement thread local implicit activaion counter: + int implActCount = (_OT$implicitActivationsPerThread.get()).intValue(); + _OT$implicitActivationsPerThread.set(Integer.valueOf(implActCount - 1)); + } + if (shouldUnregister) //$Debug(ImplicitDeactivateMethod) + doUnregistration(); + } + } + + /** + * Define whether per-thread activation of this team should be inheritable + * such that the team will be activated automatically for any new threads + * that are spawned from a thread for which the team is already active at that time. + * + * @param inheritable whether or not activation should be inheritable to new threads + */ + public void setInheritableActivation(boolean inheritable) { + if (inheritable) + TeamThreadManager.registerTeamForActivationInheritance(this); + else + TeamThreadManager.unRegisterTeamForActivationInheritance(this); + } + + // not API (for use by the TeamThreadManager) + public boolean internalIsActiveSpecificallyFor(Thread t) { + return this._OT$activatedThreads.containsKey(t); + } + + /** + * {@inheritDoc} + */ + public boolean isActive() { + return isActive(Thread.currentThread()); + } + + /** + * {@inheritDoc} + */ + public boolean isActive(Thread thread) { + if (_OT$globalActive && EventQueue.isDispatchThread()) + return true; + if (thread.equals(ALL_THREADS)) { + return _OT$globalActive; + } + if (_OT$lazyGlobalActiveFlag) { + return true; + } else { + //if (!TeamThreadManager.getExistingThreads().contains(thread)) { // this thread is already finished! + if (!thread.isAlive()) { // this thread is already finished! + throw new IllegalThreadStateException("Called 'isActive(...)' for a thread which is no longer running!"); + } + return _OT$activatedThreads.containsKey(thread); + } + } + +// ***** for restoring the activation state after a within block: ---->***** + private static final int _OT$INACTIVE = 0; + private static final int _OT$IMPLICIT_ACTIVE = 1; + private static final int _OT$EXPLICIT_ACTIVE = 2; + + /** + * {@inheritDoc} + */ + public synchronized int _OT$saveActivationState() { + int old_state = _OT$INACTIVE; + if (_OT$lazyGlobalActiveFlag) { + old_state = _OT$EXPLICIT_ACTIVE; + } else { + Thread current_thread = Thread.currentThread(); + if (_OT$activatedThreads.containsKey(current_thread)) { + old_state = _OT$IMPLICIT_ACTIVE; + if (((Boolean)_OT$activatedThreads.get(current_thread)).booleanValue()) { + old_state = _OT$EXPLICIT_ACTIVE; + } + } + } + return old_state; + } + + /** + * {@inheritDoc} + */ + public void _OT$restoreActivationState(int old_state) { + synchronized (_OT$registrationLock) { + if (old_state == _OT$INACTIVE) // team was inactive before: + deactivate(); + else { // team was active before: has to be reactivated: + boolean explicit = (old_state == _OT$EXPLICIT_ACTIVE); + synchronized (this) { + _OT$activatedThreads.put(Thread.currentThread(), Boolean.valueOf(explicit)); + } + doRegistration(); + } + } + } +// ***** <----for restoring the activation state after a within block. ***** + + + private void doRegistration() { + if (_OT$registrationState == _OT$UNREGISTERED) { + _OT$registerAtBases(); + _OT$registrationState = _OT$REGISTERED; + } + } + + private void doUnregistration() { + if (_OT$registrationState == _OT$REGISTERED) { + _OT$unregisterFromBases(); + _OT$registrationState = _OT$UNREGISTERED; + } + } + + /** + * This method will be implemented by generated code in subteams. + * It registers the team at every base playing one of its roles. + * Don't call it from client code. + */ + public void _OT$registerAtBases() {} + + /** + * This method will be implemented by generated code in subteams. + * It unregisters the team from every base playing one of its roles. + * Don't call it from client code. + */ + public void _OT$unregisterFromBases() {} + + //public int _OT$activationState = -1; // TODO: remove usage of this from generated code + + + /** + * {@inheritDoc} + */ + public boolean hasRole(Object aBase) { + // overriding method to be generated by the compiler for each team with bound roles. + return false; + } + + /** + * {@inheritDoc} + */ + public boolean hasRole(Object aBase, Class<?> roleType) { + // overriding method to be generated by the compiler for each team with bound roles. + throw new IllegalArgumentException("No such bound role type in this team: "+roleType.getName()); + } + + /** + * {@inheritDoc} + */ + public Object getRole(Object aBase) { + // overriding method to be generated by the compiler for each team with bound roles. + return null; + } + + /** + * {@inheritDoc} + */ + public <T> T getRole(Object aBase, Class<T> roleType) { + // overriding method to be generated by the compiler for each team with bound roles. + return null; + } + + /** + * {@inheritDoc} + */ + public Object[] getAllRoles() { + // overriding method to be generated by the compiler for each team with bound roles. + return new Object[0]; + } + + /** + * {@inheritDoc} + */ + public <T> T[] getAllRoles(Class<T> roleType) { + // overriding method to be generated by the compiler for each team with bound roles. + throw new IllegalArgumentException("Class org.objectteams.Team has no bound roles."); + } + + /** Internal variable to be set from generated code. */ + private boolean _OT$isExecutingCallin = false; + + /** + * Method only for internal use by generated code. + */ + public boolean _OT$setExecutingCallin(boolean newFlag) { + boolean oldVal = _OT$isExecutingCallin; + _OT$isExecutingCallin = newFlag; + return oldVal; + } + + /** + * {@inheritDoc} + */ + public boolean isExecutingCallin() { + return _OT$isExecutingCallin; + } + + /** + * {@inheritDoc} + */ + public void unregisterRole(Object aRole) { + // overriding method to be generated by the compiler for each team with bound roles. + } + + /** + * {@inheritDoc} + */ + public void unregisterRole(Object aRole, Class<?> roleType) { + // overriding method to be generated by the compiler for each team with bound roles. + } + + @Override + protected void finalize() throws Throwable { + // nop, hook for the debugger + @SuppressWarnings("unused") + int i= 2+3; // Note: body must not be empty for debuggger to be able to stop. + } // $Debug(FinalizeMethod) +} diff --git a/othersrc/OTRE/src/org/objectteams/TeamThreadManager.java b/othersrc/OTRE/src/org/objectteams/TeamThreadManager.java new file mode 100644 index 000000000..ce1b174a6 --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/TeamThreadManager.java @@ -0,0 +1,97 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2006-2008 Berlin Institute of Technology, 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: TeamThreadManager.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +import java.util.HashSet; +import java.util.WeakHashMap; + + +/** + * This class is for internal use, only. + * + * Maintain information about existing threads as to manage + * team activation per thread vs. globally. + * + * @author Chistine Hundt + * @author Stephan Herrmann + */ +public class TeamThreadManager { + + private static Object token = new Object(); + + private static HashSet<ITeam> globalActiveTeams = new HashSet<ITeam>(); + private static WeakHashMap<ITeam,Object> teamsWithActivationInheritance = new WeakHashMap<ITeam,Object>(); + private static HashSet<Thread> existingThreads = new HashSet<Thread>(); + + public static boolean newThreadStarted(boolean isMain, Thread parent) { + if (!isMain && (new Exception().getStackTrace().length > 3)) + return false; + // workaround for application hang on Mac OS with Apple JVM: + Thread currentThread = Thread.currentThread(); + if (System.getProperty("os.name").startsWith("Mac")) + if (currentThread.getName().equals("AWT-Shutdown")) + return false; + + ITeam[] globalTeams; + ITeam[] inheritableTeams; + synchronized (TeamThreadManager.class) { + existingThreads.add(currentThread); + + globalTeams = globalActiveTeams.toArray(new ITeam[globalActiveTeams.size()]); + inheritableTeams = teamsWithActivationInheritance.keySet().toArray(new ITeam[teamsWithActivationInheritance.size()]); + } + // activate teams outside synchronized block: + for (ITeam t : globalTeams) + t.activate(currentThread); // small version? global -> already registered...! + if (parent != null) + for (ITeam t : inheritableTeams) + if (t.internalIsActiveSpecificallyFor(parent)) + t.activate(currentThread); // pass activation from parent to child thread + return true; + } + public static void threadEnded() { + ITeam[] teamsToDeactivate = internalThreadEnded(); + // + remove per thread activation: + for (ITeam t : teamsToDeactivate) + //t.deactivate(Thread.currentThread()); // small version? + t.deactivateForEndedThread(Thread.currentThread()); + } + private synchronized static ITeam[] internalThreadEnded() { + existingThreads.remove(Thread.currentThread()); + // fetch all global active teams for deactivation: + return globalActiveTeams.toArray(new ITeam[globalActiveTeams.size()]); + } + + public synchronized static void addGlobalActiveTeam(ITeam t) { + globalActiveTeams.add(t); + } + + public synchronized static void removeGlobalActiveTeam(ITeam t) { + globalActiveTeams.remove(t); + } + + public static HashSet<Thread> getExistingThreads() { + return existingThreads; + } + public static void registerTeamForActivationInheritance(ITeam aTeam) { + teamsWithActivationInheritance.put(aTeam,token); + } + public static void unRegisterTeamForActivationInheritance(ITeam aTeam) { + teamsWithActivationInheritance.remove(aTeam); + } + +} diff --git a/othersrc/OTRE/src/org/objectteams/UnsupportedFeatureException.java b/othersrc/OTRE/src/org/objectteams/UnsupportedFeatureException.java new file mode 100644 index 000000000..9b1c1ffcf --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/UnsupportedFeatureException.java @@ -0,0 +1,60 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2004-2009 Berlin Institute of Technology, 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: UnsupportedFeatureException.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * @author resix + */ +public class UnsupportedFeatureException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 1L; + private static final String _bugmsg = "\nThe program encountered an unsupported situation! "; + + /** + * + */ + public UnsupportedFeatureException() { + super(_bugmsg); + } + + /** + * @param message + */ + public UnsupportedFeatureException(String message) { + super(_bugmsg + "\n" + message); + StackTraceElement[] ste = new StackTraceElement[0]; + setStackTrace(ste); + } + + /** + * @param cause + */ + public UnsupportedFeatureException(Throwable cause) { + super(_bugmsg + cause.toString()); + } + + /** + * @param message + * @param cause + */ + public UnsupportedFeatureException(String message, Throwable cause) { + super(_bugmsg + message/*+cause.toString()*/); + } +} diff --git a/othersrc/OTRE/src/org/objectteams/WrongRoleException.java b/othersrc/OTRE/src/org/objectteams/WrongRoleException.java new file mode 100644 index 000000000..da84d3322 --- /dev/null +++ b/othersrc/OTRE/src/org/objectteams/WrongRoleException.java @@ -0,0 +1,57 @@ +/********************************************************************** + * This file is part of the "Object Teams Runtime Environment" + * + * Copyright 2003-2009 Berlin Institute of Technology, 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: WrongRoleException.java 23408 2010-02-03 18:07:35Z stephan $ + * + * Please visit http://www.objectteams.org for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + **********************************************************************/ +package org.objectteams; + +/** + * This exception is thrown by the OT/J infra structure if a role for a given base object + * was requested during lifting, but a role with an incompatible type was already + * registered for that base object. Can only happen if a compile time warning occurred. + * @author Stephan Herrmann + */ +public class WrongRoleException extends RuntimeException { + /** + * + */ + private static final long serialVersionUID = 1L; + private Class clazz; + private Object base; + private Object role; + + /** + * @param clazz + * @param base + * @param role + */ + public WrongRoleException (Class clazz, Object base, Object role) { + this.clazz = clazz; + this.base = base; + this.role = role; + } + + public String getMessage() { + String baseClazz = base.getClass().getName(); + String roleClazz = role.getClass().getName(); + return "The compiler has warned you about ambiguous role bindings.\n" + + "Now lifting to " + clazz + + " fails with the following objects\n" + + "(see OT/J language definition para. 2.3.4(d)):\n" + + "Provided:\n Base object: " + base + "\n" + " Base type: " + + baseClazz + "\n" + + "Found in cache:\n Role object: " + role + "\n" + + " Role type: " + roleClazz; + } +} |